[
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Build\n\non: [push, pull_request]\n\nenv:\n  # CMake flags used for default builds -- can be overridden by matrix.flags\n  FLAGS: >\n    -DWITH_QT5=ON\n    -DWITH_AKONADI=ON\n    -DWITH_ALSA=ON\n    -DWITH_DIAMONDCARD=ON\n    -DWITH_GSM=ON\n    -DWITH_SPEEX=ON\n    -DWITH_ZRTP=OFF\n  # Essential packages required by all builds\n  PACKAGES_REQUIRED: >\n    bison\n    cmake\n    flex\n    libccrtp-dev\n    libmagic-dev\n    libreadline-dev\n    libsndfile1-dev\n    libucommon-dev\n    libxml2-dev\n    linux-libc-dev\n  # Additional packages required by default builds -- can be overridden by\n  # matrix.packages\n  #  (gettext is explitly added due to LP #1932371)\n  PACKAGES: >\n    gettext\n    libasound2-dev\n    libgsm1-dev\n    libkf5akonadi-dev\n    libkf5contacts-dev\n    libspeex-dev\n    libspeexdsp-dev\n    qtdeclarative5-dev\n    qttools5-dev\n\njobs:\n  build:\n    # Job name, including description and compiler if applicable\n    name: >\n      ${{ format('Build {0} {1}',\n        ((matrix.descr && format('({0})', matrix.descr)) || ''),\n        ((matrix.gcc && format('[GCC {0}]', matrix.gcc)) ||\n          (matrix.clang && format('[Clang {0}]', matrix.clang)) || '')\n      ) }}\n    # '  # Patch around Vim syntax bug\n\n    runs-on: ${{ matrix.os || 'ubuntu-latest' }}\n\n    strategy:\n      matrix:\n        include:\n          # Test building with GCC and Clang (current default version)\n          - gcc: default\n          - clang: default\n\n          # Test some other GCC/Clang versions, namely the lowest and highest\n          # available at the moment, making sure to include at least one for\n          # each Ubuntu release provided by GitHub Actions.\n          - gcc: 7\n            os: ubuntu-20.04\n          - gcc: 12\n            os: ubuntu-22.04\n          - clang: 6.0\n            os: ubuntu-20.04\n          - clang: 15\n            os: ubuntu-22.04\n\n          # Test with all options disabled\n          - descr: 'All options disabled'\n            flags: >\n              -DWITH_QT5=OFF\n              -DWITH_ALSA=OFF\n              -DWITH_GSM=OFF\n              -DWITH_SPEEX=OFF\n              -DWITH_ZRTP=OFF\n            # The empty string would evaluate to false and fail to override\n            # $PACKAGES, so we use this true-but-still-empty hack instead.\n            packages: ' '\n\n          # Test with Qt enabled and Akonadi disabled\n          - descr: 'w/o Akonadi'\n            flags: >\n              -DWITH_QT5=ON\n              -DWITH_AKONADI=OFF\n              -DWITH_ALSA=OFF\n              -DWITH_GSM=OFF\n              -DWITH_SPEEX=OFF\n              -DWITH_ZRTP=OFF\n            packages: >\n              qtdeclarative5-dev\n              qttools5-dev\n\n          # Test building with bcg729\n          - descr: 'w/ bcg729'\n            bcg729-branch: 'master'\n            flags: >\n              -DWITH_QT5=OFF\n              -DWITH_ALSA=OFF\n              -DWITH_G729=ON\n            packages: ' '  # true-but-empty value\n\n          # Also test the old pre-1.0.2 API (see issue #104)\n          - descr: 'w/ bcg729 (old API)'\n            bcg729-branch: '1.0.1'\n            # (bcg729 1.0.1 used Autotools instead of CMake)\n            bcg729-autotools: true\n            flags: >\n              -DWITH_QT5=OFF\n              -DWITH_ALSA=OFF\n              -DWITH_G729=ON\n            packages: >\n              autoconf\n              automake\n              libtool\n              pkg-config\n\n    steps:\n      # Install all packages necessary for this build\n      - name: Install packages\n        run: |\n          sudo apt-get update\n          sudo apt-get -y install $PACKAGES $PACKAGES_REQUIRED\n        env:\n          PACKAGES:          ${{ matrix.packages || env.PACKAGES }}\n          PACKAGES_REQUIRED: ${{ env.PACKAGES_REQUIRED }}\n\n      # Set up a specific version of GCC or Clang if matrix.gcc/clang is set\n      #\n      # Note: This must come *after* `apt-get update` above, see:\n      #       https://github.com/egor-tensin/setup-clang/issues/5\n      - name: Set up GCC (${{ matrix.gcc || 'n/a' }})\n        if: ${{ matrix.gcc }}\n        uses: egor-tensin/setup-gcc@v1\n        with:\n          # Note that these actions use 'latest' to designate the (usually not\n          # latest) default version.  This would result in somewhat confusing\n          # output, so we use 'default' instead and perform our own renaming.\n          version: ${{ ((matrix.gcc == 'default') && 'latest') || matrix.gcc }}\n      - name: Set up Clang (${{ matrix.clang || 'n/a' }})\n        if: ${{ matrix.clang }}\n        uses: egor-tensin/setup-clang@v1\n        with:\n          version: ${{ ((matrix.clang == 'default') && 'latest') || matrix.clang }}\n\n      # Download and build bcg729 if necessary for this build\n      - name: Download and build bcg729 (${{ matrix.bcg729-branch || 'n/a' }})\n        if: ${{ matrix.bcg729-branch }}\n        run: |\n          git clone https://github.com/BelledonneCommunications/bcg729.git \\\n                    --branch \"$BCG729_BRANCH\"\n          cd bcg729\n          if $BCG729_AUTOTOOLS; then\n            ./autogen.sh\n            ./configure\n          else\n            cmake .\n          fi\n          make\n          sudo make install\n        env:\n          BCG729_BRANCH:    ${{ matrix.bcg729-branch }}\n          BCG729_AUTOTOOLS: ${{ (matrix.bcg729-autotools && 'true') || 'false' }}\n\n      # Everything is now set up, ready to checkout/configure/build/install\n\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Configure\n        run: cmake -B ${{github.workspace}}/build ${{env.FLAGS}}\n        env:\n          FLAGS: ${{ matrix.flags || env.FLAGS }}\n\n      - name: Build\n        run: cmake --build ${{github.workspace}}/build\n\n      - name: Install\n        run: cmake --install ${{github.workspace}}/build\n                    --prefix ${{github.workspace}}/_install\n"
  },
  {
    "path": ".gitignore",
    "content": "# generated by cmake\n*.cmake\n!cmake/*.cmake\n*.depends\nCMakeFiles\nMakefile\n/CMakeCache.txt\n/twinkle_config.h\n/twinkle.desktop\n\n# generated by build\n*.o\n*.qm\nui_*.h\nmoc_*.cpp\nsrc/twinkle-console\nsrc/gui/twinkle\nsrc/gui/twinkle_automoc.cpp\nsrc/gui/qrc_icons.cpp\nsrc/gui/qrc_icons.cxx\nsrc/gui/qrc_qml.cpp\nsrc/gui/qrc_qml.cxx\nsrc/parser/parser.cxx\nsrc/parser/parser.hxx\nsrc/parser/scanner.cxx\nsrc/sdp/sdp_parser.cxx\nsrc/sdp/sdp_parser.hxx\nsrc/sdp/sdp_scanner.cxx\n\n# Qt Creator files\nCMakeLists.txt.user*\n\n# generic\n*~\n\n"
  },
  {
    "path": "AUTHORS",
    "content": "Author of Twinkle:\n\nMichel de Boer <michel@twinklephone.com> designed and implemented Twinkle.\nLubos Dolezel <lubos@dolezel.info> ported Twinkle to Qt4/5 and took over further development.\n\nContributions:\n* Werner Dittmann (ZRTP/SRTP)\n* Bogdan Harjoc (AKAv1-MD5, Service-Route)\n* Roman Imankulov (command line editing)\n* Ondrej Moris (codec preprocessing)\n* Rickard Petzall (ALSA)\n\nTwinkle contains the following 3rd party software packages:\n- GSM codec from Jutta Degener and Carsten Bormann\n\tsee directory src/audio/gsm for more info\n- G.711/G.726 codec from Sun Microsystems\n\tsee src/audio/README_G711 for more info\n- G.722 codec from Steve Underwood\n\tsee src/audio/g722.h for more info\n- iLBC implementation from RFC 3951 (www.ilbcfreeware.org)\n- Parts of the STUN project from sourceforge\n\thttp://sourceforge.net/projects/stun\n- Parts of libsrv at http://libsrv.sourceforge.net/\n\nDynamic linked libraries:\n- RTP, ZRTP and SRTP functionality is provided by the\n  GNU ccRTP stack: http://www.gnu.org/software/ccrtp\n\nTranslators:\nCzech   Marek Straka\nDutch\tMichel de Boer\nGerman  Joerg Reisenweber\nFrench  Olivier Aufrere\nRussian Michail Chodorenko\nSwedish Daniel Nylander\n\nMichel de Boer\nhttps://mfnboer.home.xs4all.nl/twinkle/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8.10...3.22 FATAL_ERROR)\n\nproject(twinkle)\n\nset(PRODUCT_VERSION \"1.10.3\")\nset(PRODUCT_DATE    \"February 18, 2022\")\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++11\")\n# Add -DDEBUG for non-release builds, or uCommon will unilaterally define NDEBUG\n# (https://lists.gnu.org/archive/html/bug-commoncpp/2019-12/msg00000.html)\nset_directory_properties(PROPERTIES\n\tCOMPILE_DEFINITIONS $<$<OR:$<CONFIG:>,$<CONFIG:Debug>>:DEBUG>)\n\ninclude(CMakeDependentOption)\n\nOPTION(WITH_ZRTP\t\t\"Enable ZRTP encrypted calls\" OFF)\nOPTION(WITH_SPEEX\t\t\"Enable the Speex codec\" OFF)\nOPTION(WITH_ILBC\t\t\"Enable the iLBC codec\" OFF)\nOPTION(WITH_ALSA\t\t\"Enable ALSA support\" ON)\nCMAKE_DEPENDENT_OPTION(WITH_DIAMONDCARD\t\"Enable Diamondcard integration\" ON \"WITH_QT5\" OFF)\nOPTION(WITH_QT5\t\t\t\"Enable Qt 5 GUI\" ON)\nCMAKE_DEPENDENT_OPTION(WITH_DBUS \"Enable use of QtDBus (GUI only)\" ON \"WITH_QT5\" OFF)\nCMAKE_DEPENDENT_OPTION(WITH_AKONADI \"Enable Akonadi support\" OFF WITH_QT5 OFF)\nOPTION(WITH_G729\t\t\"Enable G.729A support\" OFF)\nOPTION(WITH_GSM\t\t\t\"Use external GSM library\" OFF)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_LIST_DIR}/cmake\")\n\ninclude (CheckCXX11Regex)\ncheck_cxx11_regex(HAVE_CXX11_REGEX)\nif (HAVE_CXX11_REGEX)\n\tmessage(STATUS \"C++11 regular expressions OK\")\nelse (HAVE_CXX11_REGEX)\n\tmessage(FATAL_ERROR \"C++11 regular expressions not supported!\")\nendif (HAVE_CXX11_REGEX)\n\n# Link against libatomic and libresolv on systems where they are present (e.g.\n# Linux), but not on systems that do not make use of them (e.g. FreeBSD).\n\n# Debian annoyingly does not ship a libatomic.so symlink, so we need to add\n# libatomic.so.1 to the list.  (https://bugs.debian.org/1010728)\nfind_library(HAVE_LIBATOMIC NAMES atomic libatomic.so.1)\nif (HAVE_LIBATOMIC)\n\tset(ATOMIC_LIBRARY ${HAVE_LIBATOMIC})\n\tmessage(STATUS \"libatomic: ${ATOMIC_LIBRARY}\")\nendif (HAVE_LIBATOMIC)\n\nfind_library(HAVE_LIBRESOLV resolv)\nif (HAVE_LIBRESOLV)\n\tset(RESOLV_LIBRARY ${HAVE_LIBRESOLV})\n\tmessage(STATUS \"libresolv: ${RESOLV_LIBRARY}\")\nendif (HAVE_LIBRESOLV)\n\ninclude (CheckIncludeFile)\ninclude (CheckIncludeFiles)\ninclude (CheckSymbolExists)\ninclude (CMakePushCheckState)\ninclude (CheckCXXSourceCompiles)\ninclude (TestBigEndian)\n\nfind_package(LibXml2 REQUIRED)\nfind_package(LibMagic REQUIRED)\nfind_package(LibSndfile REQUIRED)\nfind_package(Readline REQUIRED)\nfind_package(BISON REQUIRED)\nfind_package(FLEX REQUIRED)\nfind_package(Ucommon REQUIRED)\nfind_package(Commoncpp REQUIRED)\nfind_package(Ccrtp REQUIRED)\n\ninclude_directories(\n\t${LIBXML2_INCLUDE_DIR}\n\t${LibMagic_INCLUDE_DIR}\n\t${LIBSNDFILE_INCLUDE_DIR}\n\t${Readline_INCLUDE_DIR}\n\t${UCOMMON_INCLUDE_DIR}\n\t${COMMONCPP_INCLUDE_DIR}\n\t${CCRTP_INCLUDE_DIR}\n)\n\nif (WITH_QT5)\n\tfind_package(Qt5Widgets REQUIRED)\n\tfind_package(Qt5LinguistTools REQUIRED)\n\tfind_package(Qt5Quick REQUIRED)\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS} ${Qt5Quick_EXECUTABLE_COMPILE_FLAGS}\")\n\tinclude_directories(${Qt5Widgets_INCLUDES} ${Qt5Quick_INCLUDES})\n\tadd_definitions(${Qt5Widgets_DEFINITIONS} ${Qt5Quick_DEFINITIONS})\n\n\tif (WITH_DBUS)\n\t\tfind_package(Qt5DBus REQUIRED)\n\t\tset(HAVE_DBUS TRUE)\n\t\tinclude_directories(${Qt5DBus_INCLUDES})\n\tendif (WITH_DBUS)\n\n\tif (WITH_AKONADI)\n\t\tfind_package(KF5Akonadi REQUIRED)\n\t\tfind_package(KF5Contacts REQUIRED)\n\t\tmessage(STATUS \"Akonadi OK\")\n\t\tset(HAVE_AKONADI TRUE)\n\t\tinclude_directories(${KF5Akonadi_INCLUDES} ${KF5Contacts_INCLUDES})\n\tendif (WITH_AKONADI)\nendif (WITH_QT5)\n\nif (WITH_ALSA)\n\tfind_package(ALSA)\n\n\tif (ALSA_FOUND)\n\t\tmessage(STATUS \"libasound OK\")\n\t\tset(HAVE_LIBASOUND TRUE)\n\n\t\tinclude_directories(${ALSA_INCLUDE_DIR})\n\telse (ALSA_FOUND)\n\t\tmessage(FATAL_ERROR \"libasound not found!\")\n\tendif (ALSA_FOUND)\nendif (WITH_ALSA)\n\nif (WITH_ZRTP)\n\tfind_package(Zrtpcpp)\n\t\n\tif (ZRTPCPP_FOUND)\n\t\tmessage(STATUS \"libzrtpcpp OK\")\n\t\tset(HAVE_ZRTP TRUE)\n\t\t\n\t\tinclude_directories(${ZRTPCPP_INCLUDE_DIR})\n\telse (ZRTPCPP_FOUND)\n\t\tmessage(FATAL_ERROR \"libzrtpcpp not found!\")\n\tendif (ZRTPCPP_FOUND)\nendif (WITH_ZRTP)\n\nif (WITH_SPEEX)\n\tfind_package(Speex)\n\t\n\tif (SPEEX_FOUND)\n\t\tmessage(STATUS \"Speex OK\")\n\t\tset(HAVE_SPEEX TRUE)\n\t\t\n\t\tinclude_directories(${SPEEX_INCLUDE_DIR})\n\telse (SPEEX_FOUND)\n\t\tmessage(FATAL_ERROR \"Speex not found!\")\n\tendif (SPEEX_FOUND)\nendif (WITH_SPEEX)\n\nif (WITH_ILBC)\n\tfind_package(Ilbc)\n\t\n\tif (ILBC_FOUND)\n\t\tmessage(STATUS \"iLBC OK\")\n\t\tset(HAVE_ILBC TRUE)\n\t\tif (ILBC_CPP)\n\t\t\tset(HAVE_ILBC_CPP TRUE)\n\t\tendif (ILBC_CPP)\n\t\t\n\t\tinclude_directories(${ILBC_INCLUDE_DIR})\n\telse (ILBC_FOUND)\n\t\tmessage(FATAL_ERROR \"iLBC not found!\")\n\tendif (ILBC_FOUND)\nendif (WITH_ILBC)\n\nif (WITH_G729)\n\tfind_package(G729)\n\t\n\tif (G729_FOUND)\n\t\tmessage(STATUS \"bcg729 OK\")\n\t\tset(HAVE_BCG729 TRUE)\n\n\t\tif (G729_ANNEX_B)\n\t\t\tset(HAVE_BCG729_ANNEX_B TRUE)\n\t\tendif (G729_ANNEX_B)\n\t\t\n\t\tinclude_directories(${G729_INCLUDE_DIR})\n\telse (G729_FOUND)\n\t\tmessage(FATAL_ERROR \"bcg729 not found!\")\n\tendif (G729_FOUND)\nendif (WITH_G729)\n\nif (WITH_GSM)\n\tfind_package(Gsm)\n\n\tif (GSM_FOUND)\n\t\tmessage(STATUS \"gsm OK\")\n\t\tset(HAVE_GSM TRUE)\n\n\t\tinclude_directories(${GSM_INCLUDE_DIR})\n\telse (GSM_FOUND)\n\t\tmessage(FATAL_ERROR \"gsm not found!\")\n\tendif (GSM_FOUND)\nendif (WITH_GSM)\n\ncheck_include_file(unistd.h HAVE_UNISTD_H)\ncheck_include_file(linux/types.h HAVE_LINUX_TYPES_H)\ncheck_include_files(\"sys/socket.h;linux/errqueue.h\" HAVE_LINUX_ERRQUEUE_H)\n\ncheck_symbol_exists(strerror_r \"string.h\" HAVE_STRERROR_R)\nif (HAVE_STRERROR_R)\n\t# Check whether the return type is (int) or (char *)\n\t# Code taken from Apache Thrift's ConfigureChecks.cmake\n\tcheck_cxx_source_compiles(\"\n\t\t#include <string.h>\n\t\tint main() {\n\t\t\tchar b;\n\t\t\tchar *a = strerror_r(0, &b, 0);\n\t\t\treturn 0;\n\t\t}\n\t\" STRERROR_R_CHAR_P)\nendif (HAVE_STRERROR_R)\n\ncmake_push_check_state()\nlist(APPEND CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIBRARY})\ncheck_cxx_source_compiles(\"\n\t#include <sys/types.h>\n\t#include <netinet/in.h>\n\t#include <arpa/nameser.h>\n\t#include <resolv.h>\n\n\tint main() { res_init(); return 0; }\n\" HAVE_RES_INIT)\ncmake_pop_check_state()\n\ntest_big_endian(WORDS_BIGENDIAN)\n\nset(datadir \"${CMAKE_INSTALL_PREFIX}/share/twinkle\")\nconfigure_file(twinkle_config.h.in twinkle_config.h)\nconfigure_file(twinkle.desktop.in twinkle.desktop)\n\ninclude_directories(\"${CMAKE_BINARY_DIR}\")\n\ninstall(FILES\n\t${CMAKE_CURRENT_SOURCE_DIR}/data/providers.csv\n\t${CMAKE_CURRENT_SOURCE_DIR}/data/ringtone.wav\n\t${CMAKE_CURRENT_SOURCE_DIR}/data/ringback.wav\n\t${CMAKE_CURRENT_SOURCE_DIR}/src/gui/images/twinkle16.png\n\t${CMAKE_CURRENT_SOURCE_DIR}/src/gui/images/twinkle32.png\n\t${CMAKE_CURRENT_SOURCE_DIR}/src/gui/images/twinkle48.png\n\tDESTINATION share/twinkle)\ninstall(FILES\n\t${CMAKE_CURRENT_SOURCE_DIR}/src/gui/images/twinkle48.png\n\tDESTINATION share/pixmaps\n\tRENAME twinkle.png)\ninstall(FILES\n\t${CMAKE_CURRENT_BINARY_DIR}/twinkle.desktop\n\tDESTINATION share/applications)\n\ninstall(FILES src/gui/images/twinkle16.png\n\tRENAME twinkle.png\n\tDESTINATION share/icons/hicolor/16x16/apps)\ninstall(FILES src/gui/images/twinkle24.png\n\tRENAME twinkle.png\n\tDESTINATION share/icons/hicolor/24x24/apps)\ninstall(FILES src/gui/images/twinkle32.png\n\tRENAME twinkle.png\n\tDESTINATION share/icons/hicolor/32x32/apps)\ninstall(FILES src/gui/images/twinkle48.png\n\tRENAME twinkle.png\n\tDESTINATION share/icons/hicolor/48x48/apps)\ninstall(FILES data/twinkle.svg\n\tDESTINATION share/icons/hicolor/scalable/apps)\n\ninstall(FILES data/twinkle.1\n\tDESTINATION share/man/man1)\n\nadd_subdirectory(src)\n\n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "Doxyfile",
    "content": "# Doxyfile 1.5.0\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project\n#\n# All text after a hash (#) is considered a comment and will be ignored\n# The format is:\n#       TAG = value [value, ...]\n# For lists items can also be appended using:\n#       TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\" \")\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded \n# by quotes) that should identify the project.\n\nPROJECT_NAME           = Twinkle\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. \n# This could be handy for archiving the generated documentation or \n# if some version control system is used.\n\nPROJECT_NUMBER         = \n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) \n# base path where the generated documentation will be put. \n# If a relative path is entered, it will be relative to the location \n# where doxygen was started. If left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = doc\n\n# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create \n# 4096 sub-directories (in 2 levels) under the output directory of each output \n# format and will distribute the generated files over these directories. \n# Enabling this option can be useful when feeding doxygen a huge amount of \n# source files, where putting all generated files in the same directory would \n# otherwise cause performance problems for the file system.\n\nCREATE_SUBDIRS         = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all \n# documentation generated by doxygen is written. Doxygen will use this \n# information to generate all constant output in the proper language. \n# The default language is English, other supported languages are: \n# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, \n# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, \n# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, \n# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, \n# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.\n\nOUTPUT_LANGUAGE        = English\n\n# This tag can be used to specify the encoding used in the generated output. \n# The encoding is not always determined by the language that is chosen, \n# but also whether or not the output is meant for Windows or non-Windows users. \n# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES \n# forces the Windows encoding (this is the default for the Windows binary), \n# whereas setting the tag to NO uses a Unix-style encoding (the default for \n# all platforms other than Windows).\n\nUSE_WINDOWS_ENCODING   = NO\n\n# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will \n# include brief member descriptions after the members that are listed in \n# the file and class documentation (similar to JavaDoc). \n# Set to NO to disable this.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend \n# the brief description of a member or function before the detailed description. \n# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the \n# brief descriptions will be completely suppressed.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator \n# that is used to form the text in various listings. Each string \n# in this list, if found as the leading text of the brief description, will be \n# stripped from the text and the result after processing the whole list, is \n# used as the annotated text. Otherwise, the brief description is used as-is. \n# If left blank, the following values are used (\"$name\" is automatically \n# replaced with the name of the entity): \"The $name class\" \"The $name widget\" \n# \"The $name file\" \"is\" \"provides\" \"specifies\" \"contains\" \n# \"represents\" \"a\" \"an\" \"the\"\n\nABBREVIATE_BRIEF       = \n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then \n# Doxygen will generate a detailed section even if there is only a brief \n# description.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all \n# inherited members of a class in the documentation of that class as if those \n# members were ordinary class members. Constructors, destructors and assignment \n# operators of the base classes will not be shown.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full \n# path before files name in the file list and in the header files. If set \n# to NO the shortest path that makes the file name unique will be used.\n\nFULL_PATH_NAMES        = YES\n\n# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag \n# can be used to strip a user-defined part of the path. Stripping is \n# only done if one of the specified strings matches the left-hand part of \n# the path. The tag can be used to show relative paths in the file list. \n# If left blank the directory from which doxygen is run is used as the \n# path to strip.\n\nSTRIP_FROM_PATH        = \n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of \n# the path mentioned in the documentation of a class, which tells \n# the reader which header file to include in order to use a class. \n# If left blank only the name of the header file containing the class \n# definition is used. Otherwise one should specify the include paths that \n# are normally passed to the compiler using the -I flag.\n\nSTRIP_FROM_INC_PATH    = \n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter \n# (but less readable) file names. This can be useful is your file systems \n# doesn't support long names like on DOS, Mac, or CD-ROM.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen \n# will interpret the first line (until the first dot) of a JavaDoc-style \n# comment as the brief description. If set to NO, the JavaDoc \n# comments will behave just like the Qt-style comments (thus requiring an \n# explicit @brief command for a brief description.\n\nJAVADOC_AUTOBRIEF      = YES\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen \n# treat a multi-line C++ special comment block (i.e. a block of //! or /// \n# comments) as a brief description. This used to be the default behaviour. \n# The new default is to treat a multi-line C++ comment block as a detailed \n# description. Set this tag to YES if you prefer the old behaviour instead.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the DETAILS_AT_TOP tag is set to YES then Doxygen \n# will output the detailed description near the top, like JavaDoc.\n# If set to NO, the detailed description appears after the member \n# documentation.\n\nDETAILS_AT_TOP         = NO\n\n# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented \n# member inherits the documentation from any documented member that it \n# re-implements.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce \n# a new page for each member. If set to NO, the documentation of a member will \n# be part of the file/class/namespace that contains it.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. \n# Doxygen uses this value to replace tabs by spaces in code fragments.\n\nTAB_SIZE               = 8\n\n# This tag can be used to specify a number of aliases that acts \n# as commands in the documentation. An alias has the form \"name=value\". \n# For example adding \"sideeffect=\\par Side Effects:\\n\" will allow you to \n# put the command \\sideeffect (or @sideeffect) in the documentation, which \n# will result in a user-defined paragraph with heading \"Side Effects:\". \n# You can put \\n's in the value part of an alias to insert newlines.\n\nALIASES                = \n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C \n# sources only. Doxygen will then generate output that is more tailored for C. \n# For instance, some of the names that are used will be different. The list \n# of all members will be omitted, etc.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java \n# sources only. Doxygen will then generate output that is more tailored for Java. \n# For instance, namespaces will be presented as packages, qualified scopes \n# will look different, etc.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to \n# include (a tag file for) the STL sources as input, then you should \n# set this tag to YES in order to let doxygen match functions declarations and \n# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. \n# func(std::string) {}). This also make the inheritance and collaboration \n# diagrams that involve STL classes more complete and accurate.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC \n# tag is set to YES, then doxygen will reuse the documentation of the first \n# member in the group (if any) for the other members of the group. By default \n# all members of a group must be documented explicitly.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES (the default) to allow class member groups of \n# the same type (for instance a group of public functions) to be put as a \n# subgroup of that type (e.g. under the Public Functions section). Set it to \n# NO to prevent subgrouping. Alternatively, this can be done per class using \n# the \\nosubgrouping command.\n\nSUBGROUPING            = YES\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in \n# documentation are documented, even if no documentation was available. \n# Private class members and static file members will be hidden unless \n# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES all private members of a class \n# will be included in the documentation.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_STATIC tag is set to YES all static members of a file \n# will be included in the documentation.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) \n# defined locally in source files will be included in the documentation. \n# If set to NO only classes defined in header files are included.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. When set to YES local \n# methods, which are defined in the implementation section but not in \n# the interface are included in the documentation. \n# If set to NO (the default) only methods in the interface are included.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all \n# undocumented members of documented classes, files or namespaces. \n# If set to NO (the default) these members will be included in the \n# various overviews, but no documentation section is generated. \n# This option has no effect if EXTRACT_ALL is enabled.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all \n# undocumented classes that are normally visible in the class hierarchy. \n# If set to NO (the default) these classes will be included in the various \n# overviews. This option has no effect if EXTRACT_ALL is enabled.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all \n# friend (class|struct|union) declarations. \n# If set to NO (the default) these declarations will be included in the \n# documentation.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any \n# documentation blocks found inside the body of a function. \n# If set to NO (the default) these blocks will be appended to the \n# function's detailed documentation block.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation \n# that is typed after a \\internal command is included. If the tag is set \n# to NO (the default) then the documentation will be excluded. \n# Set it to YES to include the internal documentation.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate \n# file names in lower-case letters. If set to YES upper-case letters are also \n# allowed. This is useful if you have classes or files whose names only differ \n# in case and if your file system supports case sensitive file names. Windows \n# and Mac users are advised to set this option to NO.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen \n# will show members with their full class and namespace scopes in the \n# documentation. If set to YES the scope will be hidden.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen \n# will put a list of the files that are included by a file in the documentation \n# of that file.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] \n# is inserted in the documentation for inline members.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen \n# will sort the (detailed) documentation of file and class members \n# alphabetically by member name. If set to NO the members will appear in \n# declaration order.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the \n# brief documentation of file, namespace and class members alphabetically \n# by member name. If set to NO (the default) the members will appear in \n# declaration order.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be \n# sorted by fully-qualified names, including namespaces. If set to \n# NO (the default), the class list will be sorted only by class name, \n# not including the namespace part. \n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the \n# alphabetical list.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or \n# disable (NO) the todo list. This list is created by putting \\todo \n# commands in the documentation.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or \n# disable (NO) the test list. This list is created by putting \\test \n# commands in the documentation.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or \n# disable (NO) the bug list. This list is created by putting \\bug \n# commands in the documentation.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or \n# disable (NO) the deprecated list. This list is created by putting \n# \\deprecated commands in the documentation.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional \n# documentation sections, marked by \\if sectionname ... \\endif.\n\nENABLED_SECTIONS       = \n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines \n# the initial value of a variable or define consists of for it to appear in \n# the documentation. If the initializer consists of more lines than specified \n# here it will be hidden. Use a value of 0 to hide initializers completely. \n# The appearance of the initializer of individual variables and defines in the \n# documentation can be controlled using \\showinitializer or \\hideinitializer \n# command in the documentation regardless of this setting.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated \n# at the bottom of the documentation of classes and structs. If set to YES the \n# list will mention the files that were used to generate the documentation.\n\nSHOW_USED_FILES        = YES\n\n# If the sources in your project are distributed over multiple directories \n# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy \n# in the documentation. The default is NO.\n\nSHOW_DIRECTORIES       = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that \n# doxygen should invoke to get the current version for each file (typically from the \n# version control system). Doxygen will invoke the program by executing (via \n# popen()) the command <command> <input-file>, where <command> is the value of \n# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file \n# provided by doxygen. Whatever the program writes to standard output \n# is used as the file version. See the manual for examples.\n\nFILE_VERSION_FILTER    = \n\n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated \n# by doxygen. Possible values are YES and NO. If left blank NO is used.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are \n# generated by doxygen. Possible values are YES and NO. If left blank \n# NO is used.\n\nWARNINGS               = YES\n\n# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings \n# for undocumented members. If EXTRACT_ALL is set to YES then this flag will \n# automatically be disabled.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for \n# potential errors in the documentation, such as not documenting some \n# parameters in a documented function, or documenting parameters that \n# don't exist or using markup commands wrongly.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be abled to get warnings for \n# functions that are documented, but have no documentation for their parameters \n# or return value. If set to NO (the default) doxygen will only warn about \n# wrong or incomplete parameter documentation, but not about the absence of \n# documentation.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that \n# doxygen can produce. The string should contain the $file, $line, and $text \n# tags, which will be replaced by the file and line number from which the \n# warning originated and the warning text. Optionally the format may contain \n# $version, which will be replaced by the version of the file (if it could \n# be obtained via FILE_VERSION_FILTER)\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning \n# and error messages should be written. If left blank the output is written \n# to stderr.\n\nWARN_LOGFILE           = \n\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag can be used to specify the files and/or directories that contain \n# documented source files. You may enter file names like \"myfile.cpp\" or \n# directories like \"/usr/src/myproject\". Separate the files or directories \n# with spaces.\n\nINPUT                  = src src/utils src/im src/presence src/patterns\n\n# If the value of the INPUT tag contains directories, you can use the \n# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp \n# and *.h) to filter out the source-files in the directories. If left \n# blank the following patterns are tested: \n# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx \n# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py\n\nFILE_PATTERNS          = *.cpp *.h\n\n# The RECURSIVE tag can be used to turn specify whether or not subdirectories \n# should be searched for input files as well. Possible values are YES and NO. \n# If left blank NO is used.\n\nRECURSIVE              = NO\n\n# The EXCLUDE tag can be used to specify files and/or directories that should \n# excluded from the INPUT source files. This way you can easily exclude a \n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n\nEXCLUDE                = src/twinkle_config.h\n\n# The EXCLUDE_SYMLINKS tag can be used select whether or not files or \n# directories that are symbolic links (a Unix filesystem feature) are excluded \n# from the input.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the \n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude \n# certain files from those directories. Note that the wildcards are matched \n# against the file with absolute path, so to exclude all test directories \n# for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       = \n\n# The EXAMPLE_PATH tag can be used to specify one or more files or \n# directories that contain example code fragments that are included (see \n# the \\include command).\n\nEXAMPLE_PATH           = \n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the \n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp \n# and *.h) to filter out the source-files in the directories. If left \n# blank all files are included.\n\nEXAMPLE_PATTERNS       = \n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be \n# searched for input files to be used with the \\include or \\dontinclude \n# commands irrespective of the value of the RECURSIVE tag. \n# Possible values are YES and NO. If left blank NO is used.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or \n# directories that contain image that are included in the documentation (see \n# the \\image command).\n\nIMAGE_PATH             = \n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should \n# invoke to filter for each input file. Doxygen will invoke the filter program \n# by executing (via popen()) the command <filter> <input-file>, where <filter> \n# is the value of the INPUT_FILTER tag, and <input-file> is the name of an \n# input file. Doxygen will then use the output that the filter program writes \n# to standard output.  If FILTER_PATTERNS is specified, this tag will be \n# ignored.\n\nINPUT_FILTER           = \n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern \n# basis.  Doxygen will compare the file name with each pattern and apply the \n# filter if there is a match.  The filters are a list of the form: \n# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further \n# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER \n# is applied to all files.\n\nFILTER_PATTERNS        = \n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using \n# INPUT_FILTER) will be used to filter the input files when producing source \n# files to browse (i.e. when SOURCE_BROWSER is set to YES).\n\nFILTER_SOURCE_FILES    = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will \n# be generated. Documented entities will be cross-referenced with these sources. \n# Note: To get rid of all source code in the generated output, make sure also \n# VERBATIM_HEADERS is set to NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body \n# of functions and classes directly in the documentation.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct \n# doxygen to hide any special comment blocks from generated source code \n# fragments. Normal C and C++ comments will always remain visible.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES (the default) \n# then for each documented function all documented \n# functions referencing it will be listed.\n\nREFERENCED_BY_RELATION = YES\n\n# If the REFERENCES_RELATION tag is set to YES (the default) \n# then for each documented function all documented entities \n# called/used by that function will be listed.\n\nREFERENCES_RELATION    = YES\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)\n# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from\n# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will\n# link to the source code.  Otherwise they will link to the documentstion.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code \n# will point to the HTML generated by the htags(1) tool instead of doxygen \n# built-in source browser. The htags tool is part of GNU's global source \n# tagging system (see http://www.gnu.org/software/global/global.html). You \n# will need version 4.8.6 or higher.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen \n# will generate a verbatim copy of the header file for each class for \n# which an include is specified. Set to NO to disable this.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index \n# of all compounds will be generated. Enable this if the project \n# contains a lot of classes, structs, unions or interfaces.\n\nALPHABETICAL_INDEX     = YES\n\n# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then \n# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns \n# in which this list will be split (can be a number in the range [1..20])\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all \n# classes will be put under the same header in the alphabetical index. \n# The IGNORE_PREFIX tag can be used to specify one or more prefixes that \n# should be ignored while generating the index headers.\n\nIGNORE_PREFIX          = t_\n\n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES (the default) Doxygen will \n# generate HTML output.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. \n# If a relative path is entered the value of OUTPUT_DIRECTORY will be \n# put in front of it. If left blank `html' will be used as the default path.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for \n# each generated HTML page (for example: .htm,.php,.asp). If it is left blank \n# doxygen will generate files with .html extension.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a personal HTML header for \n# each generated HTML page. If it is left blank doxygen will generate a \n# standard header.\n\nHTML_HEADER            = \n\n# The HTML_FOOTER tag can be used to specify a personal HTML footer for \n# each generated HTML page. If it is left blank doxygen will generate a \n# standard footer.\n\nHTML_FOOTER            = \n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading \n# style sheet that is used by each HTML page. It can be used to \n# fine-tune the look of the HTML output. If the tag is left blank doxygen \n# will generate a default style sheet. Note that doxygen will try to copy \n# the style sheet file to the HTML output directory, so don't put your own \n# stylesheet in the HTML output directory as well, or it will be erased!\n\nHTML_STYLESHEET        = \n\n# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, \n# files or namespaces will be aligned in HTML using tables. If set to \n# NO a bullet list will be used.\n\nHTML_ALIGN_MEMBERS     = YES\n\n# If the GENERATE_HTMLHELP tag is set to YES, additional index files \n# will be generated that can be used as input for tools like the \n# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) \n# of the generated HTML documentation.\n\nGENERATE_HTMLHELP      = NO\n\n# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can \n# be used to specify the file name of the resulting .chm file. You \n# can add a path in front of the file if the result should not be \n# written to the html output directory.\n\nCHM_FILE               = \n\n# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can \n# be used to specify the location (absolute path including file name) of \n# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run \n# the HTML help compiler on the generated index.hhp.\n\nHHC_LOCATION           = \n\n# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag \n# controls if a separate .chi index file is generated (YES) or that \n# it should be included in the master .chm file (NO).\n\nGENERATE_CHI           = NO\n\n# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag \n# controls whether a binary table of contents is generated (YES) or a \n# normal table of contents (NO) in the .chm file.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members \n# to the contents of the HTML help documentation and to the tree view.\n\nTOC_EXPAND             = NO\n\n# The DISABLE_INDEX tag can be used to turn on/off the condensed index at \n# top of each HTML page. The value NO (the default) enables the index and \n# the value YES disables it.\n\nDISABLE_INDEX          = NO\n\n# This tag can be used to set the number of enum values (range [1..20]) \n# that doxygen will group on one line in the generated HTML documentation.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be\n# generated containing a tree-like index structure (just like the one that \n# is generated for HTML Help). For this to work a browser that supports \n# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, \n# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are \n# probably better off using the HTML help feature.\n\nGENERATE_TREEVIEW      = NO\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be \n# used to set the initial width (in pixels) of the frame in which the tree \n# is shown.\n\nTREEVIEW_WIDTH         = 250\n\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will \n# generate Latex output.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. \n# If a relative path is entered the value of OUTPUT_DIRECTORY will be \n# put in front of it. If left blank `latex' will be used as the default path.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be \n# invoked. If left blank `latex' will be used as the default command name.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to \n# generate index for LaTeX. If left blank `makeindex' will be used as the \n# default command name.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact \n# LaTeX documents. This may be useful for small projects and may help to \n# save some trees in general.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used \n# by the printer. Possible values are: a4, a4wide, letter, legal and \n# executive. If left blank a4wide will be used.\n\nPAPER_TYPE             = a4wide\n\n# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX \n# packages that should be included in the LaTeX output.\n\nEXTRA_PACKAGES         = \n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for \n# the generated latex document. The header should contain everything until \n# the first chapter. If it is left blank doxygen will generate a \n# standard header. Notice: only use this tag if you know what you are doing!\n\nLATEX_HEADER           = \n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated \n# is prepared for conversion to pdf (using ps2pdf). The pdf file will \n# contain links (just like the HTML output) instead of page references \n# This makes the output suitable for online browsing using a pdf viewer.\n\nPDF_HYPERLINKS         = NO\n\n# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of \n# plain latex in the generated Makefile. Set this option to YES to get a \n# higher quality PDF documentation.\n\nUSE_PDFLATEX           = NO\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\\\batchmode. \n# command to the generated LaTeX files. This will instruct LaTeX to keep \n# running if errors occur, instead of asking the user for help. \n# This option is also used when generating formulas in HTML.\n\nLATEX_BATCHMODE        = NO\n\n# If LATEX_HIDE_INDICES is set to YES then doxygen will not \n# include the index chapters (such as File Index, Compound Index, etc.) \n# in the output.\n\nLATEX_HIDE_INDICES     = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output \n# The RTF output is optimized for Word 97 and may not look very pretty with \n# other RTF readers or editors.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. \n# If a relative path is entered the value of OUTPUT_DIRECTORY will be \n# put in front of it. If left blank `rtf' will be used as the default path.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES Doxygen generates more compact \n# RTF documents. This may be useful for small projects and may help to \n# save some trees in general.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated \n# will contain hyperlink fields. The RTF file will \n# contain links (just like the HTML output) instead of page references. \n# This makes the output suitable for online browsing using WORD or other \n# programs which support those fields. \n# Note: wordpad (write) and others do not support links.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's \n# config file, i.e. a series of assignments. You only have to provide \n# replacements, missing definitions are set to their default value.\n\nRTF_STYLESHEET_FILE    = \n\n# Set optional variables used in the generation of an rtf document. \n# Syntax is similar to doxygen's config file.\n\nRTF_EXTENSIONS_FILE    = \n\n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES (the default) Doxygen will \n# generate man pages\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. \n# If a relative path is entered the value of OUTPUT_DIRECTORY will be \n# put in front of it. If left blank `man' will be used as the default path.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to \n# the generated man pages (default is the subroutine's section .3)\n\nMAN_EXTENSION          = .3\n\n# If the MAN_LINKS tag is set to YES and Doxygen generates man output, \n# then it will generate one additional man file for each entity \n# documented in the real man page(s). These additional files \n# only source the real man page, but without them the man command \n# would be unable to find the correct page. The default is NO.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES Doxygen will \n# generate an XML file that captures the structure of \n# the code including all documentation.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. \n# If a relative path is entered the value of OUTPUT_DIRECTORY will be \n# put in front of it. If left blank `xml' will be used as the default path.\n\nXML_OUTPUT             = xml\n\n# The XML_SCHEMA tag can be used to specify an XML schema, \n# which can be used by a validating XML parser to check the \n# syntax of the XML files.\n\nXML_SCHEMA             = \n\n# The XML_DTD tag can be used to specify an XML DTD, \n# which can be used by a validating XML parser to check the \n# syntax of the XML files.\n\nXML_DTD                = \n\n# If the XML_PROGRAMLISTING tag is set to YES Doxygen will \n# dump the program listings (including syntax highlighting \n# and cross-referencing information) to the XML output. Note that \n# enabling this will significantly increase the size of the XML output.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will \n# generate an AutoGen Definitions (see autogen.sf.net) file \n# that captures the structure of the code including all \n# documentation. Note that this feature is still experimental \n# and incomplete at the moment.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES Doxygen will \n# generate a Perl module file that captures the structure of \n# the code including all documentation. Note that this \n# feature is still experimental and incomplete at the \n# moment.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES Doxygen will generate \n# the necessary Makefile rules, Perl scripts and LaTeX code to be able \n# to generate PDF and DVI output from the Perl module output.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be \n# nicely formatted so it can be parsed by a human reader.  This is useful \n# if you want to understand what is going on.  On the other hand, if this \n# tag is set to NO the size of the Perl module output will be much smaller \n# and Perl will parse it just the same.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file \n# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. \n# This is useful so different doxyrules.make files included by the same \n# Makefile don't overwrite each other's variables.\n\nPERLMOD_MAKEVAR_PREFIX = \n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor   \n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will \n# evaluate all C-preprocessor directives found in the sources and include \n# files.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro \n# names in the source code. If set to NO (the default) only conditional \n# compilation will be performed. Macro expansion can be done in a controlled \n# way by setting EXPAND_ONLY_PREDEF to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES \n# then the macro expansion is limited to the macros specified with the \n# PREDEFINED and EXPAND_AS_DEFINED tags.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files \n# in the INCLUDE_PATH (see below) will be search if a #include is found.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that \n# contain include files that are not input files but should be processed by \n# the preprocessor.\n\nINCLUDE_PATH           = \n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard \n# patterns (like *.h and *.hpp) to filter out the header-files in the \n# directories. If left blank, the patterns specified with FILE_PATTERNS will \n# be used.\n\nINCLUDE_FILE_PATTERNS  = \n\n# The PREDEFINED tag can be used to specify one or more macro names that \n# are defined before the preprocessor is started (similar to the -D option of \n# gcc). The argument of the tag is a list of macros of the form: name \n# or name=definition (no spaces). If the definition and the = are \n# omitted =1 is assumed. To prevent a macro definition from being \n# undefined via #undef or recursively expanded use the := operator \n# instead of the = operator.\n\nPREDEFINED             = \n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then \n# this tag can be used to specify a list of macro names that should be expanded. \n# The macro definition that is found in the sources will be used. \n# Use the PREDEFINED tag if you want to use a different macro definition.\n\nEXPAND_AS_DEFINED      = \n\n# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then \n# doxygen's preprocessor will remove all function-like macros that are alone \n# on a line, have an all uppercase name, and do not end with a semicolon. Such \n# function macros are typically used for boiler-plate code, and will confuse \n# the parser if not removed.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references   \n#---------------------------------------------------------------------------\n\n# The TAGFILES option can be used to specify one or more tagfiles. \n# Optionally an initial location of the external documentation \n# can be added for each tagfile. The format of a tag file without \n# this location is as follows: \n#   TAGFILES = file1 file2 ... \n# Adding location for the tag files is done as follows: \n#   TAGFILES = file1=loc1 \"file2 = loc2\" ... \n# where \"loc1\" and \"loc2\" can be relative or absolute paths or \n# URLs. If a location is present for each tag, the installdox tool \n# does not have to be run to correct the links.\n# Note that each tag file must have a unique name\n# (where the name does NOT include the path)\n# If a tag file is not located in the directory in which doxygen \n# is run, you must also specify the path to the tagfile here.\n\nTAGFILES               = \n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create \n# a tag file that is based on the input files it reads.\n\nGENERATE_TAGFILE       = \n\n# If the ALLEXTERNALS tag is set to YES all external classes will be listed \n# in the class index. If set to NO only the inherited external classes \n# will be listed.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed \n# in the modules index. If set to NO, only the current project's groups will \n# be listed.\n\nEXTERNAL_GROUPS        = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script \n# interpreter (i.e. the result of `which perl').\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool   \n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will \n# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base \n# or super classes. Setting the tag to NO turns the diagrams off. Note that \n# this option is superseded by the HAVE_DOT option below. This is only a \n# fallback. It is recommended to install and use dot, since it yields more \n# powerful graphs.\n\nCLASS_DIAGRAMS         = YES\n\n# If set to YES, the inheritance and collaboration graphs will hide \n# inheritance and usage relations if the target is undocumented \n# or is not a class.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is \n# available from the path. This tool is part of Graphviz, a graph visualization \n# toolkit from AT&T and Lucent Bell Labs. The other options in this section \n# have no effect if this option is set to NO (the default)\n\nHAVE_DOT               = NO\n\n# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen \n# will generate a graph for each documented class showing the direct and \n# indirect inheritance relations. Setting this tag to YES will force the \n# the CLASS_DIAGRAMS tag to NO.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen \n# will generate a graph for each documented class showing the direct and \n# indirect implementation dependencies (inheritance, containment, and \n# class references variables) of the class with other documented classes.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen \n# will generate a graph for groups, showing the direct groups dependencies\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES doxygen will generate inheritance and \n# collaboration diagrams in a style similar to the OMG's Unified Modeling \n# Language.\n\nUML_LOOK               = NO\n\n# If set to YES, the inheritance and collaboration graphs will show the \n# relations between templates and their instances.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT \n# tags are set to YES then doxygen will generate a graph for each documented \n# file showing the direct and indirect include dependencies of the file with \n# other documented files.\n\nINCLUDE_GRAPH          = YES\n\n# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and \n# HAVE_DOT tags are set to YES then doxygen will generate a graph for each \n# documented header file showing the documented files that directly or \n# indirectly include this file.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will \n# generate a call dependency graph for every global function or class method. \n# Note that enabling this option will significantly increase the time of a run. \n# So in most cases it will be better to enable call graphs for selected \n# functions only using the \\callgraph command.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will \n# generate a caller dependency graph for every global function or class method. \n# Note that enabling this option will significantly increase the time of a run. \n# So in most cases it will be better to enable caller graphs for selected \n# functions only using the \\callergraph command.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen \n# will graphical hierarchy of all classes instead of a textual one.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES \n# then doxygen will show the dependencies a directory has on other directories \n# in a graphical way. The dependency relations are determined by the #include\n# relations between the files in the directories.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images \n# generated by dot. Possible values are png, jpg, or gif\n# If left blank png will be used.\n\nDOT_IMAGE_FORMAT       = png\n\n# The tag DOT_PATH can be used to specify the path where the dot tool can be \n# found. If left blank, it is assumed the dot tool can be found in the path.\n\nDOT_PATH               = \n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that \n# contain dot files that are included in the documentation (see the \n# \\dotfile command).\n\nDOTFILE_DIRS           = \n\n# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width \n# (in pixels) of the graphs generated by dot. If a graph becomes larger than \n# this value, doxygen will try to truncate the graph, so that it fits within \n# the specified constraint. Beware that most browsers cannot cope with very \n# large images.\n\nMAX_DOT_GRAPH_WIDTH    = 1024\n\n# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height \n# (in pixels) of the graphs generated by dot. If a graph becomes larger than \n# this value, doxygen will try to truncate the graph, so that it fits within \n# the specified constraint. Beware that most browsers cannot cope with very \n# large images.\n\nMAX_DOT_GRAPH_HEIGHT   = 1024\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the \n# graphs generated by dot. A depth value of 3 means that only nodes reachable \n# from the root by following a path via at most 3 edges will be shown. Nodes \n# that lay further from the root node will be omitted. Note that setting this \n# option to 1 or 2 may greatly reduce the computation time needed for large \n# code bases. Also note that a graph may be further truncated if the graph's \n# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH \n# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), \n# the graph is not depth-constrained.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent \n# background. This is disabled by default, which results in a white background. \n# Warning: Depending on the platform used, enabling this option may lead to \n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to \n# read).\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output \n# files in one run (i.e. multiple -o and -T options on the command line). This \n# makes dot run faster, but since only newer versions of dot (>1.8.10) \n# support this, this feature is disabled by default.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will \n# generate a legend page explaining the meaning of the various boxes and \n# arrows in the dot generated graphs.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will \n# remove the intermediate dot files that are used to generate \n# the various graphs.\n\nDOT_CLEANUP            = YES\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to the search engine   \n#---------------------------------------------------------------------------\n\n# The SEARCHENGINE tag specifies whether or not a search engine should be \n# used. If set to NO the values of all tags below this one will be ignored.\n\nSEARCHENGINE           = NO\n"
  },
  {
    "path": "NEWS",
    "content": "18 February 2022 - 1.10.3\n=========================\n- Replace manual URL.\n\n14 February 2019 - 1.10.2\n=======================\n- Fix sound not working with ALSA 1.1.7.\n- Translation updates.\n- Various bug fixes.\n\n7 October 2016 - 1.10.1\n=======================\n- Stability fixes.\n- French translation updates.\n- Remove obsolete commoncpp2 dependency.\n- Set PA role to phone to pause music playback automatically.\n\n15 July 2016 - 1.10.0\n=======================\n- Supports newest Qt5, drops support for Qt4.\n- Source code cleanups, spelling error fixes.\n- Handle digest authentication scheme case insensitively.\n- Address book UI fixes.\n- Fix build with ucommon 7.0.\n- Call history UI fixes.\n- Support using an external GSM library.\n- Various other bug fixes.\n\n10 July 2015 - 1.9.0\n=======================\n- Ported to Qt4 and Qt5.\n- Added in-call OSD.\n- Added a new incoming call notification screen/dialog.\n- Support the G.729A codec.\n- Made Diamondcard support optional.\n- Save window geometry and state.\n- Visual feedback (button presses) in DTMF window when pushing keyboard buttons.\n- Support clipboard paste in DTMF window.\n- Removed Boost Regex dependency.\n- Eliminated some GUI freezes/stalls.\n\n25 february 2009 - 1.4.2\n========================\n- Integration with Diamondcard Worldwide Communication Service\n  (worldwide calls to regular and cell phones and SMS).\n- Show number of calls and total call duration in call history.\n- Show message size while typing an instant message.\n- Show \"referred by\" party for an incoming transferred call in systray popup.\n- Option to allow call transfer while consultation call is still in progress.\n- Improved lock file checking. No more stale lock files.\n\nBug fixes:\n----------\n- Opening an IM attachment did not work anymore.\n\nBuild fixes:\n------------\n- Link with ncurses library\n\n\n31 january 2009 - 1.4.1\n=======================\nBug fixes:\n----------\n- No sound when Twinkle is compiled without speex support.\n\nBuild fixes:\n------------\n- Compiling without KDE sometimes failed (cannot find -lqt-mt).\n- Configure script did not correctly check for the readline-devel package.\n\n\n25 january 2009 - 1.4\n=====================\n- Service route discovery during registration.\n- Codec preprocessing: automatic gain control, voice activation detection,\n  noise reduction, acoustic echo cancellation (experimental).\n- Support tel-URI as destination address for a call or instant message.\n- User profile option to expand a telephone number to a tel-URI instead\n  of a sip-URI.\n- Add descending q-value to contacts in 3XX responses for the redirection \n  services.\n- AKAv1-MD5 authentication.\n- Command line editing, history, auto-completion.\n- Ignore wrong formatted domain-parameter in digest challenge.\n- Match tel-URI in incoming call to address book.\n- Determine RTP IP address for SDP answer from RTP IP address in SDP offer.\n- Show context menu's when pressing the right mouse button instead of \n  after clicking.\n- Swedish translation\n- Resampled ringback tone from 8287 Hz to 8000 Hz\n\nBug fixes\n---------\n- Text line edit in the message form looses focus after sending an IM.\n- Twinkle does not escape reserved symbols when dialing.\n- Deregister all function causes a crash.\n- Twinkle crashes at startup in CLI mode.\n- Twinkle may freeze when an ALSA error is detected when starting\n  the ringback tone and the outgoing call gets answered very fast.\n- User profile editor did not allow spaces in a user name.\n\nNew RFC's\n---------\nRFC 3608 - Session Initiation Protocol (SIP) Extension Header Field\n           for Service Route Discovery During Registration\n\n\n24 august 2008 - 1.3.2\n======================\n- Fix in non-KDE version for gcc 4.3\n\n23 august 2008 - 1.3.1\n======================\n- Disable file attachment button in message window when destination\n  address is not filled in\n- Updated russian translation\n  \nBuild fixes\n-----------\n- Fixes for gcc 4.3 (missing includes)\n- non-KDE version failed to build\n\n\n18 august 2008 - 1.3\n====================\n- Send file attachment with instant message.\n- Show timestamp with instant messages.\n- Instant message composition indication (RFC 3994).\n- Persistent TCP connections with keep alive.\n- Do not try to send SIP messages larger than 64K via UDP.\n- Integration with libzrtcpp-1.3.0\n- Xsession support to restore Twinkle after system shutdown/startup.\n- Call snd_pcm_state to determine jitter buffer exhaustion (some ALSA \n  implementations gave problems with the old method).\n- SDP parser allows SDP body without terminating CRLF.\n- Russian translation.\n\nBug fixes\n---------\n- SIP parser did not allow white space between header name and colon.\n- With \"send in-dialog requests to proxy\" enabled and transport\n  mode set to \"auto\", in-dialog requests are wrongly sent via TCP.\n- Crash when a too large message is received.\n- Comparison of authentication parameters (e.g. algorithm) were case-sensitive.\n  These comparisons must be case-insensitive.\n- SDP parser could not parse other media transports than RTP/AVP.\n- Twinkle sent 415 response instead of 200 OK on in-dialog INFO without body.\n- Twinkle responds with 513 Message too large on an incoming call.\n- ICMP error on STUN request causes Twinkle to crash.\n- Add received-parameter to Via header of an incoming request if it contains \n  an empty rport parameter (RFC 3581)\n- Twinkle did not add Contact header and copy Record-Route header\n  to 180 response.\n  \nNew RFC's\n---------\nRFC 3994 - Indication of Message Composition for Instant Messaging\n  \n\n8 march 2008 - 1.2\n==================\n- SIP over TCP\n- Automatic selection of IP address.\n  * On a multi-homed machine you do not have to select an IP address/NIC\n    anymore.\n- Support for sending a q-value in a registration contact.\n- Send DTMF on an early media stream.\n- Choose auth over auth-int qop when server supports both for authentication.\n  This avoids problems with SIP ALGs.\n- Support tel-URI in From and To headers in incoming SIP messages.\n- Print a log rotation message at end of log when a log file is full.\n- Remove 20 character limit on profile names.\n- Reject an incoming MESSAGE with 603 if max. sessions == 0\n- Delivery notification when a 202 response is received on a MESSAGE.\n\nBug fixes\n---------\n- When you deactivate a profile that has MWI active, but MWI subscription failed,\n  and subsequently activate this profile again, then Twinkle does not subscribe to\n  MWI.\n- The max redirection value was always set to 1.\n- Leading space in the body of a SIP message causes a parse failure\n- Twinkle crashes with SIGABRT when it receives an INVITE with\n  a CSeq header that contains an invalid method.\n- Latest release of lrelease corrupted translation files.\n- Twinkle crashes on 'twinkle --cmd line'\n- If an MWI NOTIFY does not contain a voice msg summary, twinkle\n  shows a random number for the amount of messages waiting.\n- Depending on the locale Twinkle encoded a q-value with a comma\n  instead of a dot as decimal point.\n\nBuild changes\n-------------\n- Modifications for gcc 4.3.\n- Remove fast sequence of open/close calls for ALSA to avoid\n  problems with bluez.\n\n\n21 july 2007 - 1.1\n==================\n- French translation\n- Presence\n- Instant messaging\n- New CLI commands: presence, message\n\nBug fixes\n---------\n- If a session was on-hold and Twinkle received a re-INVITE without\n  SDP, it would offer SDP on-hold in the 200 OK, instead of a brand \n  new SDP offer.\n- Twinkle refused to change to another profile with the same user name\n  as the current active profile.\n- ICMP processing did not work most times (uninitialized data).\n- Replace strerror by strerror_r (caused rare SIGSEGV crashes)\n- Fix deadlock in timekeeper (caused rare freezes)\n\nNew RFC's\n---------\nRFC 3428 - Session Initiation Protocol (SIP) Extension for Instant Messaging\nRFC 3856 - A Presence Event Package for the Session Initiation Protocol (SIP)\nRFC 3863 - Presence Information Data Format (PIDF)\nRFC 3903 - Session Initiation Protocol (SIP) Extension \n           for Event State Publication\n\n\n19 may 2007 - 1.0.1\n===================\n- Czech translation\n- Check on user profiles having the same contact name at startup.\n- When comparing an incoming INVITE request-URI with the contact-name,\n  ignore the host part to avoid NAT problems.\n- A call to voice mail will not be attached to the \"redial\" button.\n- Added voice mail entry to services and systray menu.\n- New command line options: --show, --hide\n- TWINKLE_LINE environment variable in scripts. This variable contains\n  the line number (starting at 1) associated with a trigger.\n- Preload KAddressbook at startup.\n- Allow multiple occurrences of the display_msg parameter in the incoming call\n  script to create multi-line messages.\n- Handle SIP forking and early media interaction\n\nBug fixes\n---------\n- Fix conference call\n- If lock file still exists when you start Twinkle, Twinkle asks\n  if it should start anyway. When you click 'yes', Twinkle does not start.\n- Audio validation opened soundcard in stereo instead of mono\n- When quitting Twinkle while the call history window is open, a segfault occurs\n- When an incoming call is rejected when only unsupported codecs are offered,\n  it does not show as a missed call in the call history.\n- Segfault when the remote party establishes an early media session without\n  sending a to-tag in the 1XX response (some Cisco devices).\n- in_call_failed trigger was not called when the call failed before ringing.\n- Escape double quote with backslash in display name.\n- On some system Twinkle occasionally crashed at startup with the following\n  error: Xlib: unexpected async reply\n\nBuild Changes\n-------------\n- Remove AC_CHECK_HEADERS([]) from configure.in\n- Configure checks for lrelease.\n\nOther\n-----\n- Very small part of the comments has been formatted now for automatic\n  documentation generation with doxygen.\n\n\n22 jan 2007 - 1.0\n=================\n- Local address book\n- Message waiting indication (MWI)\n  * Solicited MWI as specified by RFC 3842\n  * Unsolicited MWI as implemented by Asterisk\n- Voice mail speed dial\n- Call transfer with consultation\n  * This is a combination of a consultation call on the other line\n    followed by a blind transfer.\n- Attended call transfer\n  * This is a combination of a consultation call on the other line\n    followed by a replacement from B to C of the call on the first line.\n    This is only possible if the C-party supports \"replaces\".\n    If \"replaces\" is not supported, then twinkle automatically falls\n    back to \"transfer with consultation\".\n- User identity hiding\n- Multi language support\n  This version contains Dutch and German translations\n- Send BYE when a CANCEL/2XX INVITE glare occurs.\n- When call release was not immediate due to network problems or protocol errors,\n  the line would be locked for some time. Now Twinkle releases a call in the\n  background immediately freeing the line for new calls.\n- Escape reserved symbols in a URI by their hex-notation (%hex).\n- Changed key binding for Bye from F7 to ESC\n- When a lock file exists at startup, Twinkle asks if you want to override it\n- New command line options: --force, --sip-port, --rtp-port\n- Ring tone and speaker device list now also shows playback only devices\n- Microphone device list now also shows capture only devices\n- Validate audio device settings on startup, before making a call, before\n  answering a call.\n- SIP_FROM_USER, SIP_FROM_HOST, SIP_TO_USER, SIP_TO_HOST variables for call scripts.\n- display_msg parameter added to incoming call script\n- User profile options to indicate which codec preference to follow\n- Twinkle now asks permission for an incoming REFER asynchronously. This\n  prevents blocking of the transaction layer.\n- Highlight missed calls in call history\n- Support for G.726 ATM AAL2 codeword packing\n- replaces SIP extension (RFC 3891)\n- norefesub SIP extension (RFC 4488)\n- SIP parser supports IPv6 addresses in SIP URI's and Via headers\n  (Note: Twinkle does not support transport over IPv6)\n- Support mid-call change of SSRC\n- Handling of SIGCHLD, SIGTERM and SIGINT on platforms implementing\n  LinuxThreads instead of NPTL threading (e.g. sparc)\n\nBug fixes\n---------\n- Invalid speex payload when setting ptime=30 for G.711\n- When editing the active user profile via File -> Change User -> Edit\n  QObject::connect: No such slot MphoneForm::displayUser(t_user*)\n- 32 s after call setup the DTMF button gets disabled.\n- 4XX response on INVITE does not get properly handled.\n  From/To/Subject labels are not cleared. No call history record is made.\n- The dial combobox accepted a newline through copy/past. This corrupted\n  the system settings.\n- When a far-end responds with no supported codecs, Twinkle automatically\n  releases the call. If the far-end sends an invalid response on this\n  release and the user pressed the BYE button, Twinkle crashed.\n- When using STUN the private port was put in the Via header instead of\n  the public port.\n- Twinkle crashes once in a while, while it is just sitting idle.\n\nBuild changes\n-------------\n- If libbind exists then link with libbind, otherwise link with libresolv\n  This solves GLIBC_PRIVATE errors on Fedora\n- Link with libboost_regex or libboost_regex-gcc\n\nNew RFC's\n---------\nRFC 3323 - A Privacy Mechanism for the Session Initiation Protocol (SIP)\nRFC 3325 - Private Extensions to the Session Initiation Protocol (SIP) for\n           Asserted Identity within Trusted Networks\nRFC 3842 - A Message Summary and Message Waiting Indication Event Package \n           for the Session Initiation Protocol (SIP)\nRFC 3891 - The Session Initiation Protocol (SIP) \"Replaces\" Header\nRFC 4488 - Suppression of Session Initiation Protocol (SIP)\n           REFER Method Implicit Subscription\n\n\n01 oct 2006 - Release 0.9\n=========================\n- Supports Phil Zimmermann's ZRTP and SRTP for\n  secure voice communication.\n  ZRTP/SRTP is provided by the latest version (1.5.0) of the\n  GNU ccRTP library.\n  The implementation is interoperable with Zfone beta2\n- SIP INFO method (RFC 2976)\n- DTMF via SIP INFO\n- G.726 codec (16, 24, 32 and 48 kbps modes)\n- Option to hide display\n- CLI command \"answerbye\" to answer an incoming or hangup an established call\n- Switch lines from system tray menu\n- Answer or reject a call from the KDE systray popup on incoming call\n- Icons to indicate line status\n- Default NIC option in system settings\n- Accept SDP offer without m= lines (RFC 3264 section 5, RFC 3725 flow IV)\n\nBug fixes\n---------\n- t_audio::open did not return a value\n- segmentation fault when quitting Twinkle in transient call state\n- Twinkle did not accept message/sipfrag body with a single CRLF at the end\n- user profile could not be changed on service redirect dialog\n- Twinkle did not react to 401/407 authentication challenges for\n  PRACK, REFER, SUBSCRIBE and NOTIFY\n\nBuild changes\n-------------\n- For ZRTP support you need to install libzrtpcpp first. This library\n  comes as an extension library with ccRTP.\n\n\n09 jul 2006 - Release 0.8.1\n===========================\n- Removed iLBC source code from Twinkle. To use iLBC you can\n  link Twinkle with the ilbc library (ilbc package). When you\n  have the ilbc library installed on your system, then Twinkle's\n  configure script will automatically setup the Makefiles to\n  link with the library.\n\nBug fixes\n---------\n- Name and photo lookups in KAddressbook on incoming calls may\n  freeze Twinkle.\n\nBuild improvements\n------------------\n- Added missing includes to userprofile.ui and addressfinder.h\n- Configure has new --without-speex option\n\n\n01 jul 2006 - Release 0.8\n=========================\n- iLBC\n- Make supplementary service settings persistent\n- Lookup name in address book for incoming call\n- Display photo from address book of caller on incoming call\n- Number conversion rules\n- Always popup systray notification (KDE only) on incoming call\n- Add organization and subject to incoming call popup\n- New call script trigger points: incoming call answered, incoming call failed,\n  outgoing call, outgoing call answered, outgoing call failed, local release,\n  remote release.\n- Added 'end' parameter for the incoming call script\n- Option to provision ALSA and OSS devices that are not in the standard list\n  of devices.\n- Option to auto show main window on incoming call\n- Resized the user profile window such that it fits on an 800x600 display\n- Popup the user profile selection window, when the SIP UDP port is occupied\n  during startup of Twinkle, so the user can change to another port.\n- Skip unsupported codecs in user profile during startup\n\nBug fixes\n---------\n- Sometimes the NAT discovery window never closed\n- When RTP timestamps wrap around some RTP packets may be discarded\n- When the dial history contains an entry of insane length, the\n  main window becomes insanely large on next startup\n- On rare occasions, Twinkle could respond to an incoming call for\n  a deactivated user profile.\n- Credentials cache did not get erased when a failure response other\n  than 401/407 was received on a REGISTER with credentials.\n- G.711 enocders amplified soft noise from the microphone.\n\nNewly supported RFC's\n---------------------\nRFC 3951 - Internet Low Bit Rate Codec (iLBC)\nRFC 3952 - Real-time Transport Protocol (RTP) Payload Format\n           for internet Low Bit Rate Codec (iLBC) Speech\n\nBuild notes\n-----------\n- New dependency on libboost-regex (boost package)\n\n\n07 may 2006 - Release 0.7.1\n===========================\n- Check that --call and --cmd arguments are not empty\n- When DTMF transport is \"inband\", then do not signal RFC2833 support in SDP\n\nBug fixes\n---------\n- CLI and non-KDE version hang when stopping ring tone\n- The GUI allowed payload type 96-255 for DTMF and Speex, while\n  maximum value is only 127\n- When a dynamic codec change takes place at the same time as a re-INVITE\n  Twinkle sometimes freezes.\n- Sending RFC 2833 DTMF events fails when codec is speex-wb or speex-uwb\n\n\n29 apr 2006 - Release 0.7\n=========================\n- Speex support (narrow, wide and ultra wide band)\n- Support for dynamic payload numbers for audio codecs in SDP\n- Inband DTMF (option for DTMF transport in user profile)\n- UTF-8 support to properly display non-ASCII characters\n- --cmd command line option to remotely execute CLI commands\n- --immediate command line option to perform --call and --cmd without user\n  confirmation.\n- --set-profile command line option to set the active profile.\n- Support \"?subject=<subject>\" as part of address for --call\n- The status icon are always displayed: gray -> inactive, full color -> active\n- Clicking the registration status icon fetches current registration status\n- Clicking the service icons enables/disables the service\n- Fancier popup from KDE system tray on incoming call.\n- Popup from system tray shows as long as the phone is ringing.\n- Reload button on address form\n- Remove special phone number symbols from dialed strings.\n  This option can be enabled/disabled via the user profile.\n- Remove duplicate entries from the dial history drop down box\n- Specify in the user profile what symbols are special symbols to remove.\n- Changed default for \"use domain to create unique contact header value\" to\n  \"no\"\n- New SIP protocol option: allow SDP change in INVITE responses\n- Do not ask username and password when authentication for an\n  automatic re-regsitration fails. The user may not be at his desk, and \n  the authentication dialog stalls Twinkle.\n- Ask authentication password when user profile contains authentication\n  name, but no password.\n- Improved handling of socket errors when interface goes down temporarily.\n\nBug fixes\n---------\n- If the far end holds a call and then resumes a call while Twinkle has\n  been put locally on-hold, then Twinkle will start recording sound from\n  the mic and send it to the far-end while indicating that the call is\n  still on-hold.\n- Crash on no-op SDP in re-INVITE\n- Twinkle exits when it receives SIGSTOP followed by SIGCONT\n- call release cause in history is incorrect for incoming calls.\n\nBuild improvements\n------------------\n- Break dependency on X11/xpm.h\n\n\n26 feb 2006 - Release 0.6.2\n===========================\n- Graceful termination on reception of SIGINT and SIGTERM\n\nBug fixes\n---------\n- If the URI in a received To-header is not enclosed by '<' and '>', then \n  the tag parameter is erronesouly parsed as a URI parameter instead of a \n  header parameter. This causes failing call setup, tear down, when\n  communicating with a far-end that does not enclose the URI in angle\n  brackets in the To-header.\n- Function to flush OSS buffers flushed a random amount of samples that\n  could cause sound clipping (at start of call and after call hold) when \n  using OSS.\n- In some cases Twinkle added \"user=phone\" to a URI when the URI already\n  had a user parameter.\n\n\n11 feb 2006 - Release 0.6.1\n===========================\n- action=autoanswer added to call script actions\n- Performance improvement of --call parameter\n- Synchronized dial history drop downs on main window and call dialog\n- Dial history drop down lists are stored persistently\n- Redial information is stored persistently\n\nBug fixes\n---------\n- When using STUN Twinkle freezes when making a call and the STUN\n  server does not respond within 200 ms (since version 0.2)\n- Some malformed SIP messages triggered a memory leak in the\n  parser code generated by bison (since version 0.1)\n- The lexical scanner jammed on rubbish input (since version 0.1)\n\n\n05 feb 2006 - Release 0.6\n=========================\n- Custom ring tones (package libsndfile is needed)\n- Twinkle can call a user defineable script for each incoming call.\n  With this script the user can:\n  * reject, redirect or accept a call\n  * define a specific ring tone (distinctive ringing)\n- Missed call indication\n- Call directly from the main window\n- DTMF keys can by typed directly from the keyboard at the main window.\n  Letters are converted to the corresponding digits.\n- Letters can be typed in the DTMF window. They are converted to digits.\n- Call duration in call history\n- Call duration timer while call is established\n- Added --call parameter to command line to instruct Twinkle to make\n  a call\n- Increased expiry timer for outgoing RTP packets to 160 ms\n  With this setting slow sound cards should give better sound quality\n  for the mic.\n- System setting to disable call waiting.\n- System setting to modify hangup behaviour of 3-way call. Hang up both\n  lines or only the active line.\n- Replace dots with underscores in contact value\n- Silently discard packets on the SIP port smaller than 10 bytes\n- User profile option to disable the usage of the domain name in the\n  contact header.\n- Graceful release of calls when quitting Twinkle\n- Changed call hold default from RFC2543 to RFC3264\n\nBug fixes\n---------\n- An '=' in a value of a user profile or system settings parameter\n  caused a syntax error\n- If a default startup profile was renamed, the default startup list\n  was not updated\n- When call was put on-hold using RFC2543 method, the host in the \n  SDP o= line was erroneously set to 0.0.0.0\n- When a response with wrong tags but correct branch was received, a\n  line would hang forever (RFC 3261 did not specify this scenario).\n- If far end responds with 200 OK to CANCEL, but never sends 487 on\n  INVITE as mandated by RFC 3261, then a line would hang forever\n- CPU load was sometimes excessive when using ALSA\n\n\n01 jan 2006 - Release 0.5\n=========================\n- Run multiple user profiles in parallel\n- Add/remove users while Twinkle is running\n- The SIP UDP port and RTP port settings have been moved from the user\n  profile to system settings. Changes of the default values in the user\n  profile will be lost.\n- DNS SRV support for SIP and STUN\n- ICMP processing\n- SIP failover on 503 response\n- SIP and STUN failover on ICMP error\n- When a call is originated from the call history, copy the subject to the\n  call window (prefixed with \"Re:\" when replying to a call).\n- Remove '/' from a phone number taken from KAddressbook. / is used in \n  Germany to separate the area code from the local number.\n- Queue incoming in-dialog request if ACK has not been received yet.\n- Clear credentials cache when user changes realm, username or password\n- Added micro seconds to timestamps in log\n- Detecting a soundcard playing out at slightly less than 8000 samples per\n  second is now done on the RTP queue status. This seems to be more reliable \n  than checking the ALSA sound buffer filling.\n- OSS fragment size and ALSA period size are now changeable via the system\n  settings. Some soundcard problems may be solved by changing these values.\n- Default ALSA period size for capturing lowered from 128 to 32. This seems\n  to give better performance on some sound cards.\n\nBug fixes\n---------\n- With certain ALSA settings (eg. mic=default, speaker=plughw), the ALSA \n  device got locked up after 1 call.\n- The ports used for NAT discovery via STUN stayed open.\n- When a STUN transaction for a media port failed, the GUI did not clear\n  the line information fields.\n- Sending DTMF events took many unnecessary CPU cycles\n- Parse failure when Server or User-Agent header contained comment only\n\nNewly supported RFC's\n---------------------\nRFC 2782 - A DNS RR for specifying the location of services (DNS SRV)\nRFC 3263 - Session Initiation Protocol (SIP): Locating SIP Servers\n\n\n28 nov 2005 - Release 0.4.2\n===========================\n- Microphone noise reduction (can be disabled in system settings)\n- System tray icon shows status of active line and enabled services\n- Call history option added to system tray menu\n\nBug fixes\n---------\n- Twinkle crashes at startup when the systray icon is disabled in the system settings.\n- Line stays forever in dialing state when pressing ESC in the call window\n\n\n19 nov 2005 - Release 0.4.1\n===========================\n- Fixed build problems with gcc-4.0.2 and qt3-3.4.4\n\n18 nov 2005 - Release 0.4\n=========================\n- Interface to KAddressbook\n- History of incoming and outgoing calls (successful and missed calls)\n- History of 10 last calls on call dialog window for redialling\n- Call and service menu options added to KDE sys tray icon\n- Allow a missing mandatory Expires header in a 2XX response on SUBSCRIBE\n- Big Endian support for sound playing (eg. PPC platforms)\n- System setting to start Twinkle hidden in system tray\n- System setting to start with a default profile\n- System setting to start on a default IP address\n- Command line option (-i) for IP address\n\nBug fixes\n---------\n- send a 500 failure response on a request that is received out of order\n  instead of discarding the request.\n- 64bit fix in events.cpp\n- race condition on starting/stopping audio threads could cause a crash\n- segmentation fault when RTP port could not be opened.\n- CLI looped forever on reaching EOF\n- 64bit fix in events.cpp\n- ALSA lib pcm_hw.c:590:(snd_pcm_hw_pause) SNDRV_PCM_IOCTL_PAUSE failed\n- sometimes when quitting Twinkle a segmentation fault occurred\n\nBuild improvements\n------------------\n- Removed platform dependent code from stunRand() in stun.cxx\n- It should be possible to build Twinkle without the KDE addons on a\n  non-KDE system\n- new option --without-kde added to configure to build a non-KDE version\n  of Twinkle\n\n\n22 oct 2005 - Release 0.3.2\n===========================\n- Fixed several build problams with KDE include files and\n  libraries.\n\nIf you already successfully installed release 0.3.1 then there is\nno need to upgrade to 0.3.2 as there is no new functionality.\n\n16 oct 2005 - Release 0.3.1\n===========================\nThis is a minor bug fix release.\n\nBug fixes:\n----------\n- Command line options -f and -share were broken in release 0.3\n  This release fixes the command line options.\n\n\n09 oct 2005 - Release 0.3\n=========================\n\nNew functionality:\n------------------\n- ALSA support\n- System tray icon\n- Send NAT keep alive packets when Twinkle sits behind a symmetric firewall\n  (discovered via STUN)\n- Allow missing or wrong Contact header in a 200 OK response on a REGISTER\n\nBug fixes:\n----------\n- Hostnames in Via and Warning headers were erroneously converted to lower case.\n- t_ts_non_invite::timeout assert( t==TIMER_J ) when ACK is received\n  for a non-INVITE request that had INVITE as method in the CSeq header.\n- The SIP/SDP parser accepted a port number > 65535. This caused an assert\n- Segmentation fault on some syntax errors in SIP headers\n- Line got stuck when CSeq sequence nr 0 was received. RFC 3261 allows 0.\n- With 100rel required, every 1XX after the first 1XX response were discarded.\n- Fixed build problems on 64-bit architectures.\n- Dead lock due to logging in UDP sender.\n- Segmentation fault when packet loss occurred while the sequence\n  number in the RTP packets wrapped around.\n- Route set was not recomputed on reception of a 2XX response, when a 1XX\n  repsonse before already contained a Record-Route header.\n\n\n30 jul 2005 - Release 0.2.1\n===========================\n\nNew functionality:\n------------------\n- Clear button on log view window.\n\nBug fixes:\n----------\n- The system settings window confused the speaker and mic settings.\n- Log view window sometimes opened behind other windows.\n- Segmentation fault when SUBSCRIBE with expires=0 was received to end\n  a refer subscription.\n- When a call transfer fails, the original call is received. If the line\n  for this call is not the active call however, the call should stay\n  on-hold.\n- On rare occasions a segmentation fault occurred when the ring tone\n  was stopped.\n- Log view window sometimes caused deadlock.\n\n\n24 jul 2005 - Release 0.2\n=========================\n\nNew functionality:\n------------------\n- STUN support for NAT traversal\n- Blind call transfer service\n- Reject call transfer request\n- Auto answer service\n- REFER, NOTIFY and SUBSCRIBE support for call transfer scenario's\n  * REFER is sent for blind call transfer. Twinkle accpets incoming\n    NOTIFY messages about the transfer progress.\n    Twinkle can send SUBSCRIBE to extend refer event subscription\n  * Incoming REFER within dialog is handled by Twinkle\n    Twinkle sends NOTIFY messages during transfer.\n    Incoming SUBSCRIBE to extend refer event subscription is granted.\n- Retry re-INVITE after a glare (491 response, RFC 3261 14.1)\n- Respond with 416 if a request with a non-sip URI is received\n- Multiple sound card support for playing ring tone to a different\n  device than speech\n- The To-tag in a 200 OK on a CANCEL was different than the To-tag in a provisional\n  response on the INVITE. RFC 3261 recommends that these To-tags are the same.\n  Twinkle now uses the same To-tag.\n- Show error messages to user when trying to submit invalid values on the\n  user profile\n- DTMF volume configurable via user profile\n- Log viewer\n- User profile wizard\n- Help texts for many input fields (e.g. in user profile). Help can be accessed\n  by pressing Ctrl+F1 or using the question mark from the title bar.\n\nBug fixes:\n----------\n- A retransmission of an incoming INVITE after a 2XX has been sent\n  was seen as a new INVITE.\n- If an OPTIONS request timed out then the GUI did not release its\n  lock causing a deadlock.\n- If the URI in a To, From, Contact or Reply-To header is not \n  enclosed by < and >, then the parameters (separated by a semi-colon) \n  belong to the header, NOT to the URI.\n  They were parsed as parameters of the URI. This could cause the\n  loss of a tag-parameter causing call setup failures.\n- Do not resize window when setting a long string in to, from or subject\n\nNewly supported RFC's\n---------------------\nRFC 3265 - Session Initiation Protocol (SIP)-Specific Event Notification\nRFC 3420 - Internet Media Type message/sipfrag\nRFC 3489 - Simple Traversal of User Datagram Protocol (UDP)\n           Through Network Address Translators (NATs)\nRFC 3515 - The Session Initiation Protocol (SIP) Refer Method\nRFC 3892 - The Session Initiation Protocol (SIP) Referred-By Mechanism\n        \n\n27 apr 2005 - Release 0.1\n=========================\n\nFirst release of Twinkle, a SIP VoIP client.\n\n- Basic calls\n- 2 call appearances (lines)\n- Call Waiting\n- Call Hold\n- 3-way conference calling\n- Mute\n- Call redirection on demand\n- Call redirection unconditional\n- Call redirection when busy\n- Call redirection no answer\n- Reject call redirection request\n- Call reject\n- Do not disturb\n- Send DTMF digits to navigate IVR systems\n- NAT traversal through static provisioning\n- Audio codecs: G.711 A-law, G.711 u-law, GSM\n\nSupported RFC's\n---------------\n- RFC 2327 - SDP: Session Description Protocol\n- RFC 2833 - RTP Payload for DTMF Digits\n- RFC 3261 - SIP: Session Initiation Protocol\n- RFC 3262 - Reliability of Provisional Responses in SIP\n- RFC 3264 - An Offer/Answer Model with the Session Description Protocol (SDP)\n- RFC 3581 - An extension to SIP for Symmetric Response Routing\n- RFC 3550 - RTP: A Transport Protocol for Real-Time Applications\n\nRFC 3261 is not fully implemented yet.\n\n- No TCP transport support, only UDP\n- No DNS SRV support, only DNS A-record lookup\n- Only plain SDP bodies are supported, no multi-part MIME or S/MIME\n- Only sip: URI support, no sips: URI support\n"
  },
  {
    "path": "README.md",
    "content": "[![Build](https://github.com/LubosD/twinkle/actions/workflows/main.yml/badge.svg)](https://github.com/LubosD/twinkle/actions/workflows/main.yml)\n\n# Twinkle\n\nTwinkle is a SIP-based VoIP client.\n\n## Dependencies\n\nTo compile Twinkle you need the following libraries:\n\n* A standard library with C++11 support (at least version 4.9 for libstdc++)\n* ucommon [GNU uCommon C++](http://www.gnu.org/software/commoncpp/)\n* ccRTP (version >= 1.5.0) [GNU RTP Stack](http://www.gnu.org/software/ccrtp/)\n* libxml2\n* libsndfile\n* libmagic\n* libreadline\n* Qt 5 – more specifically, the following submodules:\n  * base\n  * declarative\n  * tools\n\nThe following tools are also required:\n\n* cmake\n* bison\n* flex\n\n### Optional dependencies\n\n* alsa-lib (also known as libasound)\n* libzrtpcpp (version >= 0.9.0) [ZRTP library, ccRTP support must be enabled](https://www.gnu.org/software/ccrtp/zrtp.html)\n* bcg729 [G.729A codec library](http://www.linphone.org/technical-corner/bcg729)\n* Speex and SpeexDSP [Speex codec library](http://www.speex.org/)\n* iLBC [iLBC codec library](http://www.ilbcfreeware.org/)\n* [Akonadi](https://community.kde.org/KDE_PIM/Akonadi) (specifically, the `core` library) and [KContacts](https://cgit.kde.org/kcontacts.git)\n\n## Build\n\nFirst of all, choose which options you want to have enabled.\n\nAll possible options are:\n\n* Qt 5 GUI: `-DWITH_QT5=On` (on by default)\n* D-Bus use: `-DWITH_DBUS=On` (on by default, requires `WITH_QT5`)\n* ALSA support: `-DWITH_ALSA=On` (on by default)\n* ZRTP support: `-DWITH_ZRTP=On`\n* G.729A codec support: `-DWITH_G729=On`\n* Speex codec support: `-DWITH_SPEEX=On`\n* iLBC codec support: `-DWITH_ILBC=On`\n* Diamondcard support: `-DWITH_DIAMONDCARD=On`\n* Akonadi support: `-DWITH_AKONADI=On` (requires `WITH_QT5`)\n\n### Build instructions\n\n\t# Create a subdirectory for the build an enter it\n\tmkdir build && cd build\n\n\t# Run cmake with a list of build options\n\tcmake .. -Dexample_option=On\n\n\t# Build Twinkle\n\tmake\n\n\t# Install Twinkle\n\tmake install\n\n## Shared user data\n\nInstallation will create the following directory for shared user data\non your system:\n\n\t${CMAKE_INSTALL_PREFIX}/share/twinkle\n\nThe typical default value for `CMAKE_INSTALL_PREFIX` is `/usr/local`.\n\n## Application icon\n\nIf you want to create an application link on your desktop you\ncan find an application icon in the shared user data directory:\n\n*\t`twinkle16.png`\t(16x16 icon)\n*\t`twinkle32.png`\t(32x32 icon)\n*\t`twinkle48.png`\t(48x48 icon)\n\n## User data\n\nOn first run Twinkle will create the `.twinkle` directory in your home\ndirectory. In this directory all user data will be put:\n\n* user profiles (`.cfg`)\n* log files (`.log`)\n* system settings (`twinkle.sys`)\n* call history (`twinkle.ch`)\n* lock file (`twinkle.lck`)\n\n## Starting Twinkle\n\nGive the command: `twinkle`\n\n`twinkle -h` will show you some command line options you may use.\n\nNOTE: the CLI option is not fool proof. A command given at a wrong\n      time may crash the program. It is recommended to use the GUI.\n  \nIf you do not specify a configuration file (`-f <profile>`) on the command\nline, then Twinkle will look for configuration files in your \n`.twinkle` directory. \n\nIf you do not have any configuration file, the configuration file\neditor will startup so you can create one. If you have\nconfiguration files, then Twinkle lets you select an \nexisting configuration file. See below for some hints on\nsettings to be made with the profile configuration editor.\n\nIf you specify a configuration file name, then Twinkle will\nsuch for this configuration file in your `.twinkle` directory.\nIf you have put your configuration file in another location\nyou have to specify the full path name for the file, i.e.\nstarting with a slash.\n\nNOTE: the configuration file editor only exists in the GUI.\n      If you run the CLI mode, you must have a configuration file.\n      So first create a configuration file in GUI mode or hand edit\n      a configuration file, before running the CLI mode.\n      If you run the CLI mode and you do not specify a file name\n      on the command line, then Twinkle will use twinkle.cfg\n\n## NAT\n\nIf there is a NAT between you and your SIP server then you have\n3 options to make things work:\n\n1. Your SIP provider uses a Session Border Controller\n2. Your SIP provider offers a STUN server\n3. Make static address mappings in your NAT for SIP and RTP\n\nSTUN can be enabled in the NAT section of the user profile.\n\nFor the static address mappings enable the following in\nthe NAT section of the user profile:\n\n      Use statically configured public IP address inside SIP messages\n\nAnd fill in the public IP address of your NAT.\n\nTwinkle will then use this IP address inside SIP headers and\nSDP bodies instead of the private IP address of your machine.\n\nIn addition you have to add the following port forwardings for UDP\non your NAT\n\n\tpublic:5060 --> private:5060 (for SIP signaling)\n\tpublic:8000 --> private:8000 (for RTP on line 1)\n\tpublic:8001 --> private:8001 (for RTCP on line 1)\n\tpublic:8002 --> private:8002 (for RTP on line 2)\n\tpublic:8003 --> private:8003 (for RTCP on line 2)\n    public:8004 --> private:8004 (for RTP for call transfer)\n    public:8005 --> private:8005 (for RTCP for call transfer)\n\nIf you have changed the SIP/RTP ports in your profile you have\nto change the port forwarding rules likewise.\n\n## Log files\n\nDuring execution Twinkle will create the following log files in\nyour .twinkle directory:\n\n* `twinkle.log` (latest log file)\n* `twinkle.log.old` (previous log file)\n\nWhen `twinkle.log` is full (default is 5 MB) then it is moved to \n`twinkle.log.old` and a new `twinkle.log` is created.\n\nOn startup an existing `twinkle.log` is moved to `twinkle.log.old` and a\nnew `twinkle.log` is created.\n\n## User profile configuration\n\nA user profile contains information about your user account,\nSIP proxy, and several SIP protocol options. If you use Twinkle\nwith different user accounts you may create multiple user\nprofiles.\n\nWhen you create a new profile you first give it a name and\nthen you can make the appropriate settings. The name of the\nprofile is what later on appears in the selection box\nwhen you start Twinkle again. Or you can give the name.cfg\nat the command line (`-f` option) to immediately start that\nprofile.\n\nThe user profile is stored as `<name>.cfg` in the `.twinkle`\ndirectory where `<name>` is the name you gave to the profile.\n\nAt a minimum you have to specify the following:\n\n* User name: this is your SIP user name (eg. phone number)\n* Domain:    the domain of your provider (eg. fwd.pulver.com)\n             this could also be the IP address of your SIP proxy\n\t     if you want to do IP-to-IP dialing (without proxy) then\n\t     fill in the IP address or FQDN of your computer.\n\nIf your SIP proxy does not request authentication and the value you\nfilled in for 'Domain' can be resolved to an IP address by Twinkle,\neg. it is an IP address or an FQDN that is in an A-record of the\nDNS, then you are ready now.\n\n## Authentication\n\nIf your proxy needs authentication, then specify the following fields\nin the SIP authentication box:\n\n* Realm:\tthe realm for authentication\n\t \tyou might leave the realm empty. If you do so, then\n\t\tTwinkle will use the name and password regardless of\n\t\tthe realm put in the challenge by the proxy. For most\n\t\tnetwork setups this is fine. You only need to explicitly\n\t\tspecify a realm when you have call scenario's where\n\t\tyou have to access multiple realms. Then for the realms\n\t\tnot known to Twinkle you will be requested for a login\n\t\twhen needed.\n* Name:\t\tyour authentication name\n* Password:\tyour authentication password\n\nIf authentication fails during registration or any other SIP request\nbecause you filled in wrong values, then Twinkle will at that time\ninteractively request your login and cache it.\n\n## Outbound proxy\n\nAn outbound proxy is only needed if the domain value cannot be resolved\nto an IP address by Twinkle or because your provider demands you to\nuse an outbound proxy that is at a different IP address.\n\nCheck the 'use outbound proxy' check box in the SIP server section.\nFor outbound proxy fill in an IP address or an FQDN that can be\nresolved to an IP address via DNS.\n\nBy default only out-of-dialog requests (eg. REGISTER, OPTIONS, initial\nINVITE) are sent to the outbound proxy. In-dialog requests (eg. re-INVITE,\nBYE) are sent to the target indicated by the far end during call setup.\nBy checking 'send in-dialog requests to proxy' Twinkle will ignore this\ntarget and send these requests also to the proxy. Normally you would\nnot need this. It could be useful in a scenario where the far-end\nindicates a target that cannot be resolved to an IP address.\n\nBy checking \"Do not send a request to proxy if its destination can be\nresolved locally\" will make Twinkle always first try to figure out\nthe destination IP address itself, i.e. based on the request-URI and\nRoute-headers. Only when that fails the outbound-proxy will be tried,\nbut only for the options checked above. I.e. if you did not check\nthe 'in-dialog' option, then an in-dialog request will\nnever go to the proxy. If its destination cannot be resolved, then\nthe request will simply fail.\n\n## Registrar\n\nBy default a REGISTER will be send to the IP address resolved from\nthe domain value or to the outbound proxy if specified.\n\nIf your service provider has a dedicated registrar which is\ndifferent from these IP addresses, then you can specify the\nIP or FQDN of the registrar in the registrar-field.\n\nThe 'expiry' value is the expiry of your registration. Just before\nthe registration expires Twinkle will automatically refresh the\nregistration. The expiry time may be overruled by the registrar.\n\nThe 'registrar at startup option' will make Twinkle automatically \nsend a REGISTER on startup of the profile.  \n\n## Addressing\n\nWhen you invite someone to a call you have to enter an address.\nA SIP address has the following form:\n\n\tsip:<user>@<host-part>\n\nWhere 'user' is a user name or a phone number\nand 'host-part' is a domain name, FQDN or IP address\n\nThe only mandatory part for you to enter is the `<user>`. Twinkle\nwill fill in the other parts if you do not provide them.\nFor the host-part, Twinkle will fill in the value you configured\nas your `domain`.\n\nCurrently `sip:` is the only addressing scheme supported by Twinkle.\n\nMichel de Boer [michel@twinklephone.com]\n\nLubos Dolezel [lubos@dolezel.info]\n\n"
  },
  {
    "path": "THANKS",
    "content": "Thanks to the following people for testing and finding all\nthose lovely bugs:\n\nRichard Bos\nSchelte Bron\nRuud Linders\nJohn van der Ploeg\nMarco van Zijl\n\nThanks to Richard Bos for RPM building and advertising.\n\nThanks to Joerg Reisenweber for his excellent testing and debugging.\n\nThanks to Treeve Jelbert for helping me catching build errors in release 0.4\n\nThanks to James Le Cuirot for helping me debug several ALSA and RTP problems.\n\nQt 4 & 5 porting done by Lubos Dolezel and Michal Kubecek.\n\n"
  },
  {
    "path": "TODO",
    "content": "* make KDE support work again\n\n\tAfter cleaning up the autotools config, twinkle only builds with\n\t--without-kde. This is only temporary, the goal is to allow building\n\tagainst KDE4 libraries (and, preferrably, also KDE5).\n\n* IPv6 support\n\n\tStill missing, AFAIK.\n\n* t_gui locking\n\n\tDirty workaround for (mostly) calling GUI related methods from non-GUI\n\tthreads. Doesn't actually work with Qt4, causing segfaults. Analyze\n\tthe remaining uses and get rid of it.\n\n* MEMMAN_* tracking macros\n\n\tUgly and unreliable (too easy to forget adding them). Either replace\n\tthem with overloaded new and delete operators or kill them completely\n\tand rely on valgrind.\n\n"
  },
  {
    "path": "cmake/CheckCXX11Regex.cmake",
    "content": "# Check if C++11 regular expressions are available and actually work.\n#\n# libstdc++ 4.8 shipped with a buggy prototype for C++11 regular expressions,\n# which barely supports the simplest cases, and will throw an exception at\n# pretty much anything else.  Unfortunately, this does not cause the build to\n# fail (since all the symbols are properly exported), and results instead in\n# mysterious runtime errors.  (See issue #31 for such an example.)\n\ninclude(CMakePushCheckState)\ninclude(CheckCXXSourceRuns)\n\nfunction(check_cxx11_regex result_var)\n\t# When cross-compiling, check_cxx_source_runs() below will cause the\n\t# build to fail.  Since libstdc++ 4.8 is pretty much obsolete by now,\n\t# we may as well assume that everything is working fine.\n\tif(CMAKE_CROSSCOMPILING)\n\t\tmessage(STATUS \"Skipping C++11 regular expressions check due to cross-compilation\")\n\t\t# Returning a true value that we could recognize if needed\n\t\tset(${result_var} 42 PARENT_SCOPE)\n\t\treturn()\n\tendif()\n\n\tcmake_push_check_state()\n\tset(CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS} -std=c++11\")\n\n\t# With libstdc++ 4.8, this will compile but crash at runtime\n\tcheck_cxx_source_runs(\"\n\t    #include <regex>\n\n\t    // Regex copied from Apple's SwiftCheckCXXNativeRegex.cmake\n\t    const std::regex re(\\\"([a]+)\\\");\n\n\t    int main() {\n\t        return 0;\n\t    }\n\t\" ${result_var})\n\tset(${result_var} ${${result_var}} PARENT_SCOPE)\n\n\tcmake_pop_check_state()\nendfunction()\n"
  },
  {
    "path": "cmake/FindCcrtp.cmake",
    "content": "FIND_PATH(CCRTP_INCLUDE_DIR ccrtp/rtp.h)\nFIND_LIBRARY(CCRTP_LIBRARIES NAMES ccrtp)\n\nIF(CCRTP_INCLUDE_DIR AND CCRTP_LIBRARIES)\n\tSET(CCRTP_FOUND TRUE)\nENDIF(CCRTP_INCLUDE_DIR AND CCRTP_LIBRARIES)\n\nIF(CCRTP_FOUND)\n\tIF (NOT Ccrtp_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found ccrtp includes:\t${CCRTP_INCLUDE_DIR}/ccrtp/rtp.h\")\n\t\tMESSAGE(STATUS \"Found ccrtp library:\t${CCRTP_LIBRARIES}\")\n\tENDIF (NOT Ccrtp_FIND_QUIETLY)\nELSE(CCRTP_FOUND)\n\tIF (Ccrtp_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find ccrtp development files\")\n\tENDIF (Ccrtp_FIND_REQUIRED)\nENDIF(CCRTP_FOUND)\n"
  },
  {
    "path": "cmake/FindCommoncpp.cmake",
    "content": "FIND_PATH(COMMONCPP_INCLUDE_DIR commoncpp/config.h)\nFIND_LIBRARY(COMMONCPP_LIBRARIES NAMES commoncpp)\n\nIF(COMMONCPP_INCLUDE_DIR AND COMMONCPP_LIBRARIES)\n\tSET(COMMONCPP_FOUND TRUE)\nENDIF(COMMONCPP_INCLUDE_DIR AND COMMONCPP_LIBRARIES)\n\nIF(COMMONCPP_FOUND)\n\tIF (NOT Commoncpp_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found commoncpp includes:\t${COMMONCPP_INCLUDE_DIR}/commoncpp/config.h\")\n\t\tMESSAGE(STATUS \"Found commoncpp library:\t${COMMONCPP_LIBRARIES}\")\n\tENDIF (NOT Commoncpp_FIND_QUIETLY)\nELSE(COMMONCPP_FOUND)\n\tIF (Commoncpp_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find commoncpp development files\")\n\tENDIF (Commoncpp_FIND_REQUIRED)\nENDIF(COMMONCPP_FOUND)\n"
  },
  {
    "path": "cmake/FindG729.cmake",
    "content": "INCLUDE(CMakePushCheckState)\nINCLUDE(CheckCSourceCompiles)\n\nFIND_PATH(G729_INCLUDE_DIR bcg729/decoder.h)\nFIND_LIBRARY(G729_LIBRARY NAMES bcg729)\n\nIF(G729_INCLUDE_DIR AND G729_LIBRARY)\n\tSET(G729_FOUND TRUE)\n\n\t# The bcg729 API was changed in 1.0.2 to add support for G.729 Annex B.\n\t# This checks whether we are dealing with the old or new API.\n\tCMAKE_PUSH_CHECK_STATE()\n\tSET(CMAKE_REQUIRED_INCLUDES \"${INCLUDE_DIRECTORIES}\" \"${G729_INCLUDE_DIR}\")\n\tSET(CMAKE_REQUIRED_LIBRARIES \"${G729_LIBRARY}\")\n\tSET(CMAKE_REQUIRED_QUIET TRUE)\n\t# Try to compile something using the old (pre-1.0.2) API.\n\t#\n\t# We cannot do it the other way around, as initBcg729EncoderChannel()\n\t# did not have a prototype before 1.0.2, thus compilation would not fail\n\t# when passing it an extra argument.\n\tCHECK_C_SOURCE_COMPILES(\"\n\t\t#include <bcg729/encoder.h>\n\n\t\tint main() {\n\t\t\t/* This function requires an argument since 1.0.2 */\n\t\t\tinitBcg729EncoderChannel();\n\t\t\treturn 0;\n\t\t}\n\t\" G729_OLD_API)\n\tCMAKE_POP_CHECK_STATE()\n\n\tIF (G729_OLD_API)\n\t\tSET(G729_ANNEX_B FALSE)\n\tELSE (G729_OLD_API)\n\t\tSET(G729_ANNEX_B TRUE)\n\tENDIF (G729_OLD_API)\nENDIF(G729_INCLUDE_DIR AND G729_LIBRARY)\n\nIF(G729_FOUND)\n\tIF (NOT G729_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found bcg729 includes:\t${G729_INCLUDE_DIR}/bcg729/decoder.h\")\n\t\tMESSAGE(STATUS \"Found bcg729 library: ${G729_LIBRARY}\")\n\t\tIF (G729_ANNEX_B)\n\t\t\tMESSAGE(STATUS \"bcg729 supports Annex B; using the new (1.0.2) API\")\n\t\tELSE (G729_ANNEX_B)\n\t\t\tMESSAGE(STATUS \"bcg729 does not support Annex B; using the old (pre-1.0.2) API\")\n\t\tENDIF (G729_ANNEX_B)\n\tENDIF (NOT G729_FIND_QUIETLY)\nELSE(G729_FOUND)\n\tIF (G729_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find bcg729 development files\")\n\tENDIF (G729_FIND_REQUIRED)\nENDIF(G729_FOUND)\n"
  },
  {
    "path": "cmake/FindGsm.cmake",
    "content": "FIND_PATH(GSM_INCLUDE_DIR gsm/gsm.h)\nFIND_LIBRARY(GSM_LIBRARY NAMES gsm)\n\nIF(GSM_INCLUDE_DIR AND GSM_LIBRARY)\n\tSET(GSM_FOUND TRUE)\nENDIF(GSM_INCLUDE_DIR AND GSM_LIBRARY)\n\nIF(GSM_FOUND)\n\tIF (NOT Gsm_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found gsm includes:\t${GSM_INCLUDE_DIR}/gsm/config.h\")\n\t\tMESSAGE(STATUS \"Found gsm library:\t${GSM_LIBRARY}\")\n\tENDIF (NOT Gsm_FIND_QUIETLY)\nELSE(GSM_FOUND)\n\tIF (Gsm_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find gsm development files\")\n\tENDIF (Gsm_FIND_REQUIRED)\nENDIF(GSM_FOUND)\n"
  },
  {
    "path": "cmake/FindIlbc.cmake",
    "content": "include (CMakePushCheckState)\ninclude (CheckCXXSourceCompiles)\n\nFIND_PATH(ILBC_INCLUDE_DIR ilbc/iLBC_decode.h)\nFIND_LIBRARY(ILBC_LIBRARIES NAMES ilbc)\n\nIF(ILBC_INCLUDE_DIR AND ILBC_LIBRARIES)\n\tSET(ILBC_FOUND TRUE)\n\n\t# Check if libilbc can be used without 'extern \"C\"'\n\tCMAKE_PUSH_CHECK_STATE()\n\tLIST(APPEND CMAKE_REQUIRED_INCLUDES \"${ILBC_INCLUDE_DIR}\")\n\tLIST(APPEND CMAKE_REQUIRED_LIBRARIES \"-lilbc\")\n\tSET(CMAKE_REQUIRED_QUIET TRUE)\n\tCHECK_CXX_SOURCE_COMPILES(\"\n\t\t#include <ilbc/iLBC_decode.h>\n\n\t\tint main() {\n\t\t\tiLBC_Dec_Inst_t *iLBCdec_inst;\n\t\t\tinitDecode(iLBCdec_inst, 0, 0);\n\t\t\treturn 0;\n\t\t}\n\t\" ILBC_CPP)\n\tCMAKE_POP_CHECK_STATE()\nENDIF(ILBC_INCLUDE_DIR AND ILBC_LIBRARIES)\n\nIF(ILBC_FOUND)\n\tIF (NOT Ilbc_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found ilbc includes:\t${ILBC_INCLUDE_DIR}/ilbc/iLBC_decode.h\")\n\t\tMESSAGE(STATUS \"Found ilbc library: ${ILBC_LIBRARIES}\")\n\tENDIF (NOT Ilbc_FIND_QUIETLY)\nELSE(ILBC_FOUND)\n\tIF (Ilbc_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find ilbc development files\")\n\tENDIF (Ilbc_FIND_REQUIRED)\nENDIF(ILBC_FOUND)\n"
  },
  {
    "path": "cmake/FindLibMagic.cmake",
    "content": "# - Try to find libmagic header and library\n#\n# Usage of this module as follows:\n#\n#     find_package(LibMagic)\n#\n# Variables used by this module, they can change the default behaviour and need\n# to be set before calling find_package:\n#\n#  LibMagic_ROOT_DIR         Set this variable to the root installation of\n#                            libmagic if the module has problems finding the\n#                            proper installation path.\n#\n# Variables defined by this module:\n#\n#  LIBMAGIC_FOUND              System has libmagic and magic.h\n#  LibMagic_FILE_EXE           Path to the 'file' command (if available)\n#  LibMagic_VERSION            Version of libmagic (if available)\n#  LibMagic_LIBRARY            The libmagic library\n#  LibMagic_INCLUDE_DIR        The location of magic.h\n\nfind_path(LibMagic_ROOT_DIR\n    NAMES include/magic.h\n)\n\nif (${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n    # the static version of the library is preferred on OS X for the\n    # purposes of making packages (libmagic doesn't ship w/ OS X)\n    set(libmagic_names libmagic.a magic)\nelse ()\n    set(libmagic_names magic)\nendif ()\n\nfind_program(LibMagic_FILE_EXE\n    NAMES file\n    HINTS ${LibMagic_ROOT_DIR}/bin\n)\n\nfind_library(LibMagic_LIBRARY\n    NAMES ${libmagic_names}\n    HINTS ${LibMagic_ROOT_DIR}/lib\n)\n\nfind_path(LibMagic_INCLUDE_DIR\n    NAMES magic.h\n    HINTS ${LibMagic_ROOT_DIR}/include\n)\n\nif (LibMagic_FILE_EXE)\n    execute_process(COMMAND \"${LibMagic_FILE_EXE}\" --version\n                    ERROR_VARIABLE  LibMagic_VERSION\n                    OUTPUT_VARIABLE LibMagic_VERSION)\n    string(REGEX REPLACE \"^file-([0-9.]+).*$\" \"\\\\1\"\n           LibMagic_VERSION \"${LibMagic_VERSION}\")\n    message(STATUS \"libmagic version: ${LibMagic_VERSION}\")\nelse ()\n    set(LibMagic_VERSION NOTFOUND)\nendif ()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(LibMagic DEFAULT_MSG\n    LibMagic_LIBRARY\n    LibMagic_INCLUDE_DIR\n)\n\nmark_as_advanced(\n    LibMagic_ROOT_DIR\n    LibMagic_FILE_EXE\n    LibMagic_VERSION\n    LibMagic_LIBRARY\n    LibMagic_INCLUDE_DIR\n)\n"
  },
  {
    "path": "cmake/FindLibSndfile.cmake",
    "content": "FIND_PATH(LIBSNDFILE_INCLUDE_DIR sndfile.h)\n\nSET(LIBSNDFILE_NAMES ${LIBSNDFILE_NAMES} sndfile)\nFIND_LIBRARY(LIBSNDFILE_LIBRARY NAMES ${LIBSNDFILE_NAMES} PATH)\n\nIF(LIBSNDFILE_INCLUDE_DIR AND LIBSNDFILE_LIBRARY)\n\tSET(LIBSNDFILE_FOUND TRUE)\nENDIF(LIBSNDFILE_INCLUDE_DIR AND LIBSNDFILE_LIBRARY)\n\nIF(LIBSNDFILE_FOUND)\n\tIF(NOT LibSndfile_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found LibSndfile: ${LIBSNDFILE_LIBRARY}\")\n\tENDIF (NOT LibSndfile_FIND_QUIETLY)\nELSE(LIBSNDFILE_FOUND)\n\tIF(LibSndfile_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could not find sndfile\")\n\tENDIF(LibSndfile_FIND_REQUIRED)\nENDIF (LIBSNDFILE_FOUND)\n"
  },
  {
    "path": "cmake/FindReadline.cmake",
    "content": "# - Try to find readline include dirs and libraries \n#\n# Usage of this module as follows:\n#\n#     find_package(Readline)\n#\n# Variables used by this module, they can change the default behaviour and need\n# to be set before calling find_package:\n#\n#  Readline_ROOT_DIR         Set this variable to the root installation of\n#                            readline if the module has problems finding the\n#                            proper installation path.\n#\n# Variables defined by this module:\n#\n#  READLINE_FOUND            System has readline, include and lib dirs found\n#  Readline_INCLUDE_DIR      The readline include directories. \n#  Readline_LIBRARY          The readline library.\n\nfind_path(Readline_ROOT_DIR\n    NAMES include/readline/readline.h\n)\n\nfind_path(Readline_INCLUDE_DIR\n    NAMES readline/readline.h\n    HINTS ${Readline_ROOT_DIR}/include\n)\n\nfind_library(Readline_LIBRARY\n    NAMES readline\n    HINTS ${Readline_ROOT_DIR}/lib\n)\n\nif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)\n  set(READLINE_FOUND TRUE)\nelse(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)\n  FIND_LIBRARY(Readline_LIBRARY NAMES readline)\n  include(FindPackageHandleStandardArgs)\n  FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )\n  MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)\nendif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)\n\nmark_as_advanced(\n    Readline_ROOT_DIR\n    Readline_INCLUDE_DIR\n    Readline_LIBRARY\n)\n"
  },
  {
    "path": "cmake/FindSpeex.cmake",
    "content": "FIND_PATH(SPEEX_INCLUDE_DIR speex/speex.h)\nFIND_LIBRARY(SPEEX_LIBRARY NAMES speex)\nFIND_LIBRARY(SPEEXDSP_LIBRARY NAMES speexdsp)\n\nIF(SPEEX_INCLUDE_DIR AND SPEEX_LIBRARY AND SPEEXDSP_LIBRARY)\n\tSET(SPEEX_FOUND TRUE)\n\tSET(SPEEX_LIBRARIES ${SPEEX_LIBRARY} ${SPEEXDSP_LIBRARY})\nENDIF(SPEEX_INCLUDE_DIR AND SPEEX_LIBRARY AND SPEEXDSP_LIBRARY)\n\nIF(SPEEX_FOUND)\n\tIF (NOT Speex_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found speex includes:\t${SPEEX_INCLUDE_DIR}/speex/speex.h\")\n\t\tMESSAGE(STATUS \"Found speex library:\t${SPEEX_LIBRARIES}\")\n\tENDIF (NOT Speex_FIND_QUIETLY)\nELSE(SPEEX_FOUND)\n\tIF (Speex_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find speex development files\")\n\tENDIF (Speex_FIND_REQUIRED)\nENDIF(SPEEX_FOUND)\n"
  },
  {
    "path": "cmake/FindUcommon.cmake",
    "content": "FIND_PATH(UCOMMON_INCLUDE_DIR ucommon/ucommon.h)\nFIND_LIBRARY(UCOMMON_LIBRARIES NAMES ucommon)\nFIND_LIBRARY(USECURE_LIBRARIES NAMES usecure)\n\nIF(UCOMMON_INCLUDE_DIR AND UCOMMON_LIBRARIES AND USECURE_LIBRARIES)\n\tSET(UCOMMON_FOUND TRUE)\n\tSET(UCOMMON_LIBRARIES ${UCOMMON_LIBRARIES} ${USECURE_LIBRARIES})\nENDIF(UCOMMON_INCLUDE_DIR AND UCOMMON_LIBRARIES AND USECURE_LIBRARIES)\n\nIF(UCOMMON_FOUND)\n\tIF (NOT Ucommon_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found ucommon includes:\t${UCOMMON_INCLUDE_DIR}/ucommon/ucommon.h\")\n\t\tMESSAGE(STATUS \"Found ucommon library:\t${UCOMMON_LIBRARIES}\")\n\tENDIF (NOT Ucommon_FIND_QUIETLY)\nELSE(UCOMMON_FOUND)\n\tIF (Ucommon_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find ucommon development files\")\n\tENDIF (Ucommon_FIND_REQUIRED)\nENDIF(UCOMMON_FOUND)\n"
  },
  {
    "path": "cmake/FindZrtpcpp.cmake",
    "content": "FIND_PATH(ZRTPCPP_INCLUDE_DIR libzrtpcpp/zrtpccrtp.h)\nFIND_LIBRARY(ZRTPCPP_LIBRARIES NAMES zrtpcpp)\n\nIF(ZRTPCPP_INCLUDE_DIR AND ZRTPCPP_LIBRARIES)\n\tSET(ZRTPCPP_FOUND TRUE)\nENDIF(ZRTPCPP_INCLUDE_DIR AND ZRTPCPP_LIBRARIES)\n\nIF(ZRTPCPP_FOUND)\n\tIF (NOT Zrtpcpp_FIND_QUIETLY)\n\t\tMESSAGE(STATUS \"Found libzrtpcpp includes:\t${ZRTPCPP_INCLUDE_DIR}/libzrtpcpp/zrtpccrtp.h\")\n\t\tMESSAGE(STATUS \"Found libzrtpcpp library:\t${ZRTPCPP_LIBRARIES}\")\n\n\t\tSET(ZRTPCPP_INCLUDE_DIR ${ZRTPCPP_INCLUDE_DIR} \"${ZRTPCPP_INCLUDE_DIR}/libzrtpcpp\")\n\tENDIF (NOT Zrtpcpp_FIND_QUIETLY)\nELSE(ZRTPCPP_FOUND)\n\tIF (Zrtpcpp_FIND_REQUIRED)\n\t\tMESSAGE(FATAL_ERROR \"Could NOT find libzrtpcpp development files\")\n\tENDIF (Zrtpcpp_FIND_REQUIRED)\nENDIF(ZRTPCPP_FOUND)\n"
  },
  {
    "path": "data/providers.csv",
    "content": "# provider;domain;sip proxy;stun server\nsipgate.at;sipgate.at;;sipgate.at\nsipgate.co.uk;sipgate.co.uk;;sipgate.co.uk\nsipgate.de;sipgate.de;;sipgate.de\nSIPphone!;proxy01.sipphone.com;;stun01.sipphone.com\n"
  },
  {
    "path": "data/twinkle.1",
    "content": ".\\\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.47.2.\n.TH TWINKLE \"1\" \"January 2016\" \"Twinkle 1.10.0 - 15 July 2016\" \"User Commands\"\n.SH NAME\nTwinkle \\- Twinkle 1.10.0\n.SH SYNOPSIS\n.B twinkle\n[\\fI\\,options\\/\\fR]\n.SH OPTIONS\n.TP\n\\fB\\-c\\fR\nRun in command line interface (CLI) mode\n.TP\n\\fB\\-\\-share\\fR <dir>\nSet the share directory.\n.TP\n\\fB\\-f\\fR <profile>\nStartup with a specific profile. You will not be requested\nto choose a profile at startup. The profiles that you created\nare the .cfg files in your .twinkle directory.\nYou may specify multiple profiles separated by spaces.\n.TP\n\\fB\\-\\-force\\fR\nIf a lock file is detected at startup, then override it\nand startup.\n.HP\n\\fB\\-\\-sip\\-port\\fR <port>\n.IP\nPort for SIP signalling.\nThis port overrides the port from the system settings.\n.HP\n\\fB\\-\\-rtp\\-port\\fR <port>\n.IP\nPort for RTP.\nThis port overrides the port from the system settings.\n.HP\n\\fB\\-\\-call\\fR <address>\n.IP\nInstruct Twinkle to call the address.\nWhen Twinkle is already running, this will instruct the running\nprocess to call the address.\nThe address may be a full or partial SIP URI. A partial SIP URI\nwill be completed with the information from the user profile.\n.IP\nA subject may be passed by appending '?subject=<subject>'\nto the address.\n.IP\nExamples:\ntwinkle \\fB\\-\\-call\\fR 123456\ntwinkle \\fB\\-\\-call\\fR sip:example@example.com?subject=hello\n.HP\n\\fB\\-\\-cmd\\fR <cli command>\n.IP\nInstruct Twinkle to execute the CLI command. You can run\nall commands from the command line interface mode.\nWhen Twinkle is already running, this will instruct the running\nprocess to execute the CLI command.\n.IP\nExamples:\ntwinkle \\fB\\-\\-cmd\\fR answer\ntwinkle \\fB\\-\\-cmd\\fR mute\ntwinkle \\fB\\-\\-cmd\\fR 'transfer 12345'\n.TP\n\\fB\\-\\-immediate\\fR\nThis option can be used in conjunction with \\fB\\-\\-call\\fR or \\fB\\-\\-cmd\\fR\nIt indicates the the command or call is to be performed\nimmediately without asking the user for any confirmation.\n.HP\n\\fB\\-\\-set\\-profile\\fR <profile>\n.IP\nMake <profile> the active profile.\nWhen using this option in conjunction with \\fB\\-\\-call\\fR and \\fB\\-\\-cmd\\fR,\nthen the profile is activated before executing \\fB\\-\\-call\\fR or\n\\fB\\-\\-cmd\\fR.\n.TP\n\\fB\\-\\-show\\fR\nInstruct a running instance of Twinkle to show the main window\nand take focus.\n.TP\n\\fB\\-\\-hide\\fR\nInstruct a running instance of Twinkle to hide in the system tray.\nIf no system tray is used, then Twinkle will minimize.\n.HP\n\\fB\\-\\-help\\-cli\\fR [cli command]\n.IP\nWithout a cli command this option lists all available CLI\ncommands. With a CLI command this option prints help on\nthe CLI command.\n.TP\n\\fB\\-\\-version\\fR\nGet version information.\n.SH COPYRIGHT\nCopyright \\(co 2005\\-2015  Michel de Boer and contributors\nhttp://twinkle.dolezel.info\n.PP\nBuilt with support for: ALSA, Speex, iLBC, ZRTP\n.PP\nContributions:\n* Werner Dittmann (ZRTP/SRTP)\n* Bogdan Harjoc (AKAv1\\-MD5, Service\\-Route)\n* Roman Imankulov (command line editing)\n* Ondrej Moris (codec preprocessing)\n* Rickard Petzall (ALSA)\n.PP\nThis software contains the following software from 3rd parties:\n* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin\n* G.711/G.726 codecs from Sun Microsystems (public domain)\n* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)\n* Parts of the STUN project at http://sourceforge.net/projects/stun\n* Parts of libsrv at http://libsrv.sourceforge.net/\n.PP\nFor RTP the following dynamic libraries are linked:\n* GNU ccRTP \\- http://www.gnu.org/software/ccrtp\n* GNU uCommon C++ \\- https://www.gnu.org/software/commoncpp/\n.PP\nTwinkle comes with ABSOLUTELY NO WARRANTY.\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n.SH \"SEE ALSO\"\nThe full documentation for\n.B Twinkle\nis maintained as a Texinfo manual.  If the\n.B info\nand\n.B Twinkle\nprograms are properly installed at your site, the command\n.IP\n.B info Twinkle\n.PP\nshould give you access to the complete manual.\n"
  },
  {
    "path": "sip.protocol",
    "content": "[Protocol]\nexec=twinkle --call %u\nprotocol=sip\ninput=none\noutput=none\nhelper=true\nlisting=\nreading=false\nwriting=false\nmakedir=false\ndeleting=false\nIcon=multimedia\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "project(libtwinkle)\n\ninclude_directories(\"${CMAKE_CURRENT_SOURCE_DIR}\")\n\nadd_subdirectory(audio)\nif (NOT WITH_GSM)\n\tadd_subdirectory(audio/gsm/src)\nendif (NOT WITH_GSM)\nadd_subdirectory(audits)\nadd_subdirectory(im)\nadd_subdirectory(mwi)\nadd_subdirectory(parser)\nadd_subdirectory(patterns)\nadd_subdirectory(presence)\nadd_subdirectory(sdp)\nadd_subdirectory(sockets)\nadd_subdirectory(stun)\nadd_subdirectory(threads)\nadd_subdirectory(utils)\n\nset(LIBTWINKLE_SRCS\n\tabstract_dialog.cpp\n\taddress_book.cpp\n\tauth.cpp\n\tcall_history.cpp\n\tcall_script.cpp\n\tclient_request.cpp\n\tcmd_socket.cpp\n\tdialog.cpp\n\tdiamondcard.cpp\n\tepa.cpp\n\tevents.cpp\n\tid_object.cpp\n\tline.cpp\n\tlistener.cpp\n\tlog.cpp\n\tphone.cpp\n\tphone_user.cpp\n\tprohibit_thread.cpp\n\tredirect.cpp\n\tsender.cpp\n\tservice.cpp\n\tsession.cpp\n\tsub_refer.cpp\n\tsubscription.cpp\n\tsubscription_dialog.cpp\n\tsys_settings.cpp\n\ttimekeeper.cpp\n\ttransaction.cpp\n\ttransaction_layer.cpp\n\ttransaction_mgr.cpp\n\tuser.cpp\n\tuserintf.cpp\n\tutil.cpp\n)\n\nadd_library(libtwinkle OBJECT ${LIBTWINKLE_SRCS})\n\nset(twinkle_OBJS\n\t$<TARGET_OBJECTS:libtwinkle>\n\t$<TARGET_OBJECTS:libtwinkle-audio>\n\t$<TARGET_OBJECTS:libtwinkle-audits>\n\t$<TARGET_OBJECTS:libtwinkle-im>\n\t$<TARGET_OBJECTS:libtwinkle-mwi>\n\t$<TARGET_OBJECTS:libtwinkle-parser>\n\t$<TARGET_OBJECTS:libtwinkle-patterns>\n\t$<TARGET_OBJECTS:libtwinkle-presence>\n\t$<TARGET_OBJECTS:libtwinkle-sdp>\n\t$<TARGET_OBJECTS:libtwinkle-sockets>\n\t$<TARGET_OBJECTS:libtwinkle-stun>\n\t$<TARGET_OBJECTS:libtwinkle-threads>\n\t$<TARGET_OBJECTS:libtwinkle-utils>\n)\nif (NOT WITH_GSM)\n\tlist(APPEND twinkle_OBJS $<TARGET_OBJECTS:libtwinkle-gsm>)\nendif (NOT WITH_GSM)\n\nadd_executable(twinkle-console\n\tmain.cpp\n\t${twinkle_OBJS}\n)\n\nset(twinkle_LIBS\n\t${ATOMIC_LIBRARY}\n\t-lpthread\n\t${RESOLV_LIBRARY}\n\t${LibMagic_LIBRARY}\n\t${LIBXML2_LIBRARIES}\n\t${Readline_LIBRARY}\n\t${ILBC_LIBRARIES}\n\t${SPEEX_LIBRARIES}\n\t${ZRTPCPP_LIBRARIES}\n\t${CCRTP_LIBRARIES}\n\t${COMMONCPP_LIBRARIES}\n\t${UCOMMON_LIBRARIES}\n\t${LIBSNDFILE_LIBRARY}\n\t${ALSA_LIBRARY}\n\t${G729_LIBRARY}\n)\nif (WITH_GSM)\n\tlist(APPEND twinkle_LIBS ${GSM_LIBRARY})\nendif (WITH_GSM)\n\nif (WITH_QT5)\n\tadd_subdirectory(gui)\nendif (WITH_QT5)\n\ntarget_link_libraries(twinkle-console ${twinkle_LIBS})\n\ninstall(TARGETS twinkle-console DESTINATION bin)\n\n\n"
  },
  {
    "path": "src/abstract_dialog.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdlib>\n#include <assert.h>\n#include <iostream>\n#include \"abstract_dialog.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"phone_user.h\"\n#include \"sockets/ipaddr.h\"\n#include \"util.h\"\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n\nextern string\t\tuser_host;\nextern t_phone\t\t*phone;\n\n// Private\nvoid t_abstract_dialog::remove_client_request(t_client_request **cr) {\n\tif ((*cr)->dec_ref_count() == 0) {\n\t\tMEMMAN_DELETE(*cr);\n\t\tdelete *cr;\n\t}\n\n\t*cr = NULL;\n}\n\n// Create a request within a dialog\n// RFC 3261 12.2.1.1\nt_request *t_abstract_dialog::create_request(t_method m) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tt_request *r = new t_request(m);\n\tMEMMAN_NEW(r);\n\n\t// To header\n\tr->hdr_to.set_uri(remote_uri);\n\tr->hdr_to.set_display(remote_display);\n\tr->hdr_to.set_tag(remote_tag);\n\n\t// From header\n\tr->hdr_from.set_uri(local_uri);\n\tr->hdr_from.set_display(local_display);\n\tr->hdr_from.set_tag(local_tag);\n\n\t// Call-ID header\n\tr->hdr_call_id.set_call_id(call_id);\n\n\t// CSeq header\n\tr->hdr_cseq.set_method(m);\n\tr->hdr_cseq.set_seqnr(++local_seqnr);\n\n\t// Set Max-Forwards header\n\tr->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);\n\n\t// User-Agent\n\tSET_HDR_USER_AGENT(r->hdr_user_agent);\n\n\t// RFC 3261 12.2.1.1\n\t// Request URI and Route header\n\tr->set_route(remote_target_uri, route_set);\n        \n        // Caculate destination set. A DNS request can result in multiple\n        // IP address. In failover scenario's the request must be sent to\n        // the next IP address in the list. As the request will be copied\n        // in various places, the destination set must be calculated now.\n        // In previous version the DNS request was done by the transaction\n        // manager. This is too late as the transaction manager gets a copy\n        // of the request. The destination set should be set in the copy\n        // kept by the dialog.\n        r->calc_destinations(*user_config);\n        \n        // The Via header can only be created after the destinations\n        // are calculated, because the destination deterimines which\n        // local IP address should be used.\n        \n        // Via header\n        IPaddr local_ip = r->get_local_ip();\n\tt_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));\n\tr->hdr_via.add_via(via);\n\n\treturn r;\n}\n\nvoid t_abstract_dialog::create_route_set(t_response *r) {\n\t// Originally the check was this:\n\t// if (route_set.empty() && r->hdr_record_route.is_populated())\n\t// This prevented the route set from being altered between a 18X response\n\t// and a 2XX response. This is allowed per RFC 3261 13.2.2.4\n\tif (r->hdr_record_route.is_populated())\n\t{\n\t\troute_set = r->hdr_record_route.route_list;\n\t\troute_set.reverse();\n\t} else {\n\t\troute_set.clear();\n\t}\n}\n\nvoid t_abstract_dialog::create_remote_target(t_response *r) {\n\tif (r->hdr_contact.is_populated()) {\n\t\tremote_target_uri = r->hdr_contact.contact_list.front().uri;\n\t\tremote_target_display = r->hdr_contact.contact_list.front().display;\n\t}\n}\n\nvoid t_abstract_dialog::resend_request(t_client_request *cr) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_request *req = cr->get_request();\n\n\t// A new sequence number must be assigned\n\treq->hdr_cseq.set_seqnr(++local_seqnr);\n\n\t// Create a new via-header. Otherwise the\n\t// request will be seen as a retransmission\n\tIPaddr local_ip = req->get_local_ip();\n\treq->hdr_via.via_list.clear();\n\tt_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));\n\treq->hdr_via.add_via(via);\n\n\tcr->renew(0);\n\tsend_request(req, cr->get_tuid());\n}\n\nbool t_abstract_dialog::resend_request_auth(t_client_request *cr, t_response *resp) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_request *req = cr->get_request();\n\n\t// Add authorization header, increment CSeq and create new branch id\n\tif (phone->authorize(user_config, req, resp)) {\n\t\tresend_request(cr);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool t_abstract_dialog::redirect_request(t_client_request *cr, t_response *resp,\n\t\tt_contact_param &contact) \n{\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\t// If the response is a 3XX response then add redirection contacts\n\tif (resp->get_class() == R_3XX  && resp->hdr_contact.is_populated()) {\n\t\tcr->redirector.add_contacts(\n\t\t\t\t\tresp->hdr_contact.contact_list);\n\t}\n\n\t// Get next destination\n\tif (!cr->redirector.get_next_contact(contact)) {\n\t\t// There is no next destination\n\t\treturn false;\n\t}\n\n\tt_request *req = cr->get_request();\n\n\t// Ask user for permission to redirect if indicated by user config\n\tif (user_config->get_ask_user_to_redirect()) {\n\t\tif(!ui->cb_ask_user_to_redirect_request(user_config,\n\t\t\t\tcontact.uri, contact.display, resp->hdr_cseq.method)) \n\t\t{\n\t\t\t// User did not permit to redirect\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Change the request URI to the new URI.\n\t// As the URI changes the destination set must be recalculated\n\treq->uri = contact.uri;\n\treq->calc_destinations(*user_config);\n\n\tresend_request(cr);\n\n\treturn true;\n}\n\nbool t_abstract_dialog::failover_request(t_client_request *cr) {\n\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\t\"t_abstract_dialog::failover_request\");\n\t\n\tt_request *req = cr->get_request();\n\t\n\t// Get next destination\n\tif (!req->next_destination()) {\n\t\tlog_file->write_report(\"No next destination for failover.\",\n\t\t\t\t\"t_abstract_dialog::failover_request\");\n\t\treturn false;\n\t}\n\t\n\tresend_request(cr);\n\treturn true;\n}\n\n\n////////////\n// Public\n////////////\n\nt_abstract_dialog::t_abstract_dialog(t_phone_user *pu) :\n\tt_id_object()\n{\n\tassert(pu);\n\tphone_user = pu;\n\tcall_id_owner = false;\n\t\n\tlocal_seqnr = 0;\n\tremote_seqnr = 0;\n\tremote_seqnr_set = false;\n\t\n\tlocal_resp_nr = 0;\n\tremote_resp_nr = 0;\n\t\n\tremote_ip_port.clear();\n\t\n\tlog_file->write_header(\"t_abstract_dialog::t_abstract_dialog\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Created dialog, id=\");\n\tlog_file->write_raw(get_object_id());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n}\n\nt_abstract_dialog::~t_abstract_dialog() {\n\tlog_file->write_header(\"t_abstract_dialog::~t_abstract_dialog\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Destroy dialog, id=\");\n\tlog_file->write_raw(get_object_id());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n}\n\nt_user *t_abstract_dialog::get_user(void) const {\n\treturn phone_user->get_user_profile();\n}\n\nvoid t_abstract_dialog::recvd_response(t_response *r, t_tuid tuid, t_tid tid) {\n\t// The source address and port of a message may be 0 when the\n\t// message was sent internally.\n\tif (!r->src_ip_port.is_null()) {\n\t\tremote_ip_port = r->src_ip_port;\n\t}\n}\n\nvoid t_abstract_dialog::recvd_request(t_request *r, t_tuid tuid, t_tid tid) {\n\t// The source address and port of a message may be 0 when the\n\t// message was sent internally.\n\tif (!r->src_ip_port.is_null()) {\n\t\tremote_ip_port = r->src_ip_port;\n\t}\n}\n\nbool t_abstract_dialog::match_response(t_response *r, t_tuid tuid) {\n\treturn (call_id == r->hdr_call_id.call_id &&\n\t\tlocal_tag == r->hdr_from.tag &&\n\t\t(remote_tag.size() == 0 || remote_tag == r->hdr_to.tag));\n}\n\nbool t_abstract_dialog::match_request(t_request *r) {\n\treturn match(r->hdr_call_id.call_id, r->hdr_to.tag, r->hdr_from.tag);\n}\n\nbool t_abstract_dialog::match_partial_request(t_request *r) {\n\treturn (r->hdr_call_id.call_id == call_id &&\n\t        r->hdr_to.tag == local_tag);\n}\n\nbool t_abstract_dialog::match(const string &_call_id, const string &to_tag, \n\t\tconst string &from_tag) const\n{\n\treturn (call_id == _call_id &&\n\t\tlocal_tag == to_tag &&\n\t\tremote_tag == from_tag);\n}\n\nt_url t_abstract_dialog::get_remote_target_uri(void) const {\n\treturn remote_target_uri;\n}\n\nstring t_abstract_dialog::get_remote_target_display(void) const {\n\treturn remote_target_display;\n}\n\nt_url t_abstract_dialog::get_remote_uri(void) const {\n\treturn remote_uri;\n}\n\nstring t_abstract_dialog::get_remote_display(void) const {\n\treturn remote_display;\n}\n\nt_ip_port t_abstract_dialog::get_remote_ip_port(void) const {\n\treturn remote_ip_port;\n}\n\nstring t_abstract_dialog::get_call_id(void) const {\n\treturn call_id;\n}\n\nstring t_abstract_dialog::get_local_tag(void) const {\n\treturn local_tag;\n}\n\nstring t_abstract_dialog::get_remote_tag(void) const {\n\treturn remote_tag;\n}\n\nbool t_abstract_dialog::remote_extension_supported(const string &extension) const {\n\treturn (remote_extensions.find(extension) != remote_extensions.end());\n}\n\nbool t_abstract_dialog::is_call_id_owner(void) const {\n\treturn call_id_owner;\n}\n"
  },
  {
    "path": "src/abstract_dialog.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Abstract class for all types of SIP dialogs.\n */\n\n#ifndef _ABSTRACT_DIALOG_H\n#define _ABSTRACT_DIALOG_H\n\n#include <string>\n#include <list>\n#include <set>\n#include <queue>\n#include \"client_request.h\"\n#include \"id_object.h\"\n#include \"protocol.h\"\n#include \"sockets/url.h\"\n#include \"parser/request.h\"\n\nusing namespace std;\n\n// Forward declaration\nclass t_phone_user;\n\n/**\n * Abstract class for all types of SIP dialogs.\n * Concrete classes for all SIP dialogs inherit from this class.\n */\nclass t_abstract_dialog : public t_id_object {\nprotected:\t\n\t/** \n\t * Phone user for which this dialog is created.\n\t * This pointer should never be deleted.\n\t */\n\tt_phone_user\t*phone_user;\n\n\tstring\t\tcall_id;\t/**< SIP call id. */\n\tbool\t\tcall_id_owner;\t/**< Indicates if the call id is generated locally. */\n\tstring\t\tlocal_tag;\t/**< Local tag value. */\n\tstring\t\tremote_tag;\t/**< Remote tag value. */\n\tunsigned long\tlocal_seqnr;\t/**< Last local sequence number issued. */\n\tunsigned long\tremote_seqnr;\t/**< Last remote sequence number received. */\n\n\t/**\n\t * The remote_seqnr_set indicates if the remote_seqnr is set by the far-end.\n\t * RFC 3261 allows the CSeq sequence number to be 0. So there is no\n\t * invalid sequence number.\n\t */\n\tbool\t\tremote_seqnr_set;\n\t\n\tt_url\t\tlocal_uri;\t\t/**< URI of the local party (From/To headers). */\n\tstring\t\tlocal_display;\t\t/**< Display name of the local party. */\n\tt_url\t\tremote_uri;\t\t/**< URI of the remote party (From/To headers). */\n\tstring\t\tremote_display;\t\t/**< Display name of the remote party. */\n\t \n\t/** URI of the remote target (Contact header). This is the destination for a request. */\n\tt_url\t\tremote_target_uri;\n\tstring\t\tremote_target_display;\t/**< Display name of the remote target. */\n\t\n\tlist<t_route>\troute_set;\t\t/**< The route set. */\n\tunsigned long\tlocal_resp_nr;\t\t/**< Last local response number (for 100rel) issued. */\n\tunsigned long\tremote_resp_nr;\t\t/**< Last remote response number (for 100rel) received. */\n\tset<string>\tremote_extensions;      /**< SIP extensions supported by the remote party. */\n\t\n\t/** The IP transport/address/port from which the last SIP message was received. */\n\tt_ip_port\tremote_ip_port;\n\n\t/**\n\t * Remove a client request. Pass one of the client request\n\t * pointers to this member. The reference count of the\n\t * request will be decremented. If it becomes zero, then\n\t * the request object is deleted.\n\t * In all cases the passed pointer will be set to NULL.\n\t * @param cr [in] The client request.\n\t */\n\tvoid remove_client_request(t_client_request **cr);\n\n\t/**\n\t * Create route set from the Record-Route header of a response.\n\t * If the response does not have a Record-Route header, then the route\n\t * set is cleared.\n\t * @param r [in] The response.\n\t */\n\tvoid create_route_set(t_response *r);\n\n\t/**\n\t * Create remote target uri and display from the Contact header of a response.\n\t * @param r [in] The response.\n\t */\n\tvoid create_remote_target(t_response *r);\n\t\n\t/**\n\t * Send a request within the dialog.\n\t * Sending a request will create a SIP transaction.\n\t * @param r [in] The request.\n\t * @param tuid [in] The transaction user id to be assigend to the transaction.\n\t */\n\tvirtual void send_request(t_request *r, t_tuid tuid) = 0;\n\n\t/**\n\t * Resend an existing client request.\n\t * A new Via and CSeq header will be put in the request.\n\t * Resending is different from retransmitting. Requests are automatically\n\t * retransmitted by the transaction layer. Resending creates a new SIP\n\t * transaction. Resending is f.i. done when a request must be redirected.\n\t * @param cr [in] The client request.\n\t */\n\tvirtual void resend_request(t_client_request *cr);\n\t\n\t/**\n\t * Resend mid-dialog request with an authorization header containing\n\t * credentials for the challenge in the response. \n\t * @param cr [in] The request.\n\t * @param resp [in] The 401 or 407 response.\n\t * @return true, if resending succeeded.\n\t * @return false, if credentials could not be determined.\n\t *\n\t * @pre The response must be a 401 or 407.\n\t */\n\tbool resend_request_auth(t_client_request *cr, t_response *resp);\n\t\n\t/**\n\t * Redirect mid-dialog request to the next destination.\n\t * There are multiple reasons for redirection:\n\t *  - A 3XX response was received.\n\t *  - The request failed with a non-3XX response. A next contact should be tried.\n\t *\n\t * @param cr [in] The request.\n\t * @param resp [in] The failure response that was received on the request. \n\t * @param contact [out] Contains on successful return the contact to which the request is sent.\n\t * @return true, if the request is sent to a next destination.\n\t * @return false, if no next destination exists.\n\t */\n\tbool redirect_request(t_client_request *cr, t_response *resp,\n\t\t\tt_contact_param &contact);\n\t\n\t/**\n\t * Failover request to the next destination from DNS lookup.\n\t * @param cr [in] The request.\n\t * @return true, if the request is sent to a next destination.\n\t * @return false, if no next destination exists.\n\t */\n\tbool failover_request(t_client_request *cr);\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param pu [in] Phone user for which the dialog must be created.\n\t */\n\tt_abstract_dialog(t_phone_user *pu);\n\t\n\t/**\n\t * Destructor.\n\t */\n\tvirtual ~t_abstract_dialog();\n\n\t/**\n\t * Create a request using the stored dialog state information.\n\t * @param m [in] Request method.\n\t * @return The request.\n\t */\n\tvirtual t_request *create_request(t_method m);\n\n\t/**\n\t * Copy a dialog.\n\t * @return A copy of the dialog.\n\t */\n\tvirtual t_abstract_dialog *copy(void) = 0;\n\t\n\t/**\n\t * Get a pointer to the user profile of the user for whom this dialog\n\t * was created.\n\t * @return The user profile.\n\t */\n\tt_user *get_user(void) const;\n\n\t/**\n\t * Resend mid-dialog request with an authorization header containing\n\t * credentials for the challenge in the response.\n\t * @param resp [in] The 401 or 407 response to the request that must be resent.\n\t * @return true, if resending succeeded.\n\t * @return false, if credentials could not be determined.\n\t *\n\t * @pre The response must be a 401 or 407.\n\t */\n\tvirtual bool resend_request_auth(t_response *resp) = 0;\n\n\t/**\n\t * Redirect mid-dialog request to the next destination.\n\t * @param resp [in] The response to the request that must be resent.\n\t * @return true, if the request is sent to a next destination.\n\t * @return false, if no next destination exists.\n\t */\n\tvirtual bool redirect_request(t_response *resp) = 0;\n\t\n\t/**\n\t * Failover request to the next destination from DNS lookup.\n\t * @param resp [in] The response to the request that must be resent.\n\t * @return true, if the request is sent to a next destination.\n\t * @return false, if no next destination exists.\n\t */\n\tvirtual bool failover_request(t_response *resp) = 0;\n\n\t/**\n\t * Process a received response.\n\t * @param r [in] The received response.\n\t * @param tuid [in] The transaction user id of the transaction for the response.\n\t * @param tid [in] The transaction id of the transaction for the response.\n\t */\n\tvirtual void recvd_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process a received request.\n\t * @param r [in] The received request.\n\t * @param tuid [in] The transaction user id of the transaction for the request.\n\t * @param tid [in] The transaction id of the transaction for the request.\n\t */\n\tvirtual void recvd_request(t_request *r, t_tuid tuid, t_tid tid);\n\n\t/**\n\t * Match a response with the dialog.\n\t * @param r [in] The response.\n\t * @param tuid [in] The transaction user id of the transaction for the response.\n\t * @return true, if the response matches the dialog.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool match_response(t_response *r, t_tuid tuid);\n\n\t/**\n\t * Match a request with the dialog.\n\t * @param r [in] The request.\n\t * @return true, if the request matches the dialog.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool match_request(t_request *r);\n\t\n\t/**\n\t * Partially match a request with the dialog, i.e. do not match remote tag.\n\t * @param r [in] The request.\n\t * @return true, if the request partially matches the dialog.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool match_partial_request(t_request *r);\n\t\n\t/**\n\t * Match call-id and tags with the dialog.\n\t * @param _call_id [in] SIP call-id.\n\t * @param to_tag [in] SIP to-tag.\n\t * @param from_tag [in] SIP from-tag.\n\t * @return true, if call-id and tags match the dialog.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool match(const string &_call_id, const string &to_tag, \n\t\tconst string &from_tag) const;\n\t\n\t/**\n\t * Get the URI of the remote target.\n\t * @return remote target URI.\n\t * @see remote_target_uri\n\t */\n\tt_url get_remote_target_uri(void) const;\n\t\n\t/**\n\t * Get the display name of the remote target.\n\t * @return display name of remote target.\n\t * @see remote_target_display\n\t */\n\tstring get_remote_target_display(void) const;\n\t\n\t/**\n\t * Get the URI of the remote party.\n\t * @return URI of remote party.\n\t * @see remote_uri\n\t */\n\tt_url get_remote_uri(void) const;\n\t\n\t/**\n\t * Get the display name of the remote party.\n\t * @return display name of the remote party.\n\t * @see remote_display\n\t */\n\tstring get_remote_display(void) const;\n\t\n\t/**\n\t * Get the IP transport/address/port from which the last SIP message was received.\n\t * @return transport/address/port\n\t */\n\tt_ip_port get_remote_ip_port(void) const;\n\t\n\t/**\n\t * Get the SIP call id.\n\t * @return SIP call id.\n\t */\n\tstring get_call_id(void) const;\n\t\n\t/**\n\t * Get the local tag.\n\t * @return local tag.\n\t */\n\tstring get_local_tag(void) const;\n\t\n\t/**\n\t * Get the remote tag.\n\t * @return remote tag.\n\t */\n\tstring get_remote_tag(void) const;\n\t\n\t/**\n\t * Check if the remote party supports a particular SIP exentsion.\n\t * @param extension [in] Name of the SIP extension.\n\t * @return true, if remote party supports the extension.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool remote_extension_supported(const string &extension) const;\n\n\t/**\n\t * Check if this dialog is the owner of the call id.\n\t * @return true, if this dialog is the owner.\n\t * @return false, otherwise.\n\t * @see call_id_owner\n\t */\n\tbool is_call_id_owner(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/address_book.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"address_book.h\"\n\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n\n// Call history file\n#define ADDRESS_BOOK_FILE\t\"twinkle.ab\";\n\n// Field seperator in call history file\n#define REC_SEPARATOR\t\t'|'\n\n////////////////////////////\n// class t_address_card\n////////////////////////////\n\nstring t_address_card::get_display_name(void) const {\n\tstring s;\n\t\n\tif (!name_first.empty()) {\n\t\ts = name_first;\n\t}\n\t\n\tif (!name_infix.empty()) {\n\t\tif (!s.empty()) s += ' ';\n\t\ts += name_infix;\n\t}\n\t\n\tif (!name_last.empty()) {\n\t\tif (!s.empty()) s += ' ';\n\t\ts += name_last;\n\t}\n\t\n\treturn s;\n}\n\t\nbool t_address_card::create_file_record(vector<string> &v) const {\n\tv.clear();\n\t\n\tv.push_back(name_first);\n\tv.push_back(name_infix);\n\tv.push_back(name_last);\n\tv.push_back(sip_address);\n\tv.push_back(remark);\n\t\n\treturn true;\n}\n\t\nbool t_address_card::populate_from_file_record(const vector<string> &v) {\n\t// Check number of fields\n\tif (v.size() != 5) return false;\n\t\n\tname_first = v[0];\n\tname_infix = v[1];\n\tname_last = v[2];\n\tsip_address = v[3];\n\tremark = v[4];\n\t\n\treturn true;\n}\n\t\nbool t_address_card::operator==(const t_address_card other) const {\n\treturn (name_first == other.name_first &&\n\t\tname_infix == other.name_infix &&\n\t\tname_last == other.name_last &&\n\t\tsip_address == other.sip_address &&\n\t\tremark == other.remark);\n}\n\n\n////////////////////////////\n// class t_address_book\n////////////////////////////\n\n// Private\n\nvoid t_address_book::find_address(t_user *user_config, const t_url &u) const {\n\tif (u == last_url) return;\n\t\n\tlast_url = u;\n\tlast_name.clear();\n\t\n\t// Normalize url using number conversion rules\n\tt_url u_normalized(u);\n\tu_normalized.apply_conversion_rules(user_config);\n\t\n\tfor (list<t_address_card>::const_iterator i = records.begin();\n\t     i != records.end(); i++)\n\t{\n\t\tstring full_address = ui->expand_destination(user_config, i->sip_address,\n\t\t\t\t\tu_normalized.get_scheme());\n\t\tt_url url_phone(full_address);\n\t\tif (!url_phone.is_valid()) continue;\n\t\t\n\t\tif (u_normalized.user_host_match(url_phone,\n\t\t\tuser_config->get_remove_special_phone_symbols(),\n\t\t\tuser_config->get_special_phone_symbols()))\n\t\t{\n\t\t\tlast_name = i->get_display_name();\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n\n// Public\n\nt_address_book::t_address_book() : utils::t_record_file<t_address_card>()\n{\n\tset_header(\"first_name|infix_name|last_name|sip_address|remark\");\n\tset_separator(REC_SEPARATOR);\n\t\n\tstring s(DIR_HOME);\n\ts += \"/\";\n\ts += USER_DIR;\n\ts += \"/\";\n\ts += ADDRESS_BOOK_FILE;\n\tset_filename(s);\n}\n\nvoid t_address_book::add_address(const t_address_card &address) {\n\tmtx_records.lock();\n\trecords.push_back(address);\n\tmtx_records.unlock();\n}\n\nbool t_address_book::del_address(const t_address_card &address) {\n\tmtx_records.lock();\n\t\n\tlist<t_address_card>::iterator it = find(records.begin(), records.end(),\n\t\t\taddress);\n\t\t\t\n\tif (it == records.end()) {\n\t\tmtx_records.unlock();\n\t\treturn false;\n\t}\t\n\t\n\trecords.erase(it);\n\t\n\t// Invalidate the cache for the address finder\n\tlast_url.set_url(\"\");\n\t\n\tmtx_records.unlock();\n\treturn true;\n}\n\nbool t_address_book::update_address(const t_address_card &old_address,\n\tconst t_address_card &new_address)\n{\n\tmtx_records.lock();\n\t\n\tif (!del_address(old_address)) {\n\t\tmtx_records.unlock();\n\t\treturn false;\n\t}\n\t\n\trecords.push_back(new_address);\n\t\n\tmtx_records.unlock();\n\treturn true;\n}\n\nstring t_address_book::find_name(t_user *user_config, const t_url &u) const {\n\tmtx_records.lock();\n\tfind_address(user_config, u);\n\tstring name = last_name;\n\tmtx_records.unlock();\n\t\n\treturn name;\n}\n\nconst list<t_address_card> &t_address_book::get_address_list(void) const {\n\treturn records;\n}\n"
  },
  {
    "path": "src/address_book.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Local address book.\n */\n\n#ifndef _ADDRESS_BOOK_H\n#define _ADDRESS_BOOK_H\n\n#include <string>\n#include <list>\n\n#include \"user.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include \"utils/record_file.h\"\n\nusing namespace std;\n\n/** A single address card. */\nclass t_address_card : public utils::t_record {\npublic:\n\tstring\t\tname_last;\t/**< Last name. */\n\tstring\t\tname_first;\t/**< First name. */\n\tstring\t\tname_infix;\t/**< Infix name. */\n\tstring\t\tsip_address;\t/**< SIP address. */\n\tstring\t\tremark;\t\t/**< Remark. */\n\n\t/**\n\t * Get the display name derived from first, last and infix name.\n\t * @return The display name.\n\t */\n\tstring get_display_name(void) const;\n\t\n\tvirtual bool create_file_record(vector<string> &v) const;\n\tvirtual bool populate_from_file_record(const vector<string> &v);\n\t\n\t/** Equality check. */\n\tbool operator==(const t_address_card other) const;\n};\n\n/**\n * A book containing address cards. The user can\n * create different address books.\n */\nclass t_address_book : public utils::t_record_file<t_address_card> {\nprivate:\n\t/** @name Cache for last searched name/url mapping */\n\t//@{\n\tmutable t_url\t\tlast_url;\t/**< Last URL. */\n\tmutable string\t\tlast_name;\t/**< Lat name. */\n\t//@}\n\t\n\t/**\n\t * Find a matching address for a url and cache the display name.\n\t * @param user_config [in] The user profile.\n\t * @param u [in] The url to find.\n\t * @post If a matching address is found, then the URL and name are\n\t * put in the cache. Otherwise the cache is cleared.\n\t */\n\tvoid find_address(t_user *user_config, const t_url &u) const;\n\npublic:\n\t/** Constructor. */\n\tt_address_book();\n\n\t/**\n\t * Add an address.\n\t * @param address [in] The address to be added.\n\t */\n\tvoid add_address(const t_address_card &address);\n\t\n\t/**\n\t * Delete an address.\n\t * @return true, if the address was successfully deleted.\n\t * @return false, if the address does not exist.\n\t */\n\tbool del_address(const t_address_card &address);\n\n\t/**\n\t * Update an address.\n\t * @param old_address [in] The address to be updated.\n\t * @param new_address [in] The updated address information.\n\t * @return true, if the update was successful.\n\t * @return false, if the old address does not exist.\n\t */\n\tbool update_address(const t_address_card &old_address,\n\t\tconst t_address_card &new_address);\n\t\t\n\t/**\n\t * Find the display name for a SIP URL.\n\t * @param user_config [in] The user profile.\n\t * @param u [in] The SIP URL.\n\t * @return The display name if a match was found.\n\t * @return Empty string if no match can be found.\n\t */\n\tstring find_name(t_user *user_config, const t_url &u) const;\n\n\t/**\n\t * Get the list of addresses.\n\t * @return The list of addresses.\n\t */\n\tconst list<t_address_card> &get_address_list(void) const;\n};\n\nextern t_address_book *ab_local;\n\n#endif\n"
  },
  {
    "path": "src/audio/CMakeLists.txt",
    "content": "project(libtwinkle-audio)\n\nset(LIBTWINKLE_AUDIO-SRCS\n\taudio_device.cpp\n\taudio_decoder.cpp\n\taudio_encoder.cpp\n\taudio_codecs.cpp\n\taudio_rx.cpp\n\taudio_session.cpp\n\taudio_tx.cpp\n\tdtmf_player.cpp\n\tfreq_gen.cpp\n\tg711.cpp\n\tg721.cpp\n\tg722_decode.c\n\tg722_encode.c\n\tg723_16.cpp\n\tg723_24.cpp\n\tg723_40.cpp\n\tg72x.cpp\n\tmedia_buffer.cpp\n\trtp_telephone_event.cpp\n\ttone_gen.cpp\n\ttwinkle_rtp_session.cpp\n\ttwinkle_zrtp_ui.cpp\n)\n\nadd_library(libtwinkle-audio OBJECT ${LIBTWINKLE_AUDIO-SRCS})\n"
  },
  {
    "path": "src/audio/README_G711",
    "content": "The files in this directory comprise ANSI-C language reference implementations\nof the CCITT (International Telegraph and Telephone Consultative Committee)\nG.711, G.721 and G.723 voice compressions.  They have been tested on Sun\nSPARCstations and passed 82 out of 84 test vectors published by CCITT\n(Dec. 20, 1988) for G.721 and G.723.  [The two remaining test vectors,\nwhich the G.721 decoder implementation for u-law samples did not pass,\nmay be in error because they are identical to two other vectors for G.723_40.]\n\nThis source code is released by Sun Microsystems, Inc. to the public domain.\nPlease give your acknowledgement in product literature if this code is used\nin your product implementation.\n\nSun Microsystems supports some CCITT audio formats in Solaris 2.0 system\nsoftware.  However, Sun's implementations have been optimized for higher\nperformance on SPARCstations.\n\n\nThe source files for CCITT conversion routines in this directory are:\n\n\tg72x.h\t\theader file for g721.c, g723_24.c and g723_40.c\n\tg711.c\t\tCCITT G.711 u-law and A-law compression\n\tg72x.c\t\tcommon denominator of G.721 and G.723 ADPCM codes\n\tg721.c\t\tCCITT G.721 32Kbps ADPCM coder (with g72x.c)\n\tg723_24.c\tCCITT G.723 24Kbps ADPCM coder (with g72x.c)\n\tg723_40.c\tCCITT G.723 40Kbps ADPCM coder (with g72x.c)\n\n\nSimple conversions between u-law, A-law, and 16-bit linear PCM are invoked\nas follows:\n\n\tunsigned char\t\tucode, acode;\n\tshort\t\t\tpcm_val;\n\n\tucode = linear2ulaw(pcm_val);\n\tucode = alaw2ulaw(acode);\n\n\tacode = linear2alaw(pcm_val);\n\tacode = ulaw2alaw(ucode);\n\n\tpcm_val = ulaw2linear(ucode);\n\tpcm_val = alaw2linear(acode);\n\n\nThe other CCITT compression routines are invoked as follows:\n\n\t#include \"g72x.h\"\n\n\tstruct g72x_state\tstate;\n\tint\t\t\tsample, code;\n\n\tg72x_init_state(&state);\n\tcode = {g721,g723_24,g723_40}_encoder(sample, coding, &state);\n\tsample = {g721,g723_24,g723_40}_decoder(code, coding, &state);\n\nwhere\n\tcoding = AUDIO_ENCODING_ULAW\tfor 8-bit u-law samples\n\t\t AUDIO_ENCODING_ALAW\tfor 8-bit A-law samples\n\t\t AUDIO_ENCODING_LINEAR\tfor 16-bit linear PCM samples\n\n\n\nThis directory also includes the following sample programs:\n\n\tencode.c\tCCITT ADPCM encoder\n\tdecode.c\tCCITT ADPCM decoder\n\tMakefile\tmakefile for the sample programs\n\n\nThe sample programs contain examples of how to call the various compression\nroutines and pack/unpack the bits.  The sample programs read byte streams from\nstdin and write to stdout.  The input/output data is raw data (no file header\nor other identifying information is embedded).  The sample programs are\ninvoked as follows:\n\n\tencode [-3|4|5] [-a|u|l] <infile >outfile\n\tdecode [-3|4|5] [-a|u|l] <infile >outfile\nwhere:\n\t-3\tencode to (decode from) G.723 24kbps (3-bit) data\n\t-4\tencode to (decode from) G.721 32kbps (4-bit) data [the default]\n\t-5\tencode to (decode from) G.723 40kbps (5-bit) data\n\t-a\tencode from (decode to) A-law data\n\t-u\tencode from (decode to) u-law data [the default]\n\t-l\tencode from (decode to) 16-bit linear data\n\nExamples:\n\t# Read 16-bit linear and output G.721\n\tencode -4 -l <pcmfile >g721file\n\n\t# Read 40Kbps G.723 and output A-law\n\tdecode -5 -a <g723file >alawfile\n\n\t# Compress and then decompress u-law data using 24Kbps G.723\n\tencode -3 <ulawin | deoced -3 >ulawout\n\n"
  },
  {
    "path": "src/audio/audio_codecs.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdlib>\n#include \"audio_codecs.h\"\n\nunsigned short audio_sample_rate(t_audio_codec codec) {\n\tswitch(codec) {\n\tcase CODEC_G711_ALAW:\n\tcase CODEC_G711_ULAW:\n\tcase CODEC_GSM:\n\tcase CODEC_SPEEX_NB:\n\tcase CODEC_ILBC:\n\tcase CODEC_G729A:\n\tcase CODEC_G726_16:\n\tcase CODEC_G726_24:\n\tcase CODEC_G726_32:\n\tcase CODEC_G726_40:\n\tcase CODEC_TELEPHONE_EVENT:\n\t\treturn 8000;\n\tcase CODEC_G722:\n\tcase CODEC_SPEEX_WB:\n\t\treturn 16000;\n\tcase CODEC_SPEEX_UWB:\n\t\treturn 32000;\n\tdefault:\n\t\t// Use 8000 as default rate\n\t\treturn 8000;\n\t}\n}\n\nunsigned short audio_sample_rate_rtp(t_audio_codec codec) {\n\tswitch(codec) {\n\tcase CODEC_G722:\n\t\t// RFC 3551 (s. 4.5.2) requires the RTP clock rate to be 8 kHz\n\t\treturn 8000;\n\tdefault:\n\t\treturn audio_sample_rate(codec);\n\t}\n}\n\nunsigned short audio_sample_rate_rtp_ratio(t_audio_codec codec) {\n\treturn audio_sample_rate(codec) / audio_sample_rate_rtp(codec);\n}\n\nbool is_speex_codec(t_audio_codec codec) {\n\treturn (codec == CODEC_SPEEX_NB ||\n\t\tcodec == CODEC_SPEEX_WB ||\n\t\tcodec == CODEC_SPEEX_UWB);\n}\n\nint resample(short *input_buf, int input_len, int input_sample_rate,\n\tshort *output_buf, int output_len, int output_sample_rate)\n{\n\tif (input_sample_rate > output_sample_rate) {\n\t\tint downsample_factor = input_sample_rate / output_sample_rate;\n\t\tint output_idx = -1;\n\t\tfor (int i = 0; i < input_len; i += downsample_factor) {\n\t\t\toutput_idx = i / downsample_factor;\n\t\t\tif (output_idx >= output_len) {\n\t\t\t\t// Output buffer is full\n\t\t\t\treturn output_len;\n\t\t\t}\n\t\t\toutput_buf[output_idx] = input_buf[i];\n\t\t}\n\t\treturn output_idx + 1;\n\t} else {\n\t\tint upsample_factor = output_sample_rate / input_sample_rate;\n\t\tint output_idx = -1;\n\t\tfor (int i = 0; i < input_len; i++) {\n\t\t\tfor (int j = 0; j < upsample_factor; j++) {\n\t\t\t\toutput_idx = i * upsample_factor + j;\n\t\t\t\tif (output_idx >= output_len) {\n\t\t\t\t\t// Output buffer is full\n\t\t\t\t\treturn output_len;\n\t\t\t\t}\n\t\t\t\toutput_buf[output_idx] = input_buf[i];\n\t\t\t}\n\t\t}\n\t\treturn output_idx + 1;\n\t}\n}\n\nshort mix_linear_pcm(short pcm1, short pcm2) {\n\tlong mixed_sample = long(pcm1) + long(pcm2);\n\n\t// Compress a 17 bit PCM value into a 16-bit value.\n\t// The easy way is to divide the value by 2, but this lowers\n\t// the volume.\n\t// Only lower the volume for the loud values. As for a normal\n\t// voice call the values are not that loud, this gives better\n\t// quality.\n\tif (mixed_sample > 16384) {\n\t\tmixed_sample = 16384 + (mixed_sample - 16384) / 3;\n\t} else if (mixed_sample < -16384) {\n\t\tmixed_sample = -16384 - (-16384 - mixed_sample) / 3;\n\t}\n\n\treturn short(mixed_sample);\n}\n"
  },
  {
    "path": "src/audio/audio_codecs.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _AUDIO_CODECS_H\n#define _AUDIO_CODECS_H\n\n#include \"g711.h\"\n#include \"g72x.h\"\n\n// Audio codecs\nenum t_audio_codec {\n\tCODEC_NULL,\n\tCODEC_UNSUPPORTED,\n\tCODEC_G711_ALAW,\n\tCODEC_G711_ULAW,\n\tCODEC_GSM,\n\tCODEC_SPEEX_NB,\n\tCODEC_SPEEX_WB,\n\tCODEC_SPEEX_UWB,\n\tCODEC_ILBC,\n\tCODEC_G722,\n\tCODEC_G726_16,\n\tCODEC_G726_24,\n\tCODEC_G726_32,\n\tCODEC_G726_40,\n\tCODEC_TELEPHONE_EVENT,\n\tCODEC_G729A\n};\n\n// Default ptime values (ms) for audio codecs\n#define PTIME_G711_ALAW\t\t20\n#define PTIME_G711_ULAW\t\t20\n#define PTIME_G722\t\t20\n#define PTIME_G726\t\t20\n#define PTIME_GSM\t\t20\n#define PTIME_SPEEX\t\t20\n#define MIN_PTIME\t\t10\n#define MAX_PTIME\t\t80\n\n// Audio sample settings\n#define AUDIO_SAMPLE_SIZE\t16\n\n\n// Maximum length (in packets) for concealment of lost packets\n#define MAX_CONCEALMENT\t\t2\n\n// Size of jitter buffer in ms\n// The jitter buffer is used to smooth playing out incoming RTP packets.\n// The size of the buffer is also used as the expiry time in the ccRTP\n// stack. Packets that have timestamp that is older than then size of\n// the jitter buffer will not be sent out anymore.\n#define JITTER_BUF_MS\t\t80\n\n// Duration of the expiry timer in the RTP stack.\n// The ccRTP stack checks all data delivered to it against its clock.\n// If the data is too old it will not send it out. Data can be old\n// for several reasons:\n// \n// 1) The thread reading the soundcard has been paused for a while\n// 2) The audio card buffers sound before releasing it.\n//\n// Especially the latter seems to happen on some soundcards. Data\n// not older than defined delay are still allowed to go out. It's up\n// to the receiving and to deal with the jitter this may cause.\n#define MAX_OUT_AUDIO_DELAY_MS\t160\n\n// Buffer sizes\n#define JITTER_BUF_SIZE(sample_rate) (JITTER_BUF_MS * (sample_rate)/1000 * AUDIO_SAMPLE_SIZE/8)\n\n// Log speex errors\n#define LOG_SPEEX_ERROR(func, spxfunc, spxerr) {\\\n\tlog_file->write_header((func), LOG_NORMAL, LOG_DEBUG);\\\n\tlog_file->write_raw(\"Speex error: \");\\\n\tlog_file->write_raw((spxfunc));\\\n\tlog_file->write_raw(\" returned \");\\\n\tlog_file->write_raw((spxerr));\\\n\tlog_file->write_footer(); }\n\n// Return the sampling rate for a codec\nunsigned short audio_sample_rate(t_audio_codec codec);\n// Some codecs (namely G.722 as far as we are concerned) are required to have\n// an RTP clock rate that differs from the actual sampling rate\nunsigned short audio_sample_rate_rtp(t_audio_codec codec);\n// Ratio of sampling clock rate to RTP clock rate\nunsigned short audio_sample_rate_rtp_ratio(t_audio_codec codec);\n\n// Returns true if the codec is a speex codec\nbool is_speex_codec(t_audio_codec codec);\n\n// Resample the input buffer to the output buffer\n// Returns the number of samples put in the output buffer\n// If the output buffer is too small, the number of samples will be\n// truncated.\nint resample(short *input_buf, int input_len, int input_sample_rate,\n\tshort *output_buf, int output_len, int output_sample_rate);\n\n// Mix 2 16 bits signed linear PCM values\nshort mix_linear_pcm(short pcm1, short pcm2);\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_decoder.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <iostream>\n#include \"audio_decoder.h\"\n#include \"log.h\"\n\n#ifdef HAVE_ILBC\n#ifndef HAVE_ILBC_CPP\nextern \"C\" {\n#endif\n#include <ilbc/iLBC_decode.h>\n#ifndef HAVE_ILBC_CPP\n}\n#endif\n#endif\n\n//////////////////////////////////////////\n// class t_audio_decoder\n//////////////////////////////////////////\n\nt_audio_decoder::t_audio_decoder(uint16 default_ptime, bool plc, t_user *user_config) :\n\t_default_ptime(default_ptime),\n\t_plc(plc),\n\t_user_config(user_config)\n{}\n\nt_audio_codec t_audio_decoder::get_codec(void) const {\n\treturn _codec;\n}\n\nuint16 t_audio_decoder::get_default_ptime(void) const {\n\treturn _default_ptime;\n}\n\nbool t_audio_decoder::has_plc(void) const {\n\treturn _plc;\n}\n\nuint16 t_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {\n\treturn 0;\n}\n\n//////////////////////////////////////////\n// class t_g711a_audio_decoder\n//////////////////////////////////////////\n\nt_g711a_audio_decoder::t_g711a_audio_decoder(uint16 default_ptime, t_user *user_config) :\n\tt_audio_decoder(default_ptime, false, user_config)\n{\n\t_codec = CODEC_G711_ALAW;\n\t\n\tif (default_ptime == 0) {\n\t\t_default_ptime = PTIME_G711_ALAW;\n\t}\n}\n\nuint16 t_g711a_audio_decoder::get_ptime(uint16 payload_size) const {\n\treturn payload_size / (audio_sample_rate(_codec) / 1000);\n}\n\nuint16 t_g711a_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(pcm_buf_size >= payload_size);\n\n\tfor (int i = 0; i < payload_size; i++) {\n\t\tpcm_buf[i] = alaw2linear(payload[i]);\n\t}\n\treturn payload_size;\n}\n\nbool t_g711a_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn payload_size <= sample_buf_size;\n}\n\n//////////////////////////////////////////\n// class t_g711u_audio_decoder\n//////////////////////////////////////////\n\nt_g711u_audio_decoder::t_g711u_audio_decoder(uint16 default_ptime, t_user *user_config) :\n\tt_audio_decoder(default_ptime, false, user_config)\n{\n\t_codec = CODEC_G711_ULAW;\n\t\n\tif (default_ptime == 0) {\n\t\t_default_ptime = PTIME_G711_ULAW;\n\t}\n}\n\nuint16 t_g711u_audio_decoder::get_ptime(uint16 payload_size) const {\n\treturn payload_size / (audio_sample_rate(_codec) / 1000);\n}\n\nuint16 t_g711u_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(pcm_buf_size >= payload_size);\n\t\n\tfor (int i = 0; i < payload_size; i++) {\n\t\tpcm_buf[i] = ulaw2linear(payload[i]);\n\t}\n\treturn payload_size;\n}\n\nbool t_g711u_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn payload_size <= sample_buf_size;\n}\n\n//////////////////////////////////////////\n// class t_gsm_audio_decoder\n//////////////////////////////////////////\n\nt_gsm_audio_decoder::t_gsm_audio_decoder(t_user *user_config) :\n\tt_audio_decoder(PTIME_GSM, false, user_config)\n{\n\t_codec = CODEC_GSM;\n\tgsm_decoder = gsm_create();\n}\n\nt_gsm_audio_decoder::~t_gsm_audio_decoder() {\n\tgsm_destroy(gsm_decoder);\n}\n\nuint16 t_gsm_audio_decoder::get_ptime(uint16 payload_size) const {\n\treturn get_default_ptime();\n}\n\nuint16 t_gsm_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(pcm_buf_size >= 160);\n\t\n\tgsm_decode(gsm_decoder, payload, pcm_buf);\n\treturn 160;\n}\n\nbool t_gsm_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn payload_size == 33;\n}\n\n#ifdef HAVE_SPEEX\n//////////////////////////////////////////\n// class t_speex_audio_decoder\n//////////////////////////////////////////\n\nt_speex_audio_decoder::t_speex_audio_decoder(t_mode mode, t_user *user_config) :\n\tt_audio_decoder(0, true, user_config)\n{\n\tspeex_bits_init(&speex_bits);\n\t_mode = mode;\n\t\n\tswitch (mode) {\n\tcase MODE_NB:\n\t\t_codec = CODEC_SPEEX_NB;\n\t\tspeex_dec_state = speex_decoder_init(&speex_nb_mode);\n\t\tbreak;\n\tcase MODE_WB:\n\t\t_codec = CODEC_SPEEX_WB;\n\t\tspeex_dec_state = speex_decoder_init(&speex_wb_mode);\n\t\tbreak;\n\tcase MODE_UWB:\n\t\t_codec = CODEC_SPEEX_UWB;\n\t\tspeex_dec_state = speex_decoder_init(&speex_uwb_mode);\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n    int frame_size = 0;\n    speex_decoder_ctl(speex_dec_state, SPEEX_GET_FRAME_SIZE, &frame_size);\n\n\t// Initialize decoder with user settings\n\tint arg = (user_config->get_speex_penh() ? 1 : 0);\n\tspeex_decoder_ctl(speex_dec_state, SPEEX_SET_ENH, &arg);\n\n    _default_ptime = frame_size / (audio_sample_rate(_codec) / 1000);\n}\n\nt_speex_audio_decoder::~t_speex_audio_decoder() {\n\tspeex_bits_destroy(&speex_bits);\n\tspeex_decoder_destroy(speex_dec_state);\n}\n\nuint16 t_speex_audio_decoder::get_ptime(uint16 payload_size) const {\n\treturn get_default_ptime();\n}\n\nuint16 t_speex_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tint retval;\n\tint speex_frame_size;\n\t\n\tspeex_decoder_ctl(speex_dec_state, SPEEX_GET_FRAME_SIZE, \n\t\t\t&speex_frame_size);\n\t\t\t\n\tassert(pcm_buf_size >= speex_frame_size);\n\t\n\tspeex_bits_read_from(&speex_bits, reinterpret_cast<char *>(payload), payload_size);\n\tretval = speex_decode_int(speex_dec_state, &speex_bits, pcm_buf);\n\t\n\tif (retval < 0) {\n\t\tLOG_SPEEX_ERROR(\"t_speex_audio_decoder::decode\", \n\t\t\t\"speex_decode_int\", retval);\n\t\treturn 0;\n\t}\n\t\n\treturn speex_frame_size;\n}\n\nuint16 t_speex_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {\n\tint retval;\n\tint speex_frame_size;\n\t\n\tspeex_decoder_ctl(speex_dec_state, SPEEX_GET_FRAME_SIZE, \n\t\t&speex_frame_size);\n\t\t\n\tassert(pcm_buf_size >= speex_frame_size);\n\t\t\n\tretval = speex_decode_int(speex_dec_state, NULL, pcm_buf);\n\t\n\tif (retval < 0) {\n\t\tLOG_SPEEX_ERROR(\"t_speex_audio_decoder::conceal\", \n\t\t\t\"speex_decode_int\", retval);\n\t\treturn 0;\n\t}\n\t\n\treturn speex_frame_size;\n}\n\nbool t_speex_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn true;\n}\n#endif\n\n#ifdef HAVE_ILBC\n//////////////////////////////////////////\n// class t_ilbc_audio_decoder\n//////////////////////////////////////////\nt_ilbc_audio_decoder::t_ilbc_audio_decoder(uint16 default_ptime, t_user *user_config) :\n\tt_audio_decoder(default_ptime, true, user_config)\n{\n\t_codec = CODEC_ILBC;\n\t_last_received_ptime = 0;\n\tinitDecode(&_ilbc_decoder_20, 20, 1);\n\tinitDecode(&_ilbc_decoder_30, 30, 1);\n}\n\nuint16 t_ilbc_audio_decoder::get_ptime(uint16 payload_size) const {\n\tif (payload_size == NO_OF_BYTES_20MS) {\n\t\treturn 20;\n\t} else {\n\t\treturn 30;\n\t}\n}\n\nuint16 t_ilbc_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tfloat sample;\n\tfloat block[BLOCKL_MAX];\n\tint block_len;\n\t\n\tif (get_ptime(payload_size) == 20) {\n\t\tblock_len = BLOCKL_20MS;\n\t\tassert(pcm_buf_size >= block_len);\n\t\tiLBC_decode(block, (unsigned char*)payload, &_ilbc_decoder_20, 1);\n\t\t_last_received_ptime = 20;\n\t} else {\n\t\tblock_len = BLOCKL_30MS;\n\t\tassert(pcm_buf_size >= block_len);\n\t\tiLBC_decode(block, (unsigned char*)payload, &_ilbc_decoder_30, 1);\n\t\t_last_received_ptime = 30;\n\t}\n\t\n\tfor (int i = 0; i < block_len; i++) {\n\t\tsample = block[i];\n\t\t\n\t\tif (sample < MIN_SAMPLE) sample = MIN_SAMPLE;\n\t\tif (sample > MAX_SAMPLE) sample = MAX_SAMPLE;\n\t\t\n\t\tpcm_buf[i] = static_cast<int16>(sample);\n\t}\n\n\treturn block_len;\n}\n\nuint16 t_ilbc_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {\n\tfloat sample;\n\tfloat block[BLOCKL_MAX];\n\tint block_len;\n\t\n\tif (_last_received_ptime == 0) return 0;\n\t\n\tif (_last_received_ptime == 20) {\n\t\tblock_len = BLOCKL_20MS;\n\t\tassert(pcm_buf_size >= block_len);\n\t\tiLBC_decode(block, NULL, &_ilbc_decoder_20, 0);\n\t} else {\n\t\tblock_len = BLOCKL_30MS;\n\t\tassert(pcm_buf_size >= block_len);\n\t\tiLBC_decode(block, NULL, &_ilbc_decoder_30, 0);\n\t}\n\t\n\tfor (int i = 0; i < block_len; i++) {\n\t\tsample = block[i];\n\t\t\n\t\tif (sample < MIN_SAMPLE) sample = MIN_SAMPLE;\n\t\tif (sample > MAX_SAMPLE) sample = MAX_SAMPLE;\n\t\t\n\t\tpcm_buf[i] = static_cast<int16>(sample);\n\t}\n\t\n\treturn block_len;\n}\n\nbool t_ilbc_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn payload_size == NO_OF_BYTES_20MS || payload_size == NO_OF_BYTES_30MS;\n}\n#endif\n\n//////////////////////////////////////////\n// class t_g722_audio_decoder\n//////////////////////////////////////////\nt_g722_audio_decoder::t_g722_audio_decoder(uint16 default_ptime, t_user *user_config)\n\t: t_audio_decoder(default_ptime, false, user_config)\n{\n\t_codec = CODEC_G722;\n\t_state = g722_decode_init(NULL, 64000, 0);\n}\n\nt_g722_audio_decoder::~t_g722_audio_decoder()\n{\n\tg722_decode_release(_state);\n}\n\nuint16 t_g722_audio_decoder::get_ptime(uint16 payload_size) const\n{\n\treturn (payload_size * G722_SAMPLES_PAYLOAD_RATIO) /\n\t\t(audio_sample_rate(_codec) / 1000);\n}\n\nuint16 t_g722_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(pcm_buf_size >= (payload_size * G722_SAMPLES_PAYLOAD_RATIO));\n\n\tint nsamples = g722_decode(_state, pcm_buf, payload, payload_size);\n\n\treturn nsamples;\n}\n\nbool t_g722_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn (payload_size * G722_SAMPLES_PAYLOAD_RATIO) <= sample_buf_size;\n}\n\n//////////////////////////////////////////\n// class t_g726_audio_decoder\n//////////////////////////////////////////\nt_g726_audio_decoder::t_g726_audio_decoder(t_bit_rate bit_rate, uint16 default_ptime, \n\t\tt_user *user_config) :\n\tt_audio_decoder(default_ptime, false, user_config)\n{\n\t_bit_rate = bit_rate;\n\t\n\tif (default_ptime == 0) {\n\t\t_default_ptime = PTIME_G726;\n\t}\n\t\n\tswitch (_bit_rate) {\n\tcase BIT_RATE_16:\n\t\t_codec = CODEC_G726_16;\n\t\t_bits_per_sample = 2;\n\t\tbreak;\n\tcase BIT_RATE_24:\n\t\t_codec = CODEC_G726_24;\n\t\t_bits_per_sample = 3;\n\t\tbreak;\n\tcase BIT_RATE_32:\n\t\t_codec = CODEC_G726_32;\n\t\t_bits_per_sample = 4;\n\t\tbreak;\n\tcase BIT_RATE_40:\n\t\t_codec = CODEC_G726_40;\n\t\t_bits_per_sample = 5;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\t_packing = user_config->get_g726_packing();\n\t\n\tg72x_init_state(&_state);\n}\n\nuint16 t_g726_audio_decoder::get_ptime(uint16 payload_size) const {\n\treturn (payload_size * 8 / _bits_per_sample) / (audio_sample_rate(_codec) / 1000);\n}\n\nuint16 t_g726_audio_decoder::decode_16(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(payload_size * 4 <= pcm_buf_size);\n\n\tfor (int i = 0; i < payload_size; i++) {\n\t\tfor (int j = 0; j < 4; j++) {\n\t\t\tuint8 w;\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tw = (payload[i] >> (j*2)) & 0x3;\n\t\t\t} else {\n\t\t\t\tw = (payload[i] >> ((3-j)*2)) & 0x3;\n\t\t\t}\n\t\t\tpcm_buf[4*i+j] = g723_16_decoder(\n\t\t\t\tw, AUDIO_ENCODING_LINEAR, &_state);\n\t\t}\n\t}\n\t\n\treturn payload_size * 4;\n}\n\nuint16 t_g726_audio_decoder::decode_24(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(payload_size % 3 == 0);\n\tassert(payload_size * 8 / 3 <= pcm_buf_size);\n\n\tfor (int i = 0; i < payload_size; i += 3) {\n\t\tuint32 v = (static_cast<uint32>(payload[i+2]) << 16) |\n\t\t\t   (static_cast<uint32>(payload[i+1]) << 8) |\n\t\t\t    static_cast<uint32>(payload[i]);\n\t\t\t     \n\t\tfor (int j = 0; j < 8; j++) {\n\t\t\tuint8 w;\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tw = (v >> (j*3)) & 0x7;\n\t\t\t} else {\n\t\t\t\tw = (v >> ((7-j)*3)) & 0x7;\n\t\t\t}\n\t\t\tpcm_buf[8*(i/3)+j] = g723_24_decoder(\n\t\t\t\tw, AUDIO_ENCODING_LINEAR, &_state);\n\t\t}\n\t}\n\n\treturn payload_size * 8 / 3;\n}\n\nuint16 t_g726_audio_decoder::decode_32(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(payload_size * 2 <= pcm_buf_size);\n\n\tfor (int i = 0; i < payload_size; i++) {\n\t\tfor (int j = 0; j < 2; j++) {\n\t\t\tuint8 w;\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tw = (payload[i] >> (j*4)) & 0xf;\n\t\t\t} else {\n\t\t\t\tw = (payload[i] >> ((1-j)*4)) & 0xf;\n\t\t\t}\n\t\t\tpcm_buf[2*i+j] = g721_decoder(\n\t\t\t\tw, AUDIO_ENCODING_LINEAR, &_state);\n\t\t}\n\t}\n\t\n\treturn payload_size * 2;\n}\n\nuint16 t_g726_audio_decoder::decode_40(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(payload_size % 5 == 0);\n\tassert(payload_size * 8 / 5 <= pcm_buf_size);\n\n\tfor (int i = 0; i < payload_size; i += 5) {\n\t\tuint64 v = (static_cast<uint64>(payload[i+4]) << 32) |\n\t\t\t   (static_cast<uint64>(payload[i+3]) << 24) |\n\t\t           (static_cast<uint64>(payload[i+2]) << 16) |\n\t\t\t   (static_cast<uint64>(payload[i+1]) << 8) |\n\t\t\t    static_cast<uint64>(payload[i]);\n\t\t\t     \n\t\tfor (int j = 0; j < 8; j++) {\n\t\t\tuint8 w;\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tw = (v >> (j*5)) & 0x1f;\n\t\t\t} else {\n\t\t\t\tw = (v >> ((7-j)*5)) & 0x1f;\n\t\t\t}\n\t\t\tpcm_buf[8*(i/5)+j] = g723_40_decoder(\n\t\t\t\tw, AUDIO_ENCODING_LINEAR, &_state);\n\t\t}\n\t}\n\n\treturn payload_size * 8 / 5;\n}\n\nuint16 t_g726_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tswitch (_bit_rate) {\n\tcase BIT_RATE_16:\n\t\treturn decode_16(payload, payload_size, pcm_buf, pcm_buf_size);\n\t\tbreak;\n\tcase BIT_RATE_24:\n\t\treturn decode_24(payload, payload_size, pcm_buf, pcm_buf_size);\n\t\tbreak;\n\tcase BIT_RATE_32:\n\t\treturn decode_32(payload, payload_size, pcm_buf, pcm_buf_size);\n\t\tbreak;\n\tcase BIT_RATE_40:\n\t\treturn decode_40(payload, payload_size, pcm_buf, pcm_buf_size);\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\treturn 0;\n}\n\nbool t_g726_audio_decoder::valid_payload_size(uint16 payload_size, \n\t\tuint16 sample_buf_size) const\n{\n\tswitch (_bit_rate) {\n\tcase BIT_RATE_24:\n\t\t// Payload size must be multiple of 3\n\t\tif (payload_size % 3 != 0) return false;\n\t\tbreak;\n\tcase BIT_RATE_40:\n\t\t// Payload size must be multiple of 5\n\t\tif (payload_size % 5 != 0) return false;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\treturn (payload_size * 8 / _bits_per_sample ) <= sample_buf_size;\n}\n\n#ifdef HAVE_BCG729\n\nt_g729a_audio_decoder::t_g729a_audio_decoder(uint16 default_ptime, t_user *user_config)\n\t: t_audio_decoder(default_ptime, true, user_config)\n{\n\t_context = initBcg729DecoderChannel();\n}\n\nt_g729a_audio_decoder::~t_g729a_audio_decoder()\n{\n\tcloseBcg729DecoderChannel(_context);\n}\n\nuint16 t_g729a_audio_decoder::get_ptime(uint16 payload_size) const\n{\n\treturn (payload_size * 8) / (audio_sample_rate(_codec) / 1000);\n}\n\nuint16 t_g729a_audio_decoder::decode(uint8 *payload, uint16 payload_size,\n\t\tint16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert((payload_size % 10) == 0);\n\tassert(pcm_buf_size >= payload_size*8);\n\n\tfor (uint16 done = 0; done < payload_size; done += 10)\n\t{\n#ifdef HAVE_BCG729_ANNEX_B\n\t\tbcg729Decoder(_context, &payload[done], 0, false, false, false, &pcm_buf[done * 8]);\n#else\n\t\tbcg729Decoder(_context, &payload[done], false, &pcm_buf[done * 8]);\n#endif\n\t}\n\n\treturn payload_size * 8;\n}\n\nbool t_g729a_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const\n{\n\treturn payload_size > 0 && (payload_size % 10) == 0;\n}\n\nuint16 t_g729a_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size)\n{\n\tassert(pcm_buf_size >= 80);\n\n#ifdef HAVE_BCG729_ANNEX_B\n\tbcg729Decoder(_context, nullptr, 0, true, false, false, pcm_buf);\n#else\n\tbcg729Decoder(_context, nullptr, true, pcm_buf);\n#endif\n\treturn 80;\n}\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_decoder.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Audio decoders\n\n#ifndef _AUDIO_DECODER_H\n#define _AUDIO_DECODER_H\n\n#include \"twinkle_config.h\"\n#include \"audio_codecs.h\"\n#include \"user.h\"\n\n#ifdef HAVE_GSM\nextern \"C\" {\n#include <gsm/gsm.h>\n}\n#else\n#include \"gsm/inc/gsm.h\"\n#endif\n\n#ifdef HAVE_SPEEX\n#include <speex/speex.h>\n#include <speex/speex_preprocess.h> \n#endif\n\nextern \"C\" {\n#\tinclude \"g722.h\"\n#\tinclude \"g722_local.h\"\n}\n\n#ifdef HAVE_BCG729\nextern \"C\" {\n#\tinclude <bcg729/decoder.h>\n}\n#endif\n\n#ifdef HAVE_ILBC\n#ifndef HAVE_ILBC_CPP\nextern \"C\" {\n#endif\n#include <ilbc/iLBC_define.h>\n#ifndef HAVE_ILBC_CPP\n}\n#endif\n#endif\n\n// Abstract definition of an audio decoder\nclass t_audio_decoder {\nprotected:\n\tt_audio_codec\t_codec;\n\tuint16\t\t_default_ptime;\n\tbool\t\t_plc; // packet loss concealment\n\tt_user\t\t*_user_config;\n\n\tt_audio_decoder(uint16 default_ptime, bool plc, t_user *user_config);\n\t\npublic:\n\tvirtual ~t_audio_decoder() {};\n\t\n\tt_audio_codec get_codec(void) const;\n\tuint16 get_default_ptime(void) const;\n\tvirtual uint16 get_ptime(uint16 payload_size) const = 0;\n\t\n\t// Decode a buffer of encoded samples to 16-bit PCM.\n\t// Returns the number of pcm samples written into pcm_buf\n\t// Returns 0 if decoding failed\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size) = 0;\n\t\t\t\n\t// Indicates if codec has PLC algorithm\n\tbool has_plc(void) const;\n\t\n\t// Create a payload to conceal a lost packet.\n\t// Returns the number of pcm samples written into pcm_buf\n\t// Returns 0 if decoding failed\n\tvirtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);\n\t\n\t// Determine if the payload size is valid for this decoder\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const = 0;\n};\n\n// G.711 A-law\nclass t_g711a_audio_decoder : public t_audio_decoder {\npublic:\n\tt_g711a_audio_decoder(uint16 default_ptime, t_user *user_config);\n\t\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n\n// G.711 u-law\nclass t_g711u_audio_decoder : public t_audio_decoder {\npublic:\n\tt_g711u_audio_decoder(uint16 default_ptime, t_user *user_config);\n\t\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n\n// GSM\nclass t_gsm_audio_decoder : public t_audio_decoder {\nprivate:\n\tgsm\t\tgsm_decoder;\n\t\npublic:\n\tt_gsm_audio_decoder(t_user *user_config);\n\tvirtual ~t_gsm_audio_decoder();\n\t\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n\n#ifdef HAVE_SPEEX\n// Speex\nclass t_speex_audio_decoder : public t_audio_decoder {\npublic:\n\tenum t_mode {\n\t\tMODE_NB,\t// Narrow band\n\t\tMODE_WB,\t// Wide band\n\t\tMODE_UWB\t// Ultra wide band\n\t};\n\t\nprivate:\n\tSpeexBits\tspeex_bits;\n\tvoid\t\t*speex_dec_state;\n\tt_mode\t\t_mode;\n\t\npublic:\n\tt_speex_audio_decoder(t_mode mode, t_user *user_config);\n\tvirtual ~t_speex_audio_decoder();\n\t\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n#endif\n\n#ifdef HAVE_ILBC\n// iLBC\nclass t_ilbc_audio_decoder : public t_audio_decoder {\nprivate:\n\tiLBC_Dec_Inst_t\t_ilbc_decoder_20; // decoder for 20ms frames\n\tiLBC_Dec_Inst_t\t_ilbc_decoder_30; // decoder for 30ms frames\n\t\n\t// The number of ms received in the last frame, so the conceal function\n\t// can determine which decoder to use to conceal a lost frame.\n\tint\t\t_last_received_ptime;\n\t\npublic:\n\tt_ilbc_audio_decoder(uint16 default_ptime, t_user *user_config);\n\t\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n\n#endif\n\n// G.722\nclass t_g722_audio_decoder : public t_audio_decoder {\nprivate:\n\tg722_decode_state_t *_state;\n\npublic:\n\tt_g722_audio_decoder(uint16 default_ptime, t_user *user_config);\n\t~t_g722_audio_decoder();\n\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n\n// G.726\nclass t_g726_audio_decoder : public t_audio_decoder {\npublic:\n\tenum t_bit_rate {\n\t\tBIT_RATE_16,\n\t\tBIT_RATE_24,\n\t\tBIT_RATE_32,\n\t\tBIT_RATE_40\n\t};\n\t\nprivate:\n\tuint16 decode_16(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);\n\tuint16 decode_24(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);\n\tuint16 decode_32(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);\n\tuint16 decode_40(uint8 *payload, uint16 payload_size, int16 *pcm_buf, uint16 pcm_buf_size);\n\n\tstruct g72x_state\t_state;\n\tt_bit_rate\t\t_bit_rate;\n\tuint8\t\t\t_bits_per_sample;\n\tt_g726_packing\t\t_packing;\n\t\npublic:\n\tt_g726_audio_decoder(t_bit_rate bit_rate, uint16 default_ptime, t_user *user_config);\n\tvirtual uint16 get_ptime(uint16 payload_size) const;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size);\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;\n};\n\n#ifdef HAVE_BCG729\n\n// G.729A\nclass t_g729a_audio_decoder : public t_audio_decoder {\npublic:\n\tt_g729a_audio_decoder(uint16 default_ptime, t_user *user_config);\n\t~t_g729a_audio_decoder();\n\n\tvirtual uint16 get_ptime(uint16 payload_size) const override;\n\tvirtual uint16 decode(uint8 *payload, uint16 payload_size,\n\t\t\tint16 *pcm_buf, uint16 pcm_buf_size) override;\n\tvirtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const override;\n\tvirtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size) override;\nprivate:\n\tbcg729DecoderChannelContextStruct* _context;\n};\n\n#endif\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_device.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"audio_device.h\"\n#include <iostream>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <sys/soundcard.h>\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"log.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\n#ifdef HAVE_LIBASOUND\n#include <alsa/asoundlib.h>\n#endif\n\nt_audio_io* t_audio_io::open(const t_audio_device& dev, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)\n{\n\tt_audio_io* aio;\n\t\n\tif(dev.type == t_audio_device::OSS) {\n\t\taio = new t_oss_io();\n\t\tMEMMAN_NEW(aio);\n#ifdef HAVE_LIBASOUND\n\t} else if (dev.type == t_audio_device::ALSA) {\n\t\taio = new t_alsa_io();\n\t\tMEMMAN_NEW(aio);\n#endif\n\t} else {\n\t\tstring msg(\"Audio device not implemented\");\n\t\tlog_file->write_report(msg, \"t_audio_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\treturn 0;\n\t}\n\tif (aio->open(dev.device, playback, capture, blocking, channels, format, \n\t\t\tsample_rate, short_latency)) {\n\t\treturn aio;\n\t} else {\n\t\tstring msg(\"Open audio device failed\");\n\t\tlog_file->write_report(msg, \"t_audio_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tMEMMAN_DELETE(aio);\n\t\tdelete aio;\n\t\treturn 0L;\n\t}\n}\n\nbool t_audio_io::validate(const t_audio_device& dev, bool playback, bool capture) {\n\tt_audio_io *aio = open(dev, playback, capture, false, 1, SAMPLEFORMAT_S16, 8000, true);\n\t\n\tif (aio) {\n\t\tMEMMAN_DELETE(aio);\n\t\tdelete aio;\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nt_audio_io::~t_audio_io() {}\n\nint t_audio_io::get_sample_rate(void) const {\n\treturn _sample_rate;\n}\n\nbool t_audio_io::open(const string& device, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)\n{\n\t_sample_rate = sample_rate;\n\treturn true;\n}\n\nt_oss_io::t_oss_io() : fd(-1), play_buffersize(0), rec_buffersize(0) {\n}\n\nt_oss_io::~t_oss_io()\n{\n\tif (fd > 0) {\n\t\tint arg = 0;\n\t\tioctl(fd, SNDCTL_DSP_RESET, &arg);\n\t\tclose(fd);\n\t}\n\tfd = -1;\n}\n\nbool t_oss_io::open(const string& device, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency) \n{\n\tt_audio_io::open(device, playback, capture, blocking, channels, format, sample_rate,\n\t\tshort_latency);\n\n\tint mode = 0;\n\tint status;\n\t\n\tlog_file->write_header(\"t_oss_io::open\", LOG_NORMAL);\n\tlog_file->write_raw(\"Opening OSS device: \");\n\tlog_file->write_raw(device);\n\tlog_file->write_endl();\n\tif (playback) log_file->write_raw(\"play\\n\");\n\tif (capture) log_file->write_raw(\"capture\\n\");\n\tlog_file->write_footer();\n\t\n\tassert (playback || capture);\n\tif (playback && capture) mode |= O_RDWR;\n\telse if (playback) mode |= O_WRONLY;\n\telse if (capture) mode |= O_RDONLY;\n\t\n\t// On some systems opening the audio devices blocks if another\n\t// process or thread has opened it already. To prevent a deadlock\n\t// first try to open the device in non-blocking mode.\n\t// If the device is still open by another twinkle thread then that\n\t// is a bug, but this way at least non deadlock is caused.\n\tif(blocking) {\n\t\tfd = ::open(device.c_str(), mode | O_NONBLOCK);\n\t\tif (fd == -1) {\n\t\t\tstring msg(\"OSS audio device open failed: \");\n\t\t\tmsg += get_error_str(errno);\n\t\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\treturn false;\n\t\t} else {\n\t\t\tclose(fd);\n\t\t\tfd = -1;\n\t\t}\n\t} else mode |= O_NONBLOCK;\n\t\n\tfd = ::open(device.c_str(), mode);\n\tif (fd < 0) {\n\t\tstring msg(\"OSS audio device open failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\treturn false;\n\t}\n\t\n\t// Full duplex\n\tif (playback && capture) {\n\t\tstatus = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);\n\t\tif (status == -1) {\n\t\t\tstring msg(\"SNDCTL_DSP_SETDUPLEX ioctl failed: \");\n\t\t\tmsg += get_error_str(errno);\n\t\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tui->cb_display_msg(TRANSLATE(\"Sound card cannot be set to full duplex.\"),\n\t\t\t\tMSG_CRITICAL);\n\t\t\tclose(fd);\n\t\t\treturn false;\n\t\t}\n\t}\n\t// Set fragment size\n\tint arg;\n\tif (short_latency) {\n\t\tswitch (sys_config->get_oss_fragment_size()) {\n\t\tcase 16:\n\t\t\targ = 0x00ff0004; // 255 buffers of 2^4 bytes each\n\t\t\tbreak;\t\t\n\t\tcase 32:\n\t\t\targ = 0x00ff0005; // 255 buffers of 2^5 bytes each\n\t\t\tbreak;\n\t\tcase 64:\n\t\t\targ = 0x00ff0006; // 255 buffers of 2^5 bytes each\n\t\t\tbreak;\n\t\tcase 128:\n\t\t\targ = 0x00ff0007; // 255 buffers of 2^7 bytes each\n\t\t\tbreak;\n\t\tcase 256:\n\t\t\targ = 0x00ff0008; // 255 buffers of 2^8 bytes each\n\t\t\tbreak;\n\t\tdefault:\n\t\t\targ = 0x00ff0007; // 255 buffers of 2^7 bytes each\n\t\t}\n\t} else {\n\t\targ = 0x00ff000a; // 255 buffers of 2^10 bytes each\n\t}\n\tstatus = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg);\n\tif (status == -1) {\n\t\tstring msg(\"SNDCTL_DSP_FRAGMENT ioctl failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_display_msg(TRANSLATE(\"Cannot set buffer size on sound card.\"),\n\t\t\tMSG_CRITICAL);\n\t\tclose(fd);\n\t\treturn false;\n\t}\n\t// Channels\n\targ = channels;\n\tstatus = ioctl(fd, SNDCTL_DSP_CHANNELS, &arg);\n\tif (status == -1) {\n\t\tstring msg(\"SNDCTL_DSP_CHANNELS ioctl failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmsg = TRANSLATE(\"Sound card cannot be set to %1 channels.\");\n\t\tmsg = replace_first(msg, \"%1\", int2str(channels));\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\tif (arg != channels) {\n\t\tlog_file->write_report(\"Unable to set channels\",\n\t\t\t\"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tstring msg = \"Sound card cannot be set to \";\n\t\tmsg = TRANSLATE(\"Sound card cannot be set to %1 channels.\");\n\t\tmsg = replace_first(msg, \"%1\", int2str(channels));\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\t// Sample format\n\tint fmt;\n\tswitch (format) {\n\t\tcase SAMPLEFORMAT_S16:\n#ifdef WORDS_BIGENDIAN\n\t\t\tfmt = AFMT_S16_BE;\n#else\n\t\t\tfmt = AFMT_S16_LE;\n#endif\n\t\t\tbreak;\n\t\tcase SAMPLEFORMAT_U16:\n#ifdef WORDS_BIGENDIAN\n\t\t\tfmt = AFMT_U16_BE;\n#else\n\t\t\tfmt = AFMT_U16_LE;\n#endif\n\t\t\tbreak;\n\t\tcase SAMPLEFORMAT_S8:\n\t\t\tfmt = AFMT_S8;\n\t\t\tbreak;\n\t\tcase SAMPLEFORMAT_U8:\n\t\t\tfmt = AFMT_U8;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfmt = 0; // fail\n\t}\n\targ = fmt;\n\tstatus = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);\n\tif (status == -1) {\n\t\tstring msg(\"SNDCTL_DSP_SETFMT ioctl failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_display_msg(TRANSLATE(\"Cannot set sound card to 16 bits recording.\"),\n\t\t\tMSG_CRITICAL);\n\t\treturn false;\n\t}\n\n\targ = fmt;\n  \tstatus = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);\n\tif (status == -1) {\n\t\tstring msg(\"SOUND_PCM_WRITE_BITS ioctl failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_display_msg(TRANSLATE(\"Cannot set sound card to 16 bits playing.\"),\n\t\t\tMSG_CRITICAL);\n\t\treturn false;\n\t}\n\n\t// Sample rate\n\targ = sample_rate;\n\tstatus = ioctl(fd, SNDCTL_DSP_SPEED, &arg);\n\tif (status == -1) {\n\t\tstring msg(\"SNDCTL_DSP_SPEED ioctl failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmsg = TRANSLATE(\"Cannot set sound card sample rate to %1\");\n\t\tmsg = replace_first(msg, \"%1\", int2str(sample_rate));\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\t\n\tplay_buffersize = rec_buffersize = 0;\n\tif (playback) play_buffersize = get_buffer_space(false);\n\tif (capture) rec_buffersize = get_buffer_space(true);\n\treturn true;\n}\n\nvoid t_oss_io::enable(bool enable_playback, bool enable_recording) {\n\tint arg, status;\n\targ = enable_recording ? PCM_ENABLE_INPUT : 0;\n\targ |= enable_playback ? PCM_ENABLE_OUTPUT : 0;\n\tstatus = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &arg);\n\tif (status == -1) {\n\t\tstring msg(\"SNDCTL_DSP_SETTRIGGER ioctl failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_oss_io::enable\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t}\n}\n\nvoid t_oss_io::flush(bool playback_buffer, bool recording_buffer) {\n\tfor (int i = 0; i < 2; i++) {\n\t\t// i == 0: flush playback buffer, 1: flush recording buffer\n\t\tif (i == 0 && playback_buffer || i == 1 && recording_buffer) {\n\t\t\tint skip_bytes = ( (i==0) ? play_buffersize : \n\t\t\t\trec_buffersize) - get_buffer_space(i == 1);\n\t\t\tif(skip_bytes <= 0) continue;\n\t\t\tunsigned char *trash = new unsigned char[skip_bytes];\n\t\t\tMEMMAN_NEW_ARRAY(trash);\n\t\t\tread(trash, skip_bytes);\n\t\t\tMEMMAN_DELETE_ARRAY(trash);\n\t\t\tdelete [] trash;\n\t\t}\n\t}\n}\n\nint t_oss_io::get_buffer_space(bool is_recording_buffer)\n{\n\taudio_buf_info dsp_info;\n\tint status = ioctl(fd, is_recording_buffer ? SNDCTL_DSP_GETISPACE :\n\t\tSNDCTL_DSP_GETOSPACE, &dsp_info);\n\tif (status == -1) return 0;\n\treturn dsp_info.bytes;\n}\n\nint t_oss_io::get_buffer_size(bool is_recording_buffer)\n{\n\tif (is_recording_buffer) return rec_buffersize;\n\telse return play_buffersize;\n}\n\nbool t_oss_io::play_buffer_underrun(void) {\n\treturn get_buffer_space(false) >= get_buffer_size(false);\n}\n\n\nint t_oss_io::read(unsigned char* buf, int len) {\n\treturn ::read(fd, buf, len);\n}\n\nint t_oss_io::write(const unsigned char* buf, int len) {\n\treturn ::write(fd, buf, len);\n}\n\n\n#ifdef HAVE_LIBASOUND\nt_alsa_io::t_alsa_io() : pcm_play_ptr(0), pcm_rec_ptr(0), play_framesize(1), rec_framesize(1), \n\tplay_buffersize(0), rec_buffersize(0) {\n}\n\nt_alsa_io::~t_alsa_io() {\n\tif (pcm_play_ptr) {\n\t\tlog_file->write_header(\"t_alsa_io::~t_alsa_io\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"snd_pcm_close, handle = \");\n\t\tlog_file->write_raw(ptr2str(pcm_play_ptr));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\t// Without the snd_pcm_hw_free, snd_pcm_close sometimes fails.\n\t\tsnd_pcm_hw_free(pcm_play_ptr);\n\t\tsnd_pcm_close(pcm_play_ptr);\n\t\tpcm_play_ptr = 0;\n\t}\n\tif (pcm_rec_ptr) {\n\t\tlog_file->write_header(\"t_alsa_io::~t_alsa_io\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"snd_pcm_close, handle = \");\n\t\tlog_file->write_raw(ptr2str(pcm_rec_ptr));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tsnd_pcm_hw_free(pcm_rec_ptr);\n\t\tsnd_pcm_close(pcm_rec_ptr);\n\t\tpcm_rec_ptr = 0;\n\t}\n}\n\n\nvoid t_alsa_io::drop() {\n\tif (pcm_play_ptr) snd_pcm_drop(pcm_play_ptr);\n\tif (pcm_rec_ptr) snd_pcm_drop(pcm_rec_ptr);\n}\n\nbool t_alsa_io::open(const string& device, bool playback, bool capture, bool blocking, int channels, t_audio_sampleformat format, int sample_rate, bool short_latency)\n{\n\tt_audio_io::open(device, playback, capture, blocking, channels, format, sample_rate,\n\t\tshort_latency);\n\t\t\n\tint mode = 0;\n\tstring msg;\n\t\n\tthis->short_latency = short_latency;\n\t\n\tconst char* dev = device.c_str();\n\tif(dev[0] == 0) dev = \"default\";\n\t\n\tlog_file->write_header(\"t_alsa_io::open\", LOG_NORMAL);\n\tlog_file->write_raw(\"Opening ALSA device: \");\n\tlog_file->write_raw(dev);\n\tlog_file->write_endl();\n\tif (playback) log_file->write_raw(\"play\\n\");\n\tif (capture) log_file->write_raw(\"capture\\n\");\n\tlog_file->write_footer();\n\t\n\tif (playback && capture) {\n\t\t// Full duplex: Perform the operation in two steps\n\t\tif (!open(device, true, false, blocking, channels, format, \n\t\t\t\tsample_rate, short_latency))\n\t\t\treturn false;\n\t\tif (!open(device, false, true, blocking, channels, format, \n\t\t\t\tsample_rate, short_latency))\n\t\t\treturn false;\n\t\t\t\n\t\treturn true;\n\t}\n\tsnd_pcm_t* pcm_ptr;\n\t\n#define HANDLE_ALSA_ERROR(func) string msg(func); msg += \" failed: \"; msg += snd_strerror(err); \\\n\tlog_file->write_report(msg, \"t_alsa_io::open\", LOG_NORMAL, LOG_CRITICAL); msg = TRANSLATE(\"Opening ALSA driver failed\") + \": \" + msg; \\\n\tui->cb_display_msg(msg, MSG_CRITICAL); if(pcm_ptr) snd_pcm_close(pcm_ptr); return false;\n\t\n\tif (!blocking) mode = SND_PCM_NONBLOCK;\n\t\n\tint err = snd_pcm_open(&pcm_ptr, dev, playback ? SND_PCM_STREAM_PLAYBACK :\n\t\t\tSND_PCM_STREAM_CAPTURE, mode);\n\tif (err < 0) {\n\t\tstring msg(\"snd_pcm_open failed: \");\n\t\tmsg += snd_strerror(err);\n\t\tlog_file->write_report(msg, \"t_alsa_io::open\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmsg = \"Cannot open ALSA driver for PCM \";\n\n\t\tif (playback) {\n\t\t\tmsg = TRANSLATE(\"Cannot open ALSA driver for PCM playback\");\n\t\t} else {\n\t\t\tmsg = TRANSLATE(\"Cannot open ALSA driver for PCM capture\");\n\t\t}\n\t\tmsg += \": \";\n\t\tmsg += snd_strerror(err);\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\t\n\tlog_file->write_header(\"t_alsa_io::open\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"snd_pcm_open succeeded, handle = \");\n\tlog_file->write_raw(ptr2str(pcm_ptr));\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tsnd_pcm_hw_params_t *hw_params;\n\tsnd_pcm_sw_params_t *sw_params;\n\t\n\t// Set HW params\n\tif ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_hw_params_malloc\");\n\t}\n\tMEMMAN_NEW(hw_params);\n\t\t\t\t\n\tif ((err = snd_pcm_hw_params_any (pcm_ptr, hw_params)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_hw_params_any\");\n\t}\n\n\tif ((err = snd_pcm_hw_params_set_access (pcm_ptr, hw_params,\n\t\t\tSND_PCM_ACCESS_RW_INTERLEAVED)) < 0) \n\t{\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_hw_params_set_access\");\n\t}\n\t\n\tsnd_pcm_format_t fmt;\n\tint sample_bits;\n\tswitch (format) {\n\t\tcase SAMPLEFORMAT_S16:\n#ifdef WORDS_BIGENDIAN\n\t\t\tfmt = SND_PCM_FORMAT_S16_BE;\n#else\n\t\t\tfmt = SND_PCM_FORMAT_S16_LE;\n#endif\n\t\t\tsample_bits = 16;\n\t\t\tbreak;\n\t\tcase SAMPLEFORMAT_U16:\n#ifdef WORDS_BIGENDIAN\n\t\t\tfmt = SND_PCM_FORMAT_U16_BE;\n#else\n\t\t\tfmt = SND_PCM_FORMAT_U16_LE;\n#endif\t\t\n\t\t\tsample_bits = 16;\n\t\t\tbreak;\n\t\tcase SAMPLEFORMAT_S8:\n\t\t\tfmt = SND_PCM_FORMAT_S8;\n\t\t\tsample_bits = 8;\n\t\t\tbreak;\n\t\tcase SAMPLEFORMAT_U8:\n\t\t\tfmt = SND_PCM_FORMAT_U8;\n\t\t\tsample_bits = 8;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t{HANDLE_ALSA_ERROR(\"invalid sample format\");}\n\t}\n\t\n\tif ((err = snd_pcm_hw_params_set_format (pcm_ptr, hw_params, fmt)) < 0) {\n\t\tstring s(\"snd_pcm_hw_params_set_format(\");\n\t\ts += int2str(fmt);\n\t\ts += \")\";\n\t\tHANDLE_ALSA_ERROR(s);\n\t}\n\t\n\t// Some sound cards do not support the exact sample rate specified in the\n\t// wav files. Still we first try to set the exact sample rate instead of\n\t// specifying the 3rd parameter as -1 to choose an approximate.\n\t// For some reason on my first sound card that supports the exact rate,\n\t// I get mono sound when the -1 parameter is specified.\n\tif ((err = snd_pcm_hw_params_set_rate (pcm_ptr, hw_params, sample_rate, 0)) < 0) {\n\t\tmsg = \"Cannot set soundcard to exact sample rate of \";\n\t\tmsg += int2str(sample_rate);\n\t\tmsg += \".\\nThe card will choose a lower approximate rate.\";\n\t\tlog_file->write_report(msg, \"t_alsa_io::open\", LOG_NORMAL, LOG_WARNING);\n\t\t\n\t\tif ((err = snd_pcm_hw_params_set_rate (pcm_ptr, hw_params, sample_rate, -1)) < 0) {\n\t\t\tstring s(\"snd_pcm_hw_params_set_rate(\");\n\t\t\ts += int2str(sample_rate);\n\t\t\ts += \")\";\n\t\t\tHANDLE_ALSA_ERROR(s);\n\t\t}\n\t}\n\t\n\t// Read back card rate for reporting in the log file.\n\tunsigned int card_rate;\n\tint card_dir;\n\tsnd_pcm_hw_params_get_rate(hw_params, &card_rate, &card_dir);\n\t\n\tif ((err = snd_pcm_hw_params_set_channels (pcm_ptr, hw_params, channels)) < 0) {\n\t\tstring s(\"snd_pcm_hw_params_set_channels(\");\n\t\ts += int2str(channels);\n\t\ts += \")\";\n\t\tHANDLE_ALSA_ERROR(s);\n\t}\n\t\n\t// Note: The buffersize is in FRAMES, not BYTES (one frame = sizeof(sample) * channels)\n\tsnd_pcm_uframes_t buffersize;\n\tunsigned int periods = 8; // Double buffering\n\t\n\t// Set the size of one period in samples\n\tif (short_latency) {\n\t\tif (playback) {\n\t\t\tbuffersize = sys_config->get_alsa_play_period_size();\n\t\t} else {\n\t\t\tbuffersize = sys_config->get_alsa_capture_period_size();\n\t\t}\n\t} else {\n\t\tbuffersize = 1024;\n\t}\n\tif ((err = snd_pcm_hw_params_set_period_size_near (pcm_ptr, hw_params, \n\t\t\t&buffersize, nullptr)) < 0)\n\t{\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_hw_params_set_period_size_near\");\n\t}\n\t\n\t// The number of periods determines the ALSA application buffer size.\n\t// This size must be larger than the jitter buffer.\n\t// TODO: use some more sophisticated algorithm here: read back the period\n\t//       size and calculate the number of periods needed (only in the\n\t//       short latency case)?\n\tif (buffersize <= 64) {\n\t\tperiods *= 8;\n\t} else if (buffersize <= 256) {\n\t\tperiods *= 4;\n\t}\n\t\n\tif ((err = snd_pcm_hw_params_set_periods(pcm_ptr, hw_params, periods, 1)) < 0) {\n\t\tif ((err = snd_pcm_hw_params_set_periods_near(pcm_ptr, hw_params, \n\t\t\t\t&periods, nullptr)) < 0)\n\t\t{\n\t\t\tHANDLE_ALSA_ERROR(\"snd_pcm_hw_params_set_periods\");\n\t\t}\n\t}\n\t\n\tif ((err = snd_pcm_hw_params (pcm_ptr, hw_params)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_hw_params\");\n\t}\n\t\n\t// Find out if the sound card supports pause functionality\n\tcan_pause = (snd_pcm_hw_params_can_pause(hw_params) == 1);\n\n\tMEMMAN_DELETE(hw_params);\n\tsnd_pcm_hw_params_free(hw_params);\n\t\n\t// Set SW params\n\tif ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_sw_params_malloc\");\n\t}\n\tMEMMAN_NEW(sw_params);\n\t\n\tif ((err = snd_pcm_sw_params_current (pcm_ptr, sw_params)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_sw_params_current\");\n\t}\n\tif ((err = snd_pcm_sw_params (pcm_ptr, sw_params)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_sw_params\");\n\t}\n\t\n\tMEMMAN_DELETE(sw_params);\n\tsnd_pcm_sw_params_free(sw_params);\n\t\n\tif ((err = snd_pcm_prepare (pcm_ptr)) < 0) {\n\t\tHANDLE_ALSA_ERROR(\"snd_pcm_prepare\");\n\t}\n\tif (playback) {\n\t\tpcm_play_ptr = pcm_ptr;\n\t\tplay_framesize = (sample_bits / 8) * channels;\n\t\tplay_buffersize = (int)buffersize * periods * play_framesize;\n\t\tplay_periods = periods;\n\t\t\n\t\tlog_file->write_header(\"t_alsa_io::open\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"ALSA playback buffer settings.\\n\");\n\t\tlog_file->write_raw(\"Rate = \");\n\t\tlog_file->write_raw(card_rate);\n\t\tlog_file->write_raw(\" frames/sec\\n\");\n\t\tlog_file->write_raw(\"Frame size = \");\n\t\tlog_file->write_raw(play_framesize);\n\t\tlog_file->write_raw(\" bytes\\n\");\n\t\tlog_file->write_raw(\"Periods = \");\n\t\tlog_file->write_raw(play_periods);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"Period size = \");\n\t\tlog_file->write_raw(buffersize * play_framesize);\n\t\tlog_file->write_raw(\" bytes\\n\");\n\t\tlog_file->write_raw(\"Buffer size = \");\n\t\tlog_file->write_raw(play_buffersize);\n\t\tlog_file->write_raw(\" bytes\\n\");\n\t\tlog_file->write_raw(\"Can pause: \");\n\t\tlog_file->write_bool(can_pause);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t} else {\n\t\t// Since audio_rx checks buffer before reading, start manually\n\t\tif ((err = snd_pcm_start(pcm_ptr)) < 0) {\n\t\t\tHANDLE_ALSA_ERROR(\"snd_pcm_start\");\n\t\t}\n\t\t\n\t\tpcm_rec_ptr = pcm_ptr;\n\t\trec_framesize = (sample_bits / 8) * channels;\n\t\trec_buffersize = (int)buffersize * periods * rec_framesize;\n\t\trec_periods = periods;\n\t\t\n\t\t// HACK: snd_pcm_delay seems not to work for the dsnoop device.\n\t\t//       This code determines if snd_pcm is working. As the capture\n\t\t//       device just started, it should return zero or a small number.\n\t\t//       So if it returns that more than half of the buffer is filled\n\t\t//       already, it seems broken.\n\t\trec_delay_broken = false;\n\t\tif (get_buffer_space(true) > rec_buffersize / 2) {\n\t\t\trec_delay_broken = true;\n\t\t\tlog_file->write_report(\n\t\t\t\t\"snd_pcm_delay does not work for capture buffer.\",\n\t\t\t\t\"t_alsa_io::open\", LOG_NORMAL, LOG_DEBUG);\n\t\t}\n\t\t\n\t\tlog_file->write_header(\"t_alsa_io::open\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"ALSA capture buffer settings.\\n\");\n\t\tlog_file->write_raw(\"Rate = \");\n\t\tlog_file->write_raw(card_rate);\n\t\tlog_file->write_raw(\" frames/sec\\n\");\n\t\tlog_file->write_raw(\"Frame size = \");\n\t\tlog_file->write_raw(rec_framesize);\n\t\tlog_file->write_raw(\" bytes\\n\");\n\t\tlog_file->write_raw(\"Periods = \");\n\t\tlog_file->write_raw(rec_periods);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"Period size = \");\n\t\tlog_file->write_raw(buffersize * rec_framesize);\n\t\tlog_file->write_raw(\" bytes\\n\");\n\t\tlog_file->write_raw(\"Buffer size = \");\n\t\tlog_file->write_raw(rec_buffersize);\n\t\tlog_file->write_raw(\" bytes\\n\");\n\t\tlog_file->write_raw(\"Can pause: \");\n\t\tlog_file->write_bool(can_pause);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t}\n\t\n\treturn true;\n#undef HANDLE_ALSA_ERROR\n}\n\n\nvoid t_alsa_io::enable(bool enable_playback, bool enable_recording) {\n\tif (!can_pause) return;\n\n\tif (pcm_play_ptr) {\n\t\tsnd_pcm_pause(pcm_play_ptr, (int)enable_playback);\n\t}\n\tif (pcm_rec_ptr) {\n\t\tsnd_pcm_pause(pcm_rec_ptr, (int)enable_recording);\n\t}\n}\n\nvoid t_alsa_io::flush(bool playback_buffer, bool recording_buffer) {\n\tif (playback_buffer && pcm_play_ptr) {\n\t\t// snd_pcm_reset(pcm_play_ptr);\n\t\tsnd_pcm_drop(pcm_play_ptr);\n\t\tsnd_pcm_prepare(pcm_play_ptr);\n\t\tsnd_pcm_start(pcm_play_ptr);\n\t}\n\tif (recording_buffer && pcm_rec_ptr) {\n\t\t// For some obscure reason snd_pcm_reset causes the CPU\n\t\t// load to rise to 99.9% when playing and capturing is\n\t\t// done on the same sound card.\n\t\t// Therefor snd_pcm_reset is replaced by functions to\n\t\t// stop the card, drop samples and start again.\n\t\t// snd_pcm_reset(pcm_rec_ptr);\n\t\tsnd_pcm_drop(pcm_rec_ptr);\n\t\tsnd_pcm_prepare(pcm_rec_ptr);\n\t\tsnd_pcm_start(pcm_rec_ptr);\n\t}\n}\n\nint t_alsa_io::get_buffer_space(bool is_recording_buffer) {\n\tint rv;\n\tint err;\n\tsnd_pcm_sframes_t delay;\n\tsnd_pcm_status_t* status;\n\tsnd_pcm_status_alloca(&status);\n\t\n\tif(is_recording_buffer) {\n\t\tif(!pcm_rec_ptr) return 0;\n\t\t\n\t\t// This does not work as snd_pcm_status_get_avail_max does not return\n\t\t// accurate results.\n\t\t// snd_pcm_status(pcm_rec_ptr, status);\n\t\t// rv = rec_framesize * snd_pcm_status_get_avail_max(status);\n\t\t\n\t\tsnd_pcm_hwsync(pcm_rec_ptr);\n\t\tif ((err = snd_pcm_delay(pcm_rec_ptr, &delay)) < 0) {\n\t\t\tstring msg = \"snd_pcm_delay for capture buffer failed: \";\n\t\t\tmsg += snd_strerror(err);\n\t\t\tlog_file->write_report(msg, \"t_alsa_io::get_buffer_space\", \n\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tdelay = 0;\n\t\t\tsnd_pcm_prepare(pcm_rec_ptr);\n\t\t}\n\n\t\tif (rec_delay_broken) {\n\t\t\trv = 0; // there is no way to get an accurate number\n\t\t} else {\n\t\t\trv = int(delay * rec_framesize);\n\t\t}\n\t\t\n\t\tif (rv > rec_buffersize) {\n\t\t\trv = rec_buffersize; // capture buffer overrun\n\t\t\tsnd_pcm_prepare(pcm_rec_ptr);\n\t\t}\n\t} else {\n\t\tif(!pcm_play_ptr) return 0;\n\t\t\n\t\tsnd_pcm_status(pcm_play_ptr, status);\n\t\trv = play_framesize * snd_pcm_status_get_avail_max(status);\n\t\t\n\t\tif (rv > play_buffersize) {\n\t\t\trv = play_buffersize; // playback buffer underrun\n\t\t\tsnd_pcm_prepare(pcm_play_ptr);\n\t\t}\n\t}\n\t\n\treturn rv;\n}\n\nint t_alsa_io::get_buffer_size(bool is_recording_buffer)\n{\n\tif (is_recording_buffer) return rec_buffersize;\n\telse return play_buffersize;\n}\n\nbool t_alsa_io::play_buffer_underrun(void) {\n\tif (!pcm_play_ptr) return false;\n\treturn snd_pcm_state(pcm_play_ptr) == SND_PCM_STATE_XRUN;\n}\n\nint t_alsa_io::read(unsigned char* buf, int len) {\n\tstring msg;\n\t\n\tif (!pcm_rec_ptr) {\n\t\tlog_file->write_report(\"Illegal pcm_rec_prt.\", \n\t\t\t\"t_alsa_io::read\", LOG_NORMAL, LOG_CRITICAL);\n\t\treturn -1;\n\t}\n\t\n\tint len_frames = len / rec_framesize;\n\tint read_frames = 0;\n\t\n\tfor(;;) {\n\t\tint read = snd_pcm_readi(pcm_rec_ptr, buf, len_frames);\n\t\tif (read == -EPIPE) {\n\t\t\tmsg = \"Capture buffer overrun.\";\n\t\t\tlog_file->write_report(msg, \"t_alsa_io::read\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tsnd_pcm_prepare(pcm_rec_ptr);\n\t\t\tsnd_pcm_start(pcm_rec_ptr);\n\t\t\tcontinue;\n\t\t} else if (read <= 0) {\n\t\t\tmsg = \"PCM read error: \";\n\t\t\tmsg += snd_strerror(read);\n\t\t\tlog_file->write_report(msg, \"t_alsa_io::read\", LOG_NORMAL, LOG_DEBUG);\n\t\t\treturn -1;\n\t\t} else if (read < len_frames) {\n\t\t\tbuf += rec_framesize * read;\n\t\t\tlen_frames -= read;\n\t\t\tread_frames += read;\n\t\t\tcontinue;\n\t\t}\n\t\treturn (read_frames + read) * rec_framesize;\n\t}\n}\nint t_alsa_io::write(const unsigned char* buf, int len) {\t\n\tint len_frames = len / play_framesize;\n\tint frames_written = 0;\n\tstring msg;\n\t\n\tfor (;;) {\n\t\tif(!pcm_play_ptr) return -1;\n\t\tint written = snd_pcm_writei(pcm_play_ptr, buf, len_frames);\n\t\tif (written == -EPIPE) {\n\t\t\tmsg = \"Playback buffer underrun.\";\n\t\t\tlog_file->write_report(msg, \"t_alsa_io::write\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tsnd_pcm_prepare(pcm_play_ptr);\n\t\t\tcontinue;\n\t\t} else if (written == -EINVAL) {\n\t\t\tmsg = \"Invalid argument passed to snd_pcm_writei: \";\n\t\t\tmsg += snd_strerror(written);\n\t\t\tlog_file->write_report(msg, \"t_alsa_io::write\", LOG_NORMAL, LOG_DEBUG);\n\t\t} else if (written < 0) {\n\t\t\tmsg = \"PCM write error: \";\n\t\t\tmsg += snd_strerror(written);\n\t\t\tlog_file->write_report(msg, \"t_alsa_io::write\", LOG_NORMAL, LOG_DEBUG);\n\t\t\treturn -1;\n\t\t} else if (written < len_frames) {\n\t\t\tbuf += written * play_framesize;\n\t\t\tlen_frames -= written;\n\t\t\tframes_written += written;\n\t\t\tcontinue;\n\t\t}\n\t\treturn (frames_written + written) * play_framesize;\n\t}\n}\n\n\n// This function fills the specified list with ALSA hardware soundcards found on the system.\n// It uses plughw:xx instead of hw:xx for specifiers, because hw:xx are not practical to\n// use (e.g. they require a resampler/channel mixer in the application).\n// playback indicates if a list with playback or capture devices should be created.\nvoid alsa_fill_soundcards(list<t_audio_device>& l, bool playback)\n{\n\tint err = 0;\n\tint card = -1, device = -1;\n\tsnd_ctl_t *handle;\n\tsnd_ctl_card_info_t *info;\n\tsnd_pcm_info_t *pcminfo;\n\tsnd_ctl_card_info_alloca(&info);\n\tsnd_pcm_info_alloca(&pcminfo);\n\n\tfor (;;) {\n\t\terr = snd_card_next(&card);\n\t\tif (err < 0 || card < 0) break;\n\t\tif (card >= 0) {\n\t\t\tstring name = \"hw:\";\n\t\t\tname += int2str(card);\n\t\t\tif ((err = snd_ctl_open(&handle, name.c_str(), 0)) < 0) continue;\n\t\t\tif ((err = snd_ctl_card_info(handle, info)) < 0) {\n\t\t\t\tsnd_ctl_close(handle);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tconst char *card_name = snd_ctl_card_info_get_name(info);\n\t\t\t\n\t\t\tfor (;;) {\n\t\t\t\terr = snd_ctl_pcm_next_device(handle, &device);\n\t\t\t\tif (err < 0 || device < 0) break;\n\n\t\t\t\tsnd_pcm_info_set_device(pcminfo, device);\n\t\t\t\tsnd_pcm_info_set_subdevice(pcminfo, 0);\n\t\t\t\t\n\t\t\t\tif (playback) {\n\t\t\t\t\tsnd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);\n\t\t\t\t} else {\n\t\t\t\t\tsnd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) continue;\n\n\t\t\t\tt_audio_device dev;\n\t\t\t\tdev.device = string(\"plughw:\") + int2str(card) + \n\t\t\t\t\tstring(\",\") + int2str(device);\n\t\t\t\tdev.name = string(card_name) + \" (\";\n\t\t\t\tdev.name += snd_pcm_info_get_name(pcminfo);\n\t\t\t\tdev.name += \")\";\n\t\t\t\tdev.type = t_audio_device::ALSA;\n\t\t\t\tl.push_back(dev);\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tsnd_ctl_close(handle);\n\t\t}\n\t}\n}\n\n// endif ifdef HAVE_LIBASOUND\n#endif\n"
  },
  {
    "path": "src/audio/audio_device.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _AUDIO_DEVICE_H\n#define _AUDIO_DEVICE_H\n\n#include <string>\n#include \"twinkle_config.h\"\n\nusing namespace std;\n\n#ifndef _SYS_SETTINGS_H\nclass t_audio_device;\n#endif\n\nenum t_audio_sampleformat {\n\tSAMPLEFORMAT_U8,\n\tSAMPLEFORMAT_S8,\n\tSAMPLEFORMAT_S16,\n\tSAMPLEFORMAT_U16\n};\n\nclass t_audio_io {\npublic:\n\tvirtual ~t_audio_io();\n\tvirtual void enable(bool enable_playback, bool enable_recording) = 0;\n\tvirtual void flush(bool playback_buffer, bool recording_buffer) = 0;\n\t// Returns the number of bytes that can be written/read without blocking\n\tvirtual int get_buffer_space(bool is_recording_buffer) = 0;\n\t// Returns the size of the hardware buffer\n\tvirtual int get_buffer_size(bool is_recording_buffer) = 0;\n\t\n\t/** Check if a play buffer underrun occurred. */\n\tvirtual bool play_buffer_underrun(void) = 0;\n\t\n\tvirtual int read(unsigned char* buf, int len) = 0;\n\tvirtual int write(const unsigned char* buf, int len) = 0;\n\tvirtual int get_sample_rate(void) const;\n\n\t// Called before waiting for audio threads to exit, to unblock any\n\t// in-progress read/write so the threads can check their stop flags.\n\tvirtual void drop() {}\n\t\n\tstatic t_audio_io* open(const t_audio_device& dev, bool playback, \n\t\tbool capture, bool blocking, int channels, t_audio_sampleformat format, \n\t\tint sample_rate, bool short_latency);\n\t\t\n\t// Validate if an audio device can be opened.\n\tstatic bool validate(const t_audio_device& dev, bool playback, bool capture);\n\nprotected:\n\tvirtual bool open(const string& device, bool playback, bool capture, \n\t\tbool blocking, int channels, t_audio_sampleformat format, \n\t\tint sample_rate, bool short_latency);\n\t\t\nprivate:\n\tint _sample_rate;\n\t\n};\n\nclass t_oss_io : public t_audio_io {\npublic:\n\tt_oss_io();\n\tvirtual ~t_oss_io();\n\tvoid enable(bool enable_playback, bool enable_recording);\n\tvoid flush(bool playback_buffer, bool recording_buffer);\n\tint get_buffer_space(bool is_recording_buffer);\n\tint get_buffer_size(bool is_recording_buffer);\n\tbool play_buffer_underrun(void);\n\tint read(unsigned char* buf, int len);\n\tint write(const unsigned char* buf, int len);\nprotected:\n\tbool open(const string& device, bool playback, bool capture, bool blocking, \n\t\tint channels, t_audio_sampleformat format, int sample_rate, \n\t\tbool short_latency);\nprivate:\n\tint fd;\n\tint play_buffersize, rec_buffersize;\n};\n\n#ifdef HAVE_LIBASOUND\nclass t_alsa_io : public t_audio_io {\npublic:\n\tt_alsa_io();\n\tvirtual ~t_alsa_io();\n\tvoid enable(bool enable_playback, bool enable_recording);\n\tvoid flush(bool playback_buffer, bool recording_buffer);\n\tint get_buffer_space(bool is_recording_buffer);\n\tint get_buffer_size(bool is_recording_buffer);\n\tbool play_buffer_underrun(void);\n\tint read(unsigned char* buf, int len);\n\tint write(const unsigned char* buf, int len);\n\tvoid drop();\nprotected:\n\tbool open(const string& device, bool playback, bool capture, bool blocking,\n\t\tint channels, t_audio_sampleformat format, int sample_rate,\n\t\tbool short_latency);\nprivate:\n\tstruct _snd_pcm *pcm_play_ptr, *pcm_rec_ptr;\n\tint play_framesize, rec_framesize;\n\tint play_buffersize, rec_buffersize;\n\tint play_periods, rec_periods;\n\tbool short_latency;\n\t\n\t// snd_pcm_delay should return the number of bytes in the buffer.\n\t// For some reason however, if the capture device is a software mixer,\n\t// it returns inaccurate values.\n\t// This flag if the functionality is broken.\n\tbool rec_delay_broken;\n\t\n\t// Indicates if snd_pcm_pause works for this device\n\tbool can_pause;\n};\n#endif\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_encoder.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <iostream>\n#include \"audio_encoder.h\"\n\n#ifdef HAVE_ILBC\n#ifndef HAVE_ILBC_CPP\nextern \"C\" {\n#endif\n#include <ilbc/iLBC_encode.h>\n#ifndef HAVE_ILBC_CPP\n}\n#endif\n#endif\n\n//////////////////////////////////////////\n// class t_audio_encoder\n//////////////////////////////////////////\n\nt_audio_encoder::t_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config) :\n\t_payload_id(payload_id),\n\t_ptime(ptime),\n\t_user_config(user_config)\n{}\n\nt_audio_codec t_audio_encoder::get_codec(void) const {\n\treturn _codec;\n}\n\nuint16 t_audio_encoder::get_payload_id(void) const {\n\treturn _payload_id;\n}\n\nuint16 t_audio_encoder::get_ptime(void) const {\n\treturn _ptime;\n}\n\nuint16 t_audio_encoder::get_sample_rate(void) const {\n\treturn audio_sample_rate(_codec);\n}\n\nuint16 t_audio_encoder::get_sample_rate_rtp(void) const {\n\treturn audio_sample_rate_rtp(_codec);\n}\n\nuint16 t_audio_encoder::get_sample_rate_rtp_ratio(void) const {\n\treturn audio_sample_rate_rtp_ratio(_codec);\n}\n\nuint16 t_audio_encoder::get_max_payload_size(void) const {\n\treturn _max_payload_size;\n}\n\n\n//////////////////////////////////////////\n// class t_g711a_audio_encoder\n//////////////////////////////////////////\n\nt_g711a_audio_encoder::t_g711a_audio_encoder(uint16 payload_id, uint16 ptime, \n\t\tt_user *user_config) :\n\tt_audio_encoder(payload_id, ptime, user_config)\n{\n\t_codec = CODEC_G711_ALAW;\n\tif (ptime == 0) _ptime = PTIME_G711_ALAW;\n\t_max_payload_size = audio_sample_rate(_codec)/1000 * _ptime;\n}\n\nuint16 t_g711a_audio_encoder::encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert(nsamples <= payload_size);\n\tsilence = false;\n\t\n\tfor (int i = 0; i < nsamples; i++) {\n\t\tpayload[i] = linear2alaw(sample_buf[i]);\n\t}\n\t\n\treturn nsamples;\t\n}\n\n\n//////////////////////////////////////////\n// class t_g711u_audio_encoder\n//////////////////////////////////////////\n\nt_g711u_audio_encoder::t_g711u_audio_encoder(uint16 payload_id, uint16 ptime, \n\t\tt_user *user_config) :\n\tt_audio_encoder(payload_id, ptime, user_config)\n{\n\t_codec = CODEC_G711_ULAW;\n\tif (ptime == 0) _ptime = PTIME_G711_ULAW;\n\t_max_payload_size = audio_sample_rate(_codec)/1000 * _ptime;\n}\n\nuint16 t_g711u_audio_encoder::encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert(nsamples <= payload_size);\n\tsilence = false;\n\n\tfor (int i = 0; i < nsamples; i++) {\n\t\tpayload[i] = linear2ulaw(sample_buf[i]);\n\t}\n\t\n\treturn nsamples;\t\n}\n\n\n//////////////////////////////////////////\n// class t_gsm_audio_encoder\n//////////////////////////////////////////\n\nt_gsm_audio_encoder::t_gsm_audio_encoder(uint16 payload_id, uint16 ptime, \n\t\tt_user *user_config) :\n\tt_audio_encoder(payload_id, PTIME_GSM, user_config)\n{\n\t_codec = CODEC_GSM;\n\t_max_payload_size = 33;\n\tgsm_encoder = gsm_create();\n}\n\nt_gsm_audio_encoder::~t_gsm_audio_encoder() {\n\tgsm_destroy(gsm_encoder);\n}\n\nuint16 t_gsm_audio_encoder::encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert(payload_size >= _max_payload_size);\n\tsilence = false;\n\tgsm_encode(gsm_encoder, sample_buf, payload);\n\treturn _max_payload_size;\n}\n\n\n#ifdef HAVE_SPEEX\n//////////////////////////////////////////\n// class t_speex_audio_encoder\n//////////////////////////////////////////\n\nt_speex_audio_encoder::t_speex_audio_encoder(uint16 payload_id, uint16 ptime, \n\t\tt_mode mode, t_user *user_config) :\n\tt_audio_encoder(payload_id, PTIME_SPEEX, user_config)\n{\n\tspeex_bits_init(&speex_bits);\n\t_mode = mode;\n\t\n\tswitch (mode) {\n\tcase MODE_NB:\n\t\t_codec = CODEC_SPEEX_NB;\n\t\tspeex_enc_state = speex_encoder_init(&speex_nb_mode);\n\t\tbreak;\n\tcase MODE_WB:\n\t\t_codec = CODEC_SPEEX_WB;\n\t\tspeex_enc_state = speex_encoder_init(&speex_wb_mode);\n\t\tbreak;\n\tcase MODE_UWB:\n\t\t_codec = CODEC_SPEEX_UWB;\n\t\tspeex_enc_state = speex_encoder_init(&speex_uwb_mode);\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\tint arg;\n\t\n\t// Bit rate type\n\tswitch (_user_config->get_speex_bit_rate_type()) {\n\tcase BIT_RATE_CBR:\n\t\targ = 0;\n\t\tspeex_encoder_ctl(speex_enc_state, SPEEX_SET_VBR, &arg);\n\t\tbreak;\n\tcase BIT_RATE_VBR:\n\t\targ = 1;\n\t\tspeex_encoder_ctl(speex_enc_state, SPEEX_SET_VBR, &arg);\n\t\tbreak;\n\tcase BIT_RATE_ABR:\n\t\tif (_codec == CODEC_SPEEX_NB) {\n\t\t\targ = user_config->get_speex_abr_nb();\n\t\t\tspeex_encoder_ctl(speex_enc_state, SPEEX_SET_ABR, &arg);\n\t\t} else {\n\t\t\targ = user_config->get_speex_abr_wb();\n\t\t\tspeex_encoder_ctl(speex_enc_state, SPEEX_SET_ABR, &arg);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\t_max_payload_size = 1500;\n\t\n\t/*** ENCODER OPTIONS ***/\n\t\n\t// Discontinuos trasmission\n\targ = (_user_config->get_speex_dtx() ? 1 : 0);\n\tspeex_encoder_ctl(speex_enc_state, SPEEX_SET_DTX, &arg);\n\t\t\n\t// Quality\n\targ = _user_config->get_speex_quality();\n\tif (_user_config->get_speex_bit_rate_type() == BIT_RATE_VBR) \n\t    speex_encoder_ctl(speex_enc_state, SPEEX_SET_VBR_QUALITY, &arg);\n\telse\n\t    speex_encoder_ctl(speex_enc_state, SPEEX_SET_QUALITY, &arg);\n\n\t// Complexity\n\targ = _user_config->get_speex_complexity();\n\tspeex_encoder_ctl(speex_enc_state, SPEEX_SET_COMPLEXITY, &arg);\n}\n\nt_speex_audio_encoder::~t_speex_audio_encoder() {\n\tspeex_bits_destroy(&speex_bits);\n\tspeex_encoder_destroy(speex_enc_state);\n}\n\nuint16 t_speex_audio_encoder::encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert(payload_size >= _max_payload_size);\n\n\tsilence = false;\n\tspeex_bits_reset(&speex_bits);\n            \n    if (speex_encode_int(speex_enc_state, sample_buf, &speex_bits) == 0) \n\t\tsilence = true;\n\n\treturn speex_bits_write(&speex_bits, (char *)payload, payload_size);\n}\n#endif\n\n#ifdef HAVE_ILBC\n//////////////////////////////////////////\n// class t_ilbc_audio_encoder\n//////////////////////////////////////////\n\nt_ilbc_audio_encoder::t_ilbc_audio_encoder(uint16 payload_id, uint16 ptime,\n\t\tt_user *user_config) :\n\tt_audio_encoder(payload_id, (ptime < 25 ? 20 : 30), user_config)\n{\n\t_codec = CODEC_ILBC;\n\t_mode = _ptime;\n\t\n\tif (_mode == 20) {\n\t\t_max_payload_size = NO_OF_BYTES_20MS;\n\t} else {\n\t\t_max_payload_size = NO_OF_BYTES_30MS;\n\t}\n\t\n\tinitEncode(&_ilbc_encoder, _mode);\n}\n\nuint16 t_ilbc_audio_encoder::encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert(payload_size >= _max_payload_size);\n\tassert(nsamples == _ilbc_encoder.blockl);\n\t\n\tsilence = false;\n\tfloat block[nsamples];\n\t\n\tfor (int i = 0; i < nsamples; i++) {\n\t\tblock[i] = static_cast<float>(sample_buf[i]);\n\t}\n\t\n\tiLBC_encode((unsigned char*)payload, block, &_ilbc_encoder);\n\t\n\treturn _ilbc_encoder.no_of_bytes;\n}\n#endif\n\n//////////////////////////////////////////\n// class t_g722_audio_encoder\n//////////////////////////////////////////\n\nt_g722_audio_encoder::t_g722_audio_encoder(uint16 payload_id, uint16 ptime,\n\t\tt_user *user_config) :\n\tt_audio_encoder(payload_id, ptime, user_config)\n{\n\t_codec = CODEC_G722;\n\tif (ptime == 0) _ptime = PTIME_G722;\n\t_max_payload_size = audio_sample_rate(_codec)/1000 * _ptime / G722_SAMPLES_PAYLOAD_RATIO;\n\n\t_state = g722_encode_init(NULL, 64000, 0);\n}\n\nt_g722_audio_encoder::~t_g722_audio_encoder()\n{\n\tg722_encode_release(_state);\n}\n\nuint16 t_g722_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,\n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert((nsamples % G722_SAMPLES_PAYLOAD_RATIO) == 0);\n\tassert(payload_size >= (nsamples / G722_SAMPLES_PAYLOAD_RATIO));\n\n\tsilence = false;\n\n\tint nbytes = g722_encode(_state, payload, sample_buf, nsamples);\n\n\treturn nbytes;\n}\n\n\n//////////////////////////////////////////\n// class t_g726_encoder\n//////////////////////////////////////////\n\nt_g726_audio_encoder::t_g726_audio_encoder(uint16 payload_id, uint16 ptime, \n\t\tt_bit_rate bit_rate, t_user *user_config) :\n\tt_audio_encoder(payload_id, ptime, user_config)\n{\n\t_bit_rate = bit_rate;\n\t\n\tswitch (bit_rate) {\n\tcase BIT_RATE_16:\n\t\t_codec = CODEC_G726_16;\n\t\tbreak;\n\tcase BIT_RATE_24:\n\t\t_codec = CODEC_G726_24;\n\t\tbreak;\n\tcase BIT_RATE_32:\n\t\t_codec = CODEC_G726_32;\n\t\tbreak;\n\tcase BIT_RATE_40:\n\t\t_codec = CODEC_G726_40;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\tif (ptime == 0) _ptime = PTIME_G726;\n\t_max_payload_size = audio_sample_rate(_codec)/1000 * _ptime;\n\t_packing = user_config->get_g726_packing();\n\t\n\tg72x_init_state(&_state);\n}\n\nuint16 t_g726_audio_encoder::encode_16(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size)\n{\n\tassert(nsamples % 4 == 0);\n\tassert(nsamples / 4 <= payload_size);\n\t\n\tfor (int i = 0; i < nsamples; i += 4) {\n\t\tpayload[i >> 2] = 0;\n\t\tfor (int j = 0; j < 4; j++) {\n\t\t\tuint8 v = static_cast<uint8>(g723_16_encoder(sample_buf[i+j],\n\t\t\t\tAUDIO_ENCODING_LINEAR, &_state));\n\t\t\t\t\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tpayload[i >> 2] |= v << (j * 2);\n\t\t\t} else {\n\t\t\t\tpayload[i >> 2] |= v << ((3-j) * 2);\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn nsamples >> 2;\n}\n\nuint16 t_g726_audio_encoder::encode_24(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size)\n{\n\tassert(nsamples % 8 == 0);\n\tassert(nsamples / 8 * 3 <= payload_size);\n\n\tfor (int i = 0; i < nsamples; i += 8) {\n\t\tuint32 v = 0;\n\t\tfor (int j = 0; j < 8; j++) {\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tv |= static_cast<uint32>(g723_24_encoder(sample_buf[i+j],\n\t\t\t\t\tAUDIO_ENCODING_LINEAR, &_state)) << (j * 3);\n\t\t\t} else {\n\t\t\t\tv |= static_cast<uint32>(g723_24_encoder(sample_buf[i+j],\n\t\t\t\t\tAUDIO_ENCODING_LINEAR, &_state)) << ((7-j) * 3);\n\t\t\t}\n\t\t}\n\t\tpayload[(i >> 3) * 3] = static_cast<uint8>(v & 0xff);\n\t\tpayload[(i >> 3) * 3 + 1] = static_cast<uint8>((v >> 8) & 0xff);\n\t\tpayload[(i >> 3) * 3 + 2] = static_cast<uint8>((v >> 16) & 0xff);\n\t}\n\t\n\treturn (nsamples >> 3) * 3;\n}\n\nuint16 t_g726_audio_encoder::encode_32(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size)\n{\n\tassert(nsamples % 2 == 0);\n\tassert(nsamples / 2 <= payload_size);\n\n\tfor (int i = 0; i < nsamples; i += 2) {\n\t\tpayload[i >> 1] = 0;\n\t\tfor (int j = 0; j < 2; j++) {\n\t\t\tuint8 v = static_cast<uint8>(g721_encoder(sample_buf[i+j],\n\t\t\t\tAUDIO_ENCODING_LINEAR, &_state));\n\t\t\t\t\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tpayload[i >> 1] |= v << (j * 4);\n\t\t\t} else {\n\t\t\t\tpayload[i >> 1] |= v << ((1-j) * 4);\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn nsamples >> 1;\n}\n\nuint16 t_g726_audio_encoder::encode_40(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size)\n{\n\tassert(nsamples % 8 == 0);\n\tassert(nsamples / 8 * 5 <= payload_size);\n\n\tfor (int i = 0; i < nsamples; i += 8) {\n\t\tuint64 v = 0;\n\t\tfor (int j = 0; j < 8; j++) {\n\t\t\tif (_packing == G726_PACK_RFC3551) {\n\t\t\t\tv |= static_cast<uint64>(g723_40_encoder(sample_buf[i+j],\n\t\t\t\t\tAUDIO_ENCODING_LINEAR, &_state)) << (j * 5);\n\t\t\t} else {\n\t\t\t\tv |= static_cast<uint64>(g723_40_encoder(sample_buf[i+j],\n\t\t\t\t\tAUDIO_ENCODING_LINEAR, &_state)) << ((7-j) * 5);\n\t\t\t}\n\t\t}\n\t\tpayload[(i >> 3) * 5] = static_cast<uint8>(v & 0xff);\n\t\tpayload[(i >> 3) * 5 + 1] = static_cast<uint8>((v >> 8) & 0xff);\n\t\tpayload[(i >> 3) * 5 + 2] = static_cast<uint8>((v >> 16) & 0xff);\n\t\tpayload[(i >> 3) * 5 + 3] = static_cast<uint8>((v >> 24) & 0xff);\n\t\tpayload[(i >> 3) * 5 + 4] = static_cast<uint8>((v >> 32) & 0xff);\n\t}\n\t\n\treturn (nsamples >> 3) * 5;\n}\n\nuint16 t_g726_audio_encoder::encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tsilence = false;\n\t\n\tswitch (_bit_rate) {\n\tcase BIT_RATE_16:\n\t\treturn encode_16(sample_buf, nsamples, payload, payload_size);\n\tcase BIT_RATE_24:\n\t\treturn encode_24(sample_buf, nsamples, payload, payload_size);\n\tcase BIT_RATE_32:\n\t\treturn encode_32(sample_buf, nsamples, payload, payload_size);\n\tcase BIT_RATE_40:\n\t\treturn encode_40(sample_buf, nsamples, payload, payload_size);\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\treturn 0;\n}\n\n#ifdef HAVE_BCG729\n\nt_g729a_audio_encoder::t_g729a_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config)\n\t: t_audio_encoder(payload_id, ptime, user_config)\n{\n#ifdef HAVE_BCG729_ANNEX_B\n\t_context = initBcg729EncoderChannel(false);\n#else\n\t_context = initBcg729EncoderChannel();\n#endif\n}\n\nt_g729a_audio_encoder::~t_g729a_audio_encoder()\n{\n\tcloseBcg729EncoderChannel(_context);\n}\n\nuint16 t_g729a_audio_encoder::encode(int16 *sample_buf, uint16 nsamples,\n\t\tuint8 *payload, uint16 payload_size, bool &silence)\n{\n\tassert ((nsamples % 80) == 0);\n\tassert (payload_size >= (nsamples/8));\n\n\tsilence = false;\n\n\tfor (uint16 done = 0; done < nsamples; done += 80)\n\t{\n#ifdef HAVE_BCG729_ANNEX_B\n\t\tuint8 frame_size = 10;\n\t\tbcg729Encoder(_context, &sample_buf[done], &payload[done / 8], &frame_size);\n\t\tassert(frame_size == 10);\n#else\n\t\tbcg729Encoder(_context, &sample_buf[done], &payload[done / 8]);\n#endif\n\t}\n\n\treturn nsamples / 8;\n}\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_encoder.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Audio encoders\n\n#ifndef _AUDIO_ENCODER_H\n#define _AUDIO_ENCODER_H\n\n#include \"twinkle_config.h\"\n#include \"audio_codecs.h\"\n#include \"user.h\"\n\n#ifdef HAVE_GSM\nextern \"C\" {\n#include <gsm/gsm.h>\n}\n#else\n#include \"gsm/inc/gsm.h\"\n#endif\n\n#ifdef HAVE_SPEEX\n#include <speex/speex.h>\n#endif\n\nextern \"C\" {\n#\tinclude \"g722.h\"\n#\tinclude \"g722_local.h\"\n}\n\n#ifdef HAVE_BCG729\nextern \"C\" {\n#\tinclude <bcg729/encoder.h>\n}\n#endif\n\n#ifdef HAVE_ILBC\n#ifndef HAVE_ILBC_CPP\nextern \"C\" {\n#endif\n#include <ilbc/iLBC_define.h>\n#ifndef HAVE_ILBC_CPP\n}\n#endif\n#endif\n\n// Abstract definition of an audio encoder\nclass t_audio_encoder {\nprotected:\n\tt_audio_codec\t_codec;\n\tuint16\t\t_payload_id;\t\t// payload id for the codec\n\tuint16\t\t_ptime;\t\t\t// in milliseconds\n\tuint16\t\t_max_payload_size;\t// maximum size of payload\n\t\n\tt_user\t\t*_user_config;\n\t\n\tt_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);\n\t\npublic:\n\tvirtual ~t_audio_encoder() {};\n\t\n\tt_audio_codec get_codec(void) const;\n\tuint16 get_payload_id(void) const;\n\tuint16 get_ptime(void) const;\n\tuint16 get_sample_rate(void) const;\n\tuint16 get_sample_rate_rtp(void) const;\n\tuint16 get_sample_rate_rtp_ratio(void) const;\n\tuint16 get_max_payload_size(void) const;\n\t\n\t// Encode a 16-bit PCM sample buffer to a encoded payload\n\t// Returns the number of bytes written into the payload.\n\t// The silence flag indicates if the returned sound samples represent silence\n\t// that may be suppressed.\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence) = 0;\n};\n\n\n// G.711 A-law\nclass t_g711a_audio_encoder : public t_audio_encoder {\npublic:\n\tt_g711a_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);\n\t\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n\n\n// G.711 u-law\nclass t_g711u_audio_encoder : public t_audio_encoder {\npublic:\n\tt_g711u_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);\n\t\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n\n\n// GSM\nclass t_gsm_audio_encoder : public t_audio_encoder {\nprivate:\n\tgsm \t\tgsm_encoder;\n\t\npublic:\n\tt_gsm_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);\n\tvirtual ~t_gsm_audio_encoder();\n\t\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n\n\n#ifdef HAVE_SPEEX\nclass t_speex_audio_encoder : public t_audio_encoder {\npublic:\n\tenum t_mode {\n\t\tMODE_NB,\t// Narrow band\n\t\tMODE_WB,\t// Wide band\n\t\tMODE_UWB\t// Ultra wide band\n\t};\n\t\nprivate:\n\tSpeexBits\tspeex_bits;\n\tvoid\t\t*speex_enc_state;\n\tt_mode\t\t_mode;\n\t\npublic:\n\tt_speex_audio_encoder(uint16 payload_id, uint16 ptime, t_mode mode, \n\t\tt_user *user_config);\n\tvirtual ~t_speex_audio_encoder();\n\t\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n#endif\n\n#ifdef HAVE_ILBC\nclass t_ilbc_audio_encoder : public t_audio_encoder {\nprivate:\n\tiLBC_Enc_Inst_t\t_ilbc_encoder;\n\tuint8\t\t_mode;\t\t// 20, 30 ms (frame size)\n\t\npublic:\n\tt_ilbc_audio_encoder(uint16 payload_id, uint16 ptime, \n\t\tt_user *user_config);\n\t\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n#endif\n\nclass t_g722_audio_encoder : public t_audio_encoder {\nprivate:\n\tg722_encode_state_t *_state;\n\npublic:\n\tt_g722_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);\n\tvirtual ~t_g722_audio_encoder();\n\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples,\n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n\nclass t_g726_audio_encoder : public t_audio_encoder {\npublic:\n\tenum t_bit_rate {\n\t\tBIT_RATE_16,\n\t\tBIT_RATE_24,\n\t\tBIT_RATE_32,\n\t\tBIT_RATE_40\n\t};\n\t\nprivate:\n\tuint16 encode_16(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size);\n\tuint16 encode_24(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size);\n\tuint16 encode_32(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size);\n\tuint16 encode_40(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size);\n\n\tg72x_state\t_state;\n\tt_bit_rate\t_bit_rate;\n\tt_g726_packing\t_packing;\n\t\npublic:\n\tt_g726_audio_encoder(uint16 payload_id, uint16 ptime, t_bit_rate bit_rate, \n\t\tt_user *user_config);\n\t\t\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples, \n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\n};\n\n#ifdef HAVE_BCG729\nclass t_g729a_audio_encoder : public t_audio_encoder\n{\npublic:\n\tt_g729a_audio_encoder(uint16 payload_id, uint16 ptime, t_user *user_config);\n\tvirtual ~t_g729a_audio_encoder();\n\n\tvirtual uint16 encode(int16 *sample_buf, uint16 nsamples,\n\t\t\tuint8 *payload, uint16 payload_size, bool &silence);\nprivate:\n\tbcg729EncoderChannelContextStruct* _context;\n};\n\n#endif\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_rx.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include <cstdio>\n#include <ctime>\n#include <cstdlib>\n#include <sys/types.h>\n#include <sys/time.h>\n\n#include \"audio_rx.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"rtp_telephone_event.h\"\n#include \"userintf.h\"\n#include \"line.h\"\n#include \"sys_settings.h\"\n#include \"sequence_number.h\"\n#include \"audits/memman.h\"\n\nextern t_phone *phone;\n\n#define SAMPLE_BUF_SIZE (audio_encoder->get_ptime() * audio_encoder->get_sample_rate()/1000 *\\\n\t\t\t\tAUDIO_SAMPLE_SIZE/8)\n\n// Debug macro to print timestamp\n#define DEBUG_TS(s)\t{ gettimeofday(&debug_timer, NULL);\\\n\t\t\t  cout << \"DEBUG: \";\\\n\t\t\t  cout << debug_timer.tv_sec * 1000 +\\\n\t\t\t          debug_timer.tv_usec / 1000;\\\n\t\t\t  cout << \" \" << (s) << endl;\\\n\t\t\t}\n\n//////////\n// PRIVATE\n//////////\n\nbool t_audio_rx::get_sound_samples(unsigned short &sound_payload_size, bool &silence) {\n\tint status;\n\tstruct timespec sleeptimer;\n\t//struct timeval debug_timer;\n\t\n\tsilence = false;\n\n\tmtx_3way.lock();\n\n\tif (is_3way && !is_main_rx_3way) {\n\t\t// We are not the main receiver in a 3-way call, so\n\t\t// get the sound samples from the local media buffer.\n\t\t// This buffer will be filled by the main receiver.\n\t\tif (!media_3way_peer_rx->get(input_sample_buf, SAMPLE_BUF_SIZE)) {\n\t\t\t// The mutex is unlocked before going to sleep.\n\t\t\t// First I had the mutex unlock after the sleep.\n\t\t\t// That worked fine with LinuxThreading, but it does\n\t\t\t// not work with NPTL. It causes a deadlock when\n\t\t\t// the main receiver calls post_media_peer_rx_3way\n\t\t\t// as NPTL does not fair scheduling. This thread\n\t\t\t// simly gets the lock again and the main receiver\n\t\t\t// dies from starvation.\n\t\t\tmtx_3way.unlock();\n\n\t\t\t// There is not enough data yet. Sleep for 1 ms.\n\t\t\tsleeptimer.tv_sec = 0;\n\t\t\tsleeptimer.tv_nsec = 1000000;\n\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tmtx_3way.unlock();\n\t} else {\n\t\t// Don't keep the 3way mutex locked while waiting for the DSP.\n\t\tmtx_3way.unlock();\n\t\t\n\t\t// Get the sound samples from the DSP\n\t\tstatus = input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);\n\n\t\tif (status != SAMPLE_BUF_SIZE) {\n\t\t\tif (!logged_capture_failure) {\n\t\t\t\t// Log this failure only once\n\t\t\t\tlog_file->write_header(\"t_audio_rx::get_sound_samples\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tlog_file->write_raw(\"Audio rx line \");\n\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\tlog_file->write_raw(\": sound capture failed.\\n\");\n\t\t\t\tlog_file->write_raw(\"Status: \");\n\t\t\t\tlog_file->write_raw(status);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\tlogged_capture_failure = true;\n\t\t\t}\n\n\t\t\tstop_running = true;\n\t\t\treturn false;\n\t\t}\n\n\t\t// If line is muted, then fill sample buffer with silence.\n\t\t// Note that we keep reading the dsp, to prevent the DSP buffers\n\t\t// from filling up.\n\t\tif (get_line()->get_is_muted()) {\n\t\t\tmemset(input_sample_buf, 0, SAMPLE_BUF_SIZE);\n\t\t}\n\t}\n\n\t// Convert buffer to a buffer of shorts as the samples are 16 bits\n\tshort *sb = (short *)input_sample_buf;\n\n\tmtx_3way.lock();\n\tif (is_3way) {\n\t\t// Send the sound samples to the other receiver if we\n\t\t// are the main receiver.\n\t\t// There may be no other receiver when one of the far-ends\n\t\t// has put the call on-hold.\n\t\tif (is_main_rx_3way && peer_rx_3way) {\n\t\t\tpeer_rx_3way->post_media_peer_rx_3way(input_sample_buf, SAMPLE_BUF_SIZE,\n\t\t\t\taudio_encoder->get_sample_rate());\n\t\t}\n\n\t\t// Mix the sound samples with the 3rd party\n\t\tif (media_3way_peer_tx->get(mix_buf_3way, SAMPLE_BUF_SIZE)) {\n\t\t\tshort *mix_sb = (short *)mix_buf_3way;\n\t\t\tfor (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {\n\t\t\t\tsb[i] = mix_linear_pcm(sb[i], mix_sb[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\tmtx_3way.unlock();\n\n\t/*** PREPROCESSING & ENCODING ***/\n\n\tbool preprocessing_silence = false;\n\n#ifdef HAVE_SPEEX\n\t// speex acoustic echo cancellation\n\tif (audio_session->get_do_echo_cancellation() && !audio_session->get_echo_captured_last()) {\n\n\t    spx_int16_t *input_buf = new spx_int16_t[SAMPLE_BUF_SIZE/2];\n\t    MEMMAN_NEW_ARRAY(input_buf);\n\n\t    for (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {\n\t\tinput_buf[i] = sb[i];\n\t    }\n\n\t    speex_echo_capture(audio_session->get_speex_echo_state(), input_buf, sb);\n\t    audio_session->set_echo_captured_last(true);\n\n\t    MEMMAN_DELETE_ARRAY(input_buf);\n\t    delete [] input_buf;\n\t}\n\n\t// preprocessing\n\tpreprocessing_silence = !speex_preprocess_run(speex_preprocess_state, sb);\n\t\n\t// According to the speex API documentation the return value\n\t// from speex_preprocess_run() is only defined when VAD is\n\t// enabled. So to be safe, reset the return value, if VAD is\n\t// disabled.\n\tif (!speex_dsp_vad) preprocessing_silence = false;\n#endif\n\n\t// encoding\n\tunsigned short nsamples_real = nsamples * audio_encoder->get_sample_rate_rtp_ratio();\n\tsound_payload_size = audio_encoder->encode(sb, nsamples_real, payload, payload_size, silence);\n\n\t// recognizing silence (both from preprocessing and encoding)\n\tsilence = silence || preprocessing_silence;\n\n\treturn true;\n}\n\nbool t_audio_rx::get_dtmf_event(void) {\n\t// DTMF events are not supported in a 3-way conference\n\tif (is_3way) return false;\n\n\tif (!sema_dtmf_q.try_down()) {\n\t\t// No DTMF event available\n\t\treturn false;\n\t}\n\t\n\t// Get next DTMF event\n\tmtx_dtmf_q.lock();\n\tt_dtmf_event dtmf_event = dtmf_queue.front();\n\tdtmf_queue.pop();\n\tmtx_dtmf_q.unlock();\n\t\n\tui->cb_async_send_dtmf(get_line()->get_line_number(), dtmf_event.dtmf_tone);\n\t\n\t// Create DTMF player\n\tif (dtmf_event.inband) {\n\t\tdtmf_player = new t_inband_dtmf_player(this, audio_encoder, user_config,\n\t\t\t\tdtmf_event.dtmf_tone, timestamp, nsamples);\n\t\tMEMMAN_NEW(dtmf_player);\n\t\t\n\t\t// Log DTMF event\n\t\tlog_file->write_header(\"t_audio_rx::get_dtmf_event\", LOG_NORMAL);\n\t\tlog_file->write_raw(\"Audio rx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\": start inband DTMF tone - \");\n\t\tlog_file->write_raw(dtmf_event.dtmf_tone);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t} else {\n\t\t// The telephone events may have a different sampling rate than\n\t\t// the audio codec. Change nsamples accordingly.\n\t\tnsamples = audio_sample_rate_rtp(CODEC_TELEPHONE_EVENT)/1000 *\n\t\t\t\taudio_encoder->get_ptime();\n\t\t\n\t\tdtmf_player = new t_rtp_event_dtmf_player(this, audio_encoder, user_config,\n\t\t\t\tdtmf_event.dtmf_tone, timestamp, nsamples);\n\t\tMEMMAN_NEW(dtmf_player);\n\n\t\t// Log DTMF event\n\t\tlog_file->write_header(\"t_audio_rx::get_dtmf_event\", LOG_NORMAL);\n\t\tlog_file->write_raw(\"Audio rx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\": start DTMF event - \");\n\t\tlog_file->write_raw(dtmf_event.dtmf_tone);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"Payload type: \");\n\t\tlog_file->write_raw(pt_telephone_event);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\n\t\t// Set RTP payload format\n\t\t// HACK: the sample rate for telephone events is 8000, but the\n\t\t//       ccRTP stack does not handle it well when the sample rate\n\t\t//       changes. When the sample rate of the audio codec is kept\n\t\t//       on the ccRTP session settings, then all works fine.\n\t\trtp_session->setPayloadFormat(DynamicPayloadFormat(pt_telephone_event,\n\t\t\t\taudio_encoder->get_sample_rate_rtp()));\n\t\t\t\t// should be this: audio_sample_rate_rtp(CODEC_TELEPHONE_EVENT)\n\t\n\t\t// As all RTP event contain the same timestamp, the ccRTP stack will\n\t\t// discard packets when the timestamp gets to old.\n\t\t// Increase the expire timeout value to prevent this.\n\t\trtp_session->setExpireTimeout((JITTER_BUF_MS +\n\t\t\tuser_config->get_dtmf_duration() + user_config->get_dtmf_pause()) * 1000);\n\t}\n\n\treturn true;\n}\n\nvoid t_audio_rx::set_sound_payload_format(void) {\n\tnsamples = audio_encoder->get_sample_rate_rtp()/1000 * audio_encoder->get_ptime();\n\trtp_session->setPayloadFormat(DynamicPayloadFormat(audio_encoder->get_payload_id(),\n\t\t\taudio_encoder->get_sample_rate_rtp()));\n}\n\n//////////\n// PUBLIC\n//////////\n\nt_audio_rx::t_audio_rx(t_audio_session *_audio_session,\n\t\t   t_audio_io *_input_device, t_twinkle_rtp_session *_rtp_session,\n\t           t_audio_codec _codec, unsigned short _payload_id,\n\t           unsigned short _ptime) : sema_dtmf_q(0)\n{\n\taudio_session = _audio_session;\n\t\n\tuser_config = audio_session->get_line()->get_user();\n\tassert(user_config);\n\t\n\tinput_device = _input_device;\n\trtp_session = _rtp_session;\n\tdtmf_player = NULL;\n\tis_running = false;\n\tstop_running = false;\n\tlogged_capture_failure = false;\n\tuse_nat_keepalive = phone->use_nat_keepalive(user_config);\n\n\tpt_telephone_event = -1;\n\t\n\t// Create audio encoder\n\tswitch (_codec) {\n\tcase CODEC_G711_ALAW:\n\t\taudio_encoder = new t_g711a_audio_encoder(_payload_id, _ptime, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_G711_ULAW:\n\t\taudio_encoder = new t_g711u_audio_encoder(_payload_id, _ptime, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_GSM:\n\t\taudio_encoder = new t_gsm_audio_encoder(_payload_id, _ptime, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n#ifdef HAVE_SPEEX\n\tcase CODEC_SPEEX_NB:\n\t\taudio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_speex_audio_encoder::MODE_NB, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_SPEEX_WB:\n\t\taudio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_speex_audio_encoder::MODE_WB, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_SPEEX_UWB:\n\t\taudio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_speex_audio_encoder::MODE_UWB, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n#endif\n#ifdef HAVE_ILBC\n\tcase CODEC_ILBC:\n\t\taudio_encoder = new t_ilbc_audio_encoder(_payload_id, _ptime, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n#endif\n\tcase CODEC_G722:\n\t\taudio_encoder = new t_g722_audio_encoder(_payload_id, _ptime, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_G726_16:\n\t\taudio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_g726_audio_encoder::BIT_RATE_16, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_G726_24:\n\t\taudio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_g726_audio_encoder::BIT_RATE_24, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_G726_32:\n\t\taudio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_g726_audio_encoder::BIT_RATE_32, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n\tcase CODEC_G726_40:\n\t\taudio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,\n\t\t\t\tt_g726_audio_encoder::BIT_RATE_40, user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n#ifdef HAVE_BCG729\n\tcase CODEC_G729A:\n\t\taudio_encoder = new t_g729a_audio_encoder(_payload_id, _ptime,\n\t\t\t\t\t\t\t\t\t\t\t\t  user_config);\n\t\tMEMMAN_NEW(audio_encoder);\n\t\tbreak;\n#endif\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\tpayload_size = audio_encoder->get_max_payload_size();\n\n\tinput_sample_buf = new unsigned char[SAMPLE_BUF_SIZE];\n\tMEMMAN_NEW_ARRAY(input_sample_buf);\n\n\tpayload = new unsigned char[payload_size];\n\tMEMMAN_NEW_ARRAY(payload);\n\tnsamples = audio_encoder->get_sample_rate_rtp()/1000 * audio_encoder->get_ptime();\n\n\t// Initialize 3-way settings to 'null'\n\tmedia_3way_peer_tx = NULL;\n\tmedia_3way_peer_rx = NULL;\n\tpeer_rx_3way = NULL;\n\tmix_buf_3way = NULL;\n\tis_3way = false;\n\tis_main_rx_3way = false;\n\n#ifdef HAVE_SPEEX\n\t// initializing speex preprocessing state\n\tunsigned short nsamples_real = nsamples * audio_encoder->get_sample_rate_rtp_ratio();\n\tspeex_preprocess_state = speex_preprocess_state_init(nsamples_real, audio_encoder->get_sample_rate());\n\n\tint arg;\n\tfloat farg;\n\n\t// Noise reduction\n\targ = (user_config->get_speex_dsp_nrd() ? 1 : 0);\n\tspeex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_DENOISE, &arg);\n\targ = -30;\t\n\tspeex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &arg);\n\n\t// Automatic gain control\n\targ = (user_config->get_speex_dsp_agc() ?  1 : 0);\t\n\tspeex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC, &arg);\n\tfarg = (float) (user_config->get_speex_dsp_agc_level()) * 327.68f;\t\n\tspeex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &farg);\n\targ = 30;\t\n\tspeex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &arg);\n\n\t// Voice activity detection\n\targ = (user_config->get_speex_dsp_vad() ? 1 : 0);\n\tspeex_dsp_vad = (bool)arg;\n\tspeex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_VAD, &arg);\n\n\t// Acoustic echo cancellation \n\tif (audio_session->get_do_echo_cancellation()) {\n\t    speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE, \n\t\t\t\t audio_session->get_speex_echo_state());\n\t}\n#endif\n}\n\nt_audio_rx::~t_audio_rx() {\n\tstruct timespec sleeptimer;\n\n\tif (is_running) {\n\t\tstop_running = true;\n\t\t// Unblock any in-progress snd_pcm_readi so the thread can check\n\t\t// stop_running and exit; without this the nanosleep loop below\n\t\t// spins forever when the ALSA-PulseAudio bridge deadlocks.\n\t\tinput_device->drop();\n\t\tdo {\n\t\t\tsleeptimer.tv_sec = 0;\n\t\t\tsleeptimer.tv_nsec = 10000000;\n\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t} while (is_running);\n\t}\n\n#ifdef HAVE_SPEEX\n\t// cleaning speex preprocessing\n\tif (audio_session->get_do_echo_cancellation()) {\n\t    speex_echo_state_reset(audio_session->get_speex_echo_state());\n\t}\n\tspeex_preprocess_state_destroy(speex_preprocess_state);\n#endif\n       \n\tMEMMAN_DELETE_ARRAY(input_sample_buf);\n\tdelete [] input_sample_buf;\n\t\n\tMEMMAN_DELETE_ARRAY(payload);\n\tdelete [] payload;\n\n\tMEMMAN_DELETE(audio_encoder);\n\tdelete audio_encoder;\n\n\t// Clean up resources for 3-way conference calls\n\tif (media_3way_peer_tx) {\n\t\tMEMMAN_DELETE(media_3way_peer_tx);\n\t\tdelete media_3way_peer_tx;\n\t}\n\tif (media_3way_peer_rx) {\n\t\tMEMMAN_DELETE(media_3way_peer_rx);\n\t\tdelete media_3way_peer_rx;\n\t}\n\tif (mix_buf_3way) {\n\t\tMEMMAN_DELETE_ARRAY(mix_buf_3way);\n\t\tdelete [] mix_buf_3way;\n\t}\n\t\n\tif (dtmf_player) {\n\t\tMEMMAN_DELETE(dtmf_player);\n\t\tdelete dtmf_player;\n\t}\n}\n\nvoid t_audio_rx::set_running(bool running) {\n\tis_running = running;\n}\n\n// NOTE: no operations on the phone object are allowed inside the run() method.\n//       Such an operation needs a lock on the transaction layer. The destructor\n//       on audio_rx is called while this lock is locked. The destructor waits\n//\t in a busy loop for the run() method to finish. If the run() method would\n//       need the phone lock, this would lead to a dead lock (and a long trip\n//\t in debug hell!)\nvoid t_audio_rx::run(void) {\n\t//struct timeval debug_timer;\n\tunsigned short sound_payload_size;\n\tuint32 dtmf_rtp_timestamp;\n\t\n\tphone->add_prohibited_thread();\n\tui->add_prohibited_thread();\n\t\n\t// This flag indicates if we are currently in a silence period.\n\t// The start of a new stream is assumed to start in silence, such\n\t// that the very first RTP packet will be marked.\n\tbool silence_period = true;\n\tuint64 silence_nsamples = 0; // duration in (real) samples\n\t\n\t// This flag indicates if a sound frame can be suppressed\n\tbool suppress_samples = false;\n\n\t// The running flag is set already in t_audio_session::run to prevent\n\t// a crash when the thread gets destroyed before it starts running.\n\t// is_running = true;\n\n\t// For a 3-way conference only the main receiver has access\n\t// to the dsp.\n\tif (!is_3way || is_main_rx_3way) {\n\t\t// Enable recording\n\t\tif (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),\n\t\t\t\tsys_config->get_dev_mic())) \n\t\t{\n\t\t\tinput_device->enable(true, true);\n\t\t} else {\n\t\t\tinput_device->enable(false, true);\n\t\t}\n\n\t\t// If the stream is stopped for call-hold, then the buffer might\n\t\t// be filled with old sound samples.\n\t\tinput_device->flush(false, true);\n\t}\n\n\t// Synchronize the timestamp driven by the sampling rate\n\t// of the recording with the timestamp of the RTP session.\n\t// As the RTP session is already created in advance, the\n\t// RTP clock is a bit ahead already.\n\ttimestamp = rtp_session->getCurrentTimestamp() + nsamples;\n\n\t// This loop keeps running until the stop_running flag is set to true.\n\t// When a call is being released the stop_running flag is set to true.\n\t// At that moment the lock on the transaction layer (phone) is taken.\n\t// So do not use operations that take the phone lock, otherwise a\n\t// dead lock may occur during call release.\n\twhile (true) {\n\t\tif (stop_running) break;\n\n\t\tif (dtmf_player) {\n\t\t\trtp_session->setMark(false);\n\t\t\t// Skip samples from sound card\n\t\t\tinput_device->read(input_sample_buf, SAMPLE_BUF_SIZE);\n\t\t\tsound_payload_size = dtmf_player->get_payload(\n\t\t\t\tpayload, payload_size, timestamp, dtmf_rtp_timestamp);\n\t\t\tsilence_period = false;\n\t\t} else if (get_dtmf_event()) {\n\t\t\t// RFC 2833\n\t\t\t// Set marker in first RTP packet of a DTMF event\n\t\t\trtp_session->setMark(true);\n\t\t\t// Skip samples from sound card\n\t\t\tinput_device->read(input_sample_buf, SAMPLE_BUF_SIZE);\n\t\t\tassert(dtmf_player);\n\t\t\tsound_payload_size = dtmf_player->get_payload(\n\t\t\t\tpayload, payload_size, timestamp, dtmf_rtp_timestamp);\n\t\t\tsilence_period = false;\n\t\t} else if (get_sound_samples(sound_payload_size, suppress_samples)) {\n\t\t\tif (suppress_samples && use_nat_keepalive) {\n\t\t\t\tif (!silence_period) silence_nsamples = 0;\n\t\t\t\t\n\t\t\t\t// Send a silence packet at the NAT keep alive interval\n\t\t\t\t// to keep the NAT bindings for RTP fresh.\n\t\t\t\tsilence_nsamples += SAMPLE_BUF_SIZE / 2;\n\t\t\t\tif (silence_nsamples > \n\t\t\t\t\t(uint64_t)user_config->get_timer_nat_keepalive() * 1000 *\n\t\t\t\t\taudio_encoder->get_sample_rate())\n\t\t\t\t{\n\t\t\t\t\tsuppress_samples = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\tif (silence_period && !suppress_samples) {\n\t\t\t\t// RFC 3551 4.1\n\t\t\t\t// Set marker bit in first RTP packet after silence\n\t\t\t\trtp_session->setMark(true);\n\t\t\t} else {\n\t\t\t\trtp_session->setMark(false);\n\t\t\t}\n\t\t\tsilence_period = suppress_samples;\n\t\t} else {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If timestamp is more than 1 payload size ahead of the clock of\n\t\t// the ccRTP stack, then drop the current payload and do not advance\n\t\t// the timestamp. This will happen if the DSP delivers more\n\t\t// sound samples than the set sample rate. To compensate for this\n\t\t// samples must be dropped.\n\t\tuint32 current_timestamp = rtp_session->getCurrentTimestamp();\n\t\tif (seq32_t(timestamp) <= seq32_t(current_timestamp + nsamples)) {\n\t\t\tif (dtmf_player) {\n\t\t\t\t// Send DTMF payload\n\t\t\t\trtp_session->putData(dtmf_rtp_timestamp, payload,\n\t\t\t\t\t\t\tsound_payload_size);\n\n\t\t\t\t// If DTMF has ended then set payload back to sound\n\t\t\t\tif (dtmf_player->finished()) {\n\t\t\t\t\tset_sound_payload_format();\n\t\t\t\t\tMEMMAN_DELETE(dtmf_player);\n\t\t\t\t\tdelete dtmf_player;\n\t\t\t\t\tdtmf_player = NULL;\n\t\t\t\t}\n\t\t\t} else if (!suppress_samples) {\n\t\t\t\t// Send sound samples\n\t\t\t\t// Set the expire timeout to the jitter buffer size.\n\t\t\t\t// This allows for old packets still to be sent out.\n\t\t\t\trtp_session->setExpireTimeout(MAX_OUT_AUDIO_DELAY_MS * 1000);\n\t\t\t\trtp_session->putData(timestamp, payload, sound_payload_size);\n\t\t\t}\n\n\t\t\ttimestamp += nsamples;\n\t\t} else {\n\t\t\tlog_file->write_header(\"t_audio_rx::run\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Audio rx line \");\n\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": discarded surplus of sound samples.\\n\");\n\t\t\tlog_file->write_raw(\"Timestamp: \");\n\t\t\tlog_file->write_raw(timestamp);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Current timestamp: \");\n\t\t\tlog_file->write_raw(current_timestamp);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"nsamples: \");\n\t\t\tlog_file->write_raw(nsamples);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\n\t\t// If there is enough data in the DSP buffers to fill another\n\t\t// RTP packet then do not sleep, but immediately go to the\n\t\t// next cycle to play out the data. Probably this thread did\n\t\t// not get enough time, so the buffer filled up. The far end\n\t\t// jitter buffer has to cope with the jitter caused by this.\n\t\tif (is_3way && !is_main_rx_3way) {\n\t\t\tif (media_3way_peer_rx->size_content() >= SAMPLE_BUF_SIZE) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tif (input_device->get_buffer_space(true) >= SAMPLE_BUF_SIZE) continue;\n\t\t}\n\n\t\t// There is no data left in the DSP buffers to play out anymore.\n\t\t// So the timestamp must be in sync with the clock of the ccRTP\n\t\t// stack. It might get behind if the sound cards samples a bit\n\t\t// slower than the set sample rate. Advance the timestamp to get\n\t\t// in sync again.\n\t\tcurrent_timestamp = rtp_session->getCurrentTimestamp();\n\t\tif (seq32_t(timestamp) <= seq32_t(current_timestamp - \n\t\t\t(JITTER_BUF_MS / audio_encoder->get_ptime()) * nsamples))\n\t\t{\n\t\t\ttimestamp += nsamples * (JITTER_BUF_MS / audio_encoder->get_ptime());\n\t\t\tlog_file->write_header(\"t_audio_rx::run\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Audio rx line \");\n\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": timestamp forwarded by \");\n\t\t\tlog_file->write_raw(nsamples * (JITTER_BUF_MS /\n\t\t\t\t\taudio_encoder->get_ptime()));\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Timestamp: \");\n\t\t\tlog_file->write_raw(timestamp);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Current timestamp: \");\n\t\t\tlog_file->write_raw(current_timestamp);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"nsamples: \");\n\t\t\tlog_file->write_raw(nsamples);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\t\t\t\n\t}\n\n\tphone->remove_prohibited_thread();\n\tui->remove_prohibited_thread();\n\tis_running = false;\n}\n\nvoid t_audio_rx::set_pt_telephone_event(int pt) {\n\tpt_telephone_event = pt;\n}\n\nvoid t_audio_rx::push_dtmf(char digit, bool inband) {\n\t// Ignore invalid DTMF digits\n\tif (!is_valid_dtmf_sym(digit)) return;\n\n\t// Ignore DTMF tones in a 3-way conference\n\tif (is_3way) return;\n\n\tt_dtmf_event dtmf_event;\n\tdtmf_event.dtmf_tone = char2dtmf_ev(digit);\n\tdtmf_event.inband = inband;\n\n\tmtx_dtmf_q.lock();\n\tdtmf_queue.push(dtmf_event);\n\tmtx_dtmf_q.unlock();\n\tsema_dtmf_q.up();\n}\n\nt_line *t_audio_rx::get_line(void) const {\n\treturn audio_session->get_line();\n}\n\nvoid t_audio_rx::join_3way(bool main_rx, t_audio_rx *peer_rx) {\n\tmtx_3way.lock();\n\n\tif (is_3way) {\n\t\tlog_file->write_header(\"t_audio_rx::join_3way\", LOG_NORMAL);\n\t\tlog_file->write_raw(\"ERROR: audio rx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\" - 3way is already active.\\n\");\n\t\tlog_file->write_footer();\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_rx::join_3way\");\n\tlog_file->write_raw(\"Audio rx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tlog_file->write_raw(\": join 3-way.\\n\");\n\tif (main_rx) {\n\t\tlog_file->write_raw(\"Role is: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"Role is: non-mixing.\\n\");\n\t}\n\tif (peer_rx) {\n\t\tlog_file->write_raw(\"A peer receiver already exists.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"A peer receiver does not exist.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\t// Create media buffers for the 2 far-ends of a 3-way call.\n\t// The size of the media buffer is the size of the jitter buffer.\n\t// This allows for jitter in the RTP streams and also for\n\t// incompatible payload sizes. Eg. 1 far-end may send 20ms paylaods,\n\t// while the other sends 30ms payloads. The outgoing RTP stream might\n\t// even have another payload size.\n\t// When the data has been captured from the soundcard, it will be\n\t// checked if there is enough data available in the media buffers, i.e.\n\t// the same amount of data as captured from the soundcard for mixing.\n\t// If there is it will be retrieved and mixed.\n\t// If there isn't the captured sound will simply be sent on its own\n\t// to the far-end. Meanwhile the buffer will fill up with data such\n\t// that from the next captured sample there will be sufficient data\n\t// for mixing.\n\tmedia_3way_peer_tx = new t_media_buffer(\n\t\t\tJITTER_BUF_SIZE(audio_encoder->get_sample_rate()));\n\tMEMMAN_NEW(media_3way_peer_tx);\n\tmedia_3way_peer_rx = new t_media_buffer(\n\t\t\tJITTER_BUF_SIZE(audio_encoder->get_sample_rate()));\n\tMEMMAN_NEW(media_3way_peer_rx);\n\n\t// Create a mix buffer for one sample frame.\n\tmix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];\n\tMEMMAN_NEW_ARRAY(mix_buf_3way);\n\n\tpeer_rx_3way = peer_rx;\n\n\tis_3way = true;\n\tis_main_rx_3way = main_rx;\n\n\t// Stop DTMF tones as these are not supported in a 3way\n\tif (dtmf_player) {\n\t\tMEMMAN_DELETE(dtmf_player);\n\t\tdelete dtmf_player;\n\t\tdtmf_player = NULL;\n\t}\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_rx::set_peer_rx_3way(t_audio_rx *peer_rx) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_rx::set_peer_rx_3way\");\n\tlog_file->write_raw(\"Audio rx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tif (peer_rx) {\n\t\tlog_file->write_raw(\": set peer receiver.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\": erase peer receiver.\\n\");\n\t}\n\tif (is_main_rx_3way) {\n\t\tlog_file->write_raw(\"Role is: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"Role is: non-mixing.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\tpeer_rx_3way = peer_rx;\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_rx::set_main_rx_3way(bool main_rx) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_rx::set_main_rx_3way\");\n\tlog_file->write_raw(\"Audio rx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tif (main_rx) {\n\t\tlog_file->write_raw(\": change role to: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\": change role to: non-mixing.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\n\t// Initialize the DSP if we become the mixer and we were not before\n\tif (main_rx && !is_main_rx_3way) {\t\t\n\t\t// Enable recording\n\t\tif (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),\n\t\t\t\tsys_config->get_dev_mic())) \n\t\t{\n\t\t\tinput_device->enable(true, true);\n\t\t} else {\n\t\t\tinput_device->enable(false, true);\n\t\t}\n\n\t\t// If the stream is stopped for call-hold, then the buffer might\n\t\t// be filled with old sound samples.\n\t\tinput_device->flush(false, true);\n\t}\n\n\tis_main_rx_3way = main_rx;\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_rx::stop_3way(void) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tlog_file->write_header(\"t_audio_rx::stop_3way\");\n\t\tlog_file->write_raw(\"ERROR: audio rx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\" - 3way is not active.\\n\");\n\t\tlog_file->write_footer();\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_rx::stop_3way\");\n\tlog_file->write_raw(\"Audio rx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tlog_file->write_raw(\": stop 3-way.\\n\");\n\tlog_file->write_footer();\n\n\tis_3way = false;\n\tis_main_rx_3way = false;\n\n\tpeer_rx_3way = NULL;\n\n\tMEMMAN_DELETE(media_3way_peer_tx);\n\tdelete media_3way_peer_tx;\n\tmedia_3way_peer_tx = NULL;\n\tMEMMAN_DELETE(media_3way_peer_rx);\n\tdelete media_3way_peer_rx;\n\tmedia_3way_peer_rx = NULL;\n\tMEMMAN_DELETE_ARRAY(mix_buf_3way);\n\tdelete [] mix_buf_3way;\n\tmix_buf_3way = NULL;\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_rx::post_media_peer_tx_3way(unsigned char *media, int len,\n\t\tunsigned short peer_sample_rate) \n{\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\t// This is not a 3-way call. This is not necessarily an\n\t\t// error condition. The 3rd party may be in the process of\n\t\t// leaving the conference.\n\t\t// Simply discard the posted media\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\t\n\tif (peer_sample_rate != audio_encoder->get_sample_rate()) {\n\t\t// Resample media from peer to sample rate of this receiver\n\t\tint output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;\n\t\tshort *output_buf = new short[output_len];\n\t\tMEMMAN_NEW_ARRAY(output_buf);\n\t\tint resample_len = resample((short *)media, len / 2, peer_sample_rate,\n\t\t\t\t\toutput_buf, output_len, audio_encoder->get_sample_rate());\n\t\tmedia_3way_peer_tx->add((unsigned char *)output_buf, resample_len * 2);\n\t\tMEMMAN_DELETE_ARRAY(output_buf);\n\t\tdelete [] output_buf;\n\t} else {\n\t\tmedia_3way_peer_tx->add(media, len);\n\t}\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_rx::post_media_peer_rx_3way(unsigned char *media, int len,\n\t\tunsigned short peer_sample_rate) \n{\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\t// This is not a 3-way call. This is not necessarily an\n\t\t// error condition. The 3rd party may be in the process of\n\t\t// leaving the conference.\n\t\t// Simply discard the posted media\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\t\n\tif (peer_sample_rate != audio_encoder->get_sample_rate()) {\n\t\t// Resample media from peer to sample rate of this receiver\n\t\tint output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;\n\t\tshort *output_buf = new short[output_len];\n\t\tMEMMAN_NEW_ARRAY(output_buf);\n\t\tint resample_len = resample((short *)media, len / 2, peer_sample_rate,\n\t\t\t\t\toutput_buf, output_len, audio_encoder->get_sample_rate());\n\t\tmedia_3way_peer_rx->add((unsigned char *)output_buf, resample_len * 2);\n\t\tMEMMAN_DELETE_ARRAY(output_buf);\n\t\tdelete [] output_buf;\n\t} else {\n\t\tmedia_3way_peer_rx->add(media, len);\n\t}\n\n\tmtx_3way.unlock();\n}\n\nbool t_audio_rx::get_is_main_rx_3way(void) const {\n\treturn is_main_rx_3way;\n}\n"
  },
  {
    "path": "src/audio/audio_rx.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _AUDIO_RX_H\n#define _AUDIO_RX_H\n\n// Receive audio from the soundcard and send it to the RTP thread.\n\n#include <assert.h>\n#include <queue>\n#include <string>\n\n#include \"audio_codecs.h\"\n#include \"audio_device.h\"\n#include \"audio_encoder.h\"\n#include \"dtmf_player.h\"\n#include \"media_buffer.h\"\n#include \"user.h\"\n#include \"threads/mutex.h\"\n#include \"threads/sema.h\"\n#include \"twinkle_rtp_session.h\"\n#include \"twinkle_config.h\"\n\n#ifdef HAVE_SPEEX\n#include <speex/speex.h>\n#include <speex/speex_preprocess.h> \n#endif\n\nusing namespace std;\nusing namespace ost;\n\n// Forward declarations\nclass t_audio_session;\nclass t_line;\n\nclass t_audio_rx {\nprivate:\n\t// audio_session owning this audio receiver\n\tt_audio_session *audio_session;\n\t\n\t// User profile of user using the line\n\t// This is a pointer to the user_config owned by a phone user.\n\t// So this pointer should never be deleted.\n\tt_user\t\t\t*user_config;\n\n\t// file descriptor audio capture device\n\tt_audio_io* input_device;\n\n\t// RTP session\n\tt_twinkle_rtp_session *rtp_session;\n\n\t// Media buffer to buffer media from the peer audio trasmitter in a\n\t// 3-way call. This media stream will be mixed with the\n\t// audio captured from the soundcard.\n\tt_media_buffer\t*media_3way_peer_tx;\n\n\t// Media captured by the peer audio receiver in a 3-way conference\n\tt_media_buffer\t*media_3way_peer_rx;\n\n\t// The peer audio receiver in a 3-way conference.\n\tt_audio_rx\t*peer_rx_3way;\n\n\t// Buffer for mixing purposes in 3-way conference.\n\tunsigned char\t*mix_buf_3way;\n\n\t// Indicates if this receiver is part of a 3-way conference call\n\tbool \t\tis_3way;\n\n\t// Indicates if this is this receiver has to capture sound from the\n\t// soundcard. In a 3-way call, one receiver captures sound, while the\n\t// other receiver simply takes the sound from the main receiver.\n\tbool\t\tis_main_rx_3way;\n\n\t// Mutex to protect actions on 3-way conference data\n\tt_mutex\t\tmtx_3way;\n\t\n\t// Audio encoder\n\tt_audio_encoder\t*audio_encoder;\n\n\t// Buffer to store PCM samples for ptime ms\n\tunsigned char\t*input_sample_buf;\n\t\n\t// Indicates if NAT keep alive packets must be sent during silence\n\t// suppression.\n\tbool\t\tuse_nat_keepalive;\n\n\t// RTP payload\n\tunsigned short payload_size;\n\tunsigned char *payload;\n\tunsigned short nsamples; // number of samples taken per packet\n\n\t// Payload type for telephone-event payload.\n\tint pt_telephone_event;\n\n\t// Queue of DTMF tones to be sent\n\tstruct t_dtmf_event {\n\t\tt_dtmf_ev\tdtmf_tone;\n\t\tbool\t\tinband;\n\t};\n\t\n\tqueue<t_dtmf_event>\tdtmf_queue;\n\tt_mutex\t\t\tmtx_dtmf_q;\n\tt_semaphore\t\tsema_dtmf_q;\n\n\t// DTMF player\n\tt_dtmf_player\t*dtmf_player;\n\n\t// Inidicates if the recording thread is running\n\tvolatile bool is_running;\n\n\t// The thread exits when this indicator is set to true\n\tvolatile bool stop_running;\n\n\t// Indicates if a capture failure was already logged (log throttling).\n\tbool logged_capture_failure;\n\n\t// Timestamp for next RTP packet\n\tunsigned long timestamp;\n\n#ifdef HAVE_SPEEX\n\t/** Speex preprocessor state */\n\tSpeexPreprocessState *speex_preprocess_state;\n\t\n\t/** Speex VAD enabled? */\n\tbool speex_dsp_vad;\n#endif\n\n\t// Get sound samples for 1 RTP packet from the soundcard.\n\t// Returns false if the main loop has to start another cycle to get\n\t// samples (eg. no samples available yet).\n\t// If not enough samples are available yet, then a 1 ms sleep will be taken.\n\t// Also returns false if capturing samples from the soundcard failed.\n\t// Returns true if sounds samples are received. The samples are stored\n\t// in the payload buffer in the proper encoding.\n\t// The number bytes of the sound payload is returned in sound_payload_size\n\t// The silence flag indicates if the returned sound samples represent silence\n\t// that may be suppressed.\n\tbool get_sound_samples(unsigned short &sound_payload_size, bool &silence);\n\n\t// Get next DTMF event generated by the user.\n\t// Returns false if there is no next DTMF event\n\tbool get_dtmf_event(void);\n\n\t// Set RTP payload for outgoing sound packets based on the codec.\n\tvoid set_sound_payload_format(void);\n\npublic:\n\t// Create the audio receiver\n\t// _fd\t\tfile descriptor of capture device\n\t// _rtp_session\tRTP socket tp send the RTP stream\n\t// _codec\taudio codec to use\n\t// _ptime\tlength of the audio packets in ms\n\t// _ptime = 0 means use default ptime value for the codec\n\tt_audio_rx(t_audio_session *_audio_session, t_audio_io *_input_device,\n\t\t   t_twinkle_rtp_session *_rtp_session,\n\t           t_audio_codec _codec, unsigned short _payload_id, \n\t           unsigned short _ptime = 0);\n\n\t~t_audio_rx();\n\t\n\t// Set the is running flag\n\tvoid set_running(bool running);\n\n\tvoid run(void);\n\n\t// Set the dynamic payload type for telephone events\n\tvoid set_pt_telephone_event(int pt);\n\n\t// Push a new DTMF tone in the DTMF queue\n\tvoid push_dtmf(char digit, bool inband);\n\n\t// Get phone line belonging to this audio transmitter\n\tt_line *get_line(void) const;\n\n\t// Join a 3-way conference call.\n\t// main_rx indicates if this receiver must be the main receiver capturing\n\t// the sound from the soundcard.\n\t// The peer_rx is the peer receiver (may be NULL(\n\tvoid join_3way(bool main_rx, t_audio_rx *peer_rx);\n\n\t// Change the peer receiver in a 3-way (set to NULL to erase).\n\tvoid set_peer_rx_3way(t_audio_rx *peer_rx);\n\n\t// Change the main rx role in a 3-way\n\tvoid set_main_rx_3way(bool main_rx);\n\n\t// Delete 3rd party from a 3-way conference.\n\tvoid stop_3way(void);\n\n\t// Post media from the peer transmitter in a 3-way.\n\tvoid post_media_peer_tx_3way(unsigned char *media, int len,\n\t\tunsigned short peer_sample_rate);\n\n\t// Post media from the peer receiver in a 3-way.\n\tvoid post_media_peer_rx_3way(unsigned char *media, int len,\n\t\tunsigned short peer_sample_rate);\n\n\t// Returns if this receiver is the main receiver in a 3-way\n\tbool get_is_main_rx_3way(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_session.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n#include \"twinkle_config.h\"\n\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <cstdlib>\n#include <cstdio>\n#include \"audio_session.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\n#ifdef HAVE_ZRTP\n#include \"twinkle_zrtp_ui.h\"\n#endif\n\nstatic t_audio_session *_audio_session;\n\n///////////\n// PRIVATE\n///////////\n\nbool t_audio_session::is_3way(void) const {\n\tt_line *l = get_line();\n\tt_phone *p = l->get_phone();\n\n\treturn p->part_of_3way(l->get_line_number());\n}\n\nt_audio_session *t_audio_session::get_peer_3way(void) const {\n\tt_line *l = get_line();\n\tt_phone *p = l->get_phone();\n\n\tt_line *peer_line = p->get_3way_peer_line(l->get_line_number());\n\n\treturn peer_line->get_audio_session();\n}\n\nbool t_audio_session::open_dsp(void) {\n\tif (sys_config->equal_audio_dev(sys_config->get_dev_speaker(), \n\t\t\tsys_config->get_dev_mic())) \n\t{\n\t\treturn open_dsp_full_duplex();\n\t}\n\t\n\treturn open_dsp_speaker() && open_dsp_mic();\n}\n\nbool t_audio_session::open_dsp_full_duplex(void) {\n\n\t// Open audio device\n\tspeaker = t_audio_io::open(sys_config->get_dev_speaker(), true, true, true, 1, \n\t\tSAMPLEFORMAT_S16, audio_sample_rate(codec), true);\n\tif (!speaker) {\n\t\tstring msg(TRANSLATE2(\"CoreAudio\", \"Failed to open sound card\"));\n\t\tlog_file->write_report(msg, \"t_audio_session::open_dsp_full_duplex\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\n\t// Disable recording\n\t// If recording is not disabled, then the capture buffers will\n\t// already fill with data. Then when the audio_rx thread starts\n\t// to read blocks of 160 samples, it gets all these initial blocks\n\t// very quickly 1 per 12 ms I have seen. And hence the timestamps\n\t// for these blocks get out of sync with the RTP stack.\n\t// Also a large delay is introduced by this. So recording should\n\t// be enabled just before the data is read from the device.\n\tspeaker->enable(true, false);\n\t\n\tmic = speaker;\n\treturn true;\n}\n\nbool t_audio_session::open_dsp_speaker(void) {\n\t\n\tspeaker = t_audio_io::open(sys_config->get_dev_speaker(), true, false, true, 1, \n\t\tSAMPLEFORMAT_S16, audio_sample_rate(codec), true);\n\tif (!speaker) {\n\t\tstring msg(TRANSLATE2(\"CoreAudio\", \"Failed to open sound card\"));\n\t\tlog_file->write_report(msg, \"t_audio_session::open_dsp_speaker\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nbool t_audio_session::open_dsp_mic(void) {\n\tmic = t_audio_io::open(sys_config->get_dev_mic(), false, true, true, 1, \n\t\tSAMPLEFORMAT_S16, audio_sample_rate(codec), true);\n\tif (!mic) {\n\t\tstring msg(TRANSLATE2(\"CoreAudio\", \"Failed to open sound card\"));\n\t\tlog_file->write_report(msg, \"t_audio_session::open_dsp_mic\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_display_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\n\t// Disable recording\n\t// If recording is not disabled, then the capture buffers will\n\t// already fill with data. Then when the audio_rx thread starts\n\t// to read blocks of 160 samples, it gets all these initial blocks\n\t// very quickly 1 per 12 ms I have seen. And hence the timestamps\n\t// for these blocks get out of sync with the RTP stack.\n\t// Also a large delay is introduced by this. So recording should\n\t// be enabled just before the data is read from the device.\n\tspeaker->enable(true, false);\n\n\treturn true;\n}\n\n///////////\n// PUBLIC\n///////////\n\nt_audio_session::t_audio_session(t_session *_session,\n\t\tconst string &_recv_host, unsigned short _recv_port,\n\t        const string &_dst_host, unsigned short _dst_port,\n\t\tt_audio_codec _codec, unsigned short _ptime,\n\t\tconst map<unsigned short, t_audio_codec> &recv_payload2ac,\n\t\tconst map<t_audio_codec, unsigned short> &send_ac2payload,\n\t\tbool encrypt)\n{\n\tvalid = false;\n\n\tsession = _session;\n\taudio_rx = NULL;\n\taudio_tx = NULL;\n\tthr_audio_rx = NULL;\n\tthr_audio_tx = NULL;\n\tspeaker = NULL;\n\tmic = NULL;\n\n\tcodec = _codec;\n\tptime = _ptime;\n\t\n\tis_encrypted = false;\n\tzrtp_sas.clear();\n\t\n\t// Assume the SAS is confirmed. When a SAS is received from the ZRTP\n\t// stack, the confirmed flag will be cleared.\n\tzrtp_sas_confirmed = true;\n\t\n\tsrtp_cipher_mode.clear();\n\n\tlog_file->write_header(\"t_audio_session::t_audio_session\");\n\tlog_file->write_raw(\"Receive RTP from: \");\n\tlog_file->write_raw(_recv_host);\n\tlog_file->write_raw(\":\");\n\tlog_file->write_raw(_recv_port);\n\tlog_file->write_endl();\n\tlog_file->write_raw(\"Send RTP to: \");\n\tlog_file->write_raw(_dst_host);\n\tlog_file->write_raw(\":\");\n\tlog_file->write_raw(_dst_port);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tt_user *user_config = get_line()->get_user();\n\n\t// Create RTP session\n\ttry {\n\t\tif (_recv_host.empty() || _recv_port == 0) {\n\t\t\trtp_session = new t_twinkle_rtp_session(\n\t\t\t\tInetHostAddress(\"0.0.0.0\"));\n\t\t\tMEMMAN_NEW(rtp_session);\n\t\t} else {\n\t\t\trtp_session = new t_twinkle_rtp_session(\n\t\t\t\tInetHostAddress(_recv_host.c_str()), _recv_port);\n\t\t\tMEMMAN_NEW(rtp_session);\n\t\t}\n#ifdef HAVE_ZRTP\n\t\tZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);\n\t\tif (zque && rtp_session->is_zrtp_initialized()) {\n\t\t\tzque->setEnableZrtp(encrypt);\n\t\t\n\t\t\tif (user_config->get_zrtp_enabled()) {\n\t\t\t\t// Create the ZRTP call back interface\n\t\t\t\tTwinkleZrtpUI* twui = new TwinkleZrtpUI(this);\n\t\t\t\t\n\t\t\t\t// The ZrtpQueue keeps track of the twui - the destructor of \n\t\t\t\t// ZrtpQueue (aka t_twinkle_rtp_session) deletes this object, \n\t\t\t\t// thus no other management is required.\n\t\t\t\tzque->setUserCallback(twui);\n\t\t\t}\n\t\t}\n#endif\n\t} catch(...) {\n\t\t// If the RTPSession constructor throws an exception, no\n\t\t// object is created, so clear the pointer.\n\t\trtp_session = NULL;\n\t\tstring msg(TRANSLATE2(\"CoreAudio\", \"Failed to create a UDP socket (RTP) on port %1\"));\n\t\tmsg = replace_first(msg, \"%1\", int2str(_recv_port));\n\t\tlog_file->write_report(msg, \"t_audio_session::t_audio_session\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\treturn;\n\t}\n\n\tif (!_dst_host.empty() && _dst_port != 0) {\n\t\trtp_session->addDestination(\n\t\t\tInetHostAddress(_dst_host.c_str()), _dst_port);\n\t}\n\n\t// Set payload format for outgoing RTP packets\n\tmap<t_audio_codec, unsigned short>::const_iterator it;\n\tit = send_ac2payload.find(codec);\n\tassert(it != send_ac2payload.end());\n\tunsigned short payload_id = it->second;\n\trtp_session->setPayloadFormat(DynamicPayloadFormat(\n\t\t\tpayload_id, audio_sample_rate_rtp(codec)));\n\n\t// Open and initialize sound card\n\tt_audio_session *as_peer;\n\tif (is_3way() && (as_peer = get_peer_3way())) {\n\t\tspeaker = as_peer->get_dsp_speaker();\n\t\tmic = as_peer->get_dsp_mic();\n\t\tif (!speaker || !mic) return;\n\t} else {\n\t\tif (!open_dsp()) return;\n\t}\n\n#ifdef HAVE_SPEEX\t\n\t// Speex AEC auxiliary data initialization \n\tdo_echo_cancellation = false;\n\n\tif (user_config->get_speex_dsp_aec()) {\n\t    int nsamples = audio_sample_rate(codec) / 1000 * ptime;\n \t    speex_echo_state = speex_echo_state_init(nsamples, 5*nsamples);\n\t    do_echo_cancellation = true;\n\t    echo_captured_last = true;\n\t}\n#endif\n\n\t// Create recorder\n\tif (!_recv_host.empty() && _recv_port != 0) {\n\t\taudio_rx = new t_audio_rx(this, mic, rtp_session, codec, \n\t\t\t\tpayload_id, ptime);\n\t\tMEMMAN_NEW(audio_rx);\n\n\t\t// Setup 3-way configuration if this audio session is part of\n\t\t// a 3-way.\n\t\tif (is_3way()) {\n\t\t\tt_audio_session *peer = get_peer_3way();\n\t\t\tif (!peer || !peer->audio_rx) {\n\t\t\t\t// There is no peer rx yet, so become the main rx\n\t\t\t\taudio_rx->join_3way(true, NULL);\n\n\t\t\t\tif (peer && peer->audio_tx) {\n\t\t\t\t\tpeer->audio_tx->set_peer_rx_3way(audio_rx);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// There is a peer rx already so that must be the\n\t\t\t\t// main rx.\n\t\t\t\taudio_rx->join_3way(false, peer->audio_rx);\n\t\t\t\tpeer->audio_rx->set_peer_rx_3way(audio_rx);\n\n\t\t\t\tif (peer->audio_tx) {\n\t\t\t\t\tpeer->audio_tx->set_peer_rx_3way(audio_rx);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Create player\n\tif (!_dst_host.empty() && _dst_port != 0) {\n\t\taudio_tx = new t_audio_tx(this, speaker, rtp_session, codec,\n\t\t\t\trecv_payload2ac, ptime);\n\t\tMEMMAN_NEW(audio_tx);\n\n\t\t// Setup 3-way configuration if this audio session is part of\n\t\t// a 3-way.\n\t\tif (is_3way()) {\n\t\t\tt_audio_session *peer = get_peer_3way();\n\t\t\tif (!peer) {\n\t\t\t\t// There is no peer tx yet, so become the mixer tx\n\t\t\t\taudio_tx->join_3way(true, NULL, NULL);\n\t\t\t} else if (!peer->audio_tx) {\n\t\t\t\t// There is a peer audio session, but no peer tx,\n\t\t\t\t// so become the mixer tx\n\t\t\t\taudio_tx->join_3way(true, NULL, peer->audio_rx);\n\t\t\t} else {\n\t\t\t\t// There is a peer tx already. That must be the\n\t\t\t\t// mixer.\n\t\t\t\taudio_tx->join_3way(\n\t\t\t\t\tfalse, peer->audio_tx, peer->audio_rx);\n\t\t\t}\n\t\t}\n\t}\n\tvalid = true;\n}\n\nt_audio_session::~t_audio_session() {\n\t// Delete of the audio_rx and audio_tx objects will terminate\n\t// thread execution.\n\tif (audio_rx) {\n\t\t// Reconfigure 3-way configuration if this audio session is\n\t\t// part of a 3-way.\n\t\tif (is_3way()) {\n\t\t\tt_audio_session *peer = get_peer_3way();\n\t\t\tif (peer) {\n\t\t\t\t// Make the peer audio rx the main rx and remove\n\t\t\t\t// reference to this audio rx\n\t\t\t\tif (peer->audio_rx) {\n\t\t\t\t\tpeer->audio_rx->set_peer_rx_3way(NULL);\n\t\t\t\t\tpeer->audio_rx->set_main_rx_3way(true);\n\t\t\t\t}\n\n\t\t\t\t// Remove reference to this audio rx\n\t\t\t\tif (peer->audio_tx) {\n\t\t\t\t\tpeer->audio_tx->set_peer_rx_3way(NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tMEMMAN_DELETE(audio_rx);\n\t\tdelete audio_rx;\n\t}\n\n\tif (audio_tx) {\n\t\t// Reconfigure 3-way configuration if this audio session is\n\t\t// part of a 3-way.\n\t\tif (is_3way()) {\n\t\t\tt_audio_session *peer = get_peer_3way();\n\t\t\tif (peer) {\n\t\t\t\t// Make the peer audio tx the mixer and remove\n\t\t\t\t// reference to this audio tx\n\t\t\t\tif (peer->audio_tx) {\n\t\t\t\t\tpeer->audio_tx->set_peer_tx_3way(NULL);\n\t\t\t\t\tpeer->audio_tx->set_mixer_3way(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tMEMMAN_DELETE(audio_tx);\n\t\tdelete audio_tx;\n\t}\n\n\tif (thr_audio_rx) {\n\t\tMEMMAN_DELETE(thr_audio_rx);\n\t\tdelete thr_audio_rx;\n\t}\n\n\tif (thr_audio_tx) {\n\t\tMEMMAN_DELETE(thr_audio_tx);\n\t\tdelete thr_audio_tx;\n\t}\n\n\tif (rtp_session) {\n\t\tlog_file->write_header(\"t_audio_session::~t_audio_session\");\n\t\tlog_file->write_raw(\"Line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\": stopping RTP session.\\n\");\n\t\tlog_file->write_footer();\n\t\t\n\t\tMEMMAN_DELETE(rtp_session);\n\t\tdelete rtp_session;\n\n\t\tlog_file->write_header(\"t_audio_session::~t_audio_session\");\n\t\tlog_file->write_raw(\"Line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\": RTP session stopped.\\n\");\n\t\tlog_file->write_footer();\n\t}\n\n\tif (speaker && (!is_3way() || !get_peer_3way())) {\n\t\tif (mic == speaker) mic = 0;\n\t\tMEMMAN_DELETE(speaker);\n\t\tdelete speaker;\n\t\tspeaker = 0;\n\t}\n\t\n\tif (mic && (!is_3way() || !get_peer_3way())) {\n\t\tMEMMAN_DELETE(mic);\n\t\tdelete mic;\n\t\tmic = 0;\n\t}\n\n#ifdef HAVE_SPEEX\n\t// cleaning speech AEC\n\tif (do_echo_cancellation) {\n\t    speex_echo_state_destroy(speex_echo_state); \n\t}\n#endif\n}\n\nvoid t_audio_session::set_session(t_session *_session) {\n\tmtx_session.lock();\n\tsession = _session;\n\tmtx_session.unlock();\n}\n\nvoid t_audio_session::run(void) {\n\t_audio_session = this;\n\n\tlog_file->write_header(\"t_audio_session::run\");\n\tlog_file->write_raw(\"Line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tlog_file->write_raw(\": starting RTP session.\\n\");\n\tlog_file->write_footer();\n\n\trtp_session->startRunning();\n\n\tlog_file->write_header(\"t_audio_session::run\");\n\tlog_file->write_raw(\"Line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tlog_file->write_raw(\": RTP session started.\\n\");\n\tlog_file->write_footer();\n\n\tif (audio_rx) {\n\t\ttry {\n\t\t\t// Set the running flag now instead of at the start of\n\t\t\t// t_audio_tx::run as due to race conditions the thread might\n\t\t\t// get destroyed before the run method starts running. The\n\t\t\t// destructor still has to wait on the thread to finish.\n\t\t\taudio_rx->set_running(true);\n\t\t\t\n\t\t\tthr_audio_rx = new t_thread(main_audio_rx, NULL);\n\t\t\tMEMMAN_NEW(thr_audio_rx);\n\t\t\t// thr_audio_rx->set_sched_fifo(90);\n\t\t\tthr_audio_rx->detach();\n\t\t} catch (int) {\n\t\t\taudio_rx->set_running(false);\n\t\t\tstring msg(TRANSLATE2(\"CoreAudio\", \"Failed to create audio receiver thread.\"));\n\t\t\tlog_file->write_report(msg, \"t_audio_session::run\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\texit(1);\n\t\t}\n\t}\n\n\n\tif (audio_tx) {\n\t\ttry {\n\t\t\t// See comment above for audio_rx\n\t\t\taudio_tx->set_running(true);\n\t\t\t\n\t\t\tthr_audio_tx = new t_thread(main_audio_tx, NULL);\n\t\t\tMEMMAN_NEW(thr_audio_tx);\n\t\t\t// thr_audio_tx->set_sched_fifo(90);\n\t\t\tthr_audio_tx->detach();\n\t\t} catch (int) {\n\t\t\taudio_tx->set_running(false);\n\t\t\tstring msg(TRANSLATE2(\"CoreAudio\", \"Failed to create audio transmitter thread.\"));\n\t\t\tlog_file->write_report(msg, \"t_audio_session::run\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\texit(1);\n\t\t}\n\t}\n}\n\nvoid t_audio_session::set_pt_out_dtmf(unsigned short pt) {\n\tif (audio_rx) audio_rx->set_pt_telephone_event(pt);\n}\n\nvoid t_audio_session::set_pt_in_dtmf(unsigned short pt, unsigned short pt_alt) {\n\tif (audio_tx) audio_tx->set_pt_telephone_event(pt, pt_alt);\n}\n\nvoid t_audio_session::send_dtmf(char digit, bool inband) {\n\tif (audio_rx) audio_rx->push_dtmf(digit, inband);\n}\n\nt_line *t_audio_session::get_line(void) const {\n\tt_line *line;\n\tmtx_session.lock();\n\tline = session->get_line();\n\tmtx_session.unlock();\n\t\n\treturn line;\n}\n\nvoid t_audio_session::start_3way(void) {\n\tif (audio_rx) {\n\t\taudio_rx->join_3way(true, NULL);\n\t}\n\n\tif (audio_tx) {\n\t\taudio_tx->join_3way(true, NULL, NULL);\n\t}\n}\n\nvoid t_audio_session::stop_3way(void) {\n\tif (audio_rx) {\n\t\tt_audio_session *peer = get_peer_3way();\n\t\tif (peer) {\n\t\t\tif (peer->audio_rx) {\n\t\t\t\tpeer->audio_rx->set_peer_rx_3way(NULL);\n\t\t\t}\n\n\t\t\tif (peer->audio_tx) {\n\t\t\t\tpeer->audio_tx->set_peer_rx_3way(NULL);\n\t\t\t}\n\t\t}\n\t\taudio_rx->stop_3way();\n\t}\n\n\tif (audio_tx) {\n\t\tt_audio_session *peer = get_peer_3way();\n\t\tif (peer) {\n\t\t\tif (peer->audio_tx) {\n\t\t\t\tpeer->audio_tx->set_peer_tx_3way(NULL);\n\t\t\t}\n\t\t}\n\t\taudio_tx->stop_3way();\n\t}\n}\n\nbool t_audio_session::is_valid(void) const {\n\treturn valid;\n}\n\nt_audio_io* t_audio_session::get_dsp_speaker(void) const {\n\treturn speaker;\n}\n\nt_audio_io* t_audio_session::get_dsp_mic(void) const {\n\treturn mic;\n}\n\nbool t_audio_session::matching_sample_rates(void) const {\n\tint codec_sample_rate = audio_sample_rate(codec);\n\treturn (speaker->get_sample_rate() == codec_sample_rate &&\n\t\tmic->get_sample_rate() == codec_sample_rate);\n}\n\nvoid t_audio_session::confirm_zrtp_sas(void) {\n#ifdef HAVE_ZRTP\n\tZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);\n\tif (zque) {\n\t\tzque->SASVerified();\n\t\tset_zrtp_sas_confirmed(true);\n\t}\n#endif\n}\n\nvoid t_audio_session::reset_zrtp_sas_confirmation(void) {\n#ifdef HAVE_ZRTP\n\tZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);\n\tif (zque) {\n\t\tzque->resetSASVerified();\n\t\tset_zrtp_sas_confirmed(false);\n\t}\n#endif\n}\n\nvoid t_audio_session::enable_zrtp(void) {\n#ifdef HAVE_ZRTP\n\tZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);\n\tif (zque) {\n\t\tzque->setEnableZrtp(true);\n\t}\n#endif\n}\n\nvoid t_audio_session::zrtp_request_go_clear(void) {\n#ifdef HAVE_ZRTP\n\tZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);\n\tif (zque) {\n\t\tzque->requestGoClear();\n\t}\n#endif\n}\n\nvoid t_audio_session::zrtp_go_clear_ok(void) {\n#ifdef HAVE_ZRTP\n\tZrtpQueue* zque = dynamic_cast<ZrtpQueue*>(rtp_session);\n\tif (zque) {\n\t\tzque->goClearOk();\n\t}\n#endif\n}\n\nbool t_audio_session::get_is_encrypted(void) const {\n\tmtx_zrtp_data.lock();\n\tbool b = is_encrypted;\n\tmtx_zrtp_data.unlock();\n\treturn b;\n}\n\nstring t_audio_session::get_zrtp_sas(void) const {\n\tmtx_zrtp_data.lock();\n\tstring s = zrtp_sas;\n\tmtx_zrtp_data.unlock();\n\treturn s;\n}\n\nbool t_audio_session::get_zrtp_sas_confirmed(void) const {\n\tmtx_zrtp_data.lock();\n\tbool b = zrtp_sas_confirmed;\n\tmtx_zrtp_data.unlock();\n\treturn b;\n}\n\nstring t_audio_session::get_srtp_cipher_mode(void) const {\n\tmtx_zrtp_data.lock();\n\tstring s = srtp_cipher_mode;\n\tmtx_zrtp_data.unlock();\n\treturn s;\n}\n\nvoid t_audio_session::set_is_encrypted(bool on) {\n\tmtx_zrtp_data.lock();\n\tis_encrypted = on;\n\tmtx_zrtp_data.unlock();\n}\n\nvoid t_audio_session::set_zrtp_sas(const string &sas) {\n\tmtx_zrtp_data.lock();\n\tzrtp_sas = sas;\n\tmtx_zrtp_data.unlock();\n}\n\nvoid t_audio_session::set_zrtp_sas_confirmed(bool confirmed) {\n\tmtx_zrtp_data.lock();\n\tzrtp_sas_confirmed = confirmed;\n\tmtx_zrtp_data.unlock();\n}\n\nvoid t_audio_session::set_srtp_cipher_mode(const string &cipher_mode) {\n\tmtx_zrtp_data.lock();\n\tsrtp_cipher_mode = cipher_mode;\n\tmtx_zrtp_data.unlock();\n}\n\n\n#ifdef HAVE_SPEEX\nbool t_audio_session::get_do_echo_cancellation(void) const {\n    return do_echo_cancellation;\n}\n\nbool t_audio_session::get_echo_captured_last(void) {\n    return echo_captured_last;\n}\n\nvoid t_audio_session::set_echo_captured_last(bool value) {\n    echo_captured_last = value;\n}\n\nSpeexEchoState *t_audio_session::get_speex_echo_state(void) {\n    return speex_echo_state;\n}\n#endif\n\nvoid *main_audio_rx(void *arg) {\n\t_audio_session->audio_rx->run();\n\treturn NULL;\n}\n\nvoid *main_audio_tx(void *arg) {\n\t_audio_session->audio_tx->run();\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/audio/audio_session.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _AUDIO_SESSION_H\n#define _AUDIO_SESSION_H\n\n#include <map>\n#include <string>\n#include \"audio_rx.h\"\n#include \"audio_tx.h\"\n#include \"session.h\"\n#include \"twinkle_rtp_session.h\"\n#include \"threads/thread.h\"\n#include \"threads/mutex.h\"\n\n#ifdef HAVE_SPEEX\n#include <speex/speex_echo.h> \n#endif\n\nusing namespace std;\nusing namespace ost;\n\n// Forward declarations\nclass t_session;\nclass t_line;\n\nclass t_audio_session {\nprivate:\n\t// SIP session owning this audio session\n\tt_session\t*session;\n\t\n\t/** Mutex for concurrent access to the session. */\n\tmutable t_mutex\tmtx_session;\n\n\t// This flag indicates if the created audio session is valid.\n\t// It might be invalid because, the RTP session could not be created\n\t// or the soundcard could not be opened.\n\tbool\t\tvalid;\n\n\t// file descriptor audio device\n\tt_audio_io\t\t*speaker;\n\tt_audio_io\t\t*mic;\n\tt_twinkle_rtp_session   *rtp_session;\n\n\tt_audio_codec\tcodec;\n\tunsigned short\tptime;\t// in milliseconds\n\n\tt_thread\t*thr_audio_rx; // recording thread\n\tt_thread\t*thr_audio_tx; // playing thread\n\t\n\t// ZRTP info\n\tmutable t_mutex\tmtx_zrtp_data;\n\tbool\t\tis_encrypted;\n\tstring\t\tzrtp_sas;\n\tbool\t\tzrtp_sas_confirmed;\n\tstring\t\tsrtp_cipher_mode;\n\n#ifdef HAVE_SPEEX\n\t// Indicator whether to use (Speex) AEC \n\tbool do_echo_cancellation;\n\n\t// Indicator whether the last operation of (Speex) AEC,\n\t// speex_echo_capture or speex_echo_playback, was the speex_echo_capture\n\tbool echo_captured_last;\n\n\t// speex AEC state \n\tSpeexEchoState *speex_echo_state;\n#endif\n\n\t// 3-way conference data\n\t// Returns if this audio session is part of a 3-way conference\n\tbool is_3way(void) const;\n\n\t// Returns the peer audio session of a 3-way conference\n\tt_audio_session *get_peer_3way(void) const;\n\n\t// Open the sound card\n\tbool open_dsp(void);\n\tbool open_dsp_full_duplex(void);\n\tbool open_dsp_speaker(void);\n\tbool open_dsp_mic(void);\n\npublic:\n\n\tt_audio_rx\t*audio_rx;\n\tt_audio_tx\t*audio_tx;\n\n\n\tt_audio_session(t_session *_session,\n\t\t\tconst string &_recv_host, unsigned short _recv_port,\n\t\t        const string &_dst_host, unsigned short _dst_port,\n\t\t\tt_audio_codec _codec, unsigned short _ptime,\n\t\t\tconst map<unsigned short, t_audio_codec> &recv_payload2ac,\n\t\t\tconst map<t_audio_codec, unsigned short> &send_ac2payload,\n\t\t\tbool encrypt);\n\n\t~t_audio_session();\n\n\tvoid run(void);\n\t\n\t/**\n\t * Change the owning session.\n\t * @param _session New session owning this audio session.\n\t */\n\tvoid set_session(t_session *_session);\n\n\t// Set outgoing/incoming DTMF dynamic payload types\n\tvoid set_pt_out_dtmf(unsigned short pt);\n\tvoid set_pt_in_dtmf(unsigned short pt, unsigned short pt_alt);\n\n\t// Send DTMF digit\n\tvoid send_dtmf(char digit, bool inband);\n\n\t// Get the line that belongs to this audio session\n\tt_line *get_line(void) const;\n\n\t// Become the first session in a 3-way conference\n\tvoid start_3way(void);\n\n\t// Leave a 3-way conference\n\tvoid stop_3way(void);\n\n\t// Check if audio session is valid\n\tbool is_valid(void) const;\n\n\t// Get pointer for soundcard I/O object\n\tt_audio_io* get_dsp_speaker(void) const;\n\tt_audio_io* get_dsp_mic(void) const;\n\t\n\t// Check if sample rate from speaker and mic match with sample rate\n\t// from codec. The sample rates might not match due to 3-way conference\n\t// calls with mixed sample rate\n\tbool matching_sample_rates(void) const;\n\t\n\t// ZRTP actions\n\tvoid confirm_zrtp_sas(void);\n\tvoid reset_zrtp_sas_confirmation(void);\n\tvoid enable_zrtp(void);\n\tvoid zrtp_request_go_clear(void);\n\tvoid zrtp_go_clear_ok(void);\n\t\n\t// ZRTP data manipulations\n\tbool get_is_encrypted(void) const;\n\tstring get_zrtp_sas(void) const;\n\tbool get_zrtp_sas_confirmed(void) const;\n\tstring get_srtp_cipher_mode(void) const;\n\t\n\tvoid set_is_encrypted(bool on);\n\tvoid set_zrtp_sas(const string &sas);\n\tvoid set_zrtp_sas_confirmed(bool confirmed);\n\tvoid set_srtp_cipher_mode(const string &cipher_mode);\n\n#ifdef HAVE_SPEEX\n\t// speex acoustic echo cancellation (AEC) manipulations\n\tbool get_do_echo_cancellation(void) const;\n\tbool get_echo_captured_last(void);\n\tvoid set_echo_captured_last(bool value);\n\tSpeexEchoState *get_speex_echo_state(void);\n#endif\n\t\n};\n\n// Main functions for rx and tx threads\nvoid *main_audio_rx(void *arg);\nvoid *main_audio_tx(void *arg);\n\n#endif\n"
  },
  {
    "path": "src/audio/audio_tx.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include <cstdio>\n#include <ctime>\n#include <sys/types.h>\n#include <sys/time.h>\n#include \"audio_tx.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"line.h\"\n#include \"sequence_number.h\"\n#include \"audits/memman.h\"\n\nextern t_phone *phone;\n\n#define SAMPLE_BUF_SIZE (MAX_PTIME * sc_sample_rate/1000 * AUDIO_SAMPLE_SIZE/8)\n\n// Debug macro to print timestamp\n#define DEBUG_TS(s)\t{ gettimeofday(&debug_timer, NULL);\\\n\t\t\t  cout << \"DEBUG: \";\\\n\t\t\t  cout << debug_timer.tv_sec * 1000 +\\\n\t\t\t          debug_timer.tv_usec / 1000;\\\n\t\t\t  cout << \":\" << debug_timer.tv_sec * 1000 + debug_timer.tv_usec / 1000 - (debug_timer_prev.tv_sec * 1000 + debug_timer_prev.tv_usec / 1000);\\\n\t\t\t  cout << \" \" << (s) << endl;\\\n\t\t\t  debug_timer_prev = debug_timer;\\\n\t\t\t}\n\n//////////\n// PUBLIC\n//////////\n\nt_audio_tx::t_audio_tx(t_audio_session *_audio_session,\n\t\t   t_audio_io *_playback_device, t_twinkle_rtp_session *_rtp_session,\n\t           t_audio_codec _codec, \n\t           const map<unsigned short, t_audio_codec> &_payload2codec,\n\t           unsigned short _ptime)\n{\n\taudio_session = _audio_session;\n\t\n\tuser_config = audio_session->get_line()->get_user();\n\tassert(user_config);\n\t\n\tplayback_device = _playback_device;\n\trtp_session = _rtp_session;\n\tcodec = _codec;\n\tsc_sample_rate = audio_sample_rate(_codec);\n\tpayload2codec = _payload2codec;\n\tis_running = false;\n\tstop_running = false;\n\t\n\t// Create audio decoders\n\tmap_audio_decoder[CODEC_G711_ALAW] = new t_g711a_audio_decoder(_ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G711_ALAW]);\n\t\n\tmap_audio_decoder[CODEC_G711_ULAW] = new t_g711u_audio_decoder(_ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G711_ULAW]);\n\t\n\tmap_audio_decoder[CODEC_GSM] = new t_gsm_audio_decoder(user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_GSM]);\n\t\n#ifdef HAVE_SPEEX\n\tmap_audio_decoder[CODEC_SPEEX_NB] = new t_speex_audio_decoder(\n\t\t\tt_speex_audio_decoder::MODE_NB, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_NB]);\n\n\tmap_audio_decoder[CODEC_SPEEX_WB] = new t_speex_audio_decoder(\n\t\t\tt_speex_audio_decoder::MODE_WB, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_WB]);\n\t\n\tmap_audio_decoder[CODEC_SPEEX_UWB] = new t_speex_audio_decoder(\n\t\t\tt_speex_audio_decoder::MODE_UWB, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_UWB]);\n#endif\n#ifdef HAVE_ILBC\n\tmap_audio_decoder[CODEC_ILBC] = new t_ilbc_audio_decoder(_ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_ILBC]);\n#endif\n\n\tmap_audio_decoder[CODEC_G722] = new t_g722_audio_decoder(_ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G722]);\n\n\tmap_audio_decoder[CODEC_G726_16] = new t_g726_audio_decoder(\n\t\t\tt_g726_audio_decoder::BIT_RATE_16, _ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G726_16]);\n\t\n\tmap_audio_decoder[CODEC_G726_24] = new t_g726_audio_decoder(\n\t\t\tt_g726_audio_decoder::BIT_RATE_24, _ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G726_24]);\n\t\n\tmap_audio_decoder[CODEC_G726_32] = new t_g726_audio_decoder(\n\t\t\tt_g726_audio_decoder::BIT_RATE_32, _ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G726_32]);\n\t\n\tmap_audio_decoder[CODEC_G726_40] = new t_g726_audio_decoder(\n\t\t\tt_g726_audio_decoder::BIT_RATE_40, _ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G726_40]);\n#ifdef HAVE_BCG729\n\tmap_audio_decoder[CODEC_G729A] = new t_g729a_audio_decoder(\n\t\t\t_ptime, user_config);\n\tMEMMAN_NEW(map_audio_decoder[CODEC_G729A]);\n#endif\n\n\tptime = map_audio_decoder[codec]->get_default_ptime();\n\n\tsample_buf = new unsigned char[SAMPLE_BUF_SIZE];\n\tMEMMAN_NEW_ARRAY(sample_buf);\n\n\t// Create concealment buffers\n\tfor (int i = 0; i < MAX_CONCEALMENT; i++) {\n\t\tconceal_buf[i] = new unsigned char[SAMPLE_BUF_SIZE];\n\t\tMEMMAN_NEW_ARRAY(conceal_buf[i]);\n\t\tconceal_buflen[i] = 0;\n\t}\n\tconceal_num = 0;\n\tconceal_pos = 0;\n\n\t// Initialize jitter buffer\n\tjitter_buf = new unsigned char[JITTER_BUF_SIZE(sc_sample_rate)];\n\tMEMMAN_NEW_ARRAY(jitter_buf);\n\tjitter_buf_len = 0;\n\tload_jitter_buf = true;\n\tsoundcard_buf_size = playback_device->get_buffer_size(false);\n\n\t// Initialize 3-way settings\n\tis_3way = false;\n\tis_3way_mixer = false;\n\tmedia_3way_peer_tx = NULL;\n\tpeer_tx_3way = NULL;\n\tpeer_rx_3way = NULL;\n\tmix_buf_3way = NULL;\n\t\n\t// Initialize telephone event settings\n\tpt_telephone_event = -1;\n\tpt_telephone_event_alt = 1;\n}\n\nt_audio_tx::~t_audio_tx() {\n\tstruct timespec sleeptimer;\n\t\n\tif (is_running) {\n\t\tstop_running = true;\n\t\t// Unblock any in-progress snd_pcm_writei so the thread can check\n\t\t// stop_running and exit; without this the nanosleep loop below\n\t\t// spins forever when the ALSA-PulseAudio bridge deadlocks.\n\t\tplayback_device->drop();\n\t\tdo {\n\t\t\tsleeptimer.tv_sec = 0;\n\t\t\tsleeptimer.tv_nsec = 10000000;\n\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t\tcontinue;\n\t\t} while (is_running);\n\t}\n\n\tMEMMAN_DELETE_ARRAY(sample_buf);\n\tdelete [] sample_buf;\n\tMEMMAN_DELETE_ARRAY(jitter_buf);\n\tdelete [] jitter_buf;\n\n\tfor (int i = 0; i < MAX_CONCEALMENT; i++) {\n\t\tMEMMAN_DELETE_ARRAY(conceal_buf[i]);\n\t\tdelete [] conceal_buf[i];\n\t}\n\t\n\t// Destroy audio decoders\n\tfor (map<t_audio_codec, t_audio_decoder *>::iterator i = map_audio_decoder.begin();\n\t     i != map_audio_decoder.end(); i++)\n\t{\n\t\tMEMMAN_DELETE(i->second);\n\t\tdelete i->second;\n\t}\n\n\t// Cleanup 3-way resources\n\tif (media_3way_peer_tx) {\n\t\tMEMMAN_DELETE(media_3way_peer_tx);\n\t\tdelete media_3way_peer_tx;\n\t}\n\tif (mix_buf_3way) {\n\t\tMEMMAN_DELETE_ARRAY(mix_buf_3way);\n\t\tdelete [] mix_buf_3way;\n\t}\n}\n\nvoid t_audio_tx::retain_for_concealment(unsigned char *buf, unsigned short len) {\n\tif (conceal_num == 0) {\n\t\tmemcpy(conceal_buf[0], buf, len);\n\t\tconceal_buflen[0] = len;\n\t\tconceal_num = 1;\n\t\tconceal_pos = 0;\n\t\treturn;\n\t}\n\n\tif (conceal_num < MAX_CONCEALMENT) {\n\t\tmemcpy(conceal_buf[conceal_num], buf, len);\n\t\tconceal_buflen[conceal_num] = len;\n\t\tconceal_num++;\n\t\treturn;\n\t}\n\n\tmemcpy(conceal_buf[conceal_pos], buf, len);\n\tconceal_buflen[conceal_pos] = len;\n\tconceal_pos = (conceal_pos + 1) % MAX_CONCEALMENT;\n}\n\nvoid t_audio_tx::conceal(short num) {\n\t// Some codecs have a PLC.\n\t// Only use this PLC is the sound card sample rate equals the codec\n\t// sample rate. If they differ, then we should resample the codec\n\t// samples. As this should be a rare case, we are lazy here. In\n\t// this rare case, use Twinkle's low-tech PLC.\n\tif (map_audio_decoder[codec]->has_plc() && audio_sample_rate(codec) == sc_sample_rate) {\n\t\tshort *sb = (short *)sample_buf;\n\t\tfor (int i = 0; i < num; i++) {\n\t\t\tint nsamples;\n\t\t\tnsamples = map_audio_decoder[codec]->conceal(sb, SAMPLE_BUF_SIZE);\n\t\t\tif (nsamples > 0) {\n\t\t\t\tplay_pcm(sample_buf, nsamples * 2);\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn;\n\t}\n\n\t// Replay previous packets for other codecs\n\tshort i = (conceal_pos + (MAX_CONCEALMENT - num)) % MAX_CONCEALMENT;\n\n\tif (i >= conceal_pos) {\n\t\tfor (int j = i; j < MAX_CONCEALMENT; j++) {\n\t\t\tplay_pcm(conceal_buf[j], conceal_buflen[j]);\n\t\t}\n\n\t\tfor (int j = 0; j < conceal_pos; j++) {\n\t\t\tplay_pcm(conceal_buf[j], conceal_buflen[j]);\n\t\t}\n\t} else {\n\t\tfor (int j = i; j < conceal_pos; j++) {\n\t\t\tplay_pcm(conceal_buf[j], conceal_buflen[j]);\n\t\t}\n\t}\n}\n\nvoid t_audio_tx::clear_conceal_buf(void) {\n\tconceal_pos = 0;\n\tconceal_num = 0;\n}\n\nvoid t_audio_tx::play_pcm(unsigned char *buf, unsigned short len, bool only_3rd_party) {\n\tint status;\n\t//struct timeval debug_timer, debug_timer_prev;\n\n\tunsigned char *playbuf = buf;\n\n\t// If there is only sound from the 3rd party in a 3-way, then check\n\t// if there is still enough sound in the buffer of the DSP to be\n\t// played. If not, then play out the sound from the 3rd party only.\n\tif (only_3rd_party) {\n\t\t/* Does not work on all ALSA implementations.\n\t\tif (playback_device->get_buffer_space(false) < soundcard_buf_size - len) {\n\t\t*/\n\t\tif (!playback_device->play_buffer_underrun()) {\n\t\t\t// There is still sound in the DSP buffers to be\n\t\t\t// played, so let's wait. Maybe in the next cycle\n\t\t\t// an RTP packet from the far-end will be received.\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// If we are in a 3-way then send the samples to the peer audio\n\t// receiver for mixing\n\tif (!only_3rd_party && is_3way && peer_rx_3way) {\n\t\tpeer_rx_3way->post_media_peer_tx_3way(buf, len, sc_sample_rate);\n\t}\n\n\t// If we are in a 3-way conference and we are not the mixer then\n\t// send the sound samples to the mixer\n\tif (is_3way && !is_3way_mixer) {\n\t\tif (peer_tx_3way) {\n\t\t\tpeer_tx_3way->post_media_peer_tx_3way(buf, len, sc_sample_rate);\n\t\t\treturn;\n\t\t} else {\n\t\t\t// There is no peer.\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Mix audio for 3-way conference\n\tif (is_3way && is_3way_mixer) {\n\t\tif (media_3way_peer_tx->get(mix_buf_3way, len)) {\n\t\t\tshort *mix_sb = (short *)mix_buf_3way;\n\t\t\tshort *sb = (short *)buf;\n\t\t\tfor (int i = 0; i < len / 2; i++) {\n\t\t\t\tmix_sb[i] = mix_linear_pcm(sb[i], mix_sb[i]);\n\t\t\t}\n\n\t\t\tplaybuf = mix_buf_3way;\n\t\t}\n\t}\n\n\t// Fill jitter buffer before playing\n\tif (load_jitter_buf) {\n\t\tif (jitter_buf_len + len < JITTER_BUF_SIZE(sc_sample_rate)) {\n\t\t\tmemcpy(jitter_buf + jitter_buf_len, playbuf, len);\n\t\t\tjitter_buf_len += len;\n\t\t} else {\n\t\t\t// Write the contents of the jitter buffer to the DSP.\n\t\t\t// The buffers in the DSP will now function as jitter\n\t\t\t// buffer.\n\t\t\tstatus = playback_device->write(jitter_buf, jitter_buf_len);\n\t\t\tif (status != jitter_buf_len) {\n\t\t\t\tstring msg(\"Writing to dsp failed: \");\n\t\t\t\tmsg += get_error_str(errno);\n\t\t\t\tlog_file->write_report(msg, \"t_audio_tx::play_pcm\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t}\n\n\t\t\t// Write passed sound samples to DSP.\n\t\t\tstatus = playback_device->write(playbuf, len);\n\t\t\tif (status != len) {\n\t\t\t\tstring msg(\"Writing to dsp failed: \");\n\t\t\t\tmsg += get_error_str(errno);\n\t\t\t\tlog_file->write_report(msg, \"t_audio_tx::play_pcm\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t}\n\n\t\t\tload_jitter_buf = false;\n\t\t}\n\n\t\treturn;\n\t}\n\n\t// If buffer on soundcard is empty, then the jitter buffer needs\n\t// to be refilled. This should only occur when no RTP packets\n\t// have been received for a while (silence suppression or packet loss)\n\t/*\n\t * This code does not work on all ALSA implementations, e.g. ALSA via pulse audio\n\tint bufferspace = playback_device->get_buffer_space(false);\n\tif (bufferspace == soundcard_buf_size && len <= JITTER_BUF_SIZE(sc_sample_rate)) {\n\t*/\n\tif (playback_device->play_buffer_underrun()) {\n\t\tmemcpy(jitter_buf, playbuf, len);\n\t\tjitter_buf_len = len;\n\t\tload_jitter_buf = true;\n\t\tlog_file->write_header(\"t_audio_tx::play_pcm\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Audio tx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\": jitter buffer empty.\\n\");\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\n\t// If the play-out buffer contains the maximum number of\n\t// packets then start skipping packets to prevent\n\t// unacceptable delay.\n\t// This can only happen if the thread did not get\n\t// processing time for a while and RTP packets start to\n\t// pile up.\n\t// Or if a soundcard plays out the samples at just less then\n\t// the requested sample rate.\n\t/* Not needed anymore, the ::run loop already discards incoming RTP packets\n\t   with a late timestamp. This seems to solve the slow soundcard problem\n\t   better. The solution below caused annoying ticks in the playout.\n\t   \n\tif (soundcard_buf_size - bufferspace > JITTER_BUF_SIZE + len) {\n\t\tlog_file->write_header(\"t_audio_tx::play_pcm\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Audio tx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\": jitter buffer overflow: \");\n\t\tlog_file->write_raw(bufferspace);\n\t\tlog_file->write_raw(\" bytes.\\n\");\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\t*/\n\n\t// Write passed sound samples to DSP.\n\tstatus = playback_device->write(playbuf, len);\n\t\n\tif (status != len) {\n\t\tstring msg(\"Writing to dsp failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_audio_tx::play_pcm\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n}\n\nvoid t_audio_tx::set_running(bool running) {\n\tis_running = running;\n}\n\nvoid t_audio_tx::run(void) {\n\tconst AppDataUnit* adu;\n\tstruct timespec sleeptimer;\n\t//struct timeval debug_timer, debug_timer_prev;\n\tint last_seqnum = -1; // seqnum of last received RTP packet\n\t\n\t// RTP packets with multiple SSRCs may be received. Each SSRC\n\t// represents an audio stream. Twinkle will only play 1 audio stream.\n\t// On a reception of a new SSRC, Twinkle will switch over to play the\n\t// new stream. This supports devices that change SSRC during a call.\n\tuint32 ssrc_current = 0;\n\t\n\tbool recvd_dtmf = false; // indicates if last RTP packets is a DTMF event\n\n\t// The running flag is set already in t_audio_session::run to prevent\n\t// a crash when the thread gets destroyed before it starts running.\n\t// is_running = true;\n\n\tuint32 rtp_timestamp = 0;\n\t\n\t// This thread may not take the lock on the transaction layer to\n\t// prevent dead locks\n\tphone->add_prohibited_thread();\n\tui->add_prohibited_thread();\n\t\n\twhile (true) {\n\t\tdo {\n\t\t\tadu = NULL;\n\t\t\tif (stop_running) break;\n\t\t\trtp_timestamp = rtp_session->getFirstTimestamp();\n\t\t\tadu = rtp_session->getData(\n\t\t\t\t\trtp_session->getFirstTimestamp());\n\t\t\tif (adu == NULL || adu->getSize() <= 0) {\n\t\t\t\t// There is no packet available. This may have\n\t\t\t\t// several reasons:\n\t\t\t\t// - the thread scheduling granularity does\n\t\t\t\t//   not match ptime\n\t\t\t\t// - packet lost\n\t\t\t\t// - packet delayed\n\t\t\t\t// Wait another cycle for a packet. The\n\t\t\t\t// jitter buffer will cope with this variation.\n\t\t\t\tif (adu) {\n\t\t\t\t\tdelete adu;\n\t\t\t\t\tadu = NULL;\n\t\t\t\t}\n\n\t\t\t\t// If we are the mixer in a 3-way call and there\n\t\t\t\t// is enough media from the other far-end then\n\t\t\t\t// this must be sent to the dsp.\n\t\t\t\tif (is_3way && is_3way_mixer &&\n\t\t\t\t    media_3way_peer_tx->size_content() >=\n\t\t\t\t    \tptime * (audio_sample_rate(codec) / 1000) * 2)\n\t\t\t\t{\n\t\t\t\t\t// Fill the sample buffer with silence\n\t\t\t\t\tint len = ptime * (audio_sample_rate(codec) / 1000) * 2;\n\t\t\t\t\tmemset(sample_buf, 0, len);\n\t\t\t\t\tplay_pcm(sample_buf, len, true);\n\t\t\t\t}\n\n\t\t\t\t// Sleep ptime ms\n\t\t\t\tsleeptimer.tv_sec = 0;\n\n\t\t\t\tif (ptime >= 20) {\n\t\t\t\t\tsleeptimer.tv_nsec =\n\t\t\t\t\t\tptime * 1000000 - 10000000;\n\t\t\t\t} else {\n\t\t\t\t\t// With a thread schedule of 10ms\n\t\t\t\t\t// granularity, this will schedule the\n\t\t\t\t\t// thread every 10ms.\n\t\t\t\t\tsleeptimer.tv_nsec = 5000000;\n\t\t\t\t}\n\t\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t\t}\n\t\t} while (adu == NULL || (adu->getSize() <= 0));\n\t\t\n\t\tif (stop_running) {\n\t\t\tif (adu) delete adu;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (adu) {\n\t\t\t// adu is created by ccRTP, but we have to delete it,\n\t\t\t// so report it to MEMMAN\n\t\t\tMEMMAN_NEW(const_cast<ost::AppDataUnit*>(adu));\n\t\t}\n\n\t\t// Check for a codec change\n\t\tmap<unsigned short, t_audio_codec>::const_iterator it_codec;\n\t\tit_codec = payload2codec.find(adu->getType());\n\t\tt_audio_codec recvd_codec = CODEC_NULL;\n\t\tif (it_codec != payload2codec.end()) {\n\t\t\trecvd_codec = it_codec->second;\n\t\t}\n\t\t\n\t\t// Switch over to new SSRC\n\t\tif (last_seqnum == -1 || ssrc_current != adu->getSource().getID()) {\n\t\t\tif (recvd_codec != CODEC_NULL) {\n\t\t\t\tssrc_current = adu->getSource().getID();\n\t\t\t\t\n\t\t\t\t// An SSRC defines a sequence number space. So a new\n\t\t\t\t// SSRC starts with a new random sequence number\n\t\t\t\tlast_seqnum = -1;\n\t\t\t\t\n\t\t\t\tlog_file->write_header(\"t_audio_tx::run\", \n\t\t\t\t\tLOG_NORMAL);\n\t\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\tlog_file->write_raw(\": play SSRC \");\n\t\t\t\tlog_file->write_raw(ssrc_current);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t} else {\n\t\t\t\t// SSRC received had an unsupported codec\n\t\t\t\t// Discard.\n\t\t\t\t// KLUDGE: for now this supports a scenario where a\n\t\t\t\t// far-end starts ZRTP negotiation by sending CN\n\t\t\t\t// packets with a separate SSRC while ZRTP is disabled\n\t\t\t\t// in Twinkle. Twinkle will then receive the CN packets\n\t\t\t\t// and discard them here as CN is an unsupported codec.\n\t\t\t\tlog_file->write_header(\"t_audio_tx::run\", \n\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\tlog_file->write_raw(\": SSRC received (\");\n\t\t\t\tlog_file->write_raw(adu->getSource().getID());\n\t\t\t\tlog_file->write_raw(\") has unsupported codec \");\n\t\t\t\tlog_file->write_raw(adu->getType());\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\t\t\tdelete adu;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t\n\t\tmap<t_audio_codec, t_audio_decoder *>::const_iterator it_decoder;\n\t\tit_decoder = map_audio_decoder.find(recvd_codec);\n\t\tif (it_decoder != map_audio_decoder.end()) {\n\t\t\tif (codec != recvd_codec) {\n\t\t\t\tcodec = recvd_codec;\n\t\t\t\tget_line()->ci_set_recv_codec(codec);\n\t\t\t\tui->cb_async_recv_codec_changed(get_line()->get_line_number(),\n\t\t\t\t\tcodec);\n\n\t\t\t\tlog_file->write_header(\"t_audio_tx::run\", \n\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\tlog_file->write_raw(\": codec change to \");\n\t\t\t\tlog_file->write_raw(ui->format_codec(codec));\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t}\n\t\t} else {\n\t\t\tif (adu->getType() == pt_telephone_event ||\n\t\t\t    adu->getType() == pt_telephone_event_alt) \n\t\t\t{\n\t\t\t\trecvd_dtmf = true;\n\t\t\t} else {\n\t\t\t\tif (codec != CODEC_UNSUPPORTED) {\n\t\t\t\t\tcodec = CODEC_UNSUPPORTED;\n\t\t\t\t\tget_line()->ci_set_recv_codec(codec);\n\t\t\t\t\tui->cb_async_recv_codec_changed(\n\t\t\t\t\t\tget_line()->get_line_number(), codec);\n\t\n\t\t\t\t\tlog_file->write_header(\"t_audio_tx::run\", \n\t\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\t\tlog_file->write_raw(\": payload type \");\n\t\t\t\t\tlog_file->write_raw(adu->getType());\n\t\t\t\t\tlog_file->write_raw(\" not supported\\n\");\n\t\t\t\t\tlog_file->write_footer();\n\t\t\t\t}\n\t\n\t\t\t\tlast_seqnum = adu->getSeqNum();\n\t\t\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\t\t\tdelete adu;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// DTMF event\n\t\tif (recvd_dtmf) {\n\t\t\t// NOTE: the DTMF tone will be detected here\n\t\t\t// while there might still be data in the jitter\n\t\t\t// buffer. If the jitter buffer was already sent\n\t\t\t// to the DSP, then the DSP will continue to play\n\t\t\t// out the buffer sound samples.\n\n\t\t\tif (dtmf_previous_timestamp != rtp_timestamp) {\n\t\t\t\t// A new DTMF tone has been received.\n\t\t\t\tdtmf_previous_timestamp = rtp_timestamp;\n\t\t\t\tt_rtp_telephone_event *e =\n\t\t\t\t\t(t_rtp_telephone_event *)adu->getData();\n\t\t\t\tui->cb_async_dtmf_detected(get_line()->get_line_number(),\n\t\t\t\t\te->get_event());\n\n\t\t\t\t// Log DTMF event\n\t\t\t\tlog_file->write_header(\"t_audio_tx::run\");\n\t\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\tlog_file->write_raw(\": detected DTMF event - \");\n\t\t\t\tlog_file->write_raw(e->get_event());\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t}\n\n\t\t\trecvd_dtmf = false;\n\t\t\tlast_seqnum = adu->getSeqNum();\n\t\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\t\tdelete adu;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Discard invalide payload sizes\n\t\tif (!map_audio_decoder[codec]->valid_payload_size(\n\t\t\t\tadu->getSize(), SAMPLE_BUF_SIZE / 2))\n\t\t{\n\t\t\tlog_file->write_header(\"t_audio_tx::run\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": RTP payload size (\");\n\t\t\tlog_file->write_raw((unsigned long)(adu->getSize()));\n\t\t\tlog_file->write_raw(\" bytes) invalid for \\n\");\n\t\t\tlog_file->write_raw(ui->format_codec(codec));\n\t\t\tlog_file->write_footer();\n\t\t\tlast_seqnum = adu->getSeqNum();\n\t\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\t\tdelete adu;\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tunsigned short recvd_ptime;\n\t\trecvd_ptime = map_audio_decoder[codec]->get_ptime(adu->getSize());\n\n\t\t// Log a change of ptime\n\t\tif (ptime != recvd_ptime) {\n\t\t\tlog_file->write_header(\"t_audio_tx::run\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": ptime changed from \");\n\t\t\tlog_file->write_raw(ptime);\n\t\t\tlog_file->write_raw(\" ms to \");\n\t\t\tlog_file->write_raw(recvd_ptime);\n\t\t\tlog_file->write_raw(\" ms\\n\");\n\t\t\tlog_file->write_footer();\n\t\t\tptime = recvd_ptime;\n\t\t}\n\t\t\n\t\t// Check for lost packets\n\t\t// This must be done before decoding the received samples as the\n\t\t// speex decoder has its own PLC algorithm for which it needs the decoding\n\t\t// state before decoding the new samples.\n\t\tseq16_t seq_recvd(adu->getSeqNum());\n\t\tseq16_t seq_last(static_cast<uint16>(last_seqnum));\n\t\tif (last_seqnum != -1 && seq_recvd - seq_last > 1) {\n\t\t\t// Packets have been lost\n\t\t\tuint16 num_lost = (seq_recvd - seq_last) - 1;\n\t\t\tlog_file->write_header(\"t_audio_tx::run\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": \");\n\t\t\tlog_file->write_raw(num_lost);\n\t\t\tlog_file->write_raw(\" RTP packets lost.\\n\");\n\t\t\tlog_file->write_footer();\n\n\t\t\tif (num_lost <= conceal_num) {\n\t\t\t\t// Conceal packet loss\n\t\t\t\tconceal(num_lost);\n\t\t\t}\n\t\t\tclear_conceal_buf();\n\t\t}\n\t\t\n\t\t// Determine if resampling is needed due to dynamic change to\n\t\t// codec with other sample rate.\n\t\tshort downsample_factor = 1;\n\t\tshort upsample_factor = 1;\n\t\tif (audio_sample_rate(codec) > sc_sample_rate) {\n\t\t\tdownsample_factor = audio_sample_rate(codec) / sc_sample_rate;\n\t\t} else if (audio_sample_rate(codec) < sc_sample_rate) {\n\t\t\tupsample_factor = sc_sample_rate / audio_sample_rate(codec);\n\t\t}\n\t\t\n\t\t// Create sample buffer. If no resampling is needed, the sample\n\t\t// buffer from the audio_tx object can be used directly.\n\t\t// Otherwise a temporary sample buffers is created that will\n\t\t// be resampled to the object's sample buffer later.\n\t\tshort *sb;\n\t\tint sb_size;\n\t\tif (downsample_factor > 1) {\n\t\t\tsb_size = SAMPLE_BUF_SIZE / 2 * downsample_factor;\n\t\t\tsb = new short[sb_size];\n\t\t\tMEMMAN_NEW_ARRAY(sb);\n\t\t} else if (upsample_factor > 1) {\n\t\t\tsb_size = SAMPLE_BUF_SIZE / 2;\n\t\t\tsb = new short[SAMPLE_BUF_SIZE / 2];\n\t\t\tMEMMAN_NEW_ARRAY(sb);\n\t\t} else {\n\t\t\tsb_size = SAMPLE_BUF_SIZE / 2;\n\t\t\tsb = (short *)sample_buf;\n\t\t}\n\t\t\t\t\n\t\t\t\t\n\t\t// Decode the audio\n\t\tunsigned char *payload = const_cast<uint8 *>(adu->getData());\n\t\tshort sample_size; // size in bytes\n\t\t\n\t\tsample_size = 2 * map_audio_decoder[codec]->decode(payload, adu->getSize(), sb, sb_size);\n\t\t\t\t\n\t\t// Resample if needed\n\t\tif (downsample_factor > 1) {\n\t\t\tshort *p = sb;\n\t\t\tsb = (short *)sample_buf;\n\t\t\tfor (int i = 0; i < sample_size / 2; i += downsample_factor) {\n\t\t\t\tsb[i / downsample_factor] = p[i];\n\t\t\t}\n\t\t\tMEMMAN_DELETE_ARRAY(p);\n\t\t\tdelete [] p;\n\t\t\tsample_size /= downsample_factor;\n\t\t} else if (upsample_factor > 1) {\n\t\t\tshort *p = sb;\n\t\t\tsb = (short *)sample_buf;\n\t\t\tfor (int i = 0; i < sample_size / 2; i++) {\n\t\t\t\tfor (int j = 0; j < upsample_factor; j++) {\n\t\t\t\t\tsb[i * upsample_factor + j] = p[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE_ARRAY(p);\n\t\t\tdelete [] p;\n\t\t\tsample_size *= upsample_factor;\n\t\t}\n\t\t\n\t\t// If the decoder deliverd 0 bytes, then it failed\n\t\tif (sample_size == 0) {\n\t\t\tlast_seqnum = adu->getSeqNum();\n\t\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\t\tdelete adu;\t\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\t// Discard packet if we are lacking behind. This happens if the\n\t\t// soundcard plays at a rate less than the requested sample rate.\n\t\tif (rtp_session->isWaiting(&(adu->getSource()))) {\n\n\t\t\tuint32 last_ts = rtp_session->getLastTimestamp(&(adu->getSource()));\n\t\t\tuint32 diff;\n\t\t\t\n\t\t\tdiff = last_ts - rtp_timestamp;\n\t\t\t\n\t\t\tif (diff > (uint32_t)(JITTER_BUF_SIZE(sc_sample_rate) / AUDIO_SAMPLE_SIZE) * 8)\n\t\t\t{\n\t\t\t\tlog_file->write_header(\"t_audio_tx::run\", LOG_NORMAL, LOG_DEBUG);\n\t\t\t\tlog_file->write_raw(\"Audio tx line \");\n\t\t\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\t\t\tlog_file->write_raw(\": discard delayed packet.\\n\");\n\t\t\t\tlog_file->write_raw(\"Timestamp: \");\n\t\t\t\tlog_file->write_raw(rtp_timestamp);\n\t\t\t\tlog_file->write_raw(\", Last timestamp: \");\n\t\t\t\tlog_file->write_raw((long unsigned int)last_ts);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\t\n\t\t\t\tlast_seqnum = adu->getSeqNum();\n\t\t\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\t\t\tdelete adu;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tplay_pcm(sample_buf, sample_size);\n\t\tretain_for_concealment(sample_buf, sample_size);\n\t\tlast_seqnum = adu->getSeqNum();\n\t\tMEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));\n\t\tdelete adu;\n\n\t\t// No sleep is done here but in the loop waiting\n\t\t// for a new packet. If a packet is already available\n\t\t// it can be send to the sound card immediately so\n\t\t// the play-out buffer keeps filled.\n\t\t// If the play-out buffer gets empty you hear a\n\t\t// crack in the sound.\n\n\n#ifdef HAVE_SPEEX\t\t\n\t\t// store decoded output for (optional) echo cancellation \n\t\tif (audio_session->get_do_echo_cancellation()) {\n\t\t    if (audio_session->get_echo_captured_last()) {\n\t\t\tspeex_echo_playback(audio_session->get_speex_echo_state(), (spx_int16_t *) sb);\n\t\t\taudio_session->set_echo_captured_last(false);;\n\t\t    }\n\t\t}\n#endif\n\n\t}\n\n\tphone->remove_prohibited_thread();\n\tui->remove_prohibited_thread();\n\tis_running = false;\n}\n\nvoid t_audio_tx::set_pt_telephone_event(int pt, int pt_alt) {\n\tpt_telephone_event = pt;\n\tpt_telephone_event_alt = pt_alt;\n}\n\nt_line *t_audio_tx::get_line(void) const {\n\treturn audio_session->get_line();\n}\n\nvoid t_audio_tx::join_3way(bool mixer, t_audio_tx *peer_tx, t_audio_rx *peer_rx) {\n\tmtx_3way.lock();\n\n\tif (is_3way) {\n\t\tlog_file->write_header(\"t_audio_tx::join_3way\");\n\t\tlog_file->write_raw(\"ERROR: audio tx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\" - 3way is already active.\\n\");\n\t\tlog_file->write_footer();\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_tx::join_3way\");\n\tlog_file->write_raw(\"Audio tx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tlog_file->write_raw(\": join 3-way.\\n\");\n\tif (mixer) {\n\t\tlog_file->write_raw(\"Role is: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"Role is: non-mixing.\\n\");\n\t}\n\tif (peer_tx) {\n\t\tlog_file->write_raw(\"A peer transmitter already exists.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"A peer transmitter does not exist.\\n\");\n\t}\n\tif (peer_rx) {\n\t\tlog_file->write_raw(\"A peer receiver already exists.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"A peer receiver does not exist.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\tpeer_tx_3way = peer_tx;\n\tpeer_rx_3way = peer_rx;\n\tis_3way_mixer = mixer;\n\tis_3way = true;\n\n\t// Create buffers for mixing\n\tmix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];\n\tMEMMAN_NEW_ARRAY(mix_buf_3way);\n\n\t// See comments in audio_rx.cpp for the size of this buffer.\n\tmedia_3way_peer_tx = new t_media_buffer(JITTER_BUF_SIZE(sc_sample_rate));\n\tMEMMAN_NEW(media_3way_peer_tx);\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_tx::set_peer_tx_3way(t_audio_tx *peer_tx) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_tx::set_peer_tx_3way\");\n\tlog_file->write_raw(\"Audio tx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tif (peer_tx) {\n\t\tlog_file->write_raw(\": set peer transmitter.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\": erase peer transmitter.\\n\");\n\t}\n\tif (is_3way_mixer) {\n\t\tlog_file->write_raw(\"Role is: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"Role is: non-mixing.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\n\tpeer_tx_3way = peer_tx;\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_tx::set_peer_rx_3way(t_audio_rx *peer_rx) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_tx::set_peer_rx_3way\");\n\tlog_file->write_raw(\"Audio tx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tif (peer_rx) {\n\t\tlog_file->write_raw(\": set peer receiver.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\": erase peer receiver.\\n\");\n\t}\n\tif (is_3way_mixer) {\n\t\tlog_file->write_raw(\"Role is: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\"Role is: non-mixing.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\tpeer_rx_3way = peer_rx;\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_tx::set_mixer_3way(bool mixer) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_tx::set_mixer_3way\");\n\tlog_file->write_raw(\"Audio tx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tif (mixer) {\n\t\tlog_file->write_raw(\": change role to: mixer.\\n\");\n\t} else {\n\t\tlog_file->write_raw(\": change role to: non-mixing.\\n\");\n\t}\n\tlog_file->write_footer();\n\n\tis_3way_mixer = mixer;\n\t\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_tx::stop_3way(void) {\n\tmtx_3way.lock();\n\n\tif (!is_3way) {\n\t\tlog_file->write_header(\"t_audio_tx::stop_3way\");\n\t\tlog_file->write_raw(\"ERROR: audio tx line \");\n\t\tlog_file->write_raw(get_line()->get_line_number()+1);\n\t\tlog_file->write_raw(\" - 3way is not active.\\n\");\n\t\tlog_file->write_footer();\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\n\t// Logging\n\tlog_file->write_header(\"t_audio_tx::stop_3way\");\n\tlog_file->write_raw(\"Audio tx line \");\n\tlog_file->write_raw(get_line()->get_line_number()+1);\n\tlog_file->write_raw(\": stop 3-way.\\n\");\n\tlog_file->write_footer();\n\n\tis_3way = false;\n\tis_3way_mixer = false;\n\n\tif (media_3way_peer_tx) {\n\t\tMEMMAN_DELETE(media_3way_peer_tx);\n\t\tdelete media_3way_peer_tx;\n\t\tmedia_3way_peer_tx = NULL;\n\t}\n\n\tif (mix_buf_3way) {\n\t\tMEMMAN_DELETE_ARRAY(mix_buf_3way);\n\t\tdelete [] mix_buf_3way;\n\t\tmix_buf_3way = NULL;\n\t}\n\n\tmtx_3way.unlock();\n}\n\nvoid t_audio_tx::post_media_peer_tx_3way(unsigned char *media, int len,\n\t\tunsigned short peer_sample_rate) \n{\n\tmtx_3way.lock();\n\n\tif (!is_3way || !is_3way_mixer) {\n\t\tmtx_3way.unlock();\n\t\treturn;\n\t}\n\t\n\tif (peer_sample_rate != sc_sample_rate) {\n\t\t// Resample media from peer to sample rate of this transmitter\n\t\tint output_len = (len / 2) * sc_sample_rate / peer_sample_rate;\n\t\tshort *output_buf = new short[output_len];\n\t\tMEMMAN_NEW_ARRAY(output_buf);\n\t\tint resample_len = resample((short *)media, len / 2, peer_sample_rate,\n\t\t\t\t\toutput_buf, output_len, sc_sample_rate);\n\t\tmedia_3way_peer_tx->add((unsigned char *)output_buf, resample_len * 2);\n\t\tMEMMAN_DELETE_ARRAY(output_buf);\n\t\tdelete [] output_buf;\n\t} else {\n\t\tmedia_3way_peer_tx->add(media, len);\n\t}\n\n\tmtx_3way.unlock();\n}\n\nbool t_audio_tx::get_is_3way_mixer(void) const {\n\treturn is_3way_mixer;\n}\n"
  },
  {
    "path": "src/audio/audio_tx.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _AUDIO_TX_H\n#define _AUDIO_TX_H\n\n// Receive RTP and send audio to soundcard\n\n#include <map>\n#include <string>\n#include \"audio_codecs.h\"\n#include \"audio_decoder.h\"\n#include \"audio_rx.h\"\n#include \"media_buffer.h\"\n#include \"rtp_telephone_event.h\"\n#include \"user.h\"\n#include \"threads/mutex.h\"\n#include \"audio_device.h\"\n#include \"twinkle_rtp_session.h\"\n#include \"twinkle_config.h\"\n\n#ifdef HAVE_GSM\n#include <gsm/gsm.h>\n#else\n#include \"gsm/inc/gsm.h\"\n#endif\n\nusing namespace std;\nusing namespace ost;\n\n// Forward declarations\nclass t_audio_session;\nclass t_line;\n\nclass t_audio_tx {\nprivate:\n\t// audio_session owning this audio transmitter\n\tt_audio_session *audio_session;\n\t\n\t// User profile of user using the line\n\t// This is a pointer to the user_config owned by a phone user.\n\t// So this pointer should never be deleted.\n\tt_user\t\t*user_config;\n\n\t// file descriptor audio capture device\n\tt_audio_io\t\t*playback_device;\n\tt_twinkle_rtp_session\t*rtp_session;\n\n\t// Indicates if this transmitter is part of a 3-way conference\n\tbool\t\tis_3way;\n\n\t// Indicates if this transmitter is the mixer in a 3-way conference.\n\t// The mixer will mix this audio stream with the audio from the other\n\t// party and send it to the soundcard.\n\t// If the transmitter is part of a 3-way conference, but not the\n\t// mixer, then it simply has to send its audio payload to the mixer.\n\tbool\t\tis_3way_mixer;\n\n\t// Media buffer for media from the other far-end. This buffer is\n\t// used by the mixer.\n\tt_media_buffer\t*media_3way_peer_tx;\n\n\t// The peer audio transmitter in a 3-way conference\n\tt_audio_tx\t*peer_tx_3way;\n\n\t// The audio receiver that needs input from this transmitter in\n\t// a 3-way conference.\n\tt_audio_rx\t*peer_rx_3way;\n\n\t// Buffer for mixing purposes in 3-way conference.\n\tunsigned char\t*mix_buf_3way;\n\n\t// Mutex to protect 3-way resources\n\tt_mutex\t\tmtx_3way;\n\n\t// Codec information\n\tt_audio_codec\tcodec;\n\tmap<unsigned short, t_audio_codec> payload2codec;\n\tunsigned short\tptime;\t// in milliseconds\n\t\n\t// Sample rate of sound card.\n\t// The sample rate of the sound card is set to the sample rate\n\t// used for the initial codec. The far end may dynamically switch\n\t// to a codec with another sample rate. This will not change the\n\t// sample rate of the sound card! (capture and playback cannot\n\t// be done at different sampling rates).\n\tunsigned short\tsc_sample_rate;\n\t\n\t// Mapping from codecs to decoders\n\tmap<t_audio_codec, t_audio_decoder *> map_audio_decoder;\n\n\t// Buffer to store PCM samples of a received RTP packet\n\tunsigned char\t*sample_buf;\n\n\t// Jitter buffer (PCM).\n\t// jitter_buf_len indicates the number of bytes in the jitter buffer.\n\t// At the start of playing the samples are stored in the jitter buffer.\n\t// Once the buffer is full, all samples are copied to the memory of\n\t// the soundcard. From that point the soundcard itself is the jitter\n\t// buffer.\n\tunsigned char\t*jitter_buf;\n\tunsigned short\tjitter_buf_len;\n\tbool\t\tload_jitter_buf;\n\n\t// Buffer to keep last played packets for concealment.\n\tunsigned char\t*conceal_buf[MAX_CONCEALMENT];\n\tunsigned short\tconceal_buflen[MAX_CONCEALMENT]; // length of packet\n\tshort\t\tconceal_pos; // points to the oldest packet.\n\tshort\t\tconceal_num; // number of retained packets.\n\n\tunsigned short\tsoundcard_buf_size;\n\n\t// Payload type for telephone-event payload.\n\t// Some endpoints ignore the payload type that was sent in an\n\t// outgoing INVITE and simply sends it with the payload type,\n\t// they indicated in the 200 OK. Accept both payloads for\n\t// interoperability.\n\tint pt_telephone_event;\n\tint pt_telephone_event_alt;\n\n\t// Timestamp of previous DTMF tone\n\tunsigned long\tdtmf_previous_timestamp;\n\n\t// Inidicates if the playing thread is running\n\tvolatile bool is_running;\n\n\t// The thread exits when this indicator is set to true\n\tvolatile bool stop_running;\n\n\t// Retain a packet (PCM encoded) for possible concealment.\n\tvoid retain_for_concealment(unsigned char *buf, unsigned short len);\n\n\t// Play last num packets again.\n\tvoid conceal(short num);\n\n\t// Erase concealment buffers.\n\tvoid clear_conceal_buf(void);\n\n\t// Play PCM encoded samples\n\t// - only_3rd_party indicates if there is only 3rd_party audio available,\n\t//   i.e. due to jitter, packet loss or silence suppression\n\tvoid play_pcm(unsigned char *buf, unsigned short len, bool only_3rd_party = false);\n\npublic:\n\t// Create the audio transmitter\n\t// _fd\t\tfile descriptor of capture device\n\t// _rtp_session\tRTP socket tp send the RTP stream\n\t// _codec\taudio codec to use\n\t// _ptime\tlength of the audio packets in ms\n\t// _ptime = 0 means use default ptime value for the codec\n\tt_audio_tx(t_audio_session *_audio_session, t_audio_io *_playback_device,\n\t\t   t_twinkle_rtp_session *_rtp_session,\n\t           t_audio_codec _codec, \n\t           const map<unsigned short, t_audio_codec> &_payload2codec,\n\t           unsigned short _ptime = 0);\n\n\t~t_audio_tx();\n\t\n\t// Set the is running flag\n\tvoid set_running(bool running);\n\n\tvoid run(void);\n\t\n\t// Set the dynamic payload type for telephone events\n\tvoid set_pt_telephone_event(int pt, int pt_alt);\n\n\t// Get phone line belonging to this audio transmitter\n\tt_line *get_line(void) const;\n\n\t// Join this transmitter in a 3way conference.\n\t// mixer indicates if this transmitter must be the mixer.\n\t// - peer_tx is the peer transmitter in a 3-way\n\t// - audio_rx is the audio receiver needing the output from this\n\t//   transmitter for mixing.\n\tvoid join_3way(bool mixer, t_audio_tx *peer_tx, t_audio_rx *peer_rx);\n\n\t// Change the peer rx/tx (NULL to erase)\n\tvoid set_peer_tx_3way(t_audio_tx *peer_tx);\n\tvoid set_peer_rx_3way(t_audio_rx *peer_rx);\n\n\t// Change the mixer role\n\tvoid set_mixer_3way(bool mixer);\n\n\t// Stop the 3-way conference and make it a 1-on-1 call again.\n\tvoid stop_3way(void);\n\n\t// Post media from the peer transmitter for a 3-way mixer.\n\tvoid post_media_peer_tx_3way(unsigned char *media, int len, \n\t\tunsigned short peer_sample_rate);\n\n\t// Returns if this transmitter is the mixer in a 3-way\n\tbool get_is_3way_mixer(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/dtmf_player.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <cstring>\n#include <sys/time.h>\n#include \"dtmf_player.h\"\n#include \"audio_rx.h\"\n#include \"line.h\"\n#include \"rtp_telephone_event.h\"\n#include \"log.h\"\n\n/////////////////////////////////////////\n// class t_dtmf_player\n/////////////////////////////////////////\n\nt_dtmf_player::t_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder, \n\t\t\tt_user *user_config, t_dtmf_ev dtmf_tone, uint32 dtmf_timestamp,\n\t\t\tuint16 nsamples) :\n\t_audio_rx(audio_rx),\n\t_user_config(user_config),\n\t_audio_encoder(audio_encoder),\n\t_dtmf_pause(false),\n\t_dtmf_stop(false),\n\t_dtmf_current(dtmf_tone),\n\t_dtmf_timestamp(dtmf_timestamp),\n\t_dtmf_duration(0),\n\t_nsamples(nsamples)\n{}\n\nuint32 t_dtmf_player::get_timestamp(void) {\n\treturn _dtmf_timestamp;\n}\n\nbool t_dtmf_player::finished(void) {\n\treturn _dtmf_stop;\n}\n\n/////////////////////////////////////////\n// class t_rtp_event_dtmf_player\n/////////////////////////////////////////\n\nt_rtp_event_dtmf_player::t_rtp_event_dtmf_player(t_audio_rx *audio_rx, \n\t\t\tt_audio_encoder *audio_encoder, t_user *user_config,\n\t\t\tt_dtmf_ev dtmf_tone, uint32 dtmf_timestamp,\n\t\t\tuint16 nsamples) : \n\tt_dtmf_player(audio_rx, audio_encoder, user_config, dtmf_tone, dtmf_timestamp, \n\t\t\tnsamples)\n{\n}\n\nuint16 t_rtp_event_dtmf_player::get_payload(uint8 *payload, \n\t\tuint16 payload_size, uint32 timestamp, uint32 &rtp_timestamp)\n{\n\tt_rtp_telephone_event *dtmf_payload = (t_rtp_telephone_event *)payload;\n\tassert(sizeof(t_rtp_telephone_event) <= payload_size);\n\n\t// RFC 2833 3.5, 3.6\n\tdtmf_payload->set_event(_dtmf_current);\n\tdtmf_payload->set_reserved(false);\n\tdtmf_payload->set_volume(_user_config->get_dtmf_volume());\n\n\tif (_dtmf_pause) {\n\t\t// Trailing pause phase of a DTMF tone\n\t\t// Repeat the last packet\n\t\tdtmf_payload->set_end(true);\n\n\t\tint pause_duration = timestamp - _dtmf_timestamp - _dtmf_duration +\n\t\t\t\t     _nsamples;\n\t\tif (pause_duration / _nsamples * _audio_encoder->get_ptime() >=\n\t\t\t\t\t_user_config->get_dtmf_pause())\n\t\t{\n\t\t\t// This is the last packet to be sent for the\n\t\t\t// current DTMF tone.\n\t\t\t_dtmf_stop = true;\n\t\t\tlog_file->write_header(\"t_rtp_event_dtmf_player::get_payload\",\n\t\t\t\t\tLOG_NORMAL);\n\t\t\tlog_file->write_raw(\"Audio rx line \");\n\t\t\tlog_file->write_raw(_audio_rx->get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": finish DTMF event - \");\n\t\t\tlog_file->write_raw(_dtmf_current);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t} else {\n\t\t// Play phase of a DTMF tone\n\t\t// The duration counts from the start of the tone.\n\t\t_dtmf_duration = timestamp - _dtmf_timestamp + _nsamples;\n\n\t\t// Check if the tone must end\n\t\tif (_dtmf_duration / _nsamples * _audio_encoder->get_ptime() >=\n\t\t\t\t\t\t_user_config->get_dtmf_duration())\n\t\t{\n\t\t\tdtmf_payload->set_end(true);\n\t\t\t_dtmf_pause = true;\n\t\t} else {\n\t\t\tdtmf_payload->set_end(false);\n\t\t}\n\t}\n\n\tdtmf_payload->set_duration(_dtmf_duration);\n\trtp_timestamp = _dtmf_timestamp;\n\treturn sizeof(t_rtp_telephone_event);\n}\n\n\n/////////////////////////////////////////\n// class t_inband_dtmf_player\n/////////////////////////////////////////\n\nt_inband_dtmf_player::t_inband_dtmf_player(t_audio_rx *audio_rx, \n\t\tt_audio_encoder *audio_encoder, t_user *user_config,\n\t\tt_dtmf_ev dtmf_tone, uint32 dtmf_timestamp,\n\t\tuint16 nsamples) :\n\tt_dtmf_player(audio_rx, audio_encoder, user_config, dtmf_tone, dtmf_timestamp, \n\t\t\tnsamples),\n\t_freq_gen(dtmf_tone, -(user_config->get_dtmf_volume()))\n{\n}\n\nuint16 t_inband_dtmf_player::get_payload(uint8 *payload, \n\t\tuint16 payload_size, uint32 timestamp, uint32 &rtp_timestamp)\n{\n\tuint16 nsamples_real = _nsamples * _audio_encoder->get_sample_rate_rtp_ratio();\n\tint16 sample_buf[nsamples_real];\n\n\tif (_dtmf_pause) {\n\t\tint pause_duration = timestamp - _dtmf_timestamp - _dtmf_duration +\n\t\t\t\t     _nsamples;\n\t\t\t\t     \n\t\tmemset(sample_buf, 0, nsamples_real * 2);\n\t\t\t\t     \n\t\tif (pause_duration / _nsamples * _audio_encoder->get_ptime() >=\n\t\t\t\t\t_user_config->get_dtmf_pause())\n\t\t{\n\t\t\t// This is the last packet to be sent for the\n\t\t\t// current DTMF tone.\n\t\t\t_dtmf_stop = true;\n\t\t\tlog_file->write_header(\"t_inband_dtmf_player::get_payload\", LOG_NORMAL);\n\t\t\tlog_file->write_raw(\"Audio rx line \");\n\t\t\tlog_file->write_raw(_audio_rx->get_line()->get_line_number()+1);\n\t\t\tlog_file->write_raw(\": finish DTMF event - \");\n\t\t\tlog_file->write_raw(_dtmf_current);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t} else {\n\t\t// Timestamp and interval for _freq_gen must be in usec\n\t\tuint32 ts_start = (timestamp - _dtmf_timestamp) * 1000000 *\n\t\t\t\t\t_audio_encoder->get_sample_rate_rtp_ratio() /\n\t\t\t\t\t_audio_encoder->get_sample_rate();\n\t\t_freq_gen.get_samples(sample_buf, nsamples_real, ts_start,\n\t\t\t1000000.0 / _audio_encoder->get_sample_rate());\n\t\t\n\t\t// The duration counts from the start of the tone.\n\t\t_dtmf_duration = timestamp - _dtmf_timestamp + _nsamples;\n\n\t\t// Check if the tone must end\n\t\tif (_dtmf_duration / _nsamples * _audio_encoder->get_ptime() >=\n\t\t\t\t\t\t_user_config->get_dtmf_duration())\n\t\t{\n\t\t\t_dtmf_pause = true;\n\t\t}\n\t}\n\t\n\t// Encode audio samples\n\tbool silence;\n\trtp_timestamp = timestamp;\n\treturn _audio_encoder->encode(sample_buf, nsamples_real, payload, payload_size, silence);\n}\n"
  },
  {
    "path": "src/audio/dtmf_player.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Classes to generate RTP payloads for DTMF tones.\n\n#ifndef _DTMF_PLAYER_H\n#define _DTMF_PLAYER_H\n\n#include \"twinkle_config.h\"\n#include \"audio_encoder.h\"\n#include \"freq_gen.h\"\n#include \"user.h\"\n\n// Forward declarations\nclass t_audio_rx;\n\n// Abstract class defintion for DTMF player\nclass t_dtmf_player {\nprotected:\n\t// Audio receiver owning the DTMF player.\n\tt_audio_rx\t*_audio_rx;\n\t\n\tt_user\t\t*_user_config;\n\tt_audio_encoder\t*_audio_encoder;\n\n\t// Settings for current DTMF tone\n\tbool\t\t_dtmf_pause;      // indicates if playing is in the pause phase\n\tbool\t\t_dtmf_stop;\t  // indicates if DTMF should be stopped\n\tt_dtmf_ev\t_dtmf_current;    // Currently played DTMF tone\n\tuint32\t\t_dtmf_timestamp;  // RTP timestamp (start of tone)\n\tuint16\t\t_dtmf_duration;   // Duration of the tone currently played\n\tuint16\t\t_nsamples; \t  // number of samples taken per packet\n\t\npublic:\n\tt_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,\n\t\tt_user *user_config, t_dtmf_ev dtmf_tone, uint32 dtmf_timestamp,\n\t\tuint16 nsamples);\n\t\t\n\tvirtual ~t_dtmf_player() {};\n\t\n\t// Get payload for the DTMF tone\n\t// rtp_timestamp will be set with the timestamp value to be put in the\n\t// RTP header\n\t// Returns the payload size\n\tvirtual uint16 get_payload(uint8 *payload, uint16 payload_size,\n\t\tuint32 timestamp, uint32 &rtp_timestamp) = 0;\n\t\n\tuint32 get_timestamp(void);\n\t\n\t// Returns true when last payload has been delivered.\n\tbool finished(void);\n};\n\n\n// DTMF player for RFC 2833 RTP telephone events\nclass t_rtp_event_dtmf_player : public t_dtmf_player {\npublic:\n\tt_rtp_event_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,\n\t\tt_user *user_config,  t_dtmf_ev dtmf_tone, uint32 dtmf_timestamp,\n\t\tuint16 nsamples);\n\t\n\tvirtual uint16 get_payload(uint8 *payload, uint16 payload_size,\n\t\tuint32 timestamp, uint32 &rtp_timestamp);\n};\n\n\n// DTMF player for inband tones\nclass t_inband_dtmf_player : public t_dtmf_player {\nprivate:\n\t// Frequency generator to generate the inband tones.\n\tt_freq_gen\t_freq_gen;\n\t\npublic:\n\tt_inband_dtmf_player(t_audio_rx *audio_rx, t_audio_encoder *audio_encoder,\n\t\tt_user *user_config, t_dtmf_ev dtmf_tone, uint32 dtmf_timestamp,\n\t\tuint16 nsamples);\n\t\n\tvirtual uint16 get_payload(uint8 *payload, uint16 payload_size,\n\t\tuint32 timestamp, uint32 &rtp_timestamp);\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/freq_gen.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"freq_gen.h\"\n#include <iostream>\n#include <cassert>\n#include <cmath>\n#include \"rtp_telephone_event.h\"\n\n#define PI\t3.141592653589793\n\nt_freq_gen::t_freq_gen(vector<uint16> frequencies, int8 db_level) {\n\tassert(frequencies.size() > 0);\n\tassert(db_level <= 0);\n\t\n\t_frequencies = frequencies;\n\t\n\t// dB = 20 * log(amplitude / 32768)\n\t// 32767 is used below as +32768 does not fit in 16 bits\n\t_amplitude = int16(pow(10.0, db_level / 20.0) * 32767 / _frequencies.size());\n}\n\nt_freq_gen::t_freq_gen(t_dtmf_ev dtmf, int8 db_level) : _frequencies(2)\n{\n\tassert(db_level <= 0);\n\t\n\tswitch (dtmf) {\n\tcase TEL_EV_DTMF_1:\n\t\t_frequencies[0] = 697;\n\t\t_frequencies[1] = 1209;\n\t\tbreak;\n\tcase TEL_EV_DTMF_2:\n\t\t_frequencies[0] = 697;\n\t\t_frequencies[1] = 1336;\n\t\tbreak;\n\tcase TEL_EV_DTMF_3:\n\t\t_frequencies[0] = 697;\n\t\t_frequencies[1] = 1477;\n\t\tbreak;\n\tcase TEL_EV_DTMF_A:\n\t\t_frequencies[0] = 697;\n\t\t_frequencies[1] = 1633;\n\t\tbreak;\n\tcase TEL_EV_DTMF_4:\n\t\t_frequencies[0] = 770;\n\t\t_frequencies[1] = 1209;\n\t\tbreak;\n\tcase TEL_EV_DTMF_5:\n\t\t_frequencies[0] = 770;\n\t\t_frequencies[1] = 1336;\n\t\tbreak;\n\tcase TEL_EV_DTMF_6:\n\t\t_frequencies[0] = 770;\n\t\t_frequencies[1] = 1477;\n\t\tbreak;\n\tcase TEL_EV_DTMF_B:\n\t\t_frequencies[0] = 770;\n\t\t_frequencies[1] = 1633;\n\t\tbreak;\n\tcase TEL_EV_DTMF_7:\n\t\t_frequencies[0] = 852;\n\t\t_frequencies[1] = 1209;\n\t\tbreak;\n\tcase TEL_EV_DTMF_8:\n\t\t_frequencies[0] = 852;\n\t\t_frequencies[1] = 1336;\n\t\tbreak;\n\tcase TEL_EV_DTMF_9:\n\t\t_frequencies[0] = 852;\n\t\t_frequencies[1] = 1477;\n\t\tbreak;\n\tcase TEL_EV_DTMF_C:\n\t\t_frequencies[0] = 852;\n\t\t_frequencies[1] = 1633;\n\t\tbreak;\n\tcase TEL_EV_DTMF_STAR:\n\t\t_frequencies[0] = 941;\n\t\t_frequencies[1] = 1209;\n\t\tbreak;\n\tcase TEL_EV_DTMF_0:\n\t\t_frequencies[0] = 941;\n\t\t_frequencies[1] = 1336;\n\t\tbreak;\n\tcase TEL_EV_DTMF_POUND:\n\t\t_frequencies[0] = 941;\n\t\t_frequencies[1] = 1477;\n\t\tbreak;\n\tcase TEL_EV_DTMF_D:\n\t\t_frequencies[0] = 941;\n\t\t_frequencies[1] = 1633;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\t_frequencies.clear();\n\t}\n\t\t\n\t// dB = 20 * log(amplitude / 32768)\n\t// 32767 is used below as +32768 does not fit in 16 bits\n\t_amplitude = int16(pow(10.0, db_level / 20.0) * 32767 / _frequencies.size());\n}\n\nint16 t_freq_gen::get_sample(uint32 ts_usec) const {\n\tdouble freq_sum = 0.0;\n\n\tfor (vector<uint16>::const_iterator f = _frequencies.begin();\n\t     f != _frequencies.end(); f++)\n\t{\n\t\tfreq_sum += sin(*f * 2.0 * PI * (double)ts_usec / 1000000.0);\n\t}\n\t\n\treturn (int16)(_amplitude * freq_sum);\n}\n\nvoid t_freq_gen::get_samples(int16 *sample_buf, uint16 buf_len, \n\t\t\tuint32 ts_start, double interval) const\n{\n\tfor (uint16 i = 0; i < buf_len; i++) {\n\t\tsample_buf[i] = get_sample(uint32(ts_start + interval * i));\n\t}\n}\n"
  },
  {
    "path": "src/audio/freq_gen.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Frequency generator\n//\n// This file contains definitions of the frequency generator.\n// The frequency generator generates tones build from a list of\n// frequencies.\n\n#ifndef _FREQ_GEN_H\n#define _FREQ_GEN_H\n\n#include <vector>\n#include <commoncpp/config.h>\n\n#include \"rtp_telephone_event.h\"\n\nusing namespace std;\n\nclass t_freq_gen {\nprivate:\n\tvector<uint16>\t_frequencies;\n\tint16\t\t_amplitude;\n\t\npublic:\n\tt_freq_gen(vector<uint16> frequencies, int8 db_level);\n\tt_freq_gen(t_dtmf_ev dtmf, int8 db_level);\n\t\n\t// Get sound sample on a particular timestamp in us.\n\tint16 get_sample(uint32 ts_usec) const;\n\tvoid get_samples(int16 *sample_buf, uint16 buf_len, \n\t\t\tuint32 ts_start, double interval) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/g711.cpp",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n\n/*\n * g711.c\n *\n * u-law, A-law and linear PCM conversions.\n */\n\n/*\n * December 30, 1994:\n * Functions linear2alaw, linear2ulaw have been updated to correctly\n * convert unquantized 16 bit values.\n * Tables for direct u- to A-law and A- to u-law conversions have been\n * corrected.\n * Borge Lindberg, Center for PersonKommunikation, Aalborg University.\n * bli@cpk.auc.dk\n *\n */\n\n#include \"g711.h\"\n \n#define\tSIGN_BIT\t(0x80)\t\t/* Sign bit for a A-law byte. */\n#define\tQUANT_MASK\t(0xf)\t\t/* Quantization field mask. */\n#define\tNSEGS\t\t(8)\t\t/* Number of A-law segments. */\n#define\tSEG_SHIFT\t(4)\t\t/* Left shift for segment number. */\n#define\tSEG_MASK\t(0x70)\t\t/* Segment field mask. */\n\nstatic short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,\n\t\t\t    0x1FF, 0x3FF, 0x7FF, 0xFFF};\nstatic short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,\n\t\t\t    0x3FF, 0x7FF, 0xFFF, 0x1FFF};\n\n/* copy from CCITT G.711 specifications */\nunsigned char _u2a[128] = {\t\t\t/* u- to A-law conversions */\n\t1,\t1,\t2,\t2,\t3,\t3,\t4,\t4,\n\t5,\t5,\t6,\t6,\t7,\t7,\t8,\t8,\n\t9,\t10,\t11,\t12,\t13,\t14,\t15,\t16,\n\t17,\t18,\t19,\t20,\t21,\t22,\t23,\t24,\n\t25,\t27,\t29,\t31,\t33,\t34,\t35,\t36,\n\t37,\t38,\t39,\t40,\t41,\t42,\t43,\t44,\n\t46,\t48,\t49,\t50,\t51,\t52,\t53,\t54,\n\t55,\t56,\t57,\t58,\t59,\t60,\t61,\t62,\n\t64,\t65,\t66,\t67,\t68,\t69,\t70,\t71,\n\t72,\t73,\t74,\t75,\t76,\t77,\t78,\t79,\n/* corrected:\n\t81,\t82,\t83,\t84,\t85,\t86,\t87,\t88, \n   should be: */\n\t80,\t82,\t83,\t84,\t85,\t86,\t87,\t88,\n\t89,\t90,\t91,\t92,\t93,\t94,\t95,\t96,\n\t97,\t98,\t99,\t100,\t101,\t102,\t103,\t104,\n\t105,\t106,\t107,\t108,\t109,\t110,\t111,\t112,\n\t113,\t114,\t115,\t116,\t117,\t118,\t119,\t120,\n\t121,\t122,\t123,\t124,\t125,\t126,\t127,\t128};\n\nunsigned char _a2u[128] = {\t\t\t/* A- to u-law conversions */\n\t1,\t3,\t5,\t7,\t9,\t11,\t13,\t15,\n\t16,\t17,\t18,\t19,\t20,\t21,\t22,\t23,\n\t24,\t25,\t26,\t27,\t28,\t29,\t30,\t31,\n\t32,\t32,\t33,\t33,\t34,\t34,\t35,\t35,\n\t36,\t37,\t38,\t39,\t40,\t41,\t42,\t43,\n\t44,\t45,\t46,\t47,\t48,\t48,\t49,\t49,\n\t50,\t51,\t52,\t53,\t54,\t55,\t56,\t57,\n\t58,\t59,\t60,\t61,\t62,\t63,\t64,\t64,\n\t65,\t66,\t67,\t68,\t69,\t70,\t71,\t72,\n/* corrected:\n\t73,\t74,\t75,\t76,\t77,\t78,\t79,\t79,\n   should be: */\n\t73,\t74,\t75,\t76,\t77,\t78,\t79,\t80,\n\n\t80,\t81,\t82,\t83,\t84,\t85,\t86,\t87,\n\t88,\t89,\t90,\t91,\t92,\t93,\t94,\t95,\n\t96,\t97,\t98,\t99,\t100,\t101,\t102,\t103,\n\t104,\t105,\t106,\t107,\t108,\t109,\t110,\t111,\n\t112,\t113,\t114,\t115,\t116,\t117,\t118,\t119,\n\t120,\t121,\t122,\t123,\t124,\t125,\t126,\t127};\n\nstatic short\nsearch(\n\tshort\t\tval,\n\tshort\t\t*table,\n\tshort\t\tsize)\n{\n\tshort\t\ti;\n\n\tfor (i = 0; i < size; i++) {\n\t\tif (val <= *table++)\n\t\t\treturn (i);\n\t}\n\treturn (size);\n}\n\n/*\n * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law\n *\n * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.\n *\n *\t\tLinear Input Code\tCompressed Code\n *\t------------------------\t---------------\n *\t0000000wxyza\t\t\t000wxyz\n *\t0000001wxyza\t\t\t001wxyz\n *\t000001wxyzab\t\t\t010wxyz\n *\t00001wxyzabc\t\t\t011wxyz\n *\t0001wxyzabcd\t\t\t100wxyz\n *\t001wxyzabcde\t\t\t101wxyz\n *\t01wxyzabcdef\t\t\t110wxyz\n *\t1wxyzabcdefg\t\t\t111wxyz\n *\n * For further information see John C. Bellamy's Digital Telephony, 1982,\n * John Wiley & Sons, pps 98-111 and 472-476.\n */\nunsigned char\nlinear2alaw(\n\tshort\t\tpcm_val)\t/* 2's complement (16-bit range) */\n{\n\tshort\t\tmask;\n\tshort\t\tseg;\n\tunsigned char\taval;\n\n\tpcm_val = pcm_val >> 3;\n\n\tif (pcm_val >= 0) {\n\t\tmask = 0xD5;\t\t/* sign (7th) bit = 1 */\n\t} else {\n\t\tmask = 0x55;\t\t/* sign bit = 0 */\n\t\tpcm_val = -pcm_val - 1;\n\t}\n\n\t/* Convert the scaled magnitude to segment number. */\n\tseg = search(pcm_val, seg_aend, 8);\n\n\t/* Combine the sign, segment, and quantization bits. */\n\n\tif (seg >= 8)\t\t/* out of range, return maximum value. */\n\t\treturn (unsigned char) (0x7F ^ mask);\n\telse {\n\t\taval = (unsigned char) seg << SEG_SHIFT;\n\t\tif (seg < 2)\n\t\t\taval |= (pcm_val >> 1) & QUANT_MASK;\n\t\telse\n\t\t\taval |= (pcm_val >> seg) & QUANT_MASK;\n\t\treturn (aval ^ mask);\n\t}\n}\n\n/*\n * alaw2linear() - Convert an A-law value to 16-bit linear PCM\n *\n */\nshort\nalaw2linear(\n\tunsigned char\ta_val)\n{\n\tshort\t\tt;\n\tshort\t\tseg;\n\n\ta_val ^= 0x55;\n\n\tt = (a_val & QUANT_MASK) << 4;\n\tseg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;\n\tswitch (seg) {\n\tcase 0:\n\t\tt += 8;\n\t\tbreak;\n\tcase 1:\n\t\tt += 0x108;\n\t\tbreak;\n\tdefault:\n\t\tt += 0x108;\n\t\tt <<= seg - 1;\n\t}\n\treturn ((a_val & SIGN_BIT) ? t : -t);\n}\n\n#define\tBIAS\t\t(0x84)\t\t/* Bias for linear code. */\n#define CLIP            8159\n\n/*\n * linear2ulaw() - Convert a linear PCM value to u-law\n *\n * In order to simplify the encoding process, the original linear magnitude\n * is biased by adding 33 which shifts the encoding range from (0 - 8158) to\n * (33 - 8191). The result can be seen in the following encoding table:\n *\n *\tBiased Linear Input Code\tCompressed Code\n *\t------------------------\t---------------\n *\t00000001wxyza\t\t\t000wxyz\n *\t0000001wxyzab\t\t\t001wxyz\n *\t000001wxyzabc\t\t\t010wxyz\n *\t00001wxyzabcd\t\t\t011wxyz\n *\t0001wxyzabcde\t\t\t100wxyz\n *\t001wxyzabcdef\t\t\t101wxyz\n *\t01wxyzabcdefg\t\t\t110wxyz\n *\t1wxyzabcdefgh\t\t\t111wxyz\n *\n * Each biased linear code has a leading 1 which identifies the segment\n * number. The value of the segment number is equal to 7 minus the number\n * of leading 0's. The quantization interval is directly available as the\n * four bits wxyz.  * The trailing bits (a - h) are ignored.\n *\n * Ordinarily the complement of the resulting code word is used for\n * transmission, and so the code word is complemented before it is returned.\n *\n * For further information see John C. Bellamy's Digital Telephony, 1982,\n * John Wiley & Sons, pps 98-111 and 472-476.\n */\nunsigned char\nlinear2ulaw(\n\tshort\t\tpcm_val)\t/* 2's complement (16-bit range) */\n{\n\tshort\t\tmask;\n\tshort\t\tseg;\n\tunsigned char\tuval;\n\n\t/* Get the sign and the magnitude of the value. */\n\tpcm_val = pcm_val >> 2;\n\tif (pcm_val < 0) {\n\t\tpcm_val = -pcm_val;\n\t\tmask = 0x7F;\n\t} else {\n\t\tmask = 0xFF;\n\t}\n        if ( pcm_val > CLIP ) pcm_val = CLIP;\t\t/* clip the magnitude */\n\tpcm_val += (BIAS >> 2);\n\n\t/* Convert the scaled magnitude to segment number. */\n\tseg = search(pcm_val, seg_uend, 8);\n\n\t/*\n\t * Combine the sign, segment, quantization bits;\n\t * and complement the code word.\n\t */\n\tif (seg >= 8)\t\t/* out of range, return maximum value. */\n\t\treturn (unsigned char) (0x7F ^ mask);\n\telse {\n\t\tuval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);\n\t\treturn (uval ^ mask);\n\t}\n\n}\n\n/*\n * ulaw2linear() - Convert a u-law value to 16-bit linear PCM\n *\n * First, a biased linear code is derived from the code word. An unbiased\n * output can then be obtained by subtracting 33 from the biased code.\n *\n * Note that this function expects to be passed the complement of the\n * original code word. This is in keeping with ISDN conventions.\n */\nshort\nulaw2linear(\n\tunsigned char\tu_val)\n{\n\tshort\t\tt;\n\n\t/* Complement to obtain normal u-law value. */\n\tu_val = ~u_val;\n\n\t/*\n\t * Extract and bias the quantization bits. Then\n\t * shift up by the segment number and subtract out the bias.\n\t */\n\tt = ((u_val & QUANT_MASK) << 3) + BIAS;\n\tt <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;\n\n\treturn ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));\n}\n\n/* A-law to u-law conversion */\nunsigned char\nalaw2ulaw(\n\tunsigned char\taval)\n{\n\taval &= 0xff;\n\treturn (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :\n\t    (0x7F ^ _a2u[aval ^ 0x55]));\n}\n\n/* u-law to A-law conversion */\nunsigned char\nulaw2alaw(\n\tunsigned char\tuval)\n{\n\tuval &= 0xff;\n\treturn (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :\n\t    (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));\n}\n"
  },
  {
    "path": "src/audio/g711.h",
    "content": "#ifndef _G711_H\n#define _G711_H\n\n// The linear PCM codes are signed 16 bit values\n\n// G.711 A-law\nunsigned char linear2alaw(short pcm_val);\nshort alaw2linear(unsigned char a_val);\n\n// G.711 u-law\nunsigned char linear2ulaw(short pcm_val);\nshort ulaw2linear(unsigned char u_val);\n\n// A-law <-> u-law conversions\nunsigned char alaw2ulaw(unsigned char aval);\nunsigned char ulaw2alaw(unsigned char uval);\n\n#endif\n"
  },
  {
    "path": "src/audio/g721.cpp",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n\n/*\n * g721.c\n *\n * Description:\n *\n * g721_encoder(), g721_decoder()\n *\n * These routines comprise an implementation of the CCITT G.721 ADPCM\n * coding algorithm.  Essentially, this implementation is identical to\n * the bit level description except for a few deviations which\n * take advantage of work station attributes, such as hardware 2's\n * complement arithmetic and large memory.  Specifically, certain time\n * consuming operations such as multiplications are replaced\n * with lookup tables and software 2's complement operations are\n * replaced with hardware 2's complement.\n *\n * The deviation from the bit level specification (lookup tables)\n * preserves the bit level performance specifications.\n *\n * As outlined in the G.721 Recommendation, the algorithm is broken\n * down into modules.  Each section of code below is preceded by\n * the name of the module which it is implementing.\n *\n */\n#include \"g72x.h\"\n#include \"g711.h\"\n\nstatic short qtab_721[7] = {-124, 80, 178, 246, 300, 349, 400};\n/*\n * Maps G.721 code word to reconstructed scale factor normalized log\n * magnitude values.\n */\nstatic short\t_dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425,\n\t\t\t\t425, 373, 323, 273, 213, 135, 4, -2048};\n\n/* Maps G.721 code word to log of scale factor multiplier. */\nstatic short\t_witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122,\n\t\t\t\t1122, 355, 198, 112, 64, 41, 18, -12};\n/*\n * Maps G.721 code words to a set of values whose long and short\n * term averages are computed and then compared to give an indication\n * how stationary (steady state) the signal is.\n */\nstatic short\t_fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,\n\t\t\t\t0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0};\n\n/*\n * g721_encoder()\n *\n * Encodes the input vale of linear PCM, A-law or u-law data sl and returns\n * the resulting code. -1 is returned for unknown input coding value.\n */\nint\ng721_encoder(\n\tint\t\tsl,\n\tint\t\tin_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsezi, se, sez;\t\t/* ACCUM */\n\tshort\t\td;\t\t\t/* SUBTA */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tdqsez;\t\t\t/* ADDC */\n\tshort\t\tdq, i;\n\n\tswitch (in_coding) {\t/* linearize input sample to 14-bit PCM */\n\tcase AUDIO_ENCODING_ALAW:\n\t\tsl = alaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_ULAW:\n\t\tsl = ulaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_LINEAR:\n\t\tsl >>= 2;\t\t\t/* 14-bit dynamic range */\n\t\tbreak;\n\tdefault:\n\t\treturn (-1);\n\t}\n\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tse = (sezi + predictor_pole(state_ptr)) >> 1;\t/* estimated signal */\n\n\td = sl - se;\t\t\t\t/* estimation difference */\n\n\t/* quantize the prediction difference */\n\ty = step_size(state_ptr);\t\t/* quantizer step size */\n\ti = quantize(d, y, qtab_721, 7);\t/* i = ADPCM code */\n\n\tdq = reconstruct(i & 8, _dqlntab[i], y);\t/* quantized est diff */\n\n\tsr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq;\t/* reconst. signal */\n\n\tdqsez = sr + sez - se;\t\t\t/* pole prediction diff. */\n\n\tupdate(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);\n\n\treturn (i);\n}\n\n/*\n * g721_decoder()\n *\n * Description:\n *\n * Decodes a 4-bit code of G.721 encoded data of i and\n * returns the resulting linear PCM, A-law or u-law value.\n * return -1 for unknown out_coding value.\n */\nint\ng721_decoder(\n\tint\t\ti,\n\tint\t\tout_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsezi, sei, sez, se;\t/* ACCUM */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdq;\n\tshort\t\tdqsez;\n\n\ti &= 0x0f;\t\t\t/* mask to get proper bits */\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\ty = step_size(state_ptr);\t/* dynamic quantizer step size */\n\n\tdq = reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */\n\n\tsr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq;\t/* reconst. signal */\n\n\tdqsez = sr - se + sez;\t\t\t/* pole prediction diff. */\n\n\tupdate(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);\n\n\tswitch (out_coding) {\n\tcase AUDIO_ENCODING_ALAW:\n\t\treturn (tandem_adjust_alaw(sr, se, y, i, 8, qtab_721));\n\tcase AUDIO_ENCODING_ULAW:\n\t\treturn (tandem_adjust_ulaw(sr, se, y, i, 8, qtab_721));\n\tcase AUDIO_ENCODING_LINEAR:\n\t\treturn (sr << 2);\t/* sr was 14-bit dynamic range */\n\tdefault:\n\t\treturn (-1);\n\t}\n}\n"
  },
  {
    "path": "src/audio/g722.h",
    "content": "/*\n * SpanDSP - a series of DSP components for telephony\n *\n * g722.h - The ITU G.722 codec.\n *\n * Written by Steve Underwood <steveu@coppice.org>\n *\n * Copyright (C) 2005 Steve Underwood\n *\n *  Despite my general liking of the GPL, I place my own contributions\n *  to this code in the public domain for the benefit of all mankind -\n *  even the slimy ones who might try to proprietize my work and use it\n *  to my detriment.\n *\n * Based on a single channel G.722 codec which is:\n *\n *****    Copyright (c) CMU    1993      *****\n * Computer Science, Speech Group\n * Chengxiang Lu and Alex Hauptmann\n *\n * $Id$\n */\n\n\n/*! \\file */\n\n#if !defined(_G722_H_)\n#define _G722_H_\n\n/*! \\page g722_page G.722 encoding and decoding\n\\section g722_page_sec_1 What does it do?\nThe G.722 module is a bit exact implementation of the ITU G.722 specification for all three\nspecified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests.\n\nTo allow fast and flexible interworking with narrow band telephony, the encoder and decoder\nsupport an option for the linear audio to be an 8k samples/second stream. In this mode the\ncodec is considerably faster, and still fully compatible with wideband terminals using G.722.\n\n\\section g722_page_sec_2 How does it work?\n???.\n*/\n\nenum\n{\n    G722_SAMPLE_RATE_8000 = 0x0001,\n    G722_PACKED = 0x0002\n};\n\n#ifndef INT16_MAX\n#define INT16_MAX       32767\n#endif\n#ifndef INT16_MIN\n#define INT16_MIN       (-32768)\n#endif\n\ntypedef struct\n{\n    /*! TRUE if the operating in the special ITU test mode, with the band split filters\n             disabled. */\n    int itu_test_mode;\n    /*! TRUE if the G.722 data is packed */\n    int packed;\n    /*! TRUE if encode from 8k samples/second */\n    int eight_k;\n    /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */\n    int bits_per_sample;\n\n    /*! Signal history for the QMF */\n    int x[24];\n\n    struct\n    {\n        int s;\n        int sp;\n        int sz;\n        int r[3];\n        int a[3];\n        int ap[3];\n        int p[3];\n        int d[7];\n        int b[7];\n        int bp[7];\n        int sg[7];\n        int nb;\n        int det;\n    } band[2];\n\n    unsigned int in_buffer;\n    int in_bits;\n    unsigned int out_buffer;\n    int out_bits;\n} g722_encode_state_t;\n\ntypedef struct\n{\n    /*! TRUE if the operating in the special ITU test mode, with the band split filters\n             disabled. */\n    int itu_test_mode;\n    /*! TRUE if the G.722 data is packed */\n    int packed;\n    /*! TRUE if decode to 8k samples/second */\n    int eight_k;\n    /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */\n    int bits_per_sample;\n\n    /*! Signal history for the QMF */\n    int x[24];\n\n    struct\n    {\n        int s;\n        int sp;\n        int sz;\n        int r[3];\n        int a[3];\n        int ap[3];\n        int p[3];\n        int d[7];\n        int b[7];\n        int bp[7];\n        int sg[7];\n        int nb;\n        int det;\n    } band[2];\n\n    unsigned int in_buffer;\n    int in_bits;\n    unsigned int out_buffer;\n    int out_bits;\n} g722_decode_state_t;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ng722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options);\nint g722_encode_release(g722_encode_state_t *s);\nint g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len);\n\ng722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options);\nint g722_decode_release(g722_decode_state_t *s);\nint g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/audio/g722_decode.c",
    "content": "/*\n * SpanDSP - a series of DSP components for telephony\n *\n * g722_decode.c - The ITU G.722 codec, decode part.\n *\n * Written by Steve Underwood <steveu@coppice.org>\n *\n * Copyright (C) 2005 Steve Underwood\n *\n *  Despite my general liking of the GPL, I place my own contributions\n *  to this code in the public domain for the benefit of all mankind -\n *  even the slimy ones who might try to proprietize my work and use it\n *  to my detriment.\n *\n * Based in part on a single channel G.722 codec which is:\n *\n * Copyright (c) CMU 1993\n * Computer Science, Speech Group\n * Chengxiang Lu and Alex Hauptmann\n *\n * $Id$\n */\n\n/*! \\file */\n\n#ifdef HAVE_CONFIG_H\n#include <config.h>\n#endif\n\n#include <stdio.h>\n#include <inttypes.h>\n#include <memory.h>\n#include <stdlib.h>\n#if 0\n#include <tgmath.h>\n#endif\n\n#include \"g722.h\"\n\n#if !defined(FALSE)\n#define FALSE 0\n#endif\n#if !defined(TRUE)\n#define TRUE (!FALSE)\n#endif\n\nstatic __inline__ int16_t saturate(int32_t amp)\n{\n    int16_t amp16;\n\n    /* Hopefully this is optimised for the common case - not clipping */\n    amp16 = (int16_t) amp;\n    if (amp == amp16)\n        return amp16;\n    if (amp > INT16_MAX)\n        return  INT16_MAX;\n    return  INT16_MIN;\n}\n/*- End of function --------------------------------------------------------*/\n\nstatic void block4(g722_decode_state_t *s, int band, int d);\n\nstatic void block4(g722_decode_state_t *s, int band, int d)\n{\n    int wd1;\n    int wd2;\n    int wd3;\n    int i;\n\n    /* Block 4, RECONS */\n    s->band[band].d[0] = d;\n    s->band[band].r[0] = saturate(s->band[band].s + d);\n\n    /* Block 4, PARREC */\n    s->band[band].p[0] = saturate(s->band[band].sz + d);\n\n    /* Block 4, UPPOL2 */\n    for (i = 0;  i < 3;  i++)\n        s->band[band].sg[i] = s->band[band].p[i] >> 15;\n    wd1 = saturate(s->band[band].a[1] << 2);\n\n    wd2 = (s->band[band].sg[0] == s->band[band].sg[1])  ?  -wd1  :  wd1;\n    if (wd2 > 32767)\n        wd2 = 32767;\n    wd3 = (s->band[band].sg[0] == s->band[band].sg[2])  ?  128  :  -128;\n    wd3 += (wd2 >> 7);\n    wd3 += (s->band[band].a[2]*32512) >> 15;\n    if (wd3 > 12288)\n        wd3 = 12288;\n    else if (wd3 < -12288)\n        wd3 = -12288;\n    s->band[band].ap[2] = wd3;\n\n    /* Block 4, UPPOL1 */\n    s->band[band].sg[0] = s->band[band].p[0] >> 15;\n    s->band[band].sg[1] = s->band[band].p[1] >> 15;\n    wd1 = (s->band[band].sg[0] == s->band[band].sg[1])  ?  192  :  -192;\n    wd2 = (s->band[band].a[1]*32640) >> 15;\n\n    s->band[band].ap[1] = saturate(wd1 + wd2);\n    wd3 = saturate(15360 - s->band[band].ap[2]);\n    if (s->band[band].ap[1] > wd3)\n        s->band[band].ap[1] = wd3;\n    else if (s->band[band].ap[1] < -wd3)\n        s->band[band].ap[1] = -wd3;\n\n    /* Block 4, UPZERO */\n    wd1 = (d == 0)  ?  0  :  128;\n    s->band[band].sg[0] = d >> 15;\n    for (i = 1;  i < 7;  i++)\n    {\n        s->band[band].sg[i] = s->band[band].d[i] >> 15;\n        wd2 = (s->band[band].sg[i] == s->band[band].sg[0])  ?  wd1  :  -wd1;\n        wd3 = (s->band[band].b[i]*32640) >> 15;\n        s->band[band].bp[i] = saturate(wd2 + wd3);\n    }\n\n    /* Block 4, DELAYA */\n    for (i = 6;  i > 0;  i--)\n    {\n        s->band[band].d[i] = s->band[band].d[i - 1];\n        s->band[band].b[i] = s->band[band].bp[i];\n    }\n\n    for (i = 2;  i > 0;  i--)\n    {\n        s->band[band].r[i] = s->band[band].r[i - 1];\n        s->band[band].p[i] = s->band[band].p[i - 1];\n        s->band[band].a[i] = s->band[band].ap[i];\n    }\n\n    /* Block 4, FILTEP */\n    wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]);\n    wd1 = (s->band[band].a[1]*wd1) >> 15;\n    wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]);\n    wd2 = (s->band[band].a[2]*wd2) >> 15;\n    s->band[band].sp = saturate(wd1 + wd2);\n\n    /* Block 4, FILTEZ */\n    s->band[band].sz = 0;\n    for (i = 6;  i > 0;  i--)\n    {\n        wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]);\n        s->band[band].sz += (s->band[band].b[i]*wd1) >> 15;\n    }\n    s->band[band].sz = saturate(s->band[band].sz);\n\n    /* Block 4, PREDIC */\n    s->band[band].s = saturate(s->band[band].sp + s->band[band].sz);\n}\n/*- End of function --------------------------------------------------------*/\n\ng722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options)\n{\n    if (s == NULL)\n    {\n        if ((s = (g722_decode_state_t *) malloc(sizeof(*s))) == NULL)\n            return NULL;\n    }\n    memset(s, 0, sizeof(*s));\n    if (rate == 48000)\n        s->bits_per_sample = 6;\n    else if (rate == 56000)\n        s->bits_per_sample = 7;\n    else\n        s->bits_per_sample = 8;\n    if ((options & G722_SAMPLE_RATE_8000))\n        s->eight_k = TRUE;\n    if ((options & G722_PACKED)  &&  s->bits_per_sample != 8)\n        s->packed = TRUE;\n    else\n        s->packed = FALSE;\n    s->band[0].det = 32;\n    s->band[1].det = 8;\n    return s;\n}\n/*- End of function --------------------------------------------------------*/\n\nint g722_decode_release(g722_decode_state_t *s)\n{\n    free(s);\n    return 0;\n}\n/*- End of function --------------------------------------------------------*/\n\nint g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len)\n{\n    static const int wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042 };\n    static const int rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3,  2, 1, 0 };\n    static const int ilb[32] =\n    {\n        2048, 2093, 2139, 2186, 2233, 2282, 2332,\n        2383, 2435, 2489, 2543, 2599, 2656, 2714,\n        2774, 2834, 2896, 2960, 3025, 3091, 3158,\n        3228, 3298, 3371, 3444, 3520, 3597, 3676,\n        3756, 3838, 3922, 4008\n    };\n    static const int wh[3] = {0, -214, 798};\n    static const int rh2[4] = {2, 1, 2, 1};\n    static const int qm2[4] = {-7408, -1616,  7408,   1616};\n    static const int qm4[16] =\n    {\n              0, -20456, -12896,  -8968,\n          -6288,  -4240,  -2584,  -1200,\n          20456,  12896,   8968,   6288,\n           4240,   2584,   1200,      0\n    };\n    static const int qm5[32] =\n    {\n           -280,   -280, -23352, -17560,\n         -14120, -11664,  -9752,  -8184,\n          -6864,  -5712,  -4696,  -3784,\n          -2960,  -2208,  -1520,   -880,\n          23352,  17560,  14120,  11664,\n           9752,   8184,   6864,   5712,\n           4696,   3784,   2960,   2208,\n           1520,    880,    280,   -280\n    };\n    static const int qm6[64] =\n    {\n           -136,   -136,   -136,   -136,\n         -24808, -21904, -19008, -16704,\n         -14984, -13512, -12280, -11192,\n         -10232,  -9360,  -8576,  -7856,\n          -7192,  -6576,  -6000,  -5456,\n          -4944,  -4464,  -4008,  -3576,\n          -3168,  -2776,  -2400,  -2032,\n          -1688,  -1360,  -1040,   -728,\n          24808,  21904,  19008,  16704,\n          14984,  13512,  12280,  11192,\n          10232,   9360,   8576,   7856,\n           7192,   6576,   6000,   5456,\n           4944,   4464,   4008,   3576,\n           3168,   2776,   2400,   2032,\n           1688,   1360,   1040,    728,\n            432,    136,   -432,   -136\n    };\n    static const int qmf_coeffs[12] =\n    {\n           3,  -11,   12,   32, -210,  951, 3876, -805,  362, -156,   53,  -11,\n    };\n\n    int dlowt;\n    int rlow;\n    int ihigh;\n    int dhigh;\n    int rhigh;\n    int xout1;\n    int xout2;\n    int wd1;\n    int wd2;\n    int wd3;\n    int code;\n    int outlen;\n    int i;\n    int j;\n\n    outlen = 0;\n    rhigh = 0;\n    for (j = 0;  j < len;  )\n    {\n        if (s->packed)\n        {\n            /* Unpack the code bits */\n            if (s->in_bits < s->bits_per_sample)\n            {\n                s->in_buffer |= (g722_data[j++] << s->in_bits);\n                s->in_bits += 8;\n            }\n            code = s->in_buffer & ((1 << s->bits_per_sample) - 1);\n            s->in_buffer >>= s->bits_per_sample;\n            s->in_bits -= s->bits_per_sample;\n        }\n        else\n        {\n            code = g722_data[j++];\n        }\n\n        switch (s->bits_per_sample)\n        {\n        default:\n        case 8:\n            wd1 = code & 0x3F;\n            ihigh = (code >> 6) & 0x03;\n            wd2 = qm6[wd1];\n            wd1 >>= 2;\n            break;\n        case 7:\n            wd1 = code & 0x1F;\n            ihigh = (code >> 5) & 0x03;\n            wd2 = qm5[wd1];\n            wd1 >>= 1;\n            break;\n        case 6:\n            wd1 = code & 0x0F;\n            ihigh = (code >> 4) & 0x03;\n            wd2 = qm4[wd1];\n            break;\n        }\n        /* Block 5L, LOW BAND INVQBL */\n        wd2 = (s->band[0].det*wd2) >> 15;\n        /* Block 5L, RECONS */\n        rlow = s->band[0].s + wd2;\n        /* Block 6L, LIMIT */\n        if (rlow > 16383)\n            rlow = 16383;\n        else if (rlow < -16384)\n            rlow = -16384;\n\n        /* Block 2L, INVQAL */\n        wd2 = qm4[wd1];\n        dlowt = (s->band[0].det*wd2) >> 15;\n\n        /* Block 3L, LOGSCL */\n        wd2 = rl42[wd1];\n        wd1 = (s->band[0].nb*127) >> 7;\n        wd1 += wl[wd2];\n        if (wd1 < 0)\n            wd1 = 0;\n        else if (wd1 > 18432)\n            wd1 = 18432;\n        s->band[0].nb = wd1;\n\n        /* Block 3L, SCALEL */\n        wd1 = (s->band[0].nb >> 6) & 31;\n        wd2 = 8 - (s->band[0].nb >> 11);\n        wd3 = (wd2 < 0)  ?  (ilb[wd1] << -wd2)  :  (ilb[wd1] >> wd2);\n        s->band[0].det = wd3 << 2;\n\n        block4(s, 0, dlowt);\n\n        if (!s->eight_k)\n        {\n            /* Block 2H, INVQAH */\n            wd2 = qm2[ihigh];\n            dhigh = (s->band[1].det*wd2) >> 15;\n            /* Block 5H, RECONS */\n            rhigh = dhigh + s->band[1].s;\n            /* Block 6H, LIMIT */\n            if (rhigh > 16383)\n                rhigh = 16383;\n            else if (rhigh < -16384)\n                rhigh = -16384;\n\n            /* Block 2H, INVQAH */\n            wd2 = rh2[ihigh];\n            wd1 = (s->band[1].nb*127) >> 7;\n            wd1 += wh[wd2];\n            if (wd1 < 0)\n                wd1 = 0;\n            else if (wd1 > 22528)\n                wd1 = 22528;\n            s->band[1].nb = wd1;\n\n            /* Block 3H, SCALEH */\n            wd1 = (s->band[1].nb >> 6) & 31;\n            wd2 = 10 - (s->band[1].nb >> 11);\n            wd3 = (wd2 < 0)  ?  (ilb[wd1] << -wd2)  :  (ilb[wd1] >> wd2);\n            s->band[1].det = wd3 << 2;\n\n            block4(s, 1, dhigh);\n        }\n\n        if (s->itu_test_mode)\n        {\n            amp[outlen++] = (int16_t) (rlow << 1);\n            amp[outlen++] = (int16_t) (rhigh << 1);\n        }\n        else\n        {\n            if (s->eight_k)\n            {\n                amp[outlen++] = (int16_t) (rlow << 1);\n            }\n            else\n            {\n                /* Apply the receive QMF */\n                for (i = 0;  i < 22;  i++)\n                    s->x[i] = s->x[i + 2];\n                s->x[22] = rlow + rhigh;\n                s->x[23] = rlow - rhigh;\n\n                xout1 = 0;\n                xout2 = 0;\n                for (i = 0;  i < 12;  i++)\n                {\n                    xout2 += s->x[2*i]*qmf_coeffs[i];\n                    xout1 += s->x[2*i + 1]*qmf_coeffs[11 - i];\n                }\n                amp[outlen++] = (int16_t) (xout1 >> 11);\n                amp[outlen++] = (int16_t) (xout2 >> 11);\n            }\n        }\n    }\n    return outlen;\n}\n/*- End of function --------------------------------------------------------*/\n/*- End of file ------------------------------------------------------------*/\n"
  },
  {
    "path": "src/audio/g722_encode.c",
    "content": "/*\n * SpanDSP - a series of DSP components for telephony\n *\n * g722_encode.c - The ITU G.722 codec, encode part.\n *\n * Written by Steve Underwood <steveu@coppice.org>\n *\n * Copyright (C) 2005 Steve Underwood\n *\n * All rights reserved.\n *\n *  Despite my general liking of the GPL, I place my own contributions\n *  to this code in the public domain for the benefit of all mankind -\n *  even the slimy ones who might try to proprietize my work and use it\n *  to my detriment.\n *\n * Based on a single channel 64kbps only G.722 codec which is:\n *\n *****    Copyright (c) CMU    1993      *****\n * Computer Science, Speech Group\n * Chengxiang Lu and Alex Hauptmann\n *\n * $Id$\n */\n\n/*! \\file */\n\n#ifdef HAVE_CONFIG_H\n#include <config.h>\n#endif\n\n#include <stdio.h>\n#include <inttypes.h>\n#include <memory.h>\n#include <stdlib.h>\n#if 0\n#include <tgmath.h>\n#endif\n\n#include \"g722.h\"\n\n#if !defined(FALSE)\n#define FALSE 0\n#endif\n#if !defined(TRUE)\n#define TRUE (!FALSE)\n#endif\n\nstatic __inline__ int16_t saturate(int32_t amp)\n{\n    int16_t amp16;\n\n    /* Hopefully this is optimised for the common case - not clipping */\n    amp16 = (int16_t) amp;\n    if (amp == amp16)\n        return amp16;\n    if (amp > INT16_MAX)\n        return  INT16_MAX;\n    return  INT16_MIN;\n}\n/*- End of function --------------------------------------------------------*/\n\nstatic void block4(g722_encode_state_t *s, int band, int d)\n{\n    int wd1;\n    int wd2;\n    int wd3;\n    int i;\n\n    /* Block 4, RECONS */\n    s->band[band].d[0] = d;\n    s->band[band].r[0] = saturate(s->band[band].s + d);\n\n    /* Block 4, PARREC */\n    s->band[band].p[0] = saturate(s->band[band].sz + d);\n\n    /* Block 4, UPPOL2 */\n    for (i = 0;  i < 3;  i++)\n        s->band[band].sg[i] = s->band[band].p[i] >> 15;\n    wd1 = saturate(s->band[band].a[1] << 2);\n\n    wd2 = (s->band[band].sg[0] == s->band[band].sg[1])  ?  -wd1  :  wd1;\n    if (wd2 > 32767)\n        wd2 = 32767;\n    wd3 = (wd2 >> 7) + ((s->band[band].sg[0] == s->band[band].sg[2])  ?  128  :  -128);\n    wd3 += (s->band[band].a[2]*32512) >> 15;\n    if (wd3 > 12288)\n        wd3 = 12288;\n    else if (wd3 < -12288)\n        wd3 = -12288;\n    s->band[band].ap[2] = wd3;\n\n    /* Block 4, UPPOL1 */\n    s->band[band].sg[0] = s->band[band].p[0] >> 15;\n    s->band[band].sg[1] = s->band[band].p[1] >> 15;\n    wd1 = (s->band[band].sg[0] == s->band[band].sg[1])  ?  192  :  -192;\n    wd2 = (s->band[band].a[1]*32640) >> 15;\n\n    s->band[band].ap[1] = saturate(wd1 + wd2);\n    wd3 = saturate(15360 - s->band[band].ap[2]);\n    if (s->band[band].ap[1] > wd3)\n        s->band[band].ap[1] = wd3;\n    else if (s->band[band].ap[1] < -wd3)\n        s->band[band].ap[1] = -wd3;\n\n    /* Block 4, UPZERO */\n    wd1 = (d == 0)  ?  0  :  128;\n    s->band[band].sg[0] = d >> 15;\n    for (i = 1;  i < 7;  i++)\n    {\n        s->band[band].sg[i] = s->band[band].d[i] >> 15;\n        wd2 = (s->band[band].sg[i] == s->band[band].sg[0])  ?  wd1  :  -wd1;\n        wd3 = (s->band[band].b[i]*32640) >> 15;\n        s->band[band].bp[i] = saturate(wd2 + wd3);\n    }\n\n    /* Block 4, DELAYA */\n    for (i = 6;  i > 0;  i--)\n    {\n        s->band[band].d[i] = s->band[band].d[i - 1];\n        s->band[band].b[i] = s->band[band].bp[i];\n    }\n\n    for (i = 2;  i > 0;  i--)\n    {\n        s->band[band].r[i] = s->band[band].r[i - 1];\n        s->band[band].p[i] = s->band[band].p[i - 1];\n        s->band[band].a[i] = s->band[band].ap[i];\n    }\n\n    /* Block 4, FILTEP */\n    wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]);\n    wd1 = (s->band[band].a[1]*wd1) >> 15;\n    wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]);\n    wd2 = (s->band[band].a[2]*wd2) >> 15;\n    s->band[band].sp = saturate(wd1 + wd2);\n\n    /* Block 4, FILTEZ */\n    s->band[band].sz = 0;\n    for (i = 6;  i > 0;  i--)\n    {\n        wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]);\n        s->band[band].sz += (s->band[band].b[i]*wd1) >> 15;\n    }\n    s->band[band].sz = saturate(s->band[band].sz);\n\n    /* Block 4, PREDIC */\n    s->band[band].s = saturate(s->band[band].sp + s->band[band].sz);\n}\n/*- End of function --------------------------------------------------------*/\n\ng722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options)\n{\n    if (s == NULL)\n    {\n        if ((s = (g722_encode_state_t *) malloc(sizeof(*s))) == NULL)\n            return NULL;\n    }\n    memset(s, 0, sizeof(*s));\n    if (rate == 48000)\n        s->bits_per_sample = 6;\n    else if (rate == 56000)\n        s->bits_per_sample = 7;\n    else\n        s->bits_per_sample = 8;\n    if ((options & G722_SAMPLE_RATE_8000))\n        s->eight_k = TRUE;\n    if ((options & G722_PACKED)  &&  s->bits_per_sample != 8)\n        s->packed = TRUE;\n    else\n        s->packed = FALSE;\n    s->band[0].det = 32;\n    s->band[1].det = 8;\n    return s;\n}\n/*- End of function --------------------------------------------------------*/\n\nint g722_encode_release(g722_encode_state_t *s)\n{\n    free(s);\n    return 0;\n}\n/*- End of function --------------------------------------------------------*/\n\nint g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len)\n{\n    static const int q6[32] =\n    {\n           0,   35,   72,  110,  150,  190,  233,  276,\n         323,  370,  422,  473,  530,  587,  650,  714,\n         786,  858,  940, 1023, 1121, 1219, 1339, 1458,\n        1612, 1765, 1980, 2195, 2557, 2919,    0,    0\n    };\n    static const int iln[32] =\n    {\n         0, 63, 62, 31, 30, 29, 28, 27,\n        26, 25, 24, 23, 22, 21, 20, 19,\n        18, 17, 16, 15, 14, 13, 12, 11,\n        10,  9,  8,  7,  6,  5,  4,  0\n    };\n    static const int ilp[32] =\n    {\n         0, 61, 60, 59, 58, 57, 56, 55,\n        54, 53, 52, 51, 50, 49, 48, 47,\n        46, 45, 44, 43, 42, 41, 40, 39,\n        38, 37, 36, 35, 34, 33, 32,  0\n    };\n    static const int wl[8] =\n    {\n        -60, -30, 58, 172, 334, 538, 1198, 3042\n    };\n    static const int rl42[16] =\n    {\n        0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0\n    };\n    static const int ilb[32] =\n    {\n        2048, 2093, 2139, 2186, 2233, 2282, 2332,\n        2383, 2435, 2489, 2543, 2599, 2656, 2714,\n        2774, 2834, 2896, 2960, 3025, 3091, 3158,\n        3228, 3298, 3371, 3444, 3520, 3597, 3676,\n        3756, 3838, 3922, 4008\n    };\n    static const int qm4[16] =\n    {\n             0, -20456, -12896, -8968,\n         -6288,  -4240,  -2584, -1200,\n         20456,  12896,   8968,  6288,\n          4240,   2584,   1200,     0\n    };\n    static const int qm2[4] =\n    {\n        -7408,  -1616,   7408,   1616\n    };\n    static const int qmf_coeffs[12] =\n    {\n           3,  -11,   12,   32, -210,  951, 3876, -805,  362, -156,   53,  -11,\n    };\n    static const int ihn[3] = {0, 1, 0};\n    static const int ihp[3] = {0, 3, 2};\n    static const int wh[3] = {0, -214, 798};\n    static const int rh2[4] = {2, 1, 2, 1};\n\n    int dlow;\n    int dhigh;\n    int el;\n    int wd;\n    int wd1;\n    int ril;\n    int wd2;\n    int il4;\n    int ih2;\n    int wd3;\n    int eh;\n    int mih;\n    int i;\n    int j;\n    /* Low and high band PCM from the QMF */\n    int xlow;\n    int xhigh;\n    int g722_bytes;\n    /* Even and odd tap accumulators */\n    int sumeven;\n    int sumodd;\n    int ihigh;\n    int ilow;\n    int code;\n\n    g722_bytes = 0;\n    xhigh = 0;\n    for (j = 0;  j < len;  )\n    {\n        if (s->itu_test_mode)\n        {\n            xlow =\n            xhigh = amp[j++] >> 1;\n        }\n        else\n        {\n            if (s->eight_k)\n            {\n                xlow = amp[j++] >> 1;\n            }\n            else\n            {\n                /* Apply the transmit QMF */\n                /* Shuffle the buffer down */\n                for (i = 0;  i < 22;  i++)\n                    s->x[i] = s->x[i + 2];\n                s->x[22] = amp[j++];\n                s->x[23] = amp[j++];\n\n                /* Discard every other QMF output */\n                sumeven = 0;\n                sumodd = 0;\n                for (i = 0;  i < 12;  i++)\n                {\n                    sumodd += s->x[2*i]*qmf_coeffs[i];\n                    sumeven += s->x[2*i + 1]*qmf_coeffs[11 - i];\n                }\n                xlow = (sumeven + sumodd) >> 14;\n                xhigh = (sumeven - sumodd) >> 14;\n            }\n        }\n        /* Block 1L, SUBTRA */\n        el = saturate(xlow - s->band[0].s);\n\n        /* Block 1L, QUANTL */\n        wd = (el >= 0)  ?  el  :  -(el + 1);\n\n        for (i = 1;  i < 30;  i++)\n        {\n            wd1 = (q6[i]*s->band[0].det) >> 12;\n            if (wd < wd1)\n                break;\n        }\n        ilow = (el < 0)  ?  iln[i]  :  ilp[i];\n\n        /* Block 2L, INVQAL */\n        ril = ilow >> 2;\n        wd2 = qm4[ril];\n        dlow = (s->band[0].det*wd2) >> 15;\n\n        /* Block 3L, LOGSCL */\n        il4 = rl42[ril];\n        wd = (s->band[0].nb*127) >> 7;\n        s->band[0].nb = wd + wl[il4];\n        if (s->band[0].nb < 0)\n            s->band[0].nb = 0;\n        else if (s->band[0].nb > 18432)\n            s->band[0].nb = 18432;\n\n        /* Block 3L, SCALEL */\n        wd1 = (s->band[0].nb >> 6) & 31;\n        wd2 = 8 - (s->band[0].nb >> 11);\n        wd3 = (wd2 < 0)  ?  (ilb[wd1] << -wd2)  :  (ilb[wd1] >> wd2);\n        s->band[0].det = wd3 << 2;\n\n        block4(s, 0, dlow);\n\n        if (s->eight_k)\n        {\n            /* Just leave the high bits as zero */\n            code = (0xC0 | ilow) >> (8 - s->bits_per_sample);\n        }\n        else\n        {\n            /* Block 1H, SUBTRA */\n            eh = saturate(xhigh - s->band[1].s);\n\n            /* Block 1H, QUANTH */\n            wd = (eh >= 0)  ?  eh  :  -(eh + 1);\n            wd1 = (564*s->band[1].det) >> 12;\n            mih = (wd >= wd1)  ?  2  :  1;\n            ihigh = (eh < 0)  ?  ihn[mih]  :  ihp[mih];\n\n            /* Block 2H, INVQAH */\n            wd2 = qm2[ihigh];\n            dhigh = (s->band[1].det*wd2) >> 15;\n\n            /* Block 3H, LOGSCH */\n            ih2 = rh2[ihigh];\n            wd = (s->band[1].nb*127) >> 7;\n            s->band[1].nb = wd + wh[ih2];\n            if (s->band[1].nb < 0)\n                s->band[1].nb = 0;\n            else if (s->band[1].nb > 22528)\n                s->band[1].nb = 22528;\n\n            /* Block 3H, SCALEH */\n            wd1 = (s->band[1].nb >> 6) & 31;\n            wd2 = 10 - (s->band[1].nb >> 11);\n            wd3 = (wd2 < 0)  ?  (ilb[wd1] << -wd2)  :  (ilb[wd1] >> wd2);\n            s->band[1].det = wd3 << 2;\n\n            block4(s, 1, dhigh);\n            code = ((ihigh << 6) | ilow) >> (8 - s->bits_per_sample);\n        }\n\n        if (s->packed)\n        {\n            /* Pack the code bits */\n            s->out_buffer |= (code << s->out_bits);\n            s->out_bits += s->bits_per_sample;\n            if (s->out_bits >= 8)\n            {\n                g722_data[g722_bytes++] = (uint8_t) (s->out_buffer & 0xFF);\n                s->out_bits -= 8;\n                s->out_buffer >>= 8;\n            }\n        }\n        else\n        {\n            g722_data[g722_bytes++] = (uint8_t) code;\n        }\n    }\n    return g722_bytes;\n}\n/*- End of function --------------------------------------------------------*/\n/*- End of file ------------------------------------------------------------*/\n"
  },
  {
    "path": "src/audio/g722_local.h",
    "content": "/*\n    Copyright (C) 2019  Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Our own additions to g722.h, kept separate so that the latter can easily\n// be updated by copying it as-is from Asterisk\n\n#ifndef _G722_LOCAL_H\n#define _G722_LOCAL_H\n\n// A sampling rate of 16 kHz and a bit rate of 64 kbit/s result in a ratio of\n// 2 samples per payload byte\n#define G722_SAMPLES_PAYLOAD_RATIO (16000 / (64000 / 8))\n\n#endif\n"
  },
  {
    "path": "src/audio/g723_16.cpp",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n/* 16kbps version created, used 24kbps code and changing as little as possible.\n * G.726 specs are available from ITU's gopher or WWW site (http://www.itu.ch)\n * If any errors are found, please contact me at mrand@tamu.edu\n *      -Marc Randolph\n */\n\n/*\n * g723_16.c\n *\n * Description:\n *\n * g723_16_encoder(), g723_16_decoder()\n *\n * These routines comprise an implementation of the CCITT G.726 16 Kbps\n * ADPCM coding algorithm.  Essentially, this implementation is identical to\n * the bit level description except for a few deviations which take advantage\n * of workstation attributes, such as hardware 2's complement arithmetic.\n *\n */\n#include \"g72x.h\"\n#include \"g711.h\"\n\n/*\n * Maps G.723_16 code word to reconstructed scale factor normalized log\n * magnitude values.  Comes from Table 11/G.726\n */\nstatic short\t_dqlntab[4] = { 116, 365, 365, 116}; \n\n/* Maps G.723_16 code word to log of scale factor multiplier.\n *\n * _witab[4] is actually {-22 , 439, 439, -22}, but FILTD wants it\n * as WI << 5  (multiplied by 32), so we'll do that here \n */\nstatic short\t_witab[4] = {-704, 14048, 14048, -704};\n\n/*\n * Maps G.723_16 code words to a set of values whose long and short\n * term averages are computed and then compared to give an indication\n * how stationary (steady state) the signal is.\n */\n\n/* Comes from FUNCTF */\nstatic short\t_fitab[4] = {0, 0xE00, 0xE00, 0};\n\n/* Comes from quantizer decision level tables (Table 7/G.726)\n */\nstatic short qtab_723_16[1] = {261};\n\n\n/*\n * g723_16_encoder()\n *\n * Encodes a linear PCM, A-law or u-law input sample and returns its 2-bit code.\n * Returns -1 if invalid input coding value.\n */\nint\ng723_16_encoder(\n\tint\t\tsl,\n\tint\t\tin_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsei, sezi, se, sez;\t/* ACCUM */\n\tshort\t\td;\t\t\t/* SUBTA */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdqsez;\t\t\t/* ADDC */\n\tshort\t\tdq, i;\n\n\tswitch (in_coding) {\t/* linearize input sample to 14-bit PCM */\n\tcase AUDIO_ENCODING_ALAW:\n\t\tsl = alaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_ULAW:\n\t\tsl = ulaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_LINEAR:\n\t\tsl >>= 2;\t\t/* sl of 14-bit dynamic range */\n\t\tbreak;\n\tdefault:\n\t\treturn (-1);\n\t}\n\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\td = sl - se;\t\t\t/* d = estimation diff. */\n\n\t/* quantize prediction difference d */\n\ty = step_size(state_ptr);\t/* quantizer step size */\n\ti = quantize(d, y, qtab_723_16, 1);  /* i = ADPCM code */\n\n\t      /* Since quantize() only produces a three level output\n\t       * (1, 2, or 3), we must create the fourth one on our own\n\t       */\n\tif (i == 3)                          /* i code for the zero region */\n\t  if ((d & 0x8000) == 0)             /* If d > 0, i=3 isn't right... */\n\t    i = 0;\n\t    \n\tdq = reconstruct(i & 2, _dqlntab[i], y); /* quantized diff. */\n\n\tsr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */\n\n\tdqsez = sr + sez - se;\t\t/* pole prediction diff. */\n\n\tupdate(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);\n\n\treturn (i);\n}\n\n/*\n * g723_16_decoder()\n *\n * Decodes a 2-bit CCITT G.723_16 ADPCM code and returns\n * the resulting 16-bit linear PCM, A-law or u-law sample value.\n * -1 is returned if the output coding is unknown.\n */\nint\ng723_16_decoder(\n\tint\t\ti,\n\tint\t\tout_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsezi, sei, sez, se;\t/* ACCUM */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdq;\n\tshort\t\tdqsez;\n\n\ti &= 0x03;\t\t\t/* mask to get proper bits */\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\ty = step_size(state_ptr);\t/* adaptive quantizer step size */\n\tdq = reconstruct(i & 0x02, _dqlntab[i], y); /* unquantize pred diff */\n\n\tsr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */\n\n\tdqsez = sr - se + sez;\t\t\t/* pole prediction diff. */\n\n\tupdate(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);\n\n\tswitch (out_coding) {\n\tcase AUDIO_ENCODING_ALAW:\n\t\treturn (tandem_adjust_alaw(sr, se, y, i, 2, qtab_723_16));\n\tcase AUDIO_ENCODING_ULAW:\n\t\treturn (tandem_adjust_ulaw(sr, se, y, i, 2, qtab_723_16));\n\tcase AUDIO_ENCODING_LINEAR:\n\t\treturn (sr << 2);\t/* sr was of 14-bit dynamic range */\n\tdefault:\n\t\treturn (-1);\n\t}\n}\n"
  },
  {
    "path": "src/audio/g723_24.cpp",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n\n/*\n * g723_24.c\n *\n * Description:\n *\n * g723_24_encoder(), g723_24_decoder()\n *\n * These routines comprise an implementation of the CCITT G.723 24 Kbps\n * ADPCM coding algorithm.  Essentially, this implementation is identical to\n * the bit level description except for a few deviations which take advantage\n * of workstation attributes, such as hardware 2's complement arithmetic.\n *\n */\n#include \"g72x.h\"\n#include \"g711.h\"\n\n/*\n * Maps G.723_24 code word to reconstructed scale factor normalized log\n * magnitude values.\n */\nstatic short\t_dqlntab[8] = {-2048, 135, 273, 373, 373, 273, 135, -2048};\n\n/* Maps G.723_24 code word to log of scale factor multiplier. */\nstatic short\t_witab[8] = {-128, 960, 4384, 18624, 18624, 4384, 960, -128};\n\n/*\n * Maps G.723_24 code words to a set of values whose long and short\n * term averages are computed and then compared to give an indication\n * how stationary (steady state) the signal is.\n */\nstatic short\t_fitab[8] = {0, 0x200, 0x400, 0xE00, 0xE00, 0x400, 0x200, 0};\n\nstatic short qtab_723_24[3] = {8, 218, 331};\n\n/*\n * g723_24_encoder()\n *\n * Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code.\n * Returns -1 if invalid input coding value.\n */\nint\ng723_24_encoder(\n\tint\t\tsl,\n\tint\t\tin_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsei, sezi, se, sez;\t/* ACCUM */\n\tshort\t\td;\t\t\t/* SUBTA */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdqsez;\t\t\t/* ADDC */\n\tshort\t\tdq, i;\n\n\tswitch (in_coding) {\t/* linearize input sample to 14-bit PCM */\n\tcase AUDIO_ENCODING_ALAW:\n\t\tsl = alaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_ULAW:\n\t\tsl = ulaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_LINEAR:\n\t\tsl >>= 2;\t\t/* sl of 14-bit dynamic range */\n\t\tbreak;\n\tdefault:\n\t\treturn (-1);\n\t}\n\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\td = sl - se;\t\t\t/* d = estimation diff. */\n\n\t/* quantize prediction difference d */\n\ty = step_size(state_ptr);\t/* quantizer step size */\n\ti = quantize(d, y, qtab_723_24, 3);\t/* i = ADPCM code */\n\tdq = reconstruct(i & 4, _dqlntab[i], y); /* quantized diff. */\n\n\tsr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */\n\n\tdqsez = sr + sez - se;\t\t/* pole prediction diff. */\n\n\tupdate(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);\n\n\treturn (i);\n}\n\n/*\n * g723_24_decoder()\n *\n * Decodes a 3-bit CCITT G.723_24 ADPCM code and returns\n * the resulting 16-bit linear PCM, A-law or u-law sample value.\n * -1 is returned if the output coding is unknown.\n */\nint\ng723_24_decoder(\n\tint\t\ti,\n\tint\t\tout_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsezi, sei, sez, se;\t/* ACCUM */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdq;\n\tshort\t\tdqsez;\n\n\ti &= 0x07;\t\t\t/* mask to get proper bits */\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\ty = step_size(state_ptr);\t/* adaptive quantizer step size */\n\tdq = reconstruct(i & 0x04, _dqlntab[i], y); /* unquantize pred diff */\n\n\tsr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */\n\n\tdqsez = sr - se + sez;\t\t\t/* pole prediction diff. */\n\n\tupdate(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);\n\n\tswitch (out_coding) {\n\tcase AUDIO_ENCODING_ALAW:\n\t\treturn (tandem_adjust_alaw(sr, se, y, i, 4, qtab_723_24));\n\tcase AUDIO_ENCODING_ULAW:\n\t\treturn (tandem_adjust_ulaw(sr, se, y, i, 4, qtab_723_24));\n\tcase AUDIO_ENCODING_LINEAR:\n\t\treturn (sr << 2);\t/* sr was of 14-bit dynamic range */\n\tdefault:\n\t\treturn (-1);\n\t}\n}\n"
  },
  {
    "path": "src/audio/g723_40.cpp",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n\n/*\n * g723_40.c\n *\n * Description:\n *\n * g723_40_encoder(), g723_40_decoder()\n *\n * These routines comprise an implementation of the CCITT G.723 40Kbps\n * ADPCM coding algorithm.  Essentially, this implementation is identical to\n * the bit level description except for a few deviations which\n * take advantage of workstation attributes, such as hardware 2's\n * complement arithmetic.\n *\n * The deviation from the bit level specification (lookup tables),\n * preserves the bit level performance specifications.\n *\n * As outlined in the G.723 Recommendation, the algorithm is broken\n * down into modules.  Each section of code below is preceded by\n * the name of the module which it is implementing.\n *\n */\n#include \"g72x.h\"\n#include \"g711.h\"\n\n/*\n * Maps G.723_40 code word to ructeconstructed scale factor normalized log\n * magnitude values.\n */\nstatic short\t_dqlntab[32] = {-2048, -66, 28, 104, 169, 224, 274, 318,\n\t\t\t\t358, 395, 429, 459, 488, 514, 539, 566,\n\t\t\t\t566, 539, 514, 488, 459, 429, 395, 358,\n\t\t\t\t318, 274, 224, 169, 104, 28, -66, -2048};\n\n/* Maps G.723_40 code word to log of scale factor multiplier. */\nstatic short\t_witab[32] = {448, 448, 768, 1248, 1280, 1312, 1856, 3200,\n\t\t\t4512, 5728, 7008, 8960, 11456, 14080, 16928, 22272,\n\t\t\t22272, 16928, 14080, 11456, 8960, 7008, 5728, 4512,\n\t\t\t3200, 1856, 1312, 1280, 1248, 768, 448, 448};\n\n/*\n * Maps G.723_40 code words to a set of values whose long and short\n * term averages are computed and then compared to give an indication\n * how stationary (steady state) the signal is.\n */\nstatic short\t_fitab[32] = {0, 0, 0, 0, 0, 0x200, 0x200, 0x200,\n\t\t\t0x200, 0x200, 0x400, 0x600, 0x800, 0xA00, 0xC00, 0xC00,\n\t\t\t0xC00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200, 0x200,\n\t\t\t0x200, 0x200, 0x200, 0, 0, 0, 0, 0};\n\nstatic short qtab_723_40[15] = {-122, -16, 68, 139, 198, 250, 298, 339,\n\t\t\t\t378, 413, 445, 475, 502, 528, 553};\n\n/*\n * g723_40_encoder()\n *\n * Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens\n * the resulting 5-bit CCITT G.723 40Kbps code.\n * Returns -1 if the input coding value is invalid.\n */\nint\ng723_40_encoder(\n\tint\t\tsl,\n\tint\t\tin_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsei, sezi, se, sez;\t/* ACCUM */\n\tshort\t\td;\t\t\t/* SUBTA */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdqsez;\t\t\t/* ADDC */\n\tshort\t\tdq, i;\n\n\tswitch (in_coding) {\t/* linearize input sample to 14-bit PCM */\n\tcase AUDIO_ENCODING_ALAW:\n\t\tsl = alaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_ULAW:\n\t\tsl = ulaw2linear(sl) >> 2;\n\t\tbreak;\n\tcase AUDIO_ENCODING_LINEAR:\n\t\tsl >>= 2;\t\t/* sl of 14-bit dynamic range */\n\t\tbreak;\n\tdefault:\n\t\treturn (-1);\n\t}\n\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\td = sl - se;\t\t\t/* d = estimation difference */\n\n\t/* quantize prediction difference */\n\ty = step_size(state_ptr);\t/* adaptive quantizer step size */\n\ti = quantize(d, y, qtab_723_40, 15);\t/* i = ADPCM code */\n\n\tdq = reconstruct(i & 0x10, _dqlntab[i], y);\t/* quantized diff */\n\n\tsr = (dq < 0) ? se - (dq & 0x7FFF) : se + dq; /* reconstructed signal */\n\n\tdqsez = sr + sez - se;\t\t/* dqsez = pole prediction diff. */\n\n\tupdate(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);\n\n\treturn (i);\n}\n\n/*\n * g723_40_decoder()\n *\n * Decodes a 5-bit CCITT G.723 40Kbps code and returns\n * the resulting 16-bit linear PCM, A-law or u-law sample value.\n * -1 is returned if the output coding is unknown.\n */\nint\ng723_40_decoder(\n\tint\t\ti,\n\tint\t\tout_coding,\n\tstruct g72x_state *state_ptr)\n{\n\tshort\t\tsezi, sei, sez, se;\t/* ACCUM */\n\tshort\t\ty;\t\t\t/* MIX */\n\tshort\t\tsr;\t\t\t/* ADDB */\n\tshort\t\tdq;\n\tshort\t\tdqsez;\n\n\ti &= 0x1f;\t\t\t/* mask to get proper bits */\n\tsezi = predictor_zero(state_ptr);\n\tsez = sezi >> 1;\n\tsei = sezi + predictor_pole(state_ptr);\n\tse = sei >> 1;\t\t\t/* se = estimated signal */\n\n\ty = step_size(state_ptr);\t/* adaptive quantizer step size */\n\tdq = reconstruct(i & 0x10, _dqlntab[i], y);\t/* estimation diff. */\n\n\tsr = (dq < 0) ? (se - (dq & 0x7FFF)) : (se + dq); /* reconst. signal */\n\n\tdqsez = sr - se + sez;\t\t/* pole prediction diff. */\n\n\tupdate(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr);\n\n\tswitch (out_coding) {\n\tcase AUDIO_ENCODING_ALAW:\n\t\treturn (tandem_adjust_alaw(sr, se, y, i, 0x10, qtab_723_40));\n\tcase AUDIO_ENCODING_ULAW:\n\t\treturn (tandem_adjust_ulaw(sr, se, y, i, 0x10, qtab_723_40));\n\tcase AUDIO_ENCODING_LINEAR:\n\t\treturn (sr << 2);\t/* sr was of 14-bit dynamic range */\n\tdefault:\n\t\treturn (-1);\n\t}\n}\n"
  },
  {
    "path": "src/audio/g72x.cpp",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n\n/*\n * g72x.c\n *\n * Common routines for G.721 and G.723 conversions.\n */\n\n#include <cstdlib>\n#include \"g72x.h\"\n#include \"g711.h\"\n\nstatic short power2[15] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,\n\t\t\t0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000};\n\n/*\n * quan()\n *\n * quantizes the input val against the table of size short integers.\n * It returns i if table[i - 1] <= val < table[i].\n *\n * Using linear search for simple coding.\n */\nstatic int\nquan(\n\tint\t\tval,\n\tshort\t\t*table,\n\tint\t\tsize)\n{\n\tint\t\ti;\n\n\tfor (i = 0; i < size; i++)\n\t\tif (val < *table++)\n\t\t\tbreak;\n\treturn (i);\n}\n\n/*\n * fmult()\n *\n * returns the integer product of the 14-bit integer \"an\" and\n * \"floating point\" representation (4-bit exponent, 6-bit mantessa) \"srn\".\n */\nstatic int\nfmult(\n\tint\t\tan,\n\tint\t\tsrn)\n{\n\tshort\t\tanmag, anexp, anmant;\n\tshort\t\twanexp, wanmant;\n\tshort\t\tretval;\n\n\tanmag = (an > 0) ? an : ((-an) & 0x1FFF);\n\tanexp = quan(anmag, power2, 15) - 6;\n\tanmant = (anmag == 0) ? 32 :\n\t    (anexp >= 0) ? anmag >> anexp : anmag << -anexp;\n\twanexp = anexp + ((srn >> 6) & 0xF) - 13;\n\n\twanmant = (anmant * (srn & 077) + 0x30) >> 4;\n\tretval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) :\n\t    (wanmant >> -wanexp);\n\n\treturn (((an ^ srn) < 0) ? -retval : retval);\n}\n\n/*\n * g72x_init_state()\n *\n * This routine initializes and/or resets the g72x_state structure\n * pointed to by 'state_ptr'.\n * All the initial state values are specified in the CCITT G.721 document.\n */\nvoid\ng72x_init_state(\n\tstruct g72x_state *state_ptr)\n{\n\tint\t\tcnta;\n\n\tstate_ptr->yl = 34816;\n\tstate_ptr->yu = 544;\n\tstate_ptr->dms = 0;\n\tstate_ptr->dml = 0;\n\tstate_ptr->ap = 0;\n\tfor (cnta = 0; cnta < 2; cnta++) {\n\t\tstate_ptr->a[cnta] = 0;\n\t\tstate_ptr->pk[cnta] = 0;\n\t\tstate_ptr->sr[cnta] = 32;\n\t}\n\tfor (cnta = 0; cnta < 6; cnta++) {\n\t\tstate_ptr->b[cnta] = 0;\n\t\tstate_ptr->dq[cnta] = 32;\n\t}\n\tstate_ptr->td = 0;\n}\n\n/*\n * predictor_zero()\n *\n * computes the estimated signal from 6-zero predictor.\n *\n */\nint\npredictor_zero(\n\tstruct g72x_state *state_ptr)\n{\n\tint\t\ti;\n\tint\t\tsezi;\n\n\tsezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]);\n\tfor (i = 1; i < 6; i++)\t\t\t/* ACCUM */\n\t\tsezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]);\n\treturn (sezi);\n}\n/*\n * predictor_pole()\n *\n * computes the estimated signal from 2-pole predictor.\n *\n */\nint\npredictor_pole(\n\tstruct g72x_state *state_ptr)\n{\n\treturn (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) +\n\t    fmult(state_ptr->a[0] >> 2, state_ptr->sr[0]));\n}\n/*\n * step_size()\n *\n * computes the quantization step size of the adaptive quantizer.\n *\n */\nint\nstep_size(\n\tstruct g72x_state *state_ptr)\n{\n\tint\t\ty;\n\tint\t\tdif;\n\tint\t\tal;\n\n\tif (state_ptr->ap >= 256)\n\t\treturn (state_ptr->yu);\n\telse {\n\t\ty = state_ptr->yl >> 6;\n\t\tdif = state_ptr->yu - y;\n\t\tal = state_ptr->ap >> 2;\n\t\tif (dif > 0)\n\t\t\ty += (dif * al) >> 6;\n\t\telse if (dif < 0)\n\t\t\ty += (dif * al + 0x3F) >> 6;\n\t\treturn (y);\n\t}\n}\n\n/*\n * quantize()\n *\n * Given a raw sample, 'd', of the difference signal and a\n * quantization step size scale factor, 'y', this routine returns the\n * ADPCM codeword to which that sample gets quantized.  The step\n * size scale factor division operation is done in the log base 2 domain\n * as a subtraction.\n */\nint\nquantize(\n\tint\t\td,\t/* Raw difference signal sample */\n\tint\t\ty,\t/* Step size multiplier */\n\tshort\t\t*table,\t/* quantization table */\n\tint\t\tsize)\t/* table size of short integers */\n{\n\tshort\t\tdqm;\t/* Magnitude of 'd' */\n\tshort\t\texp;\t/* Integer part of base 2 log of 'd' */\n\tshort\t\tmant;\t/* Fractional part of base 2 log */\n\tshort\t\tdl;\t/* Log of magnitude of 'd' */\n\tshort\t\tdln;\t/* Step size scale factor normalized log */\n\tint\t\ti;\n\n\t/*\n\t * LOG\n\t *\n\t * Compute base 2 log of 'd', and store in 'dl'.\n\t */\n\tdqm = abs(d);\n\texp = quan(dqm >> 1, power2, 15);\n\tmant = ((dqm << 7) >> exp) & 0x7F;\t/* Fractional portion. */\n\tdl = (exp << 7) + mant;\n\n\t/*\n\t * SUBTB\n\t *\n\t * \"Divide\" by step size multiplier.\n\t */\n\tdln = dl - (y >> 2);\n\n\t/*\n\t * QUAN\n\t *\n\t * Obtain codword i for 'd'.\n\t */\n\ti = quan(dln, table, size);\n\tif (d < 0)\t\t\t/* take 1's complement of i */\n\t\treturn ((size << 1) + 1 - i);\n\telse if (i == 0)\t\t/* take 1's complement of 0 */\n\t\treturn ((size << 1) + 1); /* new in 1988 */\n\telse\n\t\treturn (i);\n}\n/*\n * reconstruct()\n *\n * Returns reconstructed difference signal 'dq' obtained from\n * codeword 'i' and quantization step size scale factor 'y'.\n * Multiplication is performed in log base 2 domain as addition.\n */\nint\nreconstruct(\n\tint\t\tsign,\t/* 0 for non-negative value */\n\tint\t\tdqln,\t/* G.72x codeword */\n\tint\t\ty)\t/* Step size multiplier */\n{\n\tshort\t\tdql;\t/* Log of 'dq' magnitude */\n\tshort\t\tdex;\t/* Integer part of log */\n\tshort\t\tdqt;\n\tshort\t\tdq;\t/* Reconstructed difference signal sample */\n\n\tdql = dqln + (y >> 2);\t/* ADDA */\n\n\tif (dql < 0) {\n\t\treturn ((sign) ? -0x8000 : 0);\n\t} else {\t\t/* ANTILOG */\n\t\tdex = (dql >> 7) & 15;\n\t\tdqt = 128 + (dql & 127);\n\t\tdq = (dqt << 7) >> (14 - dex);\n\t\treturn ((sign) ? (dq - 0x8000) : dq);\n\t}\n}\n\n\n/*\n * update()\n *\n * updates the state variables for each output code\n */\nvoid\nupdate(\n\tint\t\tcode_size,\t/* distinguish 723_40 with others */\n\tint\t\ty,\t\t/* quantizer step size */\n\tint\t\twi,\t\t/* scale factor multiplier */\n\tint\t\tfi,\t\t/* for long/short term energies */\n\tint\t\tdq,\t\t/* quantized prediction difference */\n\tint\t\tsr,\t\t/* reconstructed signal */\n\tint\t\tdqsez,\t\t/* difference from 2-pole predictor */\n\tstruct g72x_state *state_ptr)\t/* coder state pointer */\n{\n\tint\t\tcnt;\n\tshort\t\tmag, exp;\t/* Adaptive predictor, FLOAT A */\n\tshort\t\ta2p = 0;\t/* LIMC */\n\tshort\t\ta1ul;\t\t/* UPA1 */\n\tshort\t\tpks1;\t\t/* UPA2 */\n\tshort\t\tfa1;\n\tchar\t\ttr;\t\t/* tone/transition detector */\n\tshort\t\tylint, thr2, dqthr;\n\tshort  \t\tylfrac, thr1;\n\tshort\t\tpk0;\n\n\tpk0 = (dqsez < 0) ? 1 : 0;\t/* needed in updating predictor poles */\n\n\tmag = dq & 0x7FFF;\t\t/* prediction difference magnitude */\n\t/* TRANS */\n\tylint = state_ptr->yl >> 15;\t/* exponent part of yl */\n\tylfrac = (state_ptr->yl >> 10) & 0x1F;\t/* fractional part of yl */\n\tthr1 = (32 + ylfrac) << ylint;\t\t/* threshold */\n\tthr2 = (ylint > 9) ? 31 << 10 : thr1;\t/* limit thr2 to 31 << 10 */\n\tdqthr = (thr2 + (thr2 >> 1)) >> 1;\t/* dqthr = 0.75 * thr2 */\n\tif (state_ptr->td == 0)\t\t/* signal supposed voice */\n\t\ttr = 0;\n\telse if (mag <= dqthr)\t\t/* supposed data, but small mag */\n\t\ttr = 0;\t\t\t/* treated as voice */\n\telse\t\t\t\t/* signal is data (modem) */\n\t\ttr = 1;\n\n\t/*\n\t * Quantizer scale factor adaptation.\n\t */\n\n\t/* FUNCTW & FILTD & DELAY */\n\t/* update non-steady state step size multiplier */\n\tstate_ptr->yu = y + ((wi - y) >> 5);\n\n\t/* LIMB */\n\tif (state_ptr->yu < 544)\t/* 544 <= yu <= 5120 */\n\t\tstate_ptr->yu = 544;\n\telse if (state_ptr->yu > 5120)\n\t\tstate_ptr->yu = 5120;\n\n\t/* FILTE & DELAY */\n\t/* update steady state step size multiplier */\n\tstate_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6);\n\n\t/*\n\t * Adaptive predictor coefficients.\n\t */\n\tif (tr == 1) {\t\t\t/* reset a's and b's for modem signal */\n\t\tstate_ptr->a[0] = 0;\n\t\tstate_ptr->a[1] = 0;\n\t\tstate_ptr->b[0] = 0;\n\t\tstate_ptr->b[1] = 0;\n\t\tstate_ptr->b[2] = 0;\n\t\tstate_ptr->b[3] = 0;\n\t\tstate_ptr->b[4] = 0;\n\t\tstate_ptr->b[5] = 0;\n\t} else {\t\t\t/* update a's and b's */\n\t\tpks1 = pk0 ^ state_ptr->pk[0];\t\t/* UPA2 */\n\n\t\t/* update predictor pole a[1] */\n\t\ta2p = state_ptr->a[1] - (state_ptr->a[1] >> 7);\n\t\tif (dqsez != 0) {\n\t\t\tfa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0];\n\t\t\tif (fa1 < -8191)\t/* a2p = function of fa1 */\n\t\t\t\ta2p -= 0x100;\n\t\t\telse if (fa1 > 8191)\n\t\t\t\ta2p += 0xFF;\n\t\t\telse\n\t\t\t\ta2p += fa1 >> 5;\n\n\t\t\tif (pk0 ^ state_ptr->pk[1])\n\t\t\t\t/* LIMC */\n\t\t\t\tif (a2p <= -12160)\n\t\t\t\t\ta2p = -12288;\n\t\t\t\telse if (a2p >= 12416)\n\t\t\t\t\ta2p = 12288;\n\t\t\t\telse\n\t\t\t\t\ta2p -= 0x80;\n\t\t\telse if (a2p <= -12416)\n\t\t\t\ta2p = -12288;\n\t\t\telse if (a2p >= 12160)\n\t\t\t\ta2p = 12288;\n\t\t\telse\n\t\t\t\ta2p += 0x80;\n\t\t}\n\n\t\t/* TRIGB & DELAY */\n\t\tstate_ptr->a[1] = a2p;\n\n\t\t/* UPA1 */\n\t\t/* update predictor pole a[0] */\n\t\tstate_ptr->a[0] -= state_ptr->a[0] >> 8;\n\t\tif (dqsez != 0)\n\t\t\tif (pks1 == 0)\n\t\t\t\tstate_ptr->a[0] += 192;\n\t\t\telse\n\t\t\t\tstate_ptr->a[0] -= 192;\n\n\t\t/* LIMD */\n\t\ta1ul = 15360 - a2p;\n\t\tif (state_ptr->a[0] < -a1ul)\n\t\t\tstate_ptr->a[0] = -a1ul;\n\t\telse if (state_ptr->a[0] > a1ul)\n\t\t\tstate_ptr->a[0] = a1ul;\n\n\t\t/* UPB : update predictor zeros b[6] */\n\t\tfor (cnt = 0; cnt < 6; cnt++) {\n\t\t\tif (code_size == 5)\t\t/* for 40Kbps G.723 */\n\t\t\t\tstate_ptr->b[cnt] -= state_ptr->b[cnt] >> 9;\n\t\t\telse\t\t\t/* for G.721 and 24Kbps G.723 */\n\t\t\t\tstate_ptr->b[cnt] -= state_ptr->b[cnt] >> 8;\n\t\t\tif (dq & 0x7FFF) {\t\t\t/* XOR */\n\t\t\t\tif ((dq ^ state_ptr->dq[cnt]) >= 0)\n\t\t\t\t\tstate_ptr->b[cnt] += 128;\n\t\t\t\telse\n\t\t\t\t\tstate_ptr->b[cnt] -= 128;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (cnt = 5; cnt > 0; cnt--)\n\t\tstate_ptr->dq[cnt] = state_ptr->dq[cnt-1];\n\t/* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */\n\tif (mag == 0) {\n\t\tstate_ptr->dq[0] = (dq >= 0) ? 0x20 : 0xFC20;\n\t} else {\n\t\texp = quan(mag, power2, 15);\n\t\tstate_ptr->dq[0] = (dq >= 0) ?\n\t\t    (exp << 6) + ((mag << 6) >> exp) :\n\t\t    (exp << 6) + ((mag << 6) >> exp) - 0x400;\n\t}\n\n\tstate_ptr->sr[1] = state_ptr->sr[0];\n\t/* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */\n\tif (sr == 0) {\n\t\tstate_ptr->sr[0] = 0x20;\n\t} else if (sr > 0) {\n\t\texp = quan(sr, power2, 15);\n\t\tstate_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp);\n\t} else if (sr > -32768) {\n\t\tmag = -sr;\n\t\texp = quan(mag, power2, 15);\n\t\tstate_ptr->sr[0] =  (exp << 6) + ((mag << 6) >> exp) - 0x400;\n\t} else\n\t\tstate_ptr->sr[0] = 0xFC20;\n\n\t/* DELAY A */\n\tstate_ptr->pk[1] = state_ptr->pk[0];\n\tstate_ptr->pk[0] = pk0;\n\n\t/* TONE */\n\tif (tr == 1)\t\t/* this sample has been treated as data */\n\t\tstate_ptr->td = 0;\t/* next one will be treated as voice */\n\telse if (a2p < -11776)\t/* small sample-to-sample correlation */\n\t\tstate_ptr->td = 1;\t/* signal may be data */\n\telse\t\t\t\t/* signal is voice */\n\t\tstate_ptr->td = 0;\n\n\t/*\n\t * Adaptation speed control.\n\t */\n\tstate_ptr->dms += (fi - state_ptr->dms) >> 5;\t\t/* FILTA */\n\tstate_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7);\t/* FILTB */\n\n\tif (tr == 1)\n\t\tstate_ptr->ap = 256;\n\telse if (y < 1536)\t\t\t\t\t/* SUBTC */\n\t\tstate_ptr->ap += (0x200 - state_ptr->ap) >> 4;\n\telse if (state_ptr->td == 1)\n\t\tstate_ptr->ap += (0x200 - state_ptr->ap) >> 4;\n\telse if (abs((state_ptr->dms << 2) - state_ptr->dml) >=\n\t    (state_ptr->dml >> 3))\n\t\tstate_ptr->ap += (0x200 - state_ptr->ap) >> 4;\n\telse\n\t\tstate_ptr->ap += (-state_ptr->ap) >> 4;\n}\n\n/*\n * tandem_adjust(sr, se, y, i, sign)\n *\n * At the end of ADPCM decoding, it simulates an encoder which may be receiving\n * the output of this decoder as a tandem process. If the output of the\n * simulated encoder differs from the input to this decoder, the decoder output\n * is adjusted by one level of A-law or u-law codes.\n *\n * Input:\n *\tsr\tdecoder output linear PCM sample,\n *\tse\tpredictor estimate sample,\n *\ty\tquantizer step size,\n *\ti\tdecoder input code,\n *\tsign\tsign bit of code i\n *\n * Return:\n *\tadjusted A-law or u-law compressed sample.\n */\nint\ntandem_adjust_alaw(\n\tint\t\tsr,\t/* decoder output linear PCM sample */\n\tint\t\tse,\t/* predictor estimate sample */\n\tint\t\ty,\t/* quantizer step size */\n\tint\t\ti,\t/* decoder input code */\n\tint\t\tsign,\n\tshort\t\t*qtab)\n{\n\tunsigned char\tsp;\t/* A-law compressed 8-bit code */\n\tshort\t\tdx;\t/* prediction error */\n\tchar\t\tid;\t/* quantized prediction error */\n\tint\t\tsd;\t/* adjusted A-law decoded sample value */\n\tint\t\tim;\t/* biased magnitude of i */\n\tint\t\timx;\t/* biased magnitude of id */\n\n\tif (sr <= -32768)\n\t\tsr = -1;\n\tsp = linear2alaw((sr >> 1) << 3);\t/* short to A-law compression */\n\tdx = (alaw2linear(sp) >> 2) - se;\t/* 16-bit prediction error */\n\tid = quantize(dx, y, qtab, sign - 1);\n\n\tif (id == i) {\t\t\t/* no adjustment on sp */\n\t\treturn (sp);\n\t} else {\t\t\t/* sp adjustment needed */\n\t\t/* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */\n\t\tim = i ^ sign;\t\t/* 2's complement to biased unsigned */\n\t\timx = id ^ sign;\n\n\t\tif (imx > im) {\t\t/* sp adjusted to next lower value */\n\t\t\tif (sp & 0x80) {\n\t\t\t\tsd = (sp == 0xD5) ? 0x55 :\n\t\t\t\t    ((sp ^ 0x55) - 1) ^ 0x55;\n\t\t\t} else {\n\t\t\t\tsd = (sp == 0x2A) ? 0x2A :\n\t\t\t\t    ((sp ^ 0x55) + 1) ^ 0x55;\n\t\t\t}\n\t\t} else {\t\t/* sp adjusted to next higher value */\n\t\t\tif (sp & 0x80)\n\t\t\t\tsd = (sp == 0xAA) ? 0xAA :\n\t\t\t\t    ((sp ^ 0x55) + 1) ^ 0x55;\n\t\t\telse\n\t\t\t\tsd = (sp == 0x55) ? 0xD5 :\n\t\t\t\t    ((sp ^ 0x55) - 1) ^ 0x55;\n\t\t}\n\t\treturn (sd);\n\t}\n}\n\nint\ntandem_adjust_ulaw(\n\tint\t\tsr,\t/* decoder output linear PCM sample */\n\tint\t\tse,\t/* predictor estimate sample */\n\tint\t\ty,\t/* quantizer step size */\n\tint\t\ti,\t/* decoder input code */\n\tint\t\tsign,\n\tshort\t\t*qtab)\n{\n\tunsigned char\tsp;\t/* u-law compressed 8-bit code */\n\tshort\t\tdx;\t/* prediction error */\n\tchar\t\tid;\t/* quantized prediction error */\n\tint\t\tsd;\t/* adjusted u-law decoded sample value */\n\tint\t\tim;\t/* biased magnitude of i */\n\tint\t\timx;\t/* biased magnitude of id */\n\n\tif (sr <= -32768)\n\t\tsr = 0;\n\tsp = linear2ulaw(sr << 2);\t/* short to u-law compression */\n\tdx = (ulaw2linear(sp) >> 2) - se;\t/* 16-bit prediction error */\n\tid = quantize(dx, y, qtab, sign - 1);\n\tif (id == i) {\n\t\treturn (sp);\n\t} else {\n\t\t/* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */\n\t\tim = i ^ sign;\t\t/* 2's complement to biased unsigned */\n\t\timx = id ^ sign;\n\t\tif (imx > im) {\t\t/* sp adjusted to next lower value */\n\t\t\tif (sp & 0x80)\n\t\t\t\tsd = (sp == 0xFF) ? 0x7E : sp + 1;\n\t\t\telse\n\t\t\t\tsd = (sp == 0) ? 0 : sp - 1;\n\n\t\t} else {\t\t/* sp adjusted to next higher value */\n\t\t\tif (sp & 0x80)\n\t\t\t\tsd = (sp == 0x80) ? 0x80 : sp - 1;\n\t\t\telse\n\t\t\t\tsd = (sp == 0x7F) ? 0xFE : sp + 1;\n\t\t}\n\t\treturn (sd);\n\t}\n}\n"
  },
  {
    "path": "src/audio/g72x.h",
    "content": "/*\n * This source code is a product of Sun Microsystems, Inc. and is provided\n * for unrestricted use.  Users may copy or modify this source code without\n * charge.\n *\n * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\n * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\n *\n * Sun source code is provided with no support and without any obligation on\n * the part of Sun Microsystems, Inc. to assist in its use, correction,\n * modification or enhancement.\n *\n * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\n * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\n * OR ANY PART THEREOF.\n *\n * In no event will Sun Microsystems, Inc. be liable for any lost revenue\n * or profits or other special, indirect and consequential damages, even if\n * Sun has been advised of the possibility of such damages.\n *\n * Sun Microsystems, Inc.\n * 2550 Garcia Avenue\n * Mountain View, California  94043\n */\n\n/*\n * g72x.h\n *\n * Header file for CCITT conversion routines.\n *\n */\n#ifndef _G72X_H\n#define\t_G72X_H\n\n#define\tAUDIO_ENCODING_ULAW\t(1)\t/* ISDN u-law */\n#define\tAUDIO_ENCODING_ALAW\t(2)\t/* ISDN A-law */\n#define\tAUDIO_ENCODING_LINEAR\t(3)\t/* PCM 2's-complement (0-center) */\n\n/*\n * The following is the definition of the state structure\n * used by the G.721/G.723 encoder and decoder to preserve their internal\n * state between successive calls.  The meanings of the majority\n * of the state structure fields are explained in detail in the\n * CCITT Recommendation G.721.  The field names are essentially indentical\n * to variable names in the bit level description of the coding algorithm\n * included in this Recommendation.\n */\nstruct g72x_state {\n\tlong yl;\t/* Locked or steady state step size multiplier. */\n\tshort yu;\t/* Unlocked or non-steady state step size multiplier. */\n\tshort dms;\t/* Short term energy estimate. */\n\tshort dml;\t/* Long term energy estimate. */\n\tshort ap;\t/* Linear weighting coefficient of 'yl' and 'yu'. */\n\n\tshort a[2];\t/* Coefficients of pole portion of prediction filter. */\n\tshort b[6];\t/* Coefficients of zero portion of prediction filter. */\n\tshort pk[2];\t/*\n\t\t\t * Signs of previous two samples of a partially\n\t\t\t * reconstructed signal.\n\t\t\t */\n\tshort dq[6];\t/*\n\t\t\t * Previous 6 samples of the quantized difference\n\t\t\t * signal represented in an internal floating point\n\t\t\t * format.\n\t\t\t */\n\tshort sr[2];\t/*\n\t\t\t * Previous 2 samples of the quantized difference\n\t\t\t * signal represented in an internal floating point\n\t\t\t * format.\n\t\t\t */\n\tchar td;\t/* delayed tone detect, new in 1988 version */\n};\n\nint predictor_zero(struct g72x_state *state_ptr);\nint predictor_pole(struct g72x_state *state_ptr);\nint step_size(struct g72x_state *state_ptr);\n\nint quantize(\n\tint\t\td,\t/* Raw difference signal sample */\n\tint\t\ty,\t/* Step size multiplier */\n\tshort\t\t*table,\t/* quantization table */\n\tint\t\tsize);\t/* table size of short integers */\n\nint reconstruct(\n\tint\t\tsign,\t/* 0 for non-negative value */\n\tint\t\tdqln,\t/* G.72x codeword */\n\tint\t\ty);\t/* Step size multiplier */\n\nvoid update(\n\tint\t\tcode_size,\t/* distinguish 723_40 with others */\n\tint\t\ty,\t\t/* quantizer step size */\n\tint\t\twi,\t\t/* scale factor multiplier */\n\tint\t\tfi,\t\t/* for long/short term energies */\n\tint\t\tdq,\t\t/* quantized prediction difference */\n\tint\t\tsr,\t\t/* reconstructed signal */\n\tint\t\tdqsez,\t\t/* difference from 2-pole predictor */\n\tstruct g72x_state *state_ptr);\n\nint tandem_adjust_alaw(\n\tint\t\tsr,\t/* decoder output linear PCM sample */\n\tint\t\tse,\t/* predictor estimate sample */\n\tint\t\ty,\t/* quantizer step size */\n\tint\t\ti,\t/* decoder input code */\n\tint\t\tsign,\n\tshort\t\t*qtab);\n\nint\ntandem_adjust_ulaw(\n\tint\t\tsr,\t/* decoder output linear PCM sample */\n\tint\t\tse,\t/* predictor estimate sample */\n\tint\t\ty,\t/* quantizer step size */\n\tint\t\ti,\t/* decoder input code */\n\tint\t\tsign,\n\tshort\t\t*qtab);\n\nvoid g72x_init_state(struct g72x_state *);\nint g721_encoder(\n\t\tint sample,\n\t\tint in_coding,\n\t\tstruct g72x_state *state_ptr);\nint g721_decoder(\n\t\tint code,\n\t\tint out_coding,\n\t\tstruct g72x_state *state_ptr);\nint g723_16_encoder(\n\t\tint sample,\n\t\tint in_coding,\n\t\tstruct g72x_state *state_ptr);\nint g723_16_decoder(\n\t\tint code,\n\t\tint out_coding,\n\t\tstruct g72x_state *state_ptr);\nint g723_24_encoder(\n\t\tint sample,\n\t\tint in_coding,\n\t\tstruct g72x_state *state_ptr);\nint g723_24_decoder(\n\t\tint code,\n\t\tint out_coding,\n\t\tstruct g72x_state *state_ptr);\nint g723_40_encoder(\n\t\tint sample,\n\t\tint in_coding,\n\t\tstruct g72x_state *state_ptr);\nint g723_40_decoder(\n\t\tint code,\n\t\tint out_coding,\n\t\tstruct g72x_state *state_ptr);\n\n#endif /* !_G72X_H */\n"
  },
  {
    "path": "src/audio/gsm/COPYRIGHT",
    "content": "Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,\nTechnische Universitaet Berlin\n\nAny use of this software is permitted provided that this notice is not\nremoved and that neither the authors nor the Technische Universitaet Berlin\nare deemed to have made any representations as to the suitability of this\nsoftware for any purpose nor are held responsible for any defects of\nthis software.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n\nAs a matter of courtesy, the authors request to be informed about uses\nthis software has found, about bugs in this software, and about any\nimprovements that may be of general interest.\n\nBerlin, 28.11.1994\nJutta Degener\nCarsten Bormann\n"
  },
  {
    "path": "src/audio/gsm/ChangeLog",
    "content": "\nFri Jul  5 19:26:37 1996 \tJutta Degener (jutta@cs.tu-berlin.de)\n\n\t* Release 1.0 Patchlevel 10\n\tsrc/toast_alaw.c: exchanged A-law tables for something\n\t\tslightly more A-law.\n\nTue Jul  2 12:18:20 1996  Jutta Degener (jutta@cs.tu-berlin.de)\n\n\t* Release 1.0 Patchlevel 9\n\tsrc/long_term.c: in FLOAT_MUL mode, an array was accessed past its end\n\tsrc/gsm_option.c: three options related to WAV #49 packing\n\tsrc/gsm_encode.c: support WAV #49-style encoding.\n\tsrc/gsm_decode.c: support WAV #49-style decoding.\n\ttls/sour.c: generate the WAV bit shifting code, encode\n\ttls/ginger.c: generate the WAV bit shifting code, decode\n\tThe WAV code goes back to an inofficial patch #8 that\n\tJeff Chilton sent us (hence the jump from 7 to 9).\n\tsrc/toast.c: add _fsetmode() calls to set stdin/stdout to\n\t\tbinary (from an OS/2 port by Arnd Gronenberg.)\n\nTue Mar  7 01:55:10 1995  Jutta Degener (jutta@cs.tu-berlin.de)\n\n\t* Release 1.0 Patchlevel 7\n\tsrc/long_term.c: Yet another 16-bit overflow\n\tsrc/toast.c: -C option to toast, cuts LPC time\n\tsrc/gsm_option.c: corresponding LPC_CUT option to GSM library\n\nFri Dec 30 23:33:50 1994  Jutta Degener (jutta@cs.tu-berlin.de)\n\n        * Release 1.0 Patchlevel 6\n        src/lpc.c: fixed 16-bit addition overflow in Autocorrelation code\n        src/add.c: gsm_L_asl should fall back on gsm_L_asr, not gsm_asr\n\nMon Nov 28 20:49:57 1994  Jutta Degener (jutta@cs.tu-berlin.de)\n\t\n\t* Release 1.0 Patchlevel 5\n\tsrc/toast_audio.c: initialization should return -1 on error\n\tsrc/gsm_destroy.c: #include configuration header file\n\tsrc/add.c: gsm_sub should cast its parameters to longword\n\tman/*: bug reports to {jutta,cabo}@cs.tu-berlin.de, not to toast@tub\n\tinc/private.h: longword long by default, not int\n\tinc/toast.h: read/write fopen modes \"rb\" and \"wb\", not just \"r\"\n\tsrc/toast.c: better (or different, anyway) error handling in process()\n\nTue May 10 19:41:34 1994  Jutta Degener (jutta at kugelbus)\n\t\n\t* Release 1.0 Patchlevel 4\n\tinc/private.h: GSM_ADD should cast to ulongword, not to unsigned.\n\tsrc/long_term.c: missing cast to longword.\n\tadd-test/add_test.c: Test macros too, not only functions,\n\tthanks to Simao Ferraz de Campos Neto, simao@dragon.cpqd.ansp.br\n\tGeneral cleanup: remove unused variables, add function prototypes.\n\nTue Jan 25 22:53:40 1994  Jutta Degener (jutta at kugelbus)\n\n\t* Release 1.0 Patchlevel 3\n\tchanged rpe.c's STEP macro to work with 16-bit integers,\n\tthanks to Dr Alex Lee (alexlee@solomon.technet.sg);\n\tremoved non-fatal bugs from add-test.dta, private.h\n\tand toast_audio.c, thanks to P. Emanuelsson.\n\nFri Jan 29 19:02:12 1993  Jutta Degener  (jutta at kraftbus)\n\t\n\t* Release 1.0 Patchlevel 2\n\tfixed L_add(0,-1) in src/add.c and inc/private.h,\n\tthanks to Raphael Trommer at AT&T Bell Laboratories;\n\tvarious other ANSI C compatibility details\n\nFri Oct 30 17:58:54 1992  Jutta Degener  (jutta at kraftbus)\n\n\t* Release 1.0 Patchlevel 1\n\tSwitched uid/gid in toast's [f]chown calls.\n\nWed Oct 28 14:12:35 1992  Carsten Bormann  (cabo at kubus)\n\n\t* Release 1.0: released\n\tCopyright 1992 by Jutta Degener and Carsten Bormann, Technische\n\tUniversitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n\tdetails.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n"
  },
  {
    "path": "src/audio/gsm/INSTALL",
    "content": "How to get started:\n\n   Edit the Makefile.\n\n\tYou should configure a few machine-dependencies and what\n\tcompiler you want to use.\n\n  \tThe code works both with ANSI and K&R-C.  Use\n\t-DNeedFunctionPrototypes to compile with, or\n\t-UNeedFunctionPrototypes to compile without, function\n\tprototypes in the header files.\n \n   Make addtst\n\n\tThe \"add\" program that will be compiled and run checks whether\n\tthe basic math functions of the gsm library work with your\n\tcompiler.  If it prints anything to stderr, complain (to us).\n\n   Edit inc/config.h.\n\n   Make\n\n   \tLocal versions of the gsm library and the \"compress\"-like filters\n\ttoast, untoast and tcat will be generated.\n\n   \tIf the compilation aborts because of a missing function,\n\tdeclaration, or header file, see if there's something in\n\tinc/config.h to work around it.  If not, complain.\n\n   Try it\n\n\tGrab an audio file from somewhere (raw u-law or Sun .au is fine, \n    \tlinear 16-bit in host byte order will do), copy it, toast it,\n\tuntoast it, and listen to the result.\n    \n\tThe GSM-encoded and -decoded audio should have the quality\n\tof a good phone line.  If the resulting audio is noisier than\n\tyour original, or if you hear compression artifacts, complain;\n\tthat's a bug in our software, not a bug in the GSM encoding\n\tstandard itself.\n\nInstallation\n\n   You can install the gsm library interface, or the toast binaries,\n   or both.\n\n   Edit the Makefile\n\t\n\tFill in the directories where you want to install the\n\tlibrary, header files, manual pages, and binaries.\n\n\tTurn off the installation of one half of the distribution\n\t(i.e., gsm library or toast binaries) by not setting the\n\tcorresponding directory root Makefile macro.\n\n   make install\n\n\twill install the programs \"toast\" with two links named\n\t\"tcat\" and \"untoast\", and the gsm library \"libgsm.a\" with\n\ta \"gsm.h\" header file, and their respective manual pages.\n\n\nOptimizing\n\n   This code was developed on a machine without an integer\n   multiplication instruction, where we obtained the fastest result by\n   replacing some of the integer multiplications with floating point\n   multiplications.\n\n   If your machine does multiply integers fast enough,\n   leave USE_FLOAT_MUL undefined.  The results should be the\n   same in both cases.\n\n   On machines with fast floating point arithmetic, defining\n   both USE_FLOAT_MUL and FAST makes a run-time library\n   option available that will (in a few crucial places) use\n   ``native'' floating point operations rather than the bit-by-bit\n   defined ones of the GSM standard.  If you use this fast\n   option, the outcome will not be bitwise identical to the\n   results prescribed by the standard, but it is compatible with\n   the standard encoding, and a user is unlikely to notice a\n   difference.\n\n\nBug Reports\n\n   Please direct bug reports, questions, and comments to\n   jutta@cs.tu-berlin.de and cabo@informatik.uni-bremen.de.\n\n\nGood luck,\n\n   Jutta Degener,\n   Carsten Bormann\n\n--\nCopyright 1992, 1993, 1994, by Jutta Degener and Carsten Bormann,\nTechnische Universitaet Berlin.  See the accompanying file \"COPYRIGHT\"\nfor details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n"
  },
  {
    "path": "src/audio/gsm/MACHINES",
    "content": "The gsm library has been tested successfully on the following platforms:\n\n- Various Sun4's running SunOS 4.1.2\n- SPARC1 (SunOS 4.1.1)\n- Integrated Solutions 68k Optimum running 4.3BSD UNIX with a Green Hills cc\n- NeXTstation running NeXT-OS/Mach 3.0\n- No-name AT/386 with Xenix 2.3.2 (using -DSTUPID_COMPILER)\n- RS/6000-350 running AIX 3.2.0\n- RS/6000-320 running AIX 3.1.5\n- Alliant FX80 (Concentrix 5.7)\n- SGI Indigo XS4000 (IRIX 4.0.5F)\n"
  },
  {
    "path": "src/audio/gsm/README",
    "content": "\nGSM 06.10 13 kbit/s RPE/LTP speech compression available\n--------------------------------------------------------\n\nThe Communications and Operating Systems Research Group (KBS) at the\nTechnische Universitaet Berlin is currently working on a set of\nUNIX-based tools for computer-mediated telecooperation that will be\nmade freely available.\n\nAs part of this effort we are publishing an implementation of the\nEuropean GSM 06.10 provisional standard for full-rate speech\ntranscoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse\nexcitation/long term prediction) coding at 13 kbit/s.\n\nGSM 06.10 compresses frames of 160 13-bit samples (8 kHz sampling\nrate, i.e. a frame rate of 50 Hz) into 260 bits; for compatibility\nwith typical UNIX applications, our implementation turns frames of 160\n16-bit linear samples into 33-byte frames (1650 Bytes/s).\nThe quality of the algorithm is good enough for reliable speaker\nrecognition; even music often survives transcoding in recognizable \nform (given the bandwidth limitations of 8 kHz sampling rate).\n\nThe interfaces offered are a front end modelled after compress(1), and\na library API.  Compression and decompression run faster than realtime\non most SPARCstations.  The implementation has been verified against the\nETSI standard test patterns.\n\nJutta Degener (jutta@cs.tu-berlin.de)\nCarsten Bormann (cabo@cs.tu-berlin.de)\n\nCommunications and Operating Systems Research Group, TU Berlin\nFax: +49.30.31425156, Phone: +49.30.31424315\n\n--\nCopyright 1992 by Jutta Degener and Carsten Bormann, Technische\nUniversitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\ndetails.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n"
  },
  {
    "path": "src/audio/gsm/inc/config.h",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/\n\n#ifndef\tCONFIG_H\n#define\tCONFIG_H\n\n/*efine\tSIGHANDLER_T\tint \t\t/* signal handlers are void\t*/\n/*efine HAS_SYSV_SIGNAL\t1\t\t/* sigs not blocked/reset?\t*/\n\n#define\tHAS_STDLIB_H\t1\t\t/* /usr/include/stdlib.h\t*/\n/*efine\tHAS_LIMITS_H\t1\t\t/* /usr/include/limits.h\t*/\n#define\tHAS_FCNTL_H\t1\t\t/* /usr/include/fcntl.h\t\t*/\n/*efine\tHAS_ERRNO_DECL\t1\t\t/* errno.h declares errno\t*/\n\n#define\tHAS_FSTAT \t1\t\t/* fstat syscall\t\t*/\n#define\tHAS_FCHMOD \t1\t\t/* fchmod syscall\t\t*/\n#define\tHAS_CHMOD \t1\t\t/* chmod syscall\t\t*/\n#define\tHAS_FCHOWN \t1\t\t/* fchown syscall\t\t*/\n#define\tHAS_CHOWN \t1\t\t/* chown syscall\t\t*/\n/*efine\tHAS__FSETMODE \t1\t\t/* _fsetmode -- set file mode\t*/\n\n#define\tHAS_STRING_H \t1\t\t/* /usr/include/string.h \t*/\n/*efine\tHAS_STRINGS_H\t1\t\t/* /usr/include/strings.h \t*/\n\n#define\tHAS_UNISTD_H\t1\t\t/* /usr/include/unistd.h\t*/\n#define\tHAS_UTIME\t1\t\t/* POSIX utime(path, times)\t*/\n/*efine\tHAS_UTIMES\t1\t\t/* use utimes()\tsyscall instead\t*/\n#define\tHAS_UTIME_H\t1\t\t/* UTIME header file\t\t*/\n/*efine\tHAS_UTIMBUF\t1\t\t/* struct utimbuf\t\t*/\n/*efine\tHAS_UTIMEUSEC   1\t\t/* microseconds in utimbuf?\t*/\n\n#endif\t/* CONFIG_H */\n"
  },
  {
    "path": "src/audio/gsm/inc/gsm.h",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/*$Header: /home/kbs/jutta/src/gsm/gsm-1.0/inc/RCS/gsm.h,v 1.11 1996/07/05 18:02:56 jutta Exp $*/\n\n#ifndef\tGSM_H\n#define\tGSM_H\n\n#ifdef __cplusplus\n#\tdefine\tNeedFunctionPrototypes\t1\n#endif\n\n#if __STDC__\n#\tdefine\tNeedFunctionPrototypes\t1\n#endif\n\n#ifdef _NO_PROTO\n#\tundef\tNeedFunctionPrototypes\n#endif\n\n#ifdef NeedFunctionPrototypes\n#   include\t<stdio.h>\t\t/* for FILE * \t*/\n#endif\n\n#undef GSM_P\n#if NeedFunctionPrototypes\n#\tdefine\tGSM_P( protos )\tprotos\n#else\n#\tdefine  GSM_P( protos )\t( /* protos */ )\n#endif\n\n/*\n *\tInterface\n */\n\ntypedef struct gsm_state * \tgsm;\ntypedef short\t\t   \tgsm_signal;\t\t/* signed 16 bit */\ntypedef unsigned char\t\tgsm_byte;\ntypedef gsm_byte \t\tgsm_frame[33];\t\t/* 33 * 8 bits\t */\n\n#define\tGSM_MAGIC\t\t0xD\t\t  \t/* 13 kbit/s RPE-LTP */\n\n#define\tGSM_PATCHLEVEL\t\t10\n#define\tGSM_MINOR\t\t0\n#define\tGSM_MAJOR\t\t1\n\n#define\tGSM_OPT_VERBOSE\t\t1\n#define\tGSM_OPT_FAST\t\t2\n#define\tGSM_OPT_LTP_CUT\t\t3\n#define\tGSM_OPT_WAV49\t\t4\n#define\tGSM_OPT_FRAME_INDEX\t5\n#define\tGSM_OPT_FRAME_CHAIN\t6\n\nextern gsm  gsm_create \tGSM_P((void));\nextern void gsm_destroy GSM_P((gsm));\t\n\nextern int  gsm_print   GSM_P((FILE *, gsm, gsm_byte  *));\nextern int  gsm_option  GSM_P((gsm, int, int *));\n\nextern void gsm_encode  GSM_P((gsm, gsm_signal *, gsm_byte  *));\nextern int  gsm_decode  GSM_P((gsm, gsm_byte   *, gsm_signal *));\n\nextern int  gsm_explode GSM_P((gsm, gsm_byte   *, gsm_signal *));\nextern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte   *));\n\n#undef\tGSM_P\n\n#endif\t/* GSM_H */\n"
  },
  {
    "path": "src/audio/gsm/inc/private.h",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/private.h,v 1.6 1996/07/02 10:15:26 jutta Exp $*/\n\n#ifndef\tPRIVATE_H\n#define\tPRIVATE_H\n\ntypedef short\t\t\tword;\t\t/* 16 bit signed int\t*/\ntypedef long\t\t\tlongword;\t/* 32 bit signed int\t*/\n\ntypedef unsigned short\t\tuword;\t\t/* unsigned word\t*/\ntypedef unsigned long\t\tulongword;\t/* unsigned longword\t*/\n\nstruct gsm_state {\n\n\tword\t\tdp0[ 280 ];\n\n\tword\t\tz1;\t\t/* preprocessing.c, Offset_com. */\n\tlongword\tL_z2;\t\t/*                  Offset_com. */\n\tint\t\tmp;\t\t/*                  Preemphasis\t*/\n\n\tword\t\tu[8];\t\t/* short_term_aly_filter.c\t*/\n\tword\t\tLARpp[2][8]; \t/*                              */\n\tword\t\tj;\t\t/*                              */\n\n\tword            ltp_cut;        /* long_term.c, LTP crosscorr.  */\n\tword\t\tnrp; /* 40 */\t/* long_term.c, synthesis\t*/\n\tword\t\tv[9];\t\t/* short_term.c, synthesis\t*/\n\tword\t\tmsr;\t\t/* decoder.c,\tPostprocessing\t*/\n\n\tchar\t\tverbose;\t/* only used if !NDEBUG\t\t*/\n\tchar\t\tfast;\t\t/* only used if FAST\t\t*/\n\n\tchar\t\twav_fmt;\t/* only used if WAV49 defined\t*/\n\tunsigned char\tframe_index;\t/*            odd/even chaining\t*/\n\tunsigned char\tframe_chain;\t/*   half-byte to carry forward\t*/\n};\n\n\n#define\tMIN_WORD\t(-32767 - 1)\n#define\tMAX_WORD\t  32767\n\n#define\tMIN_LONGWORD\t(-2147483647 - 1)\n#define\tMAX_LONGWORD\t  2147483647\n\n#ifdef\tSASR\t\t/* flag: >> is a signed arithmetic shift right */\n#undef\tSASR\n#define\tSASR(x, by)\t((x) >> (by))\n#else\n#define\tSASR(x, by)\t((x) >= 0 ? (x) >> (by) : (~(-((x) + 1) >> (by))))\n#endif\t/* SASR */\n\n#include \"proto.h\"\n\n/*\n *\tPrototypes from add.c\n */\nextern word\tgsm_mult \tP((word a, word b));\nextern longword gsm_L_mult \tP((word a, word b));\nextern word\tgsm_mult_r\tP((word a, word b));\n\nextern word\tgsm_div  \tP((word num, word denum));\n\nextern word\tgsm_add \tP(( word a, word b ));\nextern longword gsm_L_add \tP(( longword a, longword b ));\n\nextern word\tgsm_sub \tP((word a, word b));\nextern longword gsm_L_sub \tP((longword a, longword b));\n\nextern word\tgsm_abs \tP((word a));\n\nextern word\tgsm_norm \tP(( longword a ));\n\nextern longword gsm_L_asl  \tP((longword a, int n));\nextern word\tgsm_asl \tP((word a, int n));\n\nextern longword gsm_L_asr  \tP((longword a, int n));\nextern word\tgsm_asr  \tP((word a, int n));\n\n/*\n *  Inlined functions from add.h \n */\n\n/* \n * #define GSM_MULT_R(a, b) (* word a, word b, !(a == b == MIN_WORD) *)\t\\\n *\t(0x0FFFF & SASR(((longword)(a) * (longword)(b) + 16384), 15))\n */\n#define GSM_MULT_R(a, b) /* word a, word b, !(a == b == MIN_WORD) */\t\\\n\t(SASR( ((longword)(a) * (longword)(b) + 16384), 15 ))\n\n# define GSM_MULT(a,b)\t /* word a, word b, !(a == b == MIN_WORD) */\t\\\n\t(SASR( ((longword)(a) * (longword)(b)), 15 ))\n\n# define GSM_L_MULT(a, b) /* word a, word b */\t\\\n\t(((longword)(a) * (longword)(b)) << 1)\n\n# define GSM_L_ADD(a, b)\t\\\n\t( (a) <  0 ? ( (b) >= 0 ? (a) + (b)\t\\\n\t\t : (utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1)) \\\n\t\t   >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)utmp-2 )   \\\n\t: ((b) <= 0 ? (a) + (b)   \\\n\t          : (utmp = (ulongword)(a) + (ulongword)(b)) >= MAX_LONGWORD \\\n\t\t    ? MAX_LONGWORD : utmp))\n\n/*\n * # define GSM_ADD(a, b)\t\\\n * \t((ltmp = (longword)(a) + (longword)(b)) >= MAX_WORD \\\n * \t? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp)\n */\n/* Nonportable, but faster: */\n\n#define\tGSM_ADD(a, b)\t\\\n\t((ulongword)((ltmp = (longword)(a) + (longword)(b)) - MIN_WORD) > \\\n\t\tMAX_WORD - MIN_WORD ? (ltmp > 0 ? MAX_WORD : MIN_WORD) : ltmp)\n\n# define GSM_SUB(a, b)\t\\\n\t((ltmp = (longword)(a) - (longword)(b)) >= MAX_WORD \\\n\t? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp)\n\n# define GSM_ABS(a)\t((a) < 0 ? ((a) == MIN_WORD ? MAX_WORD : -(a)) : (a))\n\n/* Use these if necessary:\n\n# define GSM_MULT_R(a, b)\tgsm_mult_r(a, b)\n# define GSM_MULT(a, b)\t\tgsm_mult(a, b)\n# define GSM_L_MULT(a, b)\tgsm_L_mult(a, b)\n\n# define GSM_L_ADD(a, b)\tgsm_L_add(a, b)\n# define GSM_ADD(a, b)\t\tgsm_add(a, b)\n# define GSM_SUB(a, b)\t\tgsm_sub(a, b)\n\n# define GSM_ABS(a)\t\tgsm_abs(a)\n\n*/\n\n/*\n *  More prototypes from implementations..\n */\nextern void Gsm_Coder P((\n\t\tstruct gsm_state\t* S,\n\t\tword\t* s,\t/* [0..159] samples\t\tIN\t*/\n\t\tword\t* LARc,\t/* [0..7] LAR coefficients\tOUT\t*/\n\t\tword\t* Nc,\t/* [0..3] LTP lag\t\tOUT \t*/\n\t\tword\t* bc,\t/* [0..3] coded LTP gain\tOUT \t*/\n\t\tword\t* Mc,\t/* [0..3] RPE grid selection\tOUT     */\n\t\tword\t* xmaxc,/* [0..3] Coded maximum amplitude OUT\t*/\n\t\tword\t* xMc\t/* [13*4] normalized RPE samples OUT\t*/));\n\nextern void Gsm_Long_Term_Predictor P((\t\t/* 4x for 160 samples */\n\t\tstruct gsm_state * S,\n\t\tword\t* d,\t/* [0..39]   residual signal\tIN\t*/\n\t\tword\t* dp,\t/* [-120..-1] d'\t\tIN\t*/\n\t\tword\t* e,\t/* [0..40] \t\t\tOUT\t*/\n\t\tword\t* dpp,\t/* [0..40] \t\t\tOUT\t*/\n\t\tword\t* Nc,\t/* correlation lag\t\tOUT\t*/\n\t\tword\t* bc\t/* gain factor\t\t\tOUT\t*/));\n\nextern void Gsm_LPC_Analysis P((\n\t\tstruct gsm_state * S,\n\t\tword * s,\t /* 0..159 signals\tIN/OUT\t*/\n\t        word * LARc));   /* 0..7   LARc's\tOUT\t*/\n\nextern void Gsm_Preprocess P((\n\t\tstruct gsm_state * S,\n\t\tword * s, word * so));\n\nextern void Gsm_Encoding P((\n\t\tstruct gsm_state * S,\n\t\tword\t* e,\t\n\t\tword\t* ep,\t\n\t\tword\t* xmaxc,\n\t\tword\t* Mc,\t\n\t\tword\t* xMc));\n\nextern void Gsm_Short_Term_Analysis_Filter P((\n\t\tstruct gsm_state * S,\n\t\tword\t* LARc,\t/* coded log area ratio [0..7]  IN\t*/\n\t\tword\t* d\t/* st res. signal [0..159]\tIN/OUT\t*/));\n\nextern void Gsm_Decoder P((\n\t\tstruct gsm_state * S,\n\t\tword\t* LARcr,\t/* [0..7]\t\tIN\t*/\n\t\tword\t* Ncr,\t\t/* [0..3] \t\tIN \t*/\n\t\tword\t* bcr,\t\t/* [0..3]\t\tIN\t*/\n\t\tword\t* Mcr,\t\t/* [0..3] \t\tIN \t*/\n\t\tword\t* xmaxcr,\t/* [0..3]\t\tIN \t*/\n\t\tword\t* xMcr,\t\t/* [0..13*4]\t\tIN\t*/\n\t\tword\t* s));\t\t/* [0..159]\t\tOUT \t*/\n\nextern void Gsm_Decoding P((\n\t\tstruct gsm_state * S,\n\t\tword \txmaxcr,\n\t\tword\tMcr,\n\t\tword\t* xMcr,  \t/* [0..12]\t\tIN\t*/\n\t\tword\t* erp)); \t/* [0..39]\t\tOUT \t*/\n\nextern void Gsm_Long_Term_Synthesis_Filtering P((\n\t\tstruct gsm_state* S,\n\t\tword\tNcr,\n\t\tword\tbcr,\n\t\tword\t* erp,\t\t/* [0..39]\t\t  IN \t*/\n\t\tword\t* drp)); \t/* [-120..-1] IN, [0..40] OUT \t*/\n\nvoid Gsm_RPE_Decoding P((\n\tstruct gsm_state *S,\n\t\tword xmaxcr,\n\t\tword Mcr,\n\t\tword * xMcr,  /* [0..12], 3 bits             IN      */\n\t\tword * erp)); /* [0..39]                     OUT     */\n\nvoid Gsm_RPE_Encoding P((\n\t\tstruct gsm_state * S,\n\t\tword    * e,            /* -5..-1][0..39][40..44     IN/OUT  */\n\t\tword    * xmaxc,        /*                              OUT */\n\t\tword    * Mc,           /*                              OUT */\n\t\tword    * xMc));        /* [0..12]                      OUT */\n\nextern void Gsm_Short_Term_Synthesis_Filter P((\n\t\tstruct gsm_state * S,\n\t\tword\t* LARcr, \t/* log area ratios [0..7]  IN\t*/\n\t\tword\t* drp,\t\t/* received d [0...39]\t   IN\t*/\n\t\tword\t* s));\t\t/* signal   s [0..159]\t  OUT\t*/\n\nextern void Gsm_Update_of_reconstructed_short_time_residual_signal P((\n\t\tword\t* dpp,\t\t/* [0...39]\tIN\t*/\n\t\tword\t* ep,\t\t/* [0...39]\tIN\t*/\n\t\tword\t* dp));\t\t/* [-120...-1]  IN/OUT \t*/\n\n/*\n *  Tables from table.c\n */\n#ifndef\tGSM_TABLE_C\n\nextern word gsm_A[8], gsm_B[8], gsm_MIC[8], gsm_MAC[8];\nextern word gsm_INVA[8];\nextern word gsm_DLB[4], gsm_QLB[4];\nextern word gsm_H[11];\nextern word gsm_NRFAC[8];\nextern word gsm_FAC[8];\n\n#endif\t/* GSM_TABLE_C */\n\n/*\n *  Debugging\n */\n#ifdef NDEBUG\n\n#\tdefine\tgsm_debug_words(a, b, c, d)\t\t/* nil */\n#\tdefine\tgsm_debug_longwords(a, b, c, d)\t\t/* nil */\n#\tdefine\tgsm_debug_word(a, b)\t\t\t/* nil */\n#\tdefine\tgsm_debug_longword(a, b)\t\t/* nil */\n\n#else\t/* !NDEBUG => DEBUG */\n\n\textern void  gsm_debug_words     P((char * name, int, int, word *));\n\textern void  gsm_debug_longwords P((char * name, int, int, longword *));\n\textern void  gsm_debug_longword  P((char * name, longword));\n\textern void  gsm_debug_word      P((char * name, word));\n\n#endif /* !NDEBUG */\n\n#include \"unproto.h\"\n\n#endif\t/* PRIVATE_H */\n"
  },
  {
    "path": "src/audio/gsm/inc/proto.h",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/proto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/\n\n#ifndef\tPROTO_H\n#define\tPROTO_H\n\n#if __cplusplus\n#\tdefine\tNeedFunctionPrototypes\t1\n#endif\n\n#if __STDC__\n#\tdefine\tNeedFunctionPrototypes\t1\n#endif\n\n#ifdef\t_NO_PROTO\n#\tundef\tNeedFunctionPrototypes\n#endif\n\n#undef\tP\t/* gnu stdio.h actually defines this... \t*/\n#undef\tP0\n#undef\tP1\n#undef\tP2\n#undef\tP3\n#undef\tP4\n#undef\tP5\n#undef\tP6\n#undef\tP7\n#undef\tP8\n\n#if NeedFunctionPrototypes\n\n#\tdefine\tP( protos )\tprotos\n\n#\tdefine\tP0()\t\t\t\t(void)\n#\tdefine\tP1(x, a)\t\t\t(a)\n#\tdefine\tP2(x, a, b)\t\t\t(a, b)\n#\tdefine\tP3(x, a, b, c)\t\t\t(a, b, c)\n#\tdefine\tP4(x, a, b, c, d)\t\t(a, b, c, d)\t\n#\tdefine\tP5(x, a, b, c, d, e)\t\t(a, b, c, d, e)\n#\tdefine\tP6(x, a, b, c, d, e, f)\t\t(a, b, c, d, e, f)\n#\tdefine\tP7(x, a, b, c, d, e, f, g)\t(a, b, c, d, e, f, g)\n#\tdefine\tP8(x, a, b, c, d, e, f, g, h)\t(a, b, c, d, e, f, g, h)\n\n#else /* !NeedFunctionPrototypes */\n\n#\tdefine\tP( protos )\t( /* protos */ )\n\n#\tdefine\tP0()\t\t\t\t()\n#\tdefine\tP1(x, a)\t\t\tx a;\n#\tdefine\tP2(x, a, b)\t\t\tx a; b;\n#\tdefine\tP3(x, a, b, c)\t\t\tx a; b; c;\n#\tdefine\tP4(x, a, b, c, d)\t\tx a; b; c; d;\n#\tdefine\tP5(x, a, b, c, d, e)\t\tx a; b; c; d; e;\n#\tdefine\tP6(x, a, b, c, d, e, f)\t\tx a; b; c; d; e; f;\n#\tdefine\tP7(x, a, b, c, d, e, f, g)\tx a; b; c; d; e; f; g;\n#\tdefine\tP8(x, a, b, c, d, e, f, g, h)\tx a; b; c; d; e; f; g; h;\n\n#endif  /* !NeedFunctionPrototypes */\n\n#endif\t/* PROTO_H */\n"
  },
  {
    "path": "src/audio/gsm/inc/unproto.h",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/unproto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/\n\n#ifdef\tPROTO_H\t\t/* sic */\n#undef\tPROTO_H\n\n#undef\tP\n#undef\tP0\n#undef\tP1\n#undef\tP2\n#undef\tP3\n#undef\tP4\n#undef\tP5\n#undef\tP6\n#undef\tP7\n#undef\tP8\n\n#endif\t/* PROTO_H */\n"
  },
  {
    "path": "src/audio/gsm/src/CMakeLists.txt",
    "content": "project(libtwinkle-gsm)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/../inc)\n\nset(LIBTWINKLE_GSM-SRCS\n\tadd.cpp\n\tcode.cpp\n\tdebug.cpp\n\tdecode.cpp\n\tgsm_create.cpp\n\tgsm_decode.cpp\n\tgsm_destroy.cpp\n\tgsm_encode.cpp\n\tgsm_explode.cpp\n\tgsm_implode.cpp\n\tgsm_option.cpp\n\tgsm_print.cpp\n\tlong_term.cpp\n\tlpc.cpp\n\tpreprocess.cpp\n\trpe.cpp\n\tshort_term.cpp\n\ttable.cpp\n)\n\nadd_library(libtwinkle-gsm OBJECT ${LIBTWINKLE_GSM-SRCS})\n"
  },
  {
    "path": "src/audio/gsm/src/add.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/add.c,v 1.6 1996/07/02 09:57:33 jutta Exp $ */\n\n/*\n *  See private.h for the more commonly used macro versions.\n */\n\n#include\t<stdio.h>\n#include\t<assert.h>\n\n#include\t\"private.h\"\n#include\t\"gsm.h\"\n#include\t\"proto.h\"\n\n#define\tsaturate(x) \t\\\n\t((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x))\n\nword gsm_add P2((a,b), word a, word b)\n{\n\tlongword sum = (longword)a + (longword)b;\n\treturn saturate(sum);\n}\n\nword gsm_sub P2((a,b), word a, word b)\n{\n\tlongword diff = (longword)a - (longword)b;\n\treturn saturate(diff);\n}\n\nword gsm_mult P2((a,b), word a, word b)\n{\n\tif (a == MIN_WORD && b == MIN_WORD) return MAX_WORD;\n\telse return SASR( (longword)a * (longword)b, 15 );\n}\n\nword gsm_mult_r P2((a,b), word a, word b)\n{\n\tif (b == MIN_WORD && a == MIN_WORD) return MAX_WORD;\n\telse {\n\t\tlongword prod = (longword)a * (longword)b + 16384;\n\t\tprod >>= 15;\n\t\treturn prod & 0xFFFF;\n\t}\n}\n\nword gsm_abs P1((a), word a)\n{\n\treturn a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a;\n}\n\nlongword gsm_L_mult P2((a,b),word a, word b)\n{\n\tassert( a != MIN_WORD || b != MIN_WORD );\n\treturn ((longword)a * (longword)b) << 1;\n}\n\nlongword gsm_L_add P2((a,b), longword a, longword b)\n{\n\tif (a < 0) {\n\t\tif (b >= 0) return a + b;\n\t\telse {\n\t\t\tulongword A = (ulongword)-(a + 1) + (ulongword)-(b + 1);\n\t\t\treturn A >= MAX_LONGWORD ? MIN_LONGWORD :-(longword)A-2;\n\t\t}\n\t}\n\telse if (b <= 0) return a + b;\n\telse {\n\t\tulongword A = (ulongword)a + (ulongword)b;\n\t\treturn A > MAX_LONGWORD ? MAX_LONGWORD : A;\n\t}\n}\n\nlongword gsm_L_sub P2((a,b), longword a, longword b)\n{\n\tif (a >= 0) {\n\t\tif (b >= 0) return a - b;\n\t\telse {\n\t\t\t/* a>=0, b<0 */\n\n\t\t\tulongword A = (ulongword)a + -(b + 1);\n\t\t\treturn A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1);\n\t\t}\n\t}\n\telse if (b <= 0) return a - b;\n\telse {\n\t\t/* a<0, b>0 */  \n\n\t\tulongword A = (ulongword)-(a + 1) + b;\n\t\treturn A >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)A - 1;\n\t}\n}\n\nstatic unsigned char const bitoff[ 256 ] = {\n\t 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,\n\t 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n\t 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n\t 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n\t 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n\t 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n\t 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n\t 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\nword gsm_norm P1((a), longword a )\n/*\n * the number of left shifts needed to normalize the 32 bit\n * variable L_var1 for positive values on the interval\n *\n * with minimum of\n * minimum of 1073741824  (01000000000000000000000000000000) and \n * maximum of 2147483647  (01111111111111111111111111111111)\n *\n *\n * and for negative values on the interval with\n * minimum of -2147483648 (-10000000000000000000000000000000) and\n * maximum of -1073741824 ( -1000000000000000000000000000000).\n *\n * in order to normalize the result, the following\n * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 );\n *\n * (That's 'ffs', only from the left, not the right..)\n */\n{\n\tassert(a != 0);\n\n\tif (a < 0) {\n\t\tif (a <= -1073741824) return 0;\n\t\ta = ~a;\n\t}\n\n\treturn    a & 0xffff0000 \n\t\t? ( a & 0xff000000\n\t\t  ?  -1 + bitoff[ 0xFF & (a >> 24) ]\n\t\t  :   7 + bitoff[ 0xFF & (a >> 16) ] )\n\t\t: ( a & 0xff00\n\t\t  ?  15 + bitoff[ 0xFF & (a >> 8) ]\n\t\t  :  23 + bitoff[ 0xFF & a ] );\n}\n\nlongword gsm_L_asl P2((a,n), longword a, int n)\n{\n\tif (n >= 32) return 0;\n\tif (n <= -32) return -(a < 0);\n\tif (n < 0) return gsm_L_asr(a, -n);\n\treturn a << n;\n}\n\nword gsm_asl P2((a,n), word a, int n)\n{\n\tif (n >= 16) return 0;\n\tif (n <= -16) return -(a < 0);\n\tif (n < 0) return gsm_asr(a, -n);\n\treturn a << n;\n}\n\nlongword gsm_L_asr P2((a,n), longword a, int n)\n{\n\tif (n >= 32) return -(a < 0);\n\tif (n <= -32) return 0;\n\tif (n < 0) return a << -n;\n\n#\tifdef\tSASR\n\t\treturn a >> n;\n#\telse\n\t\tif (a >= 0) return a >> n;\n\t\telse return -(longword)( -(ulongword)a >> n );\n#\tendif\n}\n\nword gsm_asr P2((a,n), word a, int n)\n{\n\tif (n >= 16) return -(a < 0);\n\tif (n <= -16) return 0;\n\tif (n < 0) return a << -n;\n\n#\tifdef\tSASR\n\t\treturn a >> n;\n#\telse\n\t\tif (a >= 0) return a >> n;\n\t\telse return -(word)( -(uword)a >> n );\n#\tendif\n}\n\n/* \n *  (From p. 46, end of section 4.2.5)\n *\n *  NOTE: The following lines gives [sic] one correct implementation\n *  \t  of the div(num, denum) arithmetic operation.  Compute div\n *        which is the integer division of num by denum: with denum\n *\t  >= num > 0\n */\n\nword gsm_div P2((num,denum), word num, word denum)\n{\n\tlongword\tL_num   = num;\n\tlongword\tL_denum = denum;\n\tword\t\tdiv \t= 0;\n\tint\t\tk \t= 15;\n\n\t/* The parameter num sometimes becomes zero.\n\t * Although this is explicitly guarded against in 4.2.5,\n\t * we assume that the result should then be zero as well.\n\t */\n\n\t/* assert(num != 0); */\n\n\tassert(num >= 0 && denum >= num);\n\tif (num == 0)\n\t    return 0;\n\n\twhile (k--) {\n\t\tdiv   <<= 1;\n\t\tL_num <<= 1;\n\n\t\tif (L_num >= L_denum) {\n\t\t\tL_num -= L_denum;\n\t\t\tdiv++;\n\t\t}\n\t}\n\n\treturn div;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/code.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/code.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */\n\n#include\t\"config.h\"\n#include \t<string.h>\n\n\n#ifdef\tHAS_STDLIB_H\n#include\t<stdlib.h>\n#else\n#\tinclude \"proto.h\"\n\textern char\t* memcpy P((char *, char *, int));\n#endif\n\n#include\t\"private.h\"\n#include\t\"gsm.h\"\n#include\t\"proto.h\"\n\n/* \n *  4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER \n */\n\nvoid Gsm_Coder P8((S,s,LARc,Nc,bc,Mc,xmaxc,xMc),\n\n\tstruct gsm_state\t* S,\n\n\tword\t* s,\t/* [0..159] samples\t\t  \tIN\t*/\n\n/*\n * The RPE-LTD coder works on a frame by frame basis.  The length of\n * the frame is equal to 160 samples.  Some computations are done\n * once per frame to produce at the output of the coder the\n * LARc[1..8] parameters which are the coded LAR coefficients and \n * also to realize the inverse filtering operation for the entire\n * frame (160 samples of signal d[0..159]).  These parts produce at\n * the output of the coder:\n */\n\n\tword\t* LARc,\t/* [0..7] LAR coefficients\t\tOUT\t*/\n\n/*\n * Procedure 4.2.11 to 4.2.18 are to be executed four times per\n * frame.  That means once for each sub-segment RPE-LTP analysis of\n * 40 samples.  These parts produce at the output of the coder:\n */\n\n\tword\t* Nc,\t/* [0..3] LTP lag\t\t\tOUT \t*/\n\tword\t* bc,\t/* [0..3] coded LTP gain\t\tOUT \t*/\n\tword\t* Mc,\t/* [0..3] RPE grid selection\t\tOUT     */\n\tword\t* xmaxc,/* [0..3] Coded maximum amplitude\tOUT\t*/\n\tword\t* xMc\t/* [13*4] normalized RPE samples\tOUT\t*/\n)\n{\n\tint\tk;\n\tword\t* dp  = S->dp0 + 120;\t/* [ -120...-1 ] */\n\tword\t* dpp = dp;\t\t/* [ 0...39 ]\t */\n\n\tstatic word e[50];\n\n\tword\tso[160];\n\n\tGsm_Preprocess\t\t\t(S, s, so);\n\tGsm_LPC_Analysis\t\t(S, so, LARc);\n\tGsm_Short_Term_Analysis_Filter\t(S, LARc, so);\n\n\tfor (k = 0; k <= 3; k++, xMc += 13) {\n\n\t\tGsm_Long_Term_Predictor\t( S,\n\t\t\t\t\t so+k*40, /* d      [0..39] IN\t*/\n\t\t\t\t\t dp,\t  /* dp  [-120..-1] IN\t*/\n\t\t\t\t\te + 5,\t  /* e      [0..39] OUT\t*/\n\t\t\t\t\tdpp,\t  /* dpp    [0..39] OUT */\n\t\t\t\t\t Nc++,\n\t\t\t\t\t bc++);\n\n\t\tGsm_RPE_Encoding\t( S,\n\t\t\t\t\te + 5,\t/* e\t  ][0..39][ IN/OUT */\n\t\t\t\t\t  xmaxc++, Mc++, xMc );\n\t\t/*\n\t\t * Gsm_Update_of_reconstructed_short_time_residual_signal\n\t\t *\t\t\t( dpp, e + 5, dp );\n\t\t */\n\n\t\t{ register int i;\n\t\t  register longword ltmp;\n\t\t  for (i = 0; i <= 39; i++)\n\t\t\tdp[ i ] = GSM_ADD( e[5 + i], dpp[i] );\n\t\t}\n\t\tdp  += 40;\n\t\tdpp += 40;\n\n\t}\n\t(void)memcpy( (char *)S->dp0, (char *)(S->dp0 + 160),\n\t\t120 * sizeof(*S->dp0) );\n}\n"
  },
  {
    "path": "src/audio/gsm/src/debug.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/debug.c,v 1.2 1993/01/29 18:22:20 jutta Exp $ */\n\n#include \"private.h\"\n\n#ifndef\tNDEBUG\n\n/* If NDEBUG _is_ defined and no debugging should be performed,\n * calls to functions in this module are #defined to nothing\n * in private.h.\n */\n\n#include <stdio.h>\n#include \"proto.h\"\n\nvoid gsm_debug_words P4( (name, from, to, ptr), \n\tchar \t      * name,\n\tint\t\tfrom,\n\tint\t\tto,\n\tword\t\t* ptr)\n{\n\tint \tnprinted = 0;\n\n\tfprintf( stderr, \"%s [%d .. %d]: \", name, from, to );\n\twhile (from <= to) {\n\t\tfprintf(stderr, \"%d \", ptr[ from ] );\n\t\tfrom++;\n\t\tif (nprinted++ >= 7) {\n\t\t\tnprinted = 0;\n\t\t\tif (from < to) putc('\\n', stderr);\n\t\t}\n\t}\n\tputc('\\n', stderr);\n}\n\nvoid gsm_debug_longwords P4( (name, from, to, ptr),\n\tchar \t      * name,\n\tint\t\tfrom,\n\tint\t\tto,\n\tlongword      * ptr)\n{\n\tint \tnprinted = 0;\n\n\tfprintf( stderr, \"%s [%d .. %d]: \", name, from, to );\n\twhile (from <= to) {\n\n                fprintf(stderr, \"%ld \", ptr[ from ] );\n\t\tfrom++;\n\t\tif (nprinted++ >= 7) {\n\t\t\tnprinted = 0;\n\t\t\tif (from < to) putc('\\n', stderr);\n\t\t}\n\t}\n\tputc('\\n', stderr);\n}\n\nvoid gsm_debug_longword P2(  (name, value),\n\tchar\t\t* name,\n\tlongword\t  value\t)\n{\n        fprintf(stderr, \"%s: %ld\\n\", name, (long)value );\n}\n\nvoid gsm_debug_word P2(  (name, value),\n\tchar\t* name,\n\tword\t  value\t)\n{\n        fprintf(stderr, \"%s: %ld\\n\", name, (long)value);\n}\n\n#endif\n"
  },
  {
    "path": "src/audio/gsm/src/decode.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/decode.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */\n\n#include <stdio.h>\n\n#include\t\"private.h\"\n#include\t\"gsm.h\"\n#include\t\"proto.h\"\n\n/*\n *  4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER\n */\n\nstatic void Postprocessing P2((S,s),\n\tstruct gsm_state\t* S,\n\tregister word \t\t* s)\n{\n\tregister int\t\tk;\n\tregister word\t\tmsr = S->msr;\n\tregister longword\tltmp;\t/* for GSM_ADD */\n\tregister word\t\ttmp;\n\n\tfor (k = 160; k--; s++) {\n\t\ttmp = GSM_MULT_R( msr, 28180 );\n\t\tmsr = GSM_ADD(*s, tmp);  \t   /* Deemphasis \t     */\n\t\t*s  = GSM_ADD(msr, msr) & 0xFFF8;  /* Truncation & Upscaling */\n\t}\n\tS->msr = msr;\n}\n\nvoid Gsm_Decoder P8((S,LARcr, Ncr,bcr,Mcr,xmaxcr,xMcr,s),\n\tstruct gsm_state\t* S,\n\n\tword\t\t* LARcr,\t/* [0..7]\t\tIN\t*/\n\n\tword\t\t* Ncr,\t\t/* [0..3] \t\tIN \t*/\n\tword\t\t* bcr,\t\t/* [0..3]\t\tIN\t*/\n\tword\t\t* Mcr,\t\t/* [0..3] \t\tIN \t*/\n\tword\t\t* xmaxcr,\t/* [0..3]\t\tIN \t*/\n\tword\t\t* xMcr,\t\t/* [0..13*4]\t\tIN\t*/\n\n\tword\t\t* s)\t\t/* [0..159]\t\tOUT \t*/\n{\n\tint\t\tj, k;\n\tword\t\terp[40], wt[160];\n\tword\t\t* drp = S->dp0 + 120;\n\n\tfor (j=0; j <= 3; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13) {\n\n\t\tGsm_RPE_Decoding( S, *xmaxcr, *Mcr, xMcr, erp );\n\t\tGsm_Long_Term_Synthesis_Filtering( S, *Ncr, *bcr, erp, drp );\n\n\t\tfor (k = 0; k <= 39; k++) wt[ j * 40 + k ] =  drp[ k ];\n\t}\n\n\tGsm_Short_Term_Synthesis_Filter( S, LARcr, wt, s );\n\tPostprocessing(S, s);\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_create.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\nstatic char const\tident[] = \"$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_create.c,v 1.4 1996/07/02 09:59:05 jutta Exp $\";\n\n#include\t\"config.h\"\n\n#ifdef\tHAS_STRING_H\n#include\t<string.h>\n#else\n#\tinclude \"proto.h\"\n\textern char\t* memset P((char *, int, int));\n#endif\n\n#ifdef\tHAS_STDLIB_H\n#\tinclude\t<stdlib.h>\n#else\n#\tifdef\tHAS_MALLOC_H\n#\t\tinclude \t<malloc.h>\n#\telse\n\t\textern char * malloc();\n#\tendif\n#endif\n\n#include <stdio.h>\n\n#include \"gsm.h\"\n#include \"private.h\"\n#include \"proto.h\"\n\ngsm gsm_create P0()\n{\n\tgsm  r;\n\n\tr = (gsm)malloc(sizeof(struct gsm_state));\n\tif (!r) return r;\n\n\tmemset((char *)r, 0, sizeof(*r));\n\tr->nrp = 40;\n\n\treturn r;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_decode.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_decode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\nint gsm_decode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target)\n{\n\tword  \tLARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];\n\n#ifdef WAV49\n\tif (s->wav_fmt) {\n\n\t\tuword sr = 0;\n\n\t\ts->frame_index = !s->frame_index;\n\t\tif (s->frame_index) {\n\n\t\t\tsr = *c++;\n\t\t\tLARc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tLARc[2] = sr & 0x1f;  sr >>= 5;\n\t\t\tLARc[3] = sr & 0x1f;  sr >>= 5;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[4] = sr & 0xf;  sr >>= 4;\n\t\t\tLARc[5] = sr & 0xf;  sr >>= 4;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 5 */\n\t\t\tLARc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tLARc[7] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[0] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[0] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[1] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[2] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[3] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[4] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[5] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 10 */\n\t\t\txmc[6] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[7] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[8] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[9] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[10] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[11] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[12] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[1] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[13] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 15 */\n\t\t\txmc[14] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[15] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[16] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[17] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[18] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[19] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[20] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[21] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[22] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[23] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[24] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[25] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\t\t\t/* 20 */\n\t\t\tNc[2] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[2] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[26] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[27] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[28] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[29] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[30] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[31] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[32] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[33] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[34] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 25 */\n\t\t\txmc[35] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[36] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[37] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[38] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[3] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[3] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[39] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[40] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[41] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 30 */\n\t\t\txmc[42] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[43] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[44] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[45] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[46] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[47] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[48] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[49] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[50] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[51] = sr & 0x7;  sr >>= 3;\n\n\t\t\ts->frame_chain = sr & 0xf;\n\t\t}\n\t\telse {\n\t\t\tsr = s->frame_chain;\n\t\t\tsr |= (uword)*c++ << 4;\t\t\t/* 1 */\n\t\t\tLARc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\tLARc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr = *c++;\n\t\t\tLARc[2] = sr & 0x1f;  sr >>= 5;\n\t\t\tsr |= (uword)*c++ << 3;\n\t\t\tLARc[3] = sr & 0x1f;  sr >>= 5;\n\t\t\tLARc[4] = sr & 0xf;  sr >>= 4;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[5] = sr & 0xf;  sr >>= 4;\n\t\t\tLARc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tLARc[7] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 5 */\n\t\t\tNc[0] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\tbc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[0] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[1] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[2] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[3] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[4] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[5] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 10 */\n\t\t\txmc[7] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[8] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[9] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[10] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[11] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[12] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[1] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\tbc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[13] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[14] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 15 */\n\t\t\txmc[15] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[16] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[17] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[18] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[19] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[20] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[21] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[22] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[23] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[24] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[25] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[2] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 20 */\n\t\t\tbc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[2] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[26] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[27] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\n\t\t\txmc[28] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[29] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[30] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[31] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[32] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[33] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[34] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[35] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 25 */\n\t\t\txmc[36] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[37] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[38] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[3] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\n\t\t\tbc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[3] = sr & 0x3f;  sr >>= 6;\n\t\t\txmc[39] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[40] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[41] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[42] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[43] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 30 */\n\t\t\txmc[44] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[45] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[46] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[47] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[48] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[49] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[50] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[51] = sr & 0x7;  sr >>= 3;\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\t/* GSM_MAGIC  = (*c >> 4) & 0xF; */\n\n\t\tif (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;\n\n\t\tLARc[0]  = (*c++ & 0xF) << 2;\t\t/* 1 */\n\t\tLARc[0] |= (*c >> 6) & 0x3;\n\t\tLARc[1]  = *c++ & 0x3F;\n\t\tLARc[2]  = (*c >> 3) & 0x1F;\n\t\tLARc[3]  = (*c++ & 0x7) << 2;\n\t\tLARc[3] |= (*c >> 6) & 0x3;\n\t\tLARc[4]  = (*c >> 2) & 0xF;\n\t\tLARc[5]  = (*c++ & 0x3) << 2;\n\t\tLARc[5] |= (*c >> 6) & 0x3;\n\t\tLARc[6]  = (*c >> 3) & 0x7;\n\t\tLARc[7]  = *c++ & 0x7;\n\t\tNc[0]  = (*c >> 1) & 0x7F;\n\t\tbc[0]  = (*c++ & 0x1) << 1;\n\t\tbc[0] |= (*c >> 7) & 0x1;\n\t\tMc[0]  = (*c >> 5) & 0x3;\n\t\txmaxc[0]  = (*c++ & 0x1F) << 1;\n\t\txmaxc[0] |= (*c >> 7) & 0x1;\n\t\txmc[0]  = (*c >> 4) & 0x7;\n\t\txmc[1]  = (*c >> 1) & 0x7;\n\t\txmc[2]  = (*c++ & 0x1) << 2;\n\t\txmc[2] |= (*c >> 6) & 0x3;\n\t\txmc[3]  = (*c >> 3) & 0x7;\n\t\txmc[4]  = *c++ & 0x7;\n\t\txmc[5]  = (*c >> 5) & 0x7;\n\t\txmc[6]  = (*c >> 2) & 0x7;\n\t\txmc[7]  = (*c++ & 0x3) << 1;\t\t/* 10 */\n\t\txmc[7] |= (*c >> 7) & 0x1;\n\t\txmc[8]  = (*c >> 4) & 0x7;\n\t\txmc[9]  = (*c >> 1) & 0x7;\n\t\txmc[10]  = (*c++ & 0x1) << 2;\n\t\txmc[10] |= (*c >> 6) & 0x3;\n\t\txmc[11]  = (*c >> 3) & 0x7;\n\t\txmc[12]  = *c++ & 0x7;\n\t\tNc[1]  = (*c >> 1) & 0x7F;\n\t\tbc[1]  = (*c++ & 0x1) << 1;\n\t\tbc[1] |= (*c >> 7) & 0x1;\n\t\tMc[1]  = (*c >> 5) & 0x3;\n\t\txmaxc[1]  = (*c++ & 0x1F) << 1;\n\t\txmaxc[1] |= (*c >> 7) & 0x1;\n\t\txmc[13]  = (*c >> 4) & 0x7;\n\t\txmc[14]  = (*c >> 1) & 0x7;\n\t\txmc[15]  = (*c++ & 0x1) << 2;\n\t\txmc[15] |= (*c >> 6) & 0x3;\n\t\txmc[16]  = (*c >> 3) & 0x7;\n\t\txmc[17]  = *c++ & 0x7;\n\t\txmc[18]  = (*c >> 5) & 0x7;\n\t\txmc[19]  = (*c >> 2) & 0x7;\n\t\txmc[20]  = (*c++ & 0x3) << 1;\n\t\txmc[20] |= (*c >> 7) & 0x1;\n\t\txmc[21]  = (*c >> 4) & 0x7;\n\t\txmc[22]  = (*c >> 1) & 0x7;\n\t\txmc[23]  = (*c++ & 0x1) << 2;\n\t\txmc[23] |= (*c >> 6) & 0x3;\n\t\txmc[24]  = (*c >> 3) & 0x7;\n\t\txmc[25]  = *c++ & 0x7;\n\t\tNc[2]  = (*c >> 1) & 0x7F;\n\t\tbc[2]  = (*c++ & 0x1) << 1;\t\t/* 20 */\n\t\tbc[2] |= (*c >> 7) & 0x1;\n\t\tMc[2]  = (*c >> 5) & 0x3;\n\t\txmaxc[2]  = (*c++ & 0x1F) << 1;\n\t\txmaxc[2] |= (*c >> 7) & 0x1;\n\t\txmc[26]  = (*c >> 4) & 0x7;\n\t\txmc[27]  = (*c >> 1) & 0x7;\n\t\txmc[28]  = (*c++ & 0x1) << 2;\n\t\txmc[28] |= (*c >> 6) & 0x3;\n\t\txmc[29]  = (*c >> 3) & 0x7;\n\t\txmc[30]  = *c++ & 0x7;\n\t\txmc[31]  = (*c >> 5) & 0x7;\n\t\txmc[32]  = (*c >> 2) & 0x7;\n\t\txmc[33]  = (*c++ & 0x3) << 1;\n\t\txmc[33] |= (*c >> 7) & 0x1;\n\t\txmc[34]  = (*c >> 4) & 0x7;\n\t\txmc[35]  = (*c >> 1) & 0x7;\n\t\txmc[36]  = (*c++ & 0x1) << 2;\n\t\txmc[36] |= (*c >> 6) & 0x3;\n\t\txmc[37]  = (*c >> 3) & 0x7;\n\t\txmc[38]  = *c++ & 0x7;\n\t\tNc[3]  = (*c >> 1) & 0x7F;\n\t\tbc[3]  = (*c++ & 0x1) << 1;\n\t\tbc[3] |= (*c >> 7) & 0x1;\n\t\tMc[3]  = (*c >> 5) & 0x3;\n\t\txmaxc[3]  = (*c++ & 0x1F) << 1;\n\t\txmaxc[3] |= (*c >> 7) & 0x1;\n\t\txmc[39]  = (*c >> 4) & 0x7;\n\t\txmc[40]  = (*c >> 1) & 0x7;\n\t\txmc[41]  = (*c++ & 0x1) << 2;\n\t\txmc[41] |= (*c >> 6) & 0x3;\n\t\txmc[42]  = (*c >> 3) & 0x7;\n\t\txmc[43]  = *c++ & 0x7;\t\t\t/* 30  */\n\t\txmc[44]  = (*c >> 5) & 0x7;\n\t\txmc[45]  = (*c >> 2) & 0x7;\n\t\txmc[46]  = (*c++ & 0x3) << 1;\n\t\txmc[46] |= (*c >> 7) & 0x1;\n\t\txmc[47]  = (*c >> 4) & 0x7;\n\t\txmc[48]  = (*c >> 1) & 0x7;\n\t\txmc[49]  = (*c++ & 0x1) << 2;\n\t\txmc[49] |= (*c >> 6) & 0x3;\n\t\txmc[50]  = (*c >> 3) & 0x7;\n\t\txmc[51]  = *c & 0x7;\t\t\t/* 33 */\n\t}\n\n\tGsm_Decoder(s, LARc, Nc, bc, Mc, xmaxc, xmc, target);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_destroy.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_destroy.c,v 1.3 1994/11/28 19:52:25 jutta Exp $ */\n\n#include \"gsm.h\"\n#include \"config.h\"\n#include \"proto.h\"\n\n#ifdef\tHAS_STDLIB_H\n#\tinclude\t<stdlib.h>\n#else\n#\tifdef\tHAS_MALLOC_H\n#\t\tinclude \t<malloc.h>\n#\telse\n\t\textern void free();\n#\tendif\n#endif\n\nvoid gsm_destroy P1((S), gsm S)\n{\n\tif (S) free((char *)S);\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_encode.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_encode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */\n\n#include \"private.h\"\n#include \"gsm.h\"\n#include \"proto.h\"\n\nvoid gsm_encode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c)\n{\n\tword\t \tLARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];\n\n\tGsm_Coder(s, source, LARc, Nc, bc, Mc, xmaxc, xmc);\n\n\n\t/*\tvariable\tsize\n\n\t\tGSM_MAGIC\t4\n\n\t\tLARc[0]\t\t6\n\t\tLARc[1]\t\t6\n\t\tLARc[2]\t\t5\n\t\tLARc[3]\t\t5\n\t\tLARc[4]\t\t4\n\t\tLARc[5]\t\t4\n\t\tLARc[6]\t\t3\n\t\tLARc[7]\t\t3\n\n\t\tNc[0]\t\t7\n\t\tbc[0]\t\t2\n\t\tMc[0]\t\t2\n\t\txmaxc[0]\t6\n\t\txmc[0]\t\t3\n\t\txmc[1]\t\t3\n\t\txmc[2]\t\t3\n\t\txmc[3]\t\t3\n\t\txmc[4]\t\t3\n\t\txmc[5]\t\t3\n\t\txmc[6]\t\t3\n\t\txmc[7]\t\t3\n\t\txmc[8]\t\t3\n\t\txmc[9]\t\t3\n\t\txmc[10]\t\t3\n\t\txmc[11]\t\t3\n\t\txmc[12]\t\t3\n\n\t\tNc[1]\t\t7\n\t\tbc[1]\t\t2\n\t\tMc[1]\t\t2\n\t\txmaxc[1]\t6\n\t\txmc[13]\t\t3\n\t\txmc[14]\t\t3\n\t\txmc[15]\t\t3\n\t\txmc[16]\t\t3\n\t\txmc[17]\t\t3\n\t\txmc[18]\t\t3\n\t\txmc[19]\t\t3\n\t\txmc[20]\t\t3\n\t\txmc[21]\t\t3\n\t\txmc[22]\t\t3\n\t\txmc[23]\t\t3\n\t\txmc[24]\t\t3\n\t\txmc[25]\t\t3\n\n\t\tNc[2]\t\t7\n\t\tbc[2]\t\t2\n\t\tMc[2]\t\t2\n\t\txmaxc[2]\t6\n\t\txmc[26]\t\t3\n\t\txmc[27]\t\t3\n\t\txmc[28]\t\t3\n\t\txmc[29]\t\t3\n\t\txmc[30]\t\t3\n\t\txmc[31]\t\t3\n\t\txmc[32]\t\t3\n\t\txmc[33]\t\t3\n\t\txmc[34]\t\t3\n\t\txmc[35]\t\t3\n\t\txmc[36]\t\t3\n\t\txmc[37]\t\t3\n\t\txmc[38]\t\t3\n\n\t\tNc[3]\t\t7\n\t\tbc[3]\t\t2\n\t\tMc[3]\t\t2\n\t\txmaxc[3]\t6\n\t\txmc[39]\t\t3\n\t\txmc[40]\t\t3\n\t\txmc[41]\t\t3\n\t\txmc[42]\t\t3\n\t\txmc[43]\t\t3\n\t\txmc[44]\t\t3\n\t\txmc[45]\t\t3\n\t\txmc[46]\t\t3\n\t\txmc[47]\t\t3\n\t\txmc[48]\t\t3\n\t\txmc[49]\t\t3\n\t\txmc[50]\t\t3\n\t\txmc[51]\t\t3\n\t*/\n\n#ifdef WAV49\n\n\tif (s->wav_fmt) {\n\t\ts->frame_index = !s->frame_index;\n\t\tif (s->frame_index) {\n\n\t\t\tuword sr;\n\n\t\t\tsr = 0;\n\t\t\tsr = sr >> 6 | LARc[0] << 10;\n\t\t\tsr = sr >> 6 | LARc[1] << 10;\n\t\t\t*c++ = sr >> 4;\n\t\t\tsr = sr >> 5 | LARc[2] << 11;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 5 | LARc[3] << 11;\n\t\t\tsr = sr >> 4 | LARc[4] << 12;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 4 | LARc[5] << 12;\n\t\t\tsr = sr >> 3 | LARc[6] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | LARc[7] << 13;\n\t\t\tsr = sr >> 7 | Nc[0] << 9;\n\t\t\t*c++ = sr >> 5;\n\t\t\tsr = sr >> 2 | bc[0] << 14;\n\t\t\tsr = sr >> 2 | Mc[0] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[0] << 10;\n\t\t\t*c++ = sr >> 3;\n\t\t\tsr = sr >> 3 | xmc[0] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[1] << 13;\n\t\t\tsr = sr >> 3 | xmc[2] << 13;\n\t\t\tsr = sr >> 3 | xmc[3] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[4] << 13;\n\t\t\tsr = sr >> 3 | xmc[5] << 13;\n\t\t\tsr = sr >> 3 | xmc[6] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[7] << 13;\n\t\t\tsr = sr >> 3 | xmc[8] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[9] << 13;\n\t\t\tsr = sr >> 3 | xmc[10] << 13;\n\t\t\tsr = sr >> 3 | xmc[11] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[12] << 13;\n\t\t\tsr = sr >> 7 | Nc[1] << 9;\n\t\t\t*c++ = sr >> 5;\n\t\t\tsr = sr >> 2 | bc[1] << 14;\n\t\t\tsr = sr >> 2 | Mc[1] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[1] << 10;\n\t\t\t*c++ = sr >> 3;\n\t\t\tsr = sr >> 3 | xmc[13] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[14] << 13;\n\t\t\tsr = sr >> 3 | xmc[15] << 13;\n\t\t\tsr = sr >> 3 | xmc[16] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[17] << 13;\n\t\t\tsr = sr >> 3 | xmc[18] << 13;\n\t\t\tsr = sr >> 3 | xmc[19] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[20] << 13;\n\t\t\tsr = sr >> 3 | xmc[21] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[22] << 13;\n\t\t\tsr = sr >> 3 | xmc[23] << 13;\n\t\t\tsr = sr >> 3 | xmc[24] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[25] << 13;\n\t\t\tsr = sr >> 7 | Nc[2] << 9;\n\t\t\t*c++ = sr >> 5;\n\t\t\tsr = sr >> 2 | bc[2] << 14;\n\t\t\tsr = sr >> 2 | Mc[2] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[2] << 10;\n\t\t\t*c++ = sr >> 3;\n\t\t\tsr = sr >> 3 | xmc[26] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[27] << 13;\n\t\t\tsr = sr >> 3 | xmc[28] << 13;\n\t\t\tsr = sr >> 3 | xmc[29] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[30] << 13;\n\t\t\tsr = sr >> 3 | xmc[31] << 13;\n\t\t\tsr = sr >> 3 | xmc[32] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[33] << 13;\n\t\t\tsr = sr >> 3 | xmc[34] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[35] << 13;\n\t\t\tsr = sr >> 3 | xmc[36] << 13;\n\t\t\tsr = sr >> 3 | xmc[37] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[38] << 13;\n\t\t\tsr = sr >> 7 | Nc[3] << 9;\n\t\t\t*c++ = sr >> 5;\n\t\t\tsr = sr >> 2 | bc[3] << 14;\n\t\t\tsr = sr >> 2 | Mc[3] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[3] << 10;\n\t\t\t*c++ = sr >> 3;\n\t\t\tsr = sr >> 3 | xmc[39] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[40] << 13;\n\t\t\tsr = sr >> 3 | xmc[41] << 13;\n\t\t\tsr = sr >> 3 | xmc[42] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[43] << 13;\n\t\t\tsr = sr >> 3 | xmc[44] << 13;\n\t\t\tsr = sr >> 3 | xmc[45] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[46] << 13;\n\t\t\tsr = sr >> 3 | xmc[47] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[48] << 13;\n\t\t\tsr = sr >> 3 | xmc[49] << 13;\n\t\t\tsr = sr >> 3 | xmc[50] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[51] << 13;\n\t\t\tsr = sr >> 4;\n\t\t\t*c = sr >> 8;\n\t\t\ts->frame_chain = *c;\n\t\t}\n\t\telse {\n\t\t\tuword sr;\n\n\t\t\tsr = 0;\n\t\t\tsr = sr >> 4 | s->frame_chain << 12;\n\t\t\tsr = sr >> 6 | LARc[0] << 10;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 6 | LARc[1] << 10;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 5 | LARc[2] << 11;\n\t\t\tsr = sr >> 5 | LARc[3] << 11;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 4 | LARc[4] << 12;\n\t\t\tsr = sr >> 4 | LARc[5] << 12;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | LARc[6] << 13;\n\t\t\tsr = sr >> 3 | LARc[7] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 7 | Nc[0] << 9;\n\t\t\tsr = sr >> 2 | bc[0] << 14;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 2 | Mc[0] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[0] << 10;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[0] << 13;\n\t\t\tsr = sr >> 3 | xmc[1] << 13;\n\t\t\tsr = sr >> 3 | xmc[2] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[3] << 13;\n\t\t\tsr = sr >> 3 | xmc[4] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[5] << 13;\n\t\t\tsr = sr >> 3 | xmc[6] << 13;\n\t\t\tsr = sr >> 3 | xmc[7] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[8] << 13;\n\t\t\tsr = sr >> 3 | xmc[9] << 13;\n\t\t\tsr = sr >> 3 | xmc[10] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[11] << 13;\n\t\t\tsr = sr >> 3 | xmc[12] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 7 | Nc[1] << 9;\n\t\t\tsr = sr >> 2 | bc[1] << 14;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 2 | Mc[1] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[1] << 10;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[13] << 13;\n\t\t\tsr = sr >> 3 | xmc[14] << 13;\n\t\t\tsr = sr >> 3 | xmc[15] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[16] << 13;\n\t\t\tsr = sr >> 3 | xmc[17] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[18] << 13;\n\t\t\tsr = sr >> 3 | xmc[19] << 13;\n\t\t\tsr = sr >> 3 | xmc[20] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[21] << 13;\n\t\t\tsr = sr >> 3 | xmc[22] << 13;\n\t\t\tsr = sr >> 3 | xmc[23] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[24] << 13;\n\t\t\tsr = sr >> 3 | xmc[25] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 7 | Nc[2] << 9;\n\t\t\tsr = sr >> 2 | bc[2] << 14;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 2 | Mc[2] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[2] << 10;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[26] << 13;\n\t\t\tsr = sr >> 3 | xmc[27] << 13;\n\t\t\tsr = sr >> 3 | xmc[28] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[29] << 13;\n\t\t\tsr = sr >> 3 | xmc[30] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[31] << 13;\n\t\t\tsr = sr >> 3 | xmc[32] << 13;\n\t\t\tsr = sr >> 3 | xmc[33] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[34] << 13;\n\t\t\tsr = sr >> 3 | xmc[35] << 13;\n\t\t\tsr = sr >> 3 | xmc[36] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[37] << 13;\n\t\t\tsr = sr >> 3 | xmc[38] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 7 | Nc[3] << 9;\n\t\t\tsr = sr >> 2 | bc[3] << 14;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 2 | Mc[3] << 14;\n\t\t\tsr = sr >> 6 | xmaxc[3] << 10;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[39] << 13;\n\t\t\tsr = sr >> 3 | xmc[40] << 13;\n\t\t\tsr = sr >> 3 | xmc[41] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[42] << 13;\n\t\t\tsr = sr >> 3 | xmc[43] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t\tsr = sr >> 3 | xmc[44] << 13;\n\t\t\tsr = sr >> 3 | xmc[45] << 13;\n\t\t\tsr = sr >> 3 | xmc[46] << 13;\n\t\t\t*c++ = sr >> 7;\n\t\t\tsr = sr >> 3 | xmc[47] << 13;\n\t\t\tsr = sr >> 3 | xmc[48] << 13;\n\t\t\tsr = sr >> 3 | xmc[49] << 13;\n\t\t\t*c++ = sr >> 6;\n\t\t\tsr = sr >> 3 | xmc[50] << 13;\n\t\t\tsr = sr >> 3 | xmc[51] << 13;\n\t\t\t*c++ = sr >> 8;\n\t\t}\n\t}\n\n\telse\n\n#endif\t/* WAV49 */\n\t{\n\n\t\t*c++ =   ((GSM_MAGIC & 0xF) << 4)\t\t/* 1 */\n\t\t       | ((LARc[0] >> 2) & 0xF);\n\t\t*c++ =   ((LARc[0] & 0x3) << 6)\n\t\t       | (LARc[1] & 0x3F);\n\t\t*c++ =   ((LARc[2] & 0x1F) << 3)\n\t\t       | ((LARc[3] >> 2) & 0x7);\n\t\t*c++ =   ((LARc[3] & 0x3) << 6)\n\t\t       | ((LARc[4] & 0xF) << 2)\n\t\t       | ((LARc[5] >> 2) & 0x3);\n\t\t*c++ =   ((LARc[5] & 0x3) << 6)\n\t\t       | ((LARc[6] & 0x7) << 3)\n\t\t       | (LARc[7] & 0x7);\n\t\t*c++ =   ((Nc[0] & 0x7F) << 1)\n\t\t       | ((bc[0] >> 1) & 0x1);\n\t\t*c++ =   ((bc[0] & 0x1) << 7)\n\t\t       | ((Mc[0] & 0x3) << 5)\n\t\t       | ((xmaxc[0] >> 1) & 0x1F);\n\t\t*c++ =   ((xmaxc[0] & 0x1) << 7)\n\t\t       | ((xmc[0] & 0x7) << 4)\n\t\t       | ((xmc[1] & 0x7) << 1)\n\t\t       | ((xmc[2] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[2] & 0x3) << 6)\n\t\t       | ((xmc[3] & 0x7) << 3)\n\t\t       | (xmc[4] & 0x7);\n\t\t*c++ =   ((xmc[5] & 0x7) << 5)\t\t\t/* 10 */\n\t\t       | ((xmc[6] & 0x7) << 2)\n\t\t       | ((xmc[7] >> 1) & 0x3);\n\t\t*c++ =   ((xmc[7] & 0x1) << 7)\n\t\t       | ((xmc[8] & 0x7) << 4)\n\t\t       | ((xmc[9] & 0x7) << 1)\n\t\t       | ((xmc[10] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[10] & 0x3) << 6)\n\t\t       | ((xmc[11] & 0x7) << 3)\n\t\t       | (xmc[12] & 0x7);\n\t\t*c++ =   ((Nc[1] & 0x7F) << 1)\n\t\t       | ((bc[1] >> 1) & 0x1);\n\t\t*c++ =   ((bc[1] & 0x1) << 7)\n\t\t       | ((Mc[1] & 0x3) << 5)\n\t\t       | ((xmaxc[1] >> 1) & 0x1F);\n\t\t*c++ =   ((xmaxc[1] & 0x1) << 7)\n\t\t       | ((xmc[13] & 0x7) << 4)\n\t\t       | ((xmc[14] & 0x7) << 1)\n\t\t       | ((xmc[15] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[15] & 0x3) << 6)\n\t\t       | ((xmc[16] & 0x7) << 3)\n\t\t       | (xmc[17] & 0x7);\n\t\t*c++ =   ((xmc[18] & 0x7) << 5)\n\t\t       | ((xmc[19] & 0x7) << 2)\n\t\t       | ((xmc[20] >> 1) & 0x3);\n\t\t*c++ =   ((xmc[20] & 0x1) << 7)\n\t\t       | ((xmc[21] & 0x7) << 4)\n\t\t       | ((xmc[22] & 0x7) << 1)\n\t\t       | ((xmc[23] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[23] & 0x3) << 6)\n\t\t       | ((xmc[24] & 0x7) << 3)\n\t\t       | (xmc[25] & 0x7);\n\t\t*c++ =   ((Nc[2] & 0x7F) << 1)\t\t\t/* 20 */\n\t\t       | ((bc[2] >> 1) & 0x1);\n\t\t*c++ =   ((bc[2] & 0x1) << 7)\n\t\t       | ((Mc[2] & 0x3) << 5)\n\t\t       | ((xmaxc[2] >> 1) & 0x1F);\n\t\t*c++ =   ((xmaxc[2] & 0x1) << 7)\n\t\t       | ((xmc[26] & 0x7) << 4)\n\t\t       | ((xmc[27] & 0x7) << 1)\n\t\t       | ((xmc[28] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[28] & 0x3) << 6)\n\t\t       | ((xmc[29] & 0x7) << 3)\n\t\t       | (xmc[30] & 0x7);\n\t\t*c++ =   ((xmc[31] & 0x7) << 5)\n\t\t       | ((xmc[32] & 0x7) << 2)\n\t\t       | ((xmc[33] >> 1) & 0x3);\n\t\t*c++ =   ((xmc[33] & 0x1) << 7)\n\t\t       | ((xmc[34] & 0x7) << 4)\n\t\t       | ((xmc[35] & 0x7) << 1)\n\t\t       | ((xmc[36] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[36] & 0x3) << 6)\n\t\t       | ((xmc[37] & 0x7) << 3)\n\t\t       | (xmc[38] & 0x7);\n\t\t*c++ =   ((Nc[3] & 0x7F) << 1)\n\t\t       | ((bc[3] >> 1) & 0x1);\n\t\t*c++ =   ((bc[3] & 0x1) << 7)\n\t\t       | ((Mc[3] & 0x3) << 5)\n\t\t       | ((xmaxc[3] >> 1) & 0x1F);\n\t\t*c++ =   ((xmaxc[3] & 0x1) << 7)\n\t\t       | ((xmc[39] & 0x7) << 4)\n\t\t       | ((xmc[40] & 0x7) << 1)\n\t\t       | ((xmc[41] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[41] & 0x3) << 6)\t\t\t/* 30 */\n\t\t       | ((xmc[42] & 0x7) << 3)\n\t\t       | (xmc[43] & 0x7);\n\t\t*c++ =   ((xmc[44] & 0x7) << 5)\n\t\t       | ((xmc[45] & 0x7) << 2)\n\t\t       | ((xmc[46] >> 1) & 0x3);\n\t\t*c++ =   ((xmc[46] & 0x1) << 7)\n\t\t       | ((xmc[47] & 0x7) << 4)\n\t\t       | ((xmc[48] & 0x7) << 1)\n\t\t       | ((xmc[49] >> 2) & 0x1);\n\t\t*c++ =   ((xmc[49] & 0x3) << 6)\n\t\t       | ((xmc[50] & 0x7) << 3)\n\t\t       | (xmc[51] & 0x7);\n\n\t}\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_explode.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_explode.c,v 1.2 1996/07/02 14:32:42 jutta Exp jutta $ */\n\n#include \"private.h\"\n#include \"gsm.h\"\n#include \"proto.h\"\n\nint gsm_explode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target)\n{\n#\tdefine\tLARc\ttarget\n#\tdefine\tNc\t*((gsm_signal (*) [17])(target + 8))\n#\tdefine\tbc\t*((gsm_signal (*) [17])(target + 9))\n#\tdefine\tMc\t*((gsm_signal (*) [17])(target + 10))\n#\tdefine\txmaxc\t*((gsm_signal (*) [17])(target + 11))\n\n\n#ifdef WAV49\n\tif (s->wav_fmt) {\n\n\t\tuword sr = 0;\n\n\t\tif (s->frame_index == 1) {\n\n\t\t\tsr = *c++;\n\t\t\tLARc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tLARc[2] = sr & 0x1f;  sr >>= 5;\n\t\t\tLARc[3] = sr & 0x1f;  sr >>= 5;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[4] = sr & 0xf;  sr >>= 4;\n\t\t\tLARc[5] = sr & 0xf;  sr >>= 4;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 5 */\n\t\t\tLARc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tLARc[7] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[0] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[0] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(target + 12)\n\t\t\txmc[0] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[1] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[2] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[3] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[4] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[5] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 10 */\n\t\t\txmc[6] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[7] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[8] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[9] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[10] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[11] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[12] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[1] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[1] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(target + 29 - 13)\n\n\t\t\txmc[13] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 15 */\n\t\t\txmc[14] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[15] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[16] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[17] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[18] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[19] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[20] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[21] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[22] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[23] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[24] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[25] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\t\t\t/* 20 */\n\t\t\tNc[2] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[2] = sr & 0x3f;  sr >>= 6;\n\n#undef\txmc\n#define\txmc\t(target + 46 - 26)\n\n\t\t\txmc[26] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[27] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[28] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[29] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[30] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[31] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[32] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[33] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[34] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 25 */\n\t\t\txmc[35] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[36] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[37] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[38] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[3] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[3] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(target + 63 - 39)\n\n\t\t\txmc[39] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[40] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[41] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 30 */\n\t\t\txmc[42] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[43] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[44] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[45] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[46] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[47] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[48] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[49] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[50] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[51] = sr & 0x7;  sr >>= 3;\n\n\t\t\ts->frame_chain = sr & 0xf;\n\t\t}\n\t\telse {\n\t\t\tsr = s->frame_chain;\n\t\t\tsr |= (uword)*c++ << 4;\t\t\t/* 1 */\n\t\t\tLARc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\tLARc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr = *c++;\n\t\t\tLARc[2] = sr & 0x1f;  sr >>= 5;\n\t\t\tsr |= (uword)*c++ << 3;\n\t\t\tLARc[3] = sr & 0x1f;  sr >>= 5;\n\t\t\tLARc[4] = sr & 0xf;  sr >>= 4;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[5] = sr & 0xf;  sr >>= 4;\n\t\t\tLARc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tLARc[7] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 5 */\n\t\t\tNc[0] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\tbc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[0] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(target + 12)\n\t\t\txmc[0] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[1] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[2] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[3] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[4] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[5] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 10 */\n\t\t\txmc[7] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[8] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[9] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[10] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[11] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[12] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[1] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\tbc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[1] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(target + 29 - 13)\n\n\t\t\txmc[13] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[14] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 15 */\n\t\t\txmc[15] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[16] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[17] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[18] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[19] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[20] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[21] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[22] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[23] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[24] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[25] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[2] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 20 */\n\t\t\tbc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[2] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(target + 46 - 26)\n\t\t\txmc[26] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[27] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\n\t\t\txmc[28] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[29] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[30] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[31] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[32] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[33] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[34] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[35] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 25 */\n\t\t\txmc[36] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[37] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[38] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[3] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\n\t\t\tbc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[3] = sr & 0x3f;  sr >>= 6;\n\n#undef\txmc\n#define\txmc\t(target + 63 - 39)\n\n\t\t\txmc[39] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[40] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[41] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[42] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[43] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 30 */\n\t\t\txmc[44] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[45] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[46] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[47] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[48] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[49] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[50] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[51] = sr & 0x7;  sr >>= 3;\n\t\t}\n\t}\n\telse \n#endif\n\t{\n\t/* GSM_MAGIC  = (*c >> 4) & 0xF; */\n\n\tif (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;\n\n\tLARc[0]  = (*c++ & 0xF) << 2;\t\t/* 1 */\n\tLARc[0] |= (*c >> 6) & 0x3;\n\tLARc[1]  = *c++ & 0x3F;\n\tLARc[2]  = (*c >> 3) & 0x1F;\n\tLARc[3]  = (*c++ & 0x7) << 2;\n\tLARc[3] |= (*c >> 6) & 0x3;\n\tLARc[4]  = (*c >> 2) & 0xF;\n\tLARc[5]  = (*c++ & 0x3) << 2;\n\tLARc[5] |= (*c >> 6) & 0x3;\n\tLARc[6]  = (*c >> 3) & 0x7;\n\tLARc[7]  = *c++ & 0x7;\n\n\tNc[0]  = (*c >> 1) & 0x7F;\n\n\tbc[0]  = (*c++ & 0x1) << 1;\n\tbc[0] |= (*c >> 7) & 0x1;\n\n\tMc[0]  = (*c >> 5) & 0x3;\n\n\txmaxc[0]  = (*c++ & 0x1F) << 1;\n\txmaxc[0] |= (*c >> 7) & 0x1;\n\n#undef\txmc\n#define\txmc\t(target + 12)\n\n\txmc[0]  = (*c >> 4) & 0x7;\n\txmc[1]  = (*c >> 1) & 0x7;\n\txmc[2]  = (*c++ & 0x1) << 2;\n\txmc[2] |= (*c >> 6) & 0x3;\n\txmc[3]  = (*c >> 3) & 0x7;\n\txmc[4]  = *c++ & 0x7;\n\txmc[5]  = (*c >> 5) & 0x7;\n\txmc[6]  = (*c >> 2) & 0x7;\n\txmc[7]  = (*c++ & 0x3) << 1;\t\t/* 10 */\n\txmc[7] |= (*c >> 7) & 0x1;\n\txmc[8]  = (*c >> 4) & 0x7;\n\txmc[9]  = (*c >> 1) & 0x7;\n\txmc[10]  = (*c++ & 0x1) << 2;\n\txmc[10] |= (*c >> 6) & 0x3;\n\txmc[11]  = (*c >> 3) & 0x7;\n\txmc[12]  = *c++ & 0x7;\n\n\tNc[1]  = (*c >> 1) & 0x7F;\n\n\tbc[1]  = (*c++ & 0x1) << 1;\n\tbc[1] |= (*c >> 7) & 0x1;\n\n\tMc[1]  = (*c >> 5) & 0x3;\n\n\txmaxc[1]  = (*c++ & 0x1F) << 1;\n\txmaxc[1] |= (*c >> 7) & 0x1;\n\n#undef\txmc\n#define\txmc\t(target + 29 - 13)\n\n\txmc[13]  = (*c >> 4) & 0x7;\n\txmc[14]  = (*c >> 1) & 0x7;\n\txmc[15]  = (*c++ & 0x1) << 2;\n\txmc[15] |= (*c >> 6) & 0x3;\n\txmc[16]  = (*c >> 3) & 0x7;\n\txmc[17]  = *c++ & 0x7;\n\txmc[18]  = (*c >> 5) & 0x7;\n\txmc[19]  = (*c >> 2) & 0x7;\n\txmc[20]  = (*c++ & 0x3) << 1;\n\txmc[20] |= (*c >> 7) & 0x1;\n\txmc[21]  = (*c >> 4) & 0x7;\n\txmc[22]  = (*c >> 1) & 0x7;\n\txmc[23]  = (*c++ & 0x1) << 2;\n\txmc[23] |= (*c >> 6) & 0x3;\n\txmc[24]  = (*c >> 3) & 0x7;\n\txmc[25]  = *c++ & 0x7;\n\n\tNc[2]  = (*c >> 1) & 0x7F;\n\n\tbc[2]  = (*c++ & 0x1) << 1;\t\t/* 20 */\n\tbc[2] |= (*c >> 7) & 0x1;\n\n\tMc[2]  = (*c >> 5) & 0x3;\n\n\txmaxc[2]  = (*c++ & 0x1F) << 1;\n\txmaxc[2] |= (*c >> 7) & 0x1;\n\n#undef\txmc\n#define\txmc\t(target + 46 - 26)\n\n\txmc[26]  = (*c >> 4) & 0x7;\n\txmc[27]  = (*c >> 1) & 0x7;\n\txmc[28]  = (*c++ & 0x1) << 2;\n\txmc[28] |= (*c >> 6) & 0x3;\n\txmc[29]  = (*c >> 3) & 0x7;\n\txmc[30]  = *c++ & 0x7;\n\txmc[31]  = (*c >> 5) & 0x7;\n\txmc[32]  = (*c >> 2) & 0x7;\n\txmc[33]  = (*c++ & 0x3) << 1;\n\txmc[33] |= (*c >> 7) & 0x1;\n\txmc[34]  = (*c >> 4) & 0x7;\n\txmc[35]  = (*c >> 1) & 0x7;\n\txmc[36]  = (*c++ & 0x1) << 2;\n\txmc[36] |= (*c >> 6) & 0x3;\n\txmc[37]  = (*c >> 3) & 0x7;\n\txmc[38]  = *c++ & 0x7;\n\n\tNc[3]  = (*c >> 1) & 0x7F;\n\n\tbc[3]  = (*c++ & 0x1) << 1;\n\tbc[3] |= (*c >> 7) & 0x1;\n\n\tMc[3]  = (*c >> 5) & 0x3;\n\n\txmaxc[3]  = (*c++ & 0x1F) << 1;\n\txmaxc[3] |= (*c >> 7) & 0x1;\n\n#undef\txmc\n#define\txmc\t(target + 63 - 39)\n\n\txmc[39]  = (*c >> 4) & 0x7;\n\txmc[40]  = (*c >> 1) & 0x7;\n\txmc[41]  = (*c++ & 0x1) << 2;\n\txmc[41] |= (*c >> 6) & 0x3;\n\txmc[42]  = (*c >> 3) & 0x7;\n\txmc[43]  = *c++ & 0x7;\t\t\t/* 30  */\n\txmc[44]  = (*c >> 5) & 0x7;\n\txmc[45]  = (*c >> 2) & 0x7;\n\txmc[46]  = (*c++ & 0x3) << 1;\n\txmc[46] |= (*c >> 7) & 0x1;\n\txmc[47]  = (*c >> 4) & 0x7;\n\txmc[48]  = (*c >> 1) & 0x7;\n\txmc[49]  = (*c++ & 0x1) << 2;\n\txmc[49] |= (*c >> 6) & 0x3;\n\txmc[50]  = (*c >> 3) & 0x7;\n\txmc[51]  = *c & 0x7;\t\t\t/* 33 */\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_implode.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_implode.c,v 1.2 1996/07/02 14:32:43 jutta Exp jutta $ */\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\nvoid gsm_implode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c)\n{\n\t/*\tvariable\tsize\tindex\n\n\t\tGSM_MAGIC\t4\t-\n\n\t\tLARc[0]\t\t6\t0\n\t\tLARc[1]\t\t6\t1\n\t\tLARc[2]\t\t5\t2\n\t\tLARc[3]\t\t5\t3\n\t\tLARc[4]\t\t4\t4\n\t\tLARc[5]\t\t4\t5\n\t\tLARc[6]\t\t3\t6\n\t\tLARc[7]\t\t3\t7\n\n\t\tNc[0]\t\t7\t8\n\t\tbc[0]\t\t2\t9\n\t\tMc[0]\t\t2\t10\n\t\txmaxc[0]\t6\t11\n\t\txmc[0]\t\t3\t12\n\t\txmc[1]\t\t3\t13\n\t\txmc[2]\t\t3\t14\n\t\txmc[3]\t\t3\t15\n\t\txmc[4]\t\t3\t16\n\t\txmc[5]\t\t3\t17\n\t\txmc[6]\t\t3\t18\n\t\txmc[7]\t\t3\t19\n\t\txmc[8]\t\t3\t20\n\t\txmc[9]\t\t3\t21\n\t\txmc[10]\t\t3\t22\n\t\txmc[11]\t\t3\t23\n\t\txmc[12]\t\t3\t24\n\n\t\tNc[1]\t\t7\t25\n\t\tbc[1]\t\t2\t26\n\t\tMc[1]\t\t2\t27\n\t\txmaxc[1]\t6\t28\n\t\txmc[13]\t\t3\t29\n\t\txmc[14]\t\t3\t30\n\t\txmc[15]\t\t3\t31\n\t\txmc[16]\t\t3\t32\n\t\txmc[17]\t\t3\t33\n\t\txmc[18]\t\t3\t34\n\t\txmc[19]\t\t3\t35\n\t\txmc[20]\t\t3\t36\n\t\txmc[21]\t\t3\t37\n\t\txmc[22]\t\t3\t38\n\t\txmc[23]\t\t3\t39\n\t\txmc[24]\t\t3\t40\n\t\txmc[25]\t\t3\t41\n\n\t\tNc[2]\t\t7\t42\n\t\tbc[2]\t\t2\t43\n\t\tMc[2]\t\t2\t44\n\t\txmaxc[2]\t6\t45\n\t\txmc[26]\t\t3\t46\n\t\txmc[27]\t\t3\t47\n\t\txmc[28]\t\t3\t48\n\t\txmc[29]\t\t3\t49\n\t\txmc[30]\t\t3\t50\n\t\txmc[31]\t\t3\t51\n\t\txmc[32]\t\t3\t52\n\t\txmc[33]\t\t3\t53\n\t\txmc[34]\t\t3\t54\n\t\txmc[35]\t\t3\t55\n\t\txmc[36]\t\t3\t56\n\t\txmc[37]\t\t3\t57\n\t\txmc[38]\t\t3\t58\n\n\t\tNc[3]\t\t7\t59\n\t\tbc[3]\t\t2\t60\n\t\tMc[3]\t\t2\t61\n\t\txmaxc[3]\t6\t62\n\t\txmc[39]\t\t3\t63\n\t\txmc[40]\t\t3\t64\n\t\txmc[41]\t\t3\t65\n\t\txmc[42]\t\t3\t66\n\t\txmc[43]\t\t3\t67\n\t\txmc[44]\t\t3\t68\n\t\txmc[45]\t\t3\t69\n\t\txmc[46]\t\t3\t70\n\t\txmc[47]\t\t3\t71\n\t\txmc[48]\t\t3\t72\n\t\txmc[49]\t\t3\t73\n\t\txmc[50]\t\t3\t74\n\t\txmc[51]\t\t3\t75\n\t*/\n\n\t/*\tThere are 76 parameters per frame.  The first eight are\n\t * \tunique.  The remaining 68 are four identical subframes of\n\t * \t17 parameters each.  gsm_implode converts from a representation\n\t * \tof these parameters as values in one array of signed words\n\t * \tto the \"packed\" version of a GSM frame.\n\t */\n\n#\tdefine\tLARc\tsource\n#\tdefine\tNc\t*((gsm_signal (*) [17])(source + 8))\n#\tdefine\tbc\t*((gsm_signal (*) [17])(source + 9))\n#\tdefine\tMc\t*((gsm_signal (*) [17])(source + 10))\n#\tdefine\txmaxc\t*((gsm_signal (*) [17])(source + 11))\n\n#ifdef WAV49\n\tif (s->wav_fmt) {\n\n\t\tuword sr = 0;\n\t\tif (s->frame_index == 0) {\n\n\t\t\tsr = *c++;\n\t\t\tLARc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tLARc[2] = sr & 0x1f;  sr >>= 5;\n\t\t\tLARc[3] = sr & 0x1f;  sr >>= 5;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[4] = sr & 0xf;  sr >>= 4;\n\t\t\tLARc[5] = sr & 0xf;  sr >>= 4;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 5 */\n\t\t\tLARc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tLARc[7] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[0] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[0] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 12)\n\t\t\txmc[0] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[1] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[2] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[3] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[4] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[5] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 10 */\n\t\t\txmc[6] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[7] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[8] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[9] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[10] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[11] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[12] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[1] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[1] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 29 - 13)\n\t\t\txmc[13] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 15 */\n\t\t\txmc[14] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[15] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[16] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[17] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[18] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[19] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[20] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[21] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[22] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[23] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[24] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[25] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\t\t\t/* 20 */\n\t\t\tNc[2] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[2] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 46 - 26)\n\t\t\txmc[26] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[27] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[28] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[29] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[30] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[31] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[32] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[33] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[34] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 25 */\n\t\t\txmc[35] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[36] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[37] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[38] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 4;\n\t\t\tNc[3] = sr & 0x7f;  sr >>= 7;\n\t\t\tbc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmaxc[3] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 63 - 39)\n\n\t\t\txmc[39] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[40] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[41] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 30 */\n\t\t\txmc[42] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[43] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[44] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[45] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[46] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[47] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[48] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[49] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[50] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[51] = sr & 0x7;  sr >>= 3;\n\n\t\t\ts->frame_chain = sr & 0xf;\n\t\t}\n\t\telse {\n\t\t\tsr = s->frame_chain;\n\t\t\tsr |= (uword)*c++ << 4;\t\t\t/* 1 */\n\t\t\tLARc[0] = sr & 0x3f;  sr >>= 6;\n\t\t\tLARc[1] = sr & 0x3f;  sr >>= 6;\n\t\t\tsr = *c++;\n\t\t\tLARc[2] = sr & 0x1f;  sr >>= 5;\n\t\t\tsr |= (uword)*c++ << 3;\n\t\t\tLARc[3] = sr & 0x1f;  sr >>= 5;\n\t\t\tLARc[4] = sr & 0xf;  sr >>= 4;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\tLARc[5] = sr & 0xf;  sr >>= 4;\n\t\t\tLARc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tLARc[7] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 5 */\n\t\t\tNc[0] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\tbc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[0] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[0] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 12)\n\t\t\txmc[0] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[1] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[2] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[3] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[4] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[5] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[6] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\t\t\t/* 10 */\n\t\t\txmc[7] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[8] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[9] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[10] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[11] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[12] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[1] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\tbc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[1] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[1] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 29 - 13)\n\t\t\txmc[13] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[14] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 15 */\n\t\t\txmc[15] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[16] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[17] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[18] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[19] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[20] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[21] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[22] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[23] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[24] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[25] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[2] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 20 */\n\t\t\tbc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[2] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[2] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 46 - 26)\n\t\t\txmc[26] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[27] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\n\t\t\txmc[28] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[29] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[30] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\txmc[31] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[32] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[33] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[34] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[35] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\t/* 25 */\n\t\t\txmc[36] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[37] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[38] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\n\t\t\tNc[3] = sr & 0x7f;  sr >>= 7;\n\t\t\tsr |= (uword)*c++ << 1;\t\t\n\t\t\tbc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tMc[3] = sr & 0x3;  sr >>= 2;\n\t\t\tsr |= (uword)*c++ << 5;\n\t\t\txmaxc[3] = sr & 0x3f;  sr >>= 6;\n#undef\txmc\n#define\txmc\t(source + 63 - 39)\n\n\t\t\txmc[39] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[40] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[41] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[42] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[43] = sr & 0x7;  sr >>= 3;\n\t\t\tsr = *c++;\t\t\t\t/* 30 */\n\t\t\txmc[44] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[45] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 2;\n\t\t\txmc[46] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[47] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[48] = sr & 0x7;  sr >>= 3;\n\t\t\tsr |= (uword)*c++ << 1;\n\t\t\txmc[49] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[50] = sr & 0x7;  sr >>= 3;\n\t\t\txmc[51] = sr & 0x7;  sr >>= 3;\n\t\t}\n\t}\n\telse\n#endif \n\t{\n\n\t*c++ =   ((GSM_MAGIC & 0xF) << 4)\t\t/* 1 */\n\t       | ((LARc[0] >> 2) & 0xF);\n\t*c++ =   ((LARc[0] & 0x3) << 6)\n\t       | (LARc[1] & 0x3F);\n\t*c++ =   ((LARc[2] & 0x1F) << 3)\n\t       | ((LARc[3] >> 2) & 0x7);\n\t*c++ =   ((LARc[3] & 0x3) << 6)\n\t       | ((LARc[4] & 0xF) << 2)\n\t       | ((LARc[5] >> 2) & 0x3);\n\t*c++ =   ((LARc[5] & 0x3) << 6)\n\t       | ((LARc[6] & 0x7) << 3)\n\t       | (LARc[7] & 0x7);\n\n\n\t*c++ =   ((Nc[0] & 0x7F) << 1)\n\n\n\t       | ((bc[0] >> 1) & 0x1);\n\t*c++ =   ((bc[0] & 0x1) << 7)\n\n\n\t       | ((Mc[0] & 0x3) << 5)\n\n\t       | ((xmaxc[0] >> 1) & 0x1F);\n\t*c++ =   ((xmaxc[0] & 0x1) << 7)\n\n#undef xmc\n#define\txmc\t(source + 12)\n\n\t       | ((xmc[0] & 0x7) << 4)\n\t       | ((xmc[1] & 0x7) << 1)\n\t       | ((xmc[2] >> 2) & 0x1);\n\t*c++ =   ((xmc[2] & 0x3) << 6)\n\t       | ((xmc[3] & 0x7) << 3)\n\t       | (xmc[4] & 0x7);\n\t*c++ =   ((xmc[5] & 0x7) << 5)\t\t\t/* 10 */\n\t       | ((xmc[6] & 0x7) << 2)\n\t       | ((xmc[7] >> 1) & 0x3);\n\t*c++ =   ((xmc[7] & 0x1) << 7)\n\t       | ((xmc[8] & 0x7) << 4)\n\t       | ((xmc[9] & 0x7) << 1)\n\t       | ((xmc[10] >> 2) & 0x1);\n\t*c++ =   ((xmc[10] & 0x3) << 6)\n\t       | ((xmc[11] & 0x7) << 3)\n\t       | (xmc[12] & 0x7);\n\n\n\t*c++ =   ((Nc[1] & 0x7F) << 1)\n\n\n\t       | ((bc[1] >> 1) & 0x1);\n\t*c++ =   ((bc[1] & 0x1) << 7)\n\n\n\t       | ((Mc[1] & 0x3) << 5)\n\n\n\t       | ((xmaxc[1] >> 1) & 0x1F);\n\t*c++ =   ((xmaxc[1] & 0x1) << 7)\n\n#undef\txmc\n#define\txmc\t(source + 29 - 13)\n\n\t       | ((xmc[13] & 0x7) << 4)\n\t       | ((xmc[14] & 0x7) << 1)\n\t       | ((xmc[15] >> 2) & 0x1);\n\t*c++ =   ((xmc[15] & 0x3) << 6)\n\t       | ((xmc[16] & 0x7) << 3)\n\t       | (xmc[17] & 0x7);\n\t*c++ =   ((xmc[18] & 0x7) << 5)\n\t       | ((xmc[19] & 0x7) << 2)\n\t       | ((xmc[20] >> 1) & 0x3);\n\t*c++ =   ((xmc[20] & 0x1) << 7)\n\t       | ((xmc[21] & 0x7) << 4)\n\t       | ((xmc[22] & 0x7) << 1)\n\t       | ((xmc[23] >> 2) & 0x1);\n\t*c++ =   ((xmc[23] & 0x3) << 6)\n\t       | ((xmc[24] & 0x7) << 3)\n\t       | (xmc[25] & 0x7);\n\n\n\t*c++ =   ((Nc[2] & 0x7F) << 1)\t\t\t/* 20 */\n\n\n\t       | ((bc[2] >> 1) & 0x1);\n\t*c++ =   ((bc[2] & 0x1) << 7)\n\n\n\t       | ((Mc[2] & 0x3) << 5)\n\n\n\t       | ((xmaxc[2] >> 1) & 0x1F);\n\t*c++ =   ((xmaxc[2] & 0x1) << 7)\n\n#undef\txmc\n#define\txmc\t(source + 46 - 26)\n\n\t       | ((xmc[26] & 0x7) << 4)\n\t       | ((xmc[27] & 0x7) << 1)\n\t       | ((xmc[28] >> 2) & 0x1);\n\t*c++ =   ((xmc[28] & 0x3) << 6)\n\t       | ((xmc[29] & 0x7) << 3)\n\t       | (xmc[30] & 0x7);\n\t*c++ =   ((xmc[31] & 0x7) << 5)\n\t       | ((xmc[32] & 0x7) << 2)\n\t       | ((xmc[33] >> 1) & 0x3);\n\t*c++ =   ((xmc[33] & 0x1) << 7)\n\t       | ((xmc[34] & 0x7) << 4)\n\t       | ((xmc[35] & 0x7) << 1)\n\t       | ((xmc[36] >> 2) & 0x1);\n\t*c++ =   ((xmc[36] & 0x3) << 6)\n\t       | ((xmc[37] & 0x7) << 3)\n\t       | (xmc[38] & 0x7);\n\n\n\t*c++ =   ((Nc[3] & 0x7F) << 1)\n\n\n\t       | ((bc[3] >> 1) & 0x1);\n\t*c++ =   ((bc[3] & 0x1) << 7)\n\n\n\t       | ((Mc[3] & 0x3) << 5)\n\n\n\t       | ((xmaxc[3] >> 1) & 0x1F);\n\t*c++ =   ((xmaxc[3] & 0x1) << 7)\n\n#undef\txmc\n#define\txmc\t(source + 63 - 39)\n\n\t       | ((xmc[39] & 0x7) << 4)\n\t       | ((xmc[40] & 0x7) << 1)\n\t       | ((xmc[41] >> 2) & 0x1);\n\t*c++ =   ((xmc[41] & 0x3) << 6)\t\t\t/* 30 */\n\t       | ((xmc[42] & 0x7) << 3)\n\t       | (xmc[43] & 0x7);\n\t*c++ =   ((xmc[44] & 0x7) << 5)\n\t       | ((xmc[45] & 0x7) << 2)\n\t       | ((xmc[46] >> 1) & 0x3);\n\t*c++ =   ((xmc[46] & 0x1) << 7)\n\t       | ((xmc[47] & 0x7) << 4)\n\t       | ((xmc[48] & 0x7) << 1)\n\t       | ((xmc[49] >> 2) & 0x1);\n\t*c++ =   ((xmc[49] & 0x3) << 6)\n\t       | ((xmc[50] & 0x7) << 3)\n\t       | (xmc[51] & 0x7);\n\t}\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_option.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_option.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\nint gsm_option P3((r, opt, val), gsm r, int opt, int * val)\n{\n\tint \tresult = -1;\n\n\tswitch (opt) {\n\tcase GSM_OPT_LTP_CUT:\n#ifdef \tLTP_CUT\n\t\tresult = r->ltp_cut;\n\t\tif (val) r->ltp_cut = *val;\n#endif\n\t\tbreak;\n\n\tcase GSM_OPT_VERBOSE:\n#ifndef\tNDEBUG\n\t\tresult = r->verbose;\n\t\tif (val) r->verbose = *val;\n#endif\n\t\tbreak;\n\n\tcase GSM_OPT_FAST:\n\n#if\tdefined(FAST) && defined(USE_FLOAT_MUL)\n\t\tresult = r->fast;\n\t\tif (val) r->fast = !!*val;\n#endif\n\t\tbreak;\n\n\tcase GSM_OPT_FRAME_CHAIN:\n\n#ifdef WAV49\n\t\tresult = r->frame_chain;\n\t\tif (val) r->frame_chain = *val;\n#endif\n\t\tbreak;\n\n\tcase GSM_OPT_FRAME_INDEX:\n\n#ifdef WAV49\n\t\tresult = r->frame_index;\n\t\tif (val) r->frame_index = *val;\n#endif\n\t\tbreak;\n\n\tcase GSM_OPT_WAV49:\n\n#ifdef WAV49 \n\t\tresult = r->wav_fmt;\n\t\tif (val) r->wav_fmt = !!*val;\n#endif\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\treturn result;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/gsm_print.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_print.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */\n\n#include\t<stdio.h>\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\nint gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c)\n{\n\tword  \tLARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];\n\n\t/* GSM_MAGIC  = (*c >> 4) & 0xF; */\n\n\tif (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;\n\n\tLARc[0]  = (*c++ & 0xF) << 2;\t\t/* 1 */\n\tLARc[0] |= (*c >> 6) & 0x3;\n\tLARc[1]  = *c++ & 0x3F;\n\tLARc[2]  = (*c >> 3) & 0x1F;\n\tLARc[3]  = (*c++ & 0x7) << 2;\n\tLARc[3] |= (*c >> 6) & 0x3;\n\tLARc[4]  = (*c >> 2) & 0xF;\n\tLARc[5]  = (*c++ & 0x3) << 2;\n\tLARc[5] |= (*c >> 6) & 0x3;\n\tLARc[6]  = (*c >> 3) & 0x7;\n\tLARc[7]  = *c++ & 0x7;\n\n\n\tNc[0]  = (*c >> 1) & 0x7F;\n\tbc[0]  = (*c++ & 0x1) << 1;\n\tbc[0] |= (*c >> 7) & 0x1;\n\tMc[0]  = (*c >> 5) & 0x3;\n\txmaxc[0]  = (*c++ & 0x1F) << 1;\n\txmaxc[0] |= (*c >> 7) & 0x1;\n\txmc[0]  = (*c >> 4) & 0x7;\n\txmc[1]  = (*c >> 1) & 0x7;\n\txmc[2]  = (*c++ & 0x1) << 2;\n\txmc[2] |= (*c >> 6) & 0x3;\n\txmc[3]  = (*c >> 3) & 0x7;\n\txmc[4]  = *c++ & 0x7;\n\txmc[5]  = (*c >> 5) & 0x7;\n\txmc[6]  = (*c >> 2) & 0x7;\n\txmc[7]  = (*c++ & 0x3) << 1;\t\t/* 10 */\n\txmc[7] |= (*c >> 7) & 0x1;\n\txmc[8]  = (*c >> 4) & 0x7;\n\txmc[9]  = (*c >> 1) & 0x7;\n\txmc[10]  = (*c++ & 0x1) << 2;\n\txmc[10] |= (*c >> 6) & 0x3;\n\txmc[11]  = (*c >> 3) & 0x7;\n\txmc[12]  = *c++ & 0x7;\n\n\tNc[1]  = (*c >> 1) & 0x7F;\n\tbc[1]  = (*c++ & 0x1) << 1;\n\tbc[1] |= (*c >> 7) & 0x1;\n\tMc[1]  = (*c >> 5) & 0x3;\n\txmaxc[1]  = (*c++ & 0x1F) << 1;\n\txmaxc[1] |= (*c >> 7) & 0x1;\n\txmc[13]  = (*c >> 4) & 0x7;\n\txmc[14]  = (*c >> 1) & 0x7;\n\txmc[15]  = (*c++ & 0x1) << 2;\n\txmc[15] |= (*c >> 6) & 0x3;\n\txmc[16]  = (*c >> 3) & 0x7;\n\txmc[17]  = *c++ & 0x7;\n\txmc[18]  = (*c >> 5) & 0x7;\n\txmc[19]  = (*c >> 2) & 0x7;\n\txmc[20]  = (*c++ & 0x3) << 1;\n\txmc[20] |= (*c >> 7) & 0x1;\n\txmc[21]  = (*c >> 4) & 0x7;\n\txmc[22]  = (*c >> 1) & 0x7;\n\txmc[23]  = (*c++ & 0x1) << 2;\n\txmc[23] |= (*c >> 6) & 0x3;\n\txmc[24]  = (*c >> 3) & 0x7;\n\txmc[25]  = *c++ & 0x7;\n\n\n\tNc[2]  = (*c >> 1) & 0x7F;\n\tbc[2]  = (*c++ & 0x1) << 1;\t\t/* 20 */\n\tbc[2] |= (*c >> 7) & 0x1;\n\tMc[2]  = (*c >> 5) & 0x3;\n\txmaxc[2]  = (*c++ & 0x1F) << 1;\n\txmaxc[2] |= (*c >> 7) & 0x1;\n\txmc[26]  = (*c >> 4) & 0x7;\n\txmc[27]  = (*c >> 1) & 0x7;\n\txmc[28]  = (*c++ & 0x1) << 2;\n\txmc[28] |= (*c >> 6) & 0x3;\n\txmc[29]  = (*c >> 3) & 0x7;\n\txmc[30]  = *c++ & 0x7;\n\txmc[31]  = (*c >> 5) & 0x7;\n\txmc[32]  = (*c >> 2) & 0x7;\n\txmc[33]  = (*c++ & 0x3) << 1;\n\txmc[33] |= (*c >> 7) & 0x1;\n\txmc[34]  = (*c >> 4) & 0x7;\n\txmc[35]  = (*c >> 1) & 0x7;\n\txmc[36]  = (*c++ & 0x1) << 2;\n\txmc[36] |= (*c >> 6) & 0x3;\n\txmc[37]  = (*c >> 3) & 0x7;\n\txmc[38]  = *c++ & 0x7;\n\n\tNc[3]  = (*c >> 1) & 0x7F;\n\tbc[3]  = (*c++ & 0x1) << 1;\n\tbc[3] |= (*c >> 7) & 0x1;\n\tMc[3]  = (*c >> 5) & 0x3;\n\txmaxc[3]  = (*c++ & 0x1F) << 1;\n\txmaxc[3] |= (*c >> 7) & 0x1;\n\n\txmc[39]  = (*c >> 4) & 0x7;\n\txmc[40]  = (*c >> 1) & 0x7;\n\txmc[41]  = (*c++ & 0x1) << 2;\n\txmc[41] |= (*c >> 6) & 0x3;\n\txmc[42]  = (*c >> 3) & 0x7;\n\txmc[43]  = *c++ & 0x7;\t\t\t/* 30  */\n\txmc[44]  = (*c >> 5) & 0x7;\n\txmc[45]  = (*c >> 2) & 0x7;\n\txmc[46]  = (*c++ & 0x3) << 1;\n\txmc[46] |= (*c >> 7) & 0x1;\n\txmc[47]  = (*c >> 4) & 0x7;\n\txmc[48]  = (*c >> 1) & 0x7;\n\txmc[49]  = (*c++ & 0x1) << 2;\n\txmc[49] |= (*c >> 6) & 0x3;\n\txmc[50]  = (*c >> 3) & 0x7;\n\txmc[51]  = *c & 0x7;\t\t\t/* 33 */\n\n\tfprintf(f,\n\t      \"LARc:\\t%2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d\\n\",\n\t       LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]);\n\n\tfprintf(f, \"#1: \tNc %4.4d    bc %d    Mc %d    xmaxc %d\\n\",\n\t\tNc[0], bc[0], Mc[0], xmaxc[0]);\n\tfprintf(f,\n\"\\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\\n\",\n\t\txmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6],\n\t\txmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] );\n\n\tfprintf(f, \"#2: \tNc %4.4d    bc %d    Mc %d    xmaxc %d\\n\",\n\t\tNc[1], bc[1], Mc[1], xmaxc[1]);\n\tfprintf(f,\n\"\\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\\n\",\n\t\txmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5],\n\t\txmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11],\n\t\txmc[13+12] );\n\n\tfprintf(f, \"#3: \tNc %4.4d    bc %d    Mc %d    xmaxc %d\\n\",\n\t\tNc[2], bc[2], Mc[2], xmaxc[2]);\n\tfprintf(f,\n\"\\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\\n\",\n\t\txmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5],\n\t\txmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11],\n\t\txmc[26+12] );\n\n\tfprintf(f, \"#4: \tNc %4.4d    bc %d    Mc %d    xmaxc %d\\n\",\n\t\tNc[3], bc[3], Mc[3], xmaxc[3]);\n\tfprintf(f,\n\"\\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\\n\",\n\t\txmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5],\n\t\txmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11],\n\t\txmc[39+12] );\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/long_term.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/long_term.c,v 1.6 1996/07/02 12:33:19 jutta Exp $ */\n\n#include <stdio.h>\n#include <assert.h>\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\n/*\n *  4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION\n */\n\n\n/*\n * This module computes the LTP gain (bc) and the LTP lag (Nc)\n * for the long term analysis filter.   This is done by calculating a\n * maximum of the cross-correlation function between the current\n * sub-segment short term residual signal d[0..39] (output of\n * the short term analysis filter; for simplification the index\n * of this array begins at 0 and ends at 39 for each sub-segment of the\n * RPE-LTP analysis) and the previous reconstructed short term\n * residual signal dp[ -120 .. -1 ].  A dynamic scaling must be\n * performed to avoid overflow.\n */\n\n /* The next procedure exists in six versions.  First two integer\n  * version (if USE_FLOAT_MUL is not defined); then four floating\n  * point versions, twice with proper scaling (USE_FLOAT_MUL defined),\n  * once without (USE_FLOAT_MUL and FAST defined, and fast run-time\n  * option used).  Every pair has first a Cut version (see the -C\n  * option to toast or the LTP_CUT option to gsm_option()), then the\n  * uncut one.  (For a detailed explanation of why this is altogether\n  * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered\n  * Harmful''.)\n  */\n\n#ifndef  USE_FLOAT_MUL\n\n#ifdef\tLTP_CUT\n\nstatic void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out),\n\n\tstruct gsm_state * st,\n\n\tregister word\t* d,\t\t/* [0..39]\tIN\t*/\n\tregister word\t* dp,\t\t/* [-120..-1]\tIN\t*/\n\tword\t\t* bc_out,\t/* \t\tOUT\t*/\n\tword\t\t* Nc_out\t/* \t\tOUT\t*/\n)\n{\n\tregister int  \tk, lambda;\n\tword\t\tNc, bc;\n\tword\t\twt[40];\n\n\tlongword\tL_result;\n\tlongword\tL_max, L_power;\n\tword\t\tR, S, dmax, scal, best_k;\n\tword\t\tltp_cut;\n\n\tregister word\ttemp, wt_k;\n\n\t/*  Search of the optimum scaling of d[0..39].\n\t */\n\tdmax = 0;\n\tfor (k = 0; k <= 39; k++) {\n\t\ttemp = d[k];\n\t\ttemp = GSM_ABS( temp );\n\t\tif (temp > dmax) {\n\t\t\tdmax = temp;\n\t\t\tbest_k = k;\n\t\t}\n\t}\n\ttemp = 0;\n\tif (dmax == 0) scal = 0;\n\telse {\n\t\tassert(dmax > 0);\n\t\ttemp = gsm_norm( (longword)dmax << 16 );\n\t}\n\tif (temp > 6) scal = 0;\n\telse scal = 6 - temp;\n\tassert(scal >= 0);\n\n\t/* Search for the maximum cross-correlation and coding of the LTP lag\n\t */\n\tL_max = 0;\n\tNc    = 40;\t/* index for the maximum cross-correlation */\n\twt_k  = SASR(d[best_k], scal);\n\n\tfor (lambda = 40; lambda <= 120; lambda++) {\n\t\tL_result = (longword)wt_k * dp[best_k - lambda];\n\t\tif (L_result > L_max) {\n\t\t\tNc    = lambda;\n\t\t\tL_max = L_result;\n\t\t}\n\t}\n\t*Nc_out = Nc;\n\tL_max <<= 1;\n\n\t/*  Rescaling of L_max\n\t */\n\tassert(scal <= 100 && scal >= -100);\n\tL_max = L_max >> (6 - scal);\t/* sub(6, scal) */\n\n\tassert( Nc <= 120 && Nc >= 40);\n\n\t/*   Compute the power of the reconstructed short term residual\n\t *   signal dp[..]\n\t */\n\tL_power = 0;\n\tfor (k = 0; k <= 39; k++) {\n\n\t\tregister longword L_temp;\n\n\t\tL_temp   = SASR( dp[k - Nc], 3 );\n\t\tL_power += L_temp * L_temp;\n\t}\n\tL_power <<= 1;\t/* from L_MULT */\n\n\t/*  Normalization of L_max and L_power\n\t */\n\n\tif (L_max <= 0)  {\n\t\t*bc_out = 0;\n\t\treturn;\n\t}\n\tif (L_max >= L_power) {\n\t\t*bc_out = 3;\n\t\treturn;\n\t}\n\n\ttemp = gsm_norm( L_power );\n\n\tR = SASR( L_max   << temp, 16 );\n\tS = SASR( L_power << temp, 16 );\n\n\t/*  Coding of the LTP gain\n\t */\n\n\t/*  Table 4.3a must be used to obtain the level DLB[i] for the\n\t *  quantization of the LTP gain b to get the coded version bc.\n\t */\n\tfor (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;\n\t*bc_out = bc;\n}\n\n#endif \t/* LTP_CUT */\n\nstatic void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),\n\tregister word\t* d,\t\t/* [0..39]\tIN\t*/\n\tregister word\t* dp,\t\t/* [-120..-1]\tIN\t*/\n\tword\t\t* bc_out,\t/* \t\tOUT\t*/\n\tword\t\t* Nc_out\t/* \t\tOUT\t*/\n)\n{\n\tregister int  \tk, lambda;\n\tword\t\tNc, bc;\n\tword\t\twt[40];\n\n\tlongword\tL_max, L_power;\n\tword\t\tR, S, dmax, scal;\n\tregister word\ttemp;\n\n\t/*  Search of the optimum scaling of d[0..39].\n\t */\n\tdmax = 0;\n\n\tfor (k = 0; k <= 39; k++) {\n\t\ttemp = d[k];\n\t\ttemp = GSM_ABS( temp );\n\t\tif (temp > dmax) dmax = temp;\n\t}\n\n\ttemp = 0;\n\tif (dmax == 0) scal = 0;\n\telse {\n\t\tassert(dmax > 0);\n\t\ttemp = gsm_norm( (longword)dmax << 16 );\n\t}\n\n\tif (temp > 6) scal = 0;\n\telse scal = 6 - temp;\n\n\tassert(scal >= 0);\n\n\t/*  Initialization of a working array wt\n\t */\n\n\tfor (k = 0; k <= 39; k++) wt[k] = SASR( d[k], scal );\n\n\t/* Search for the maximum cross-correlation and coding of the LTP lag\n\t */\n\tL_max = 0;\n\tNc    = 40;\t/* index for the maximum cross-correlation */\n\n\tfor (lambda = 40; lambda <= 120; lambda++) {\n\n# undef STEP\n#\t\tdefine STEP(k) \t(longword)wt[k] * dp[k - lambda]\n\n\t\tregister longword L_result;\n\n\t\tL_result  = STEP(0)  ; L_result += STEP(1) ;\n\t\tL_result += STEP(2)  ; L_result += STEP(3) ;\n\t\tL_result += STEP(4)  ; L_result += STEP(5)  ;\n\t\tL_result += STEP(6)  ; L_result += STEP(7)  ;\n\t\tL_result += STEP(8)  ; L_result += STEP(9)  ;\n\t\tL_result += STEP(10) ; L_result += STEP(11) ;\n\t\tL_result += STEP(12) ; L_result += STEP(13) ;\n\t\tL_result += STEP(14) ; L_result += STEP(15) ;\n\t\tL_result += STEP(16) ; L_result += STEP(17) ;\n\t\tL_result += STEP(18) ; L_result += STEP(19) ;\n\t\tL_result += STEP(20) ; L_result += STEP(21) ;\n\t\tL_result += STEP(22) ; L_result += STEP(23) ;\n\t\tL_result += STEP(24) ; L_result += STEP(25) ;\n\t\tL_result += STEP(26) ; L_result += STEP(27) ;\n\t\tL_result += STEP(28) ; L_result += STEP(29) ;\n\t\tL_result += STEP(30) ; L_result += STEP(31) ;\n\t\tL_result += STEP(32) ; L_result += STEP(33) ;\n\t\tL_result += STEP(34) ; L_result += STEP(35) ;\n\t\tL_result += STEP(36) ; L_result += STEP(37) ;\n\t\tL_result += STEP(38) ; L_result += STEP(39) ;\n\n\t\tif (L_result > L_max) {\n\n\t\t\tNc    = lambda;\n\t\t\tL_max = L_result;\n\t\t}\n\t}\n\n\t*Nc_out = Nc;\n\n\tL_max <<= 1;\n\n\t/*  Rescaling of L_max\n\t */\n\tassert(scal <= 100 && scal >=  -100);\n\tL_max = L_max >> (6 - scal);\t/* sub(6, scal) */\n\n\tassert( Nc <= 120 && Nc >= 40);\n\n\t/*   Compute the power of the reconstructed short term residual\n\t *   signal dp[..]\n\t */\n\tL_power = 0;\n\tfor (k = 0; k <= 39; k++) {\n\n\t\tregister longword L_temp;\n\n\t\tL_temp   = SASR( dp[k - Nc], 3 );\n\t\tL_power += L_temp * L_temp;\n\t}\n\tL_power <<= 1;\t/* from L_MULT */\n\n\t/*  Normalization of L_max and L_power\n\t */\n\n\tif (L_max <= 0)  {\n\t\t*bc_out = 0;\n\t\treturn;\n\t}\n\tif (L_max >= L_power) {\n\t\t*bc_out = 3;\n\t\treturn;\n\t}\n\n\ttemp = gsm_norm( L_power );\n\n\tR = SASR( L_max   << temp, 16 );\n\tS = SASR( L_power << temp, 16 );\n\n\t/*  Coding of the LTP gain\n\t */\n\n\t/*  Table 4.3a must be used to obtain the level DLB[i] for the\n\t *  quantization of the LTP gain b to get the coded version bc.\n\t */\n\tfor (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;\n\t*bc_out = bc;\n}\n\n#else\t/* USE_FLOAT_MUL */\n\n#ifdef\tLTP_CUT\n\nstatic void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out),\n\tstruct gsm_state * st,\t\t/*              IN \t*/\n\tregister word\t* d,\t\t/* [0..39]\tIN\t*/\n\tregister word\t* dp,\t\t/* [-120..-1]\tIN\t*/\n\tword\t\t* bc_out,\t/* \t\tOUT\t*/\n\tword\t\t* Nc_out\t/* \t\tOUT\t*/\n)\n{\n\tregister int  \tk, lambda;\n\tword\t\tNc, bc;\n\tword\t\tltp_cut;\n\n\tfloat\t\twt_float[40];\n\tfloat\t\tdp_float_base[120], * dp_float = dp_float_base + 120;\n\n\tlongword\tL_max, L_power;\n\tword\t\tR, S, dmax, scal;\n\tregister word\ttemp;\n\n\t/*  Search of the optimum scaling of d[0..39].\n\t */\n\tdmax = 0;\n\n\tfor (k = 0; k <= 39; k++) {\n\t\ttemp = d[k];\n\t\ttemp = GSM_ABS( temp );\n\t\tif (temp > dmax) dmax = temp;\n\t}\n\n\ttemp = 0;\n\tif (dmax == 0) scal = 0;\n\telse {\n\t\tassert(dmax > 0);\n\t\ttemp = gsm_norm( (longword)dmax << 16 );\n\t}\n\n\tif (temp > 6) scal = 0;\n\telse scal = 6 - temp;\n\n\tassert(scal >= 0);\n\tltp_cut = (longword)SASR(dmax, scal) * st->ltp_cut / 100; \n\n\n\t/*  Initialization of a working array wt\n\t */\n\n\tfor (k = 0; k < 40; k++) {\n\t\tregister word w = SASR( d[k], scal );\n\t\tif (w < 0 ? w > -ltp_cut : w < ltp_cut) {\n\t\t\twt_float[k] = 0.0;\n\t\t}\n\t\telse {\n\t\t\twt_float[k] =  w;\n\t\t}\n\t}\n\tfor (k = -120; k <  0; k++) dp_float[k] =  dp[k];\n\n\t/* Search for the maximum cross-correlation and coding of the LTP lag\n\t */\n\tL_max = 0;\n\tNc    = 40;\t/* index for the maximum cross-correlation */\n\n\tfor (lambda = 40; lambda <= 120; lambda += 9) {\n\n\t\t/*  Calculate L_result for l = lambda .. lambda + 9.\n\t\t */\n\t\tregister float *lp = dp_float - lambda;\n\n\t\tregister float\tW;\n\t\tregister float\ta = lp[-8], b = lp[-7], c = lp[-6],\n\t\t\t\td = lp[-5], e = lp[-4], f = lp[-3],\n\t\t\t\tg = lp[-2], h = lp[-1];\n\t\tregister float  E; \n\t\tregister float  S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,\n\t\t\t\tS5 = 0, S6 = 0, S7 = 0, S8 = 0;\n\n#\t\tundef STEP\n#\t\tdefine\tSTEP(K, a, b, c, d, e, f, g, h) \\\n\t\t\tif ((W = wt_float[K]) != 0.0) {\t\\\n\t\t\tE = W * a; S8 += E;\t\t\\\n\t\t\tE = W * b; S7 += E;\t\t\\\n\t\t\tE = W * c; S6 += E;\t\t\\\n\t\t\tE = W * d; S5 += E;\t\t\\\n\t\t\tE = W * e; S4 += E;\t\t\\\n\t\t\tE = W * f; S3 += E;\t\t\\\n\t\t\tE = W * g; S2 += E;\t\t\\\n\t\t\tE = W * h; S1 += E;\t\t\\\n\t\t\ta  = lp[K];\t\t\t\\\n\t\t\tE = W * a; S0 += E; } else (a = lp[K])\n\n#\t\tdefine\tSTEP_A(K)\tSTEP(K, a, b, c, d, e, f, g, h)\n#\t\tdefine\tSTEP_B(K)\tSTEP(K, b, c, d, e, f, g, h, a)\n#\t\tdefine\tSTEP_C(K)\tSTEP(K, c, d, e, f, g, h, a, b)\n#\t\tdefine\tSTEP_D(K)\tSTEP(K, d, e, f, g, h, a, b, c)\n#\t\tdefine\tSTEP_E(K)\tSTEP(K, e, f, g, h, a, b, c, d)\n#\t\tdefine\tSTEP_F(K)\tSTEP(K, f, g, h, a, b, c, d, e)\n#\t\tdefine\tSTEP_G(K)\tSTEP(K, g, h, a, b, c, d, e, f)\n#\t\tdefine\tSTEP_H(K)\tSTEP(K, h, a, b, c, d, e, f, g)\n\n\t\tSTEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);\n\t\tSTEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);\n\n\t\tSTEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);\n\t\tSTEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);\n\n\t\tSTEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);\n\t\tSTEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);\n\n\t\tSTEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);\n\t\tSTEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);\n\n\t\tSTEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);\n\t\tSTEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);\n\n\t\tif (S0 > L_max) { L_max = S0; Nc = lambda;     }\n\t\tif (S1 > L_max) { L_max = S1; Nc = lambda + 1; }\n\t\tif (S2 > L_max) { L_max = S2; Nc = lambda + 2; }\n\t\tif (S3 > L_max) { L_max = S3; Nc = lambda + 3; }\n\t\tif (S4 > L_max) { L_max = S4; Nc = lambda + 4; }\n\t\tif (S5 > L_max) { L_max = S5; Nc = lambda + 5; }\n\t\tif (S6 > L_max) { L_max = S6; Nc = lambda + 6; }\n\t\tif (S7 > L_max) { L_max = S7; Nc = lambda + 7; }\n\t\tif (S8 > L_max) { L_max = S8; Nc = lambda + 8; }\n\n\t}\n\t*Nc_out = Nc;\n\n\tL_max <<= 1;\n\n\t/*  Rescaling of L_max\n\t */\n\tassert(scal <= 100 && scal >=  -100);\n\tL_max = L_max >> (6 - scal);\t/* sub(6, scal) */\n\n\tassert( Nc <= 120 && Nc >= 40);\n\n\t/*   Compute the power of the reconstructed short term residual\n\t *   signal dp[..]\n\t */\n\tL_power = 0;\n\tfor (k = 0; k <= 39; k++) {\n\n\t\tregister longword L_temp;\n\n\t\tL_temp   = SASR( dp[k - Nc], 3 );\n\t\tL_power += L_temp * L_temp;\n\t}\n\tL_power <<= 1;\t/* from L_MULT */\n\n\t/*  Normalization of L_max and L_power\n\t */\n\n\tif (L_max <= 0)  {\n\t\t*bc_out = 0;\n\t\treturn;\n\t}\n\tif (L_max >= L_power) {\n\t\t*bc_out = 3;\n\t\treturn;\n\t}\n\n\ttemp = gsm_norm( L_power );\n\n\tR = SASR( L_max   << temp, 16 );\n\tS = SASR( L_power << temp, 16 );\n\n\t/*  Coding of the LTP gain\n\t */\n\n\t/*  Table 4.3a must be used to obtain the level DLB[i] for the\n\t *  quantization of the LTP gain b to get the coded version bc.\n\t */\n\tfor (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;\n\t*bc_out = bc;\n}\n\n#endif /* LTP_CUT */\n\nstatic void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),\n\tregister word\t* d,\t\t/* [0..39]\tIN\t*/\n\tregister word\t* dp,\t\t/* [-120..-1]\tIN\t*/\n\tword\t\t* bc_out,\t/* \t\tOUT\t*/\n\tword\t\t* Nc_out\t/* \t\tOUT\t*/\n)\n{\n\tregister int  \tk, lambda;\n\tword\t\tNc, bc;\n\n\tfloat\t\twt_float[40];\n\tfloat\t\tdp_float_base[120], * dp_float = dp_float_base + 120;\n\n\tlongword\tL_max, L_power;\n\tword\t\tR, S, dmax, scal;\n\tregister word\ttemp;\n\n\t/*  Search of the optimum scaling of d[0..39].\n\t */\n\tdmax = 0;\n\n\tfor (k = 0; k <= 39; k++) {\n\t\ttemp = d[k];\n\t\ttemp = GSM_ABS( temp );\n\t\tif (temp > dmax) dmax = temp;\n\t}\n\n\ttemp = 0;\n\tif (dmax == 0) scal = 0;\n\telse {\n\t\tassert(dmax > 0);\n\t\ttemp = gsm_norm( (longword)dmax << 16 );\n\t}\n\n\tif (temp > 6) scal = 0;\n\telse scal = 6 - temp;\n\n\tassert(scal >= 0);\n\n\t/*  Initialization of a working array wt\n\t */\n\n\tfor (k =    0; k < 40; k++) wt_float[k] =  SASR( d[k], scal );\n\tfor (k = -120; k <  0; k++) dp_float[k] =  dp[k];\n\n\t/* Search for the maximum cross-correlation and coding of the LTP lag\n\t */\n\tL_max = 0;\n\tNc    = 40;\t/* index for the maximum cross-correlation */\n\n\tfor (lambda = 40; lambda <= 120; lambda += 9) {\n\n\t\t/*  Calculate L_result for l = lambda .. lambda + 9.\n\t\t */\n\t\tregister float *lp = dp_float - lambda;\n\n\t\tregister float\tW;\n\t\tregister float\ta = lp[-8], b = lp[-7], c = lp[-6],\n\t\t\t\td = lp[-5], e = lp[-4], f = lp[-3],\n\t\t\t\tg = lp[-2], h = lp[-1];\n\t\tregister float  E; \n\t\tregister float  S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,\n\t\t\t\tS5 = 0, S6 = 0, S7 = 0, S8 = 0;\n\n#\t\tundef STEP\n#\t\tdefine\tSTEP(K, a, b, c, d, e, f, g, h) \\\n\t\t\tW = wt_float[K];\t\t\\\n\t\t\tE = W * a; S8 += E;\t\t\\\n\t\t\tE = W * b; S7 += E;\t\t\\\n\t\t\tE = W * c; S6 += E;\t\t\\\n\t\t\tE = W * d; S5 += E;\t\t\\\n\t\t\tE = W * e; S4 += E;\t\t\\\n\t\t\tE = W * f; S3 += E;\t\t\\\n\t\t\tE = W * g; S2 += E;\t\t\\\n\t\t\tE = W * h; S1 += E;\t\t\\\n\t\t\ta  = lp[K];\t\t\t\\\n\t\t\tE = W * a; S0 += E\n\n#\t\tdefine\tSTEP_A(K)\tSTEP(K, a, b, c, d, e, f, g, h)\n#\t\tdefine\tSTEP_B(K)\tSTEP(K, b, c, d, e, f, g, h, a)\n#\t\tdefine\tSTEP_C(K)\tSTEP(K, c, d, e, f, g, h, a, b)\n#\t\tdefine\tSTEP_D(K)\tSTEP(K, d, e, f, g, h, a, b, c)\n#\t\tdefine\tSTEP_E(K)\tSTEP(K, e, f, g, h, a, b, c, d)\n#\t\tdefine\tSTEP_F(K)\tSTEP(K, f, g, h, a, b, c, d, e)\n#\t\tdefine\tSTEP_G(K)\tSTEP(K, g, h, a, b, c, d, e, f)\n#\t\tdefine\tSTEP_H(K)\tSTEP(K, h, a, b, c, d, e, f, g)\n\n\t\tSTEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);\n\t\tSTEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);\n\n\t\tSTEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);\n\t\tSTEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);\n\n\t\tSTEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);\n\t\tSTEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);\n\n\t\tSTEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);\n\t\tSTEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);\n\n\t\tSTEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);\n\t\tSTEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);\n\n\t\tif (S0 > L_max) { L_max = S0; Nc = lambda;     }\n\t\tif (S1 > L_max) { L_max = S1; Nc = lambda + 1; }\n\t\tif (S2 > L_max) { L_max = S2; Nc = lambda + 2; }\n\t\tif (S3 > L_max) { L_max = S3; Nc = lambda + 3; }\n\t\tif (S4 > L_max) { L_max = S4; Nc = lambda + 4; }\n\t\tif (S5 > L_max) { L_max = S5; Nc = lambda + 5; }\n\t\tif (S6 > L_max) { L_max = S6; Nc = lambda + 6; }\n\t\tif (S7 > L_max) { L_max = S7; Nc = lambda + 7; }\n\t\tif (S8 > L_max) { L_max = S8; Nc = lambda + 8; }\n\t}\n\t*Nc_out = Nc;\n\n\tL_max <<= 1;\n\n\t/*  Rescaling of L_max\n\t */\n\tassert(scal <= 100 && scal >=  -100);\n\tL_max = L_max >> (6 - scal);\t/* sub(6, scal) */\n\n\tassert( Nc <= 120 && Nc >= 40);\n\n\t/*   Compute the power of the reconstructed short term residual\n\t *   signal dp[..]\n\t */\n\tL_power = 0;\n\tfor (k = 0; k <= 39; k++) {\n\n\t\tregister longword L_temp;\n\n\t\tL_temp   = SASR( dp[k - Nc], 3 );\n\t\tL_power += L_temp * L_temp;\n\t}\n\tL_power <<= 1;\t/* from L_MULT */\n\n\t/*  Normalization of L_max and L_power\n\t */\n\n\tif (L_max <= 0)  {\n\t\t*bc_out = 0;\n\t\treturn;\n\t}\n\tif (L_max >= L_power) {\n\t\t*bc_out = 3;\n\t\treturn;\n\t}\n\n\ttemp = gsm_norm( L_power );\n\n\tR = SASR( L_max   << temp, 16 );\n\tS = SASR( L_power << temp, 16 );\n\n\t/*  Coding of the LTP gain\n\t */\n\n\t/*  Table 4.3a must be used to obtain the level DLB[i] for the\n\t *  quantization of the LTP gain b to get the coded version bc.\n\t */\n\tfor (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;\n\t*bc_out = bc;\n}\n\n#ifdef\tFAST\n#ifdef\tLTP_CUT\n\nstatic void Cut_Fast_Calculation_of_the_LTP_parameters P5((st,\n\t\t\t\t\t\t\td,dp,bc_out,Nc_out),\n\tstruct gsm_state * st,\t\t/*              IN\t*/\n\tregister word\t* d,\t\t/* [0..39]\tIN\t*/\n\tregister word\t* dp,\t\t/* [-120..-1]\tIN\t*/\n\tword\t\t* bc_out,\t/* \t\tOUT\t*/\n\tword\t\t* Nc_out\t/* \t\tOUT\t*/\n)\n{\n\tregister int  \tk, lambda;\n\tregister float\twt_float;\n\tword\t\tNc, bc;\n\tword\t\twt_max, best_k, ltp_cut;\n\n\tfloat\t\tdp_float_base[120], * dp_float = dp_float_base + 120;\n\n\tregister float\tL_result, L_max, L_power;\n\n\twt_max = 0;\n\n\tfor (k = 0; k < 40; ++k) {\n\t\tif      ( d[k] > wt_max) wt_max =  d[best_k = k];\n\t\telse if (-d[k] > wt_max) wt_max = -d[best_k = k];\n\t}\n\n\tassert(wt_max >= 0);\n\twt_float = (float)wt_max;\n\n\tfor (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k];\n\n\t/* Search for the maximum cross-correlation and coding of the LTP lag\n\t */\n\tL_max = 0;\n\tNc    = 40;\t/* index for the maximum cross-correlation */\n\n\tfor (lambda = 40; lambda <= 120; lambda++) {\n\t\tL_result = wt_float * dp_float[best_k - lambda];\n\t\tif (L_result > L_max) {\n\t\t\tNc    = lambda;\n\t\t\tL_max = L_result;\n\t\t}\n\t}\n\n\t*Nc_out = Nc;\n\tif (L_max <= 0.)  {\n\t\t*bc_out = 0;\n\t\treturn;\n\t}\n\n\t/*  Compute the power of the reconstructed short term residual\n\t *  signal dp[..]\n\t */\n\tdp_float -= Nc;\n\tL_power = 0;\n\tfor (k = 0; k < 40; ++k) {\n\t\tregister float f = dp_float[k];\n\t\tL_power += f * f;\n\t}\n\n\tif (L_max >= L_power) {\n\t\t*bc_out = 3;\n\t\treturn;\n\t}\n\n\t/*  Coding of the LTP gain\n\t *  Table 4.3a must be used to obtain the level DLB[i] for the\n\t *  quantization of the LTP gain b to get the coded version bc.\n\t */\n\tlambda = L_max / L_power * 32768.;\n\tfor (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break;\n\t*bc_out = bc;\n}\n\n#endif /* LTP_CUT */\n\nstatic void Fast_Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),\n\tregister word\t* d,\t\t/* [0..39]\tIN\t*/\n\tregister word\t* dp,\t\t/* [-120..-1]\tIN\t*/\n\tword\t\t* bc_out,\t/* \t\tOUT\t*/\n\tword\t\t* Nc_out\t/* \t\tOUT\t*/\n)\n{\n\tregister int  \tk, lambda;\n\tword\t\tNc, bc;\n\n\tfloat\t\twt_float[40];\n\tfloat\t\tdp_float_base[120], * dp_float = dp_float_base + 120;\n\n\tregister float\tL_max, L_power;\n\n\tfor (k = 0; k < 40; ++k) wt_float[k] = (float)d[k];\n\tfor (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k];\n\n\t/* Search for the maximum cross-correlation and coding of the LTP lag\n\t */\n\tL_max = 0;\n\tNc    = 40;\t/* index for the maximum cross-correlation */\n\n\tfor (lambda = 40; lambda <= 120; lambda += 9) {\n\n\t\t/*  Calculate L_result for l = lambda .. lambda + 9.\n\t\t */\n\t\tregister float *lp = dp_float - lambda;\n\n\t\tregister float\tW;\n\t\tregister float\ta = lp[-8], b = lp[-7], c = lp[-6],\n\t\t\t\td = lp[-5], e = lp[-4], f = lp[-3],\n\t\t\t\tg = lp[-2], h = lp[-1];\n\t\tregister float  E; \n\t\tregister float  S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,\n\t\t\t\tS5 = 0, S6 = 0, S7 = 0, S8 = 0;\n\n#\t\tundef STEP\n#\t\tdefine\tSTEP(K, a, b, c, d, e, f, g, h) \\\n\t\t\tW = wt_float[K];\t\t\\\n\t\t\tE = W * a; S8 += E;\t\t\\\n\t\t\tE = W * b; S7 += E;\t\t\\\n\t\t\tE = W * c; S6 += E;\t\t\\\n\t\t\tE = W * d; S5 += E;\t\t\\\n\t\t\tE = W * e; S4 += E;\t\t\\\n\t\t\tE = W * f; S3 += E;\t\t\\\n\t\t\tE = W * g; S2 += E;\t\t\\\n\t\t\tE = W * h; S1 += E;\t\t\\\n\t\t\ta  = lp[K];\t\t\t\\\n\t\t\tE = W * a; S0 += E\n\n#\t\tdefine\tSTEP_A(K)\tSTEP(K, a, b, c, d, e, f, g, h)\n#\t\tdefine\tSTEP_B(K)\tSTEP(K, b, c, d, e, f, g, h, a)\n#\t\tdefine\tSTEP_C(K)\tSTEP(K, c, d, e, f, g, h, a, b)\n#\t\tdefine\tSTEP_D(K)\tSTEP(K, d, e, f, g, h, a, b, c)\n#\t\tdefine\tSTEP_E(K)\tSTEP(K, e, f, g, h, a, b, c, d)\n#\t\tdefine\tSTEP_F(K)\tSTEP(K, f, g, h, a, b, c, d, e)\n#\t\tdefine\tSTEP_G(K)\tSTEP(K, g, h, a, b, c, d, e, f)\n#\t\tdefine\tSTEP_H(K)\tSTEP(K, h, a, b, c, d, e, f, g)\n\n\t\tSTEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);\n\t\tSTEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);\n\n\t\tSTEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);\n\t\tSTEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);\n\n\t\tSTEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);\n\t\tSTEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);\n\n\t\tSTEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);\n\t\tSTEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);\n\n\t\tSTEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);\n\t\tSTEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);\n\n\t\tif (S0 > L_max) { L_max = S0; Nc = lambda;     }\n\t\tif (S1 > L_max) { L_max = S1; Nc = lambda + 1; }\n\t\tif (S2 > L_max) { L_max = S2; Nc = lambda + 2; }\n\t\tif (S3 > L_max) { L_max = S3; Nc = lambda + 3; }\n\t\tif (S4 > L_max) { L_max = S4; Nc = lambda + 4; }\n\t\tif (S5 > L_max) { L_max = S5; Nc = lambda + 5; }\n\t\tif (S6 > L_max) { L_max = S6; Nc = lambda + 6; }\n\t\tif (S7 > L_max) { L_max = S7; Nc = lambda + 7; }\n\t\tif (S8 > L_max) { L_max = S8; Nc = lambda + 8; }\n\t}\n\t*Nc_out = Nc;\n\n\tif (L_max <= 0.)  {\n\t\t*bc_out = 0;\n\t\treturn;\n\t}\n\n\t/*  Compute the power of the reconstructed short term residual\n\t *  signal dp[..]\n\t */\n\tdp_float -= Nc;\n\tL_power = 0;\n\tfor (k = 0; k < 40; ++k) {\n\t\tregister float f = dp_float[k];\n\t\tL_power += f * f;\n\t}\n\n\tif (L_max >= L_power) {\n\t\t*bc_out = 3;\n\t\treturn;\n\t}\n\n\t/*  Coding of the LTP gain\n\t *  Table 4.3a must be used to obtain the level DLB[i] for the\n\t *  quantization of the LTP gain b to get the coded version bc.\n\t */\n\tlambda = L_max / L_power * 32768.;\n\tfor (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break;\n\t*bc_out = bc;\n}\n\n#endif\t/* FAST \t */\n#endif\t/* USE_FLOAT_MUL */\n\n\n/* 4.2.12 */\n\nstatic void Long_term_analysis_filtering P6((bc,Nc,dp,d,dpp,e),\n\tword\t\tbc,\t/* \t\t\t\t\tIN  */\n\tword\t\tNc,\t/* \t\t\t\t\tIN  */\n\tregister word\t* dp,\t/* previous d\t[-120..-1]\t\tIN  */\n\tregister word\t* d,\t/* d\t\t[0..39]\t\t\tIN  */\n\tregister word\t* dpp,\t/* estimate\t[0..39]\t\t\tOUT */\n\tregister word\t* e\t/* long term res. signal [0..39]\tOUT */\n)\n/*\n *  In this part, we have to decode the bc parameter to compute\n *  the samples of the estimate dpp[0..39].  The decoding of bc needs the\n *  use of table 4.3b.  The long term residual signal e[0..39]\n *  is then calculated to be fed to the RPE encoding section.\n */\n{\n\tregister int      k;\n\tregister longword ltmp;\n\n#\tundef STEP\n#\tdefine STEP(BP)\t\t\t\t\t\\\n\tfor (k = 0; k <= 39; k++) {\t\t\t\\\n\t\tdpp[k]  = GSM_MULT_R( BP, dp[k - Nc]);\t\\\n\t\te[k]\t= GSM_SUB( d[k], dpp[k] );\t\\\n\t}\n\n\tswitch (bc) {\n\tcase 0:\tSTEP(  3277 ); break;\n\tcase 1:\tSTEP( 11469 ); break;\n\tcase 2: STEP( 21299 ); break;\n\tcase 3: STEP( 32767 ); break; \n\t}\n}\n\nvoid Gsm_Long_Term_Predictor P7((S,d,dp,e,dpp,Nc,bc), \t/* 4x for 160 samples */\n\n\tstruct gsm_state\t* S,\n\n\tword\t* d,\t/* [0..39]   residual signal\tIN\t*/\n\tword\t* dp,\t/* [-120..-1] d'\t\tIN\t*/\n\n\tword\t* e,\t/* [0..39] \t\t\tOUT\t*/\n\tword\t* dpp,\t/* [0..39] \t\t\tOUT\t*/\n\tword\t* Nc,\t/* correlation lag\t\tOUT\t*/\n\tword\t* bc\t/* gain factor\t\t\tOUT\t*/\n)\n{\n\tassert( d  ); assert( dp ); assert( e  );\n\tassert( dpp); assert( Nc ); assert( bc );\n\n#if defined(FAST) && defined(USE_FLOAT_MUL)\n\tif (S->fast) \n#if   defined (LTP_CUT)\n\t\tif (S->ltp_cut)\n\t\t\tCut_Fast_Calculation_of_the_LTP_parameters(S,\n\t\t\t\td, dp, bc, Nc);\n\t\telse\n#endif /* LTP_CUT */\n\t\t\tFast_Calculation_of_the_LTP_parameters(d, dp, bc, Nc );\n\telse \n#endif /* FAST & USE_FLOAT_MUL */\n#ifdef LTP_CUT\n\t\tif (S->ltp_cut)\n\t\t\tCut_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc);\n\t\telse\n#endif\n\t\t\tCalculation_of_the_LTP_parameters(d, dp, bc, Nc);\n\n\tLong_term_analysis_filtering( *bc, *Nc, dp, d, dpp, e );\n}\n\n/* 4.3.2 */\nvoid Gsm_Long_Term_Synthesis_Filtering P5((S,Ncr,bcr,erp,drp),\n\tstruct gsm_state\t* S,\n\n\tword\t\t\tNcr,\n\tword\t\t\tbcr,\n\tregister word\t\t* erp,\t   /* [0..39]\t\t  \t IN */\n\tregister word\t\t* drp\t   /* [-120..-1] IN, [-120..40] OUT */\n)\n/*\n *  This procedure uses the bcr and Ncr parameter to realize the\n *  long term synthesis filtering.  The decoding of bcr needs\n *  table 4.3b.\n */\n{\n\tregister longword\tltmp;\t/* for ADD */\n\tregister int \t\tk;\n\tword\t\t\tbrp, drpp, Nr;\n\n\t/*  Check the limits of Nr.\n\t */\n\tNr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr;\n\tS->nrp = Nr;\n\tassert(Nr >= 40 && Nr <= 120);\n\n\t/*  Decoding of the LTP gain bcr\n\t */\n\tbrp = gsm_QLB[ bcr ];\n\n\t/*  Computation of the reconstructed short term residual \n\t *  signal drp[0..39]\n\t */\n\tassert(brp != MIN_WORD);\n\n\tfor (k = 0; k <= 39; k++) {\n\t\tdrpp   = GSM_MULT_R( brp, drp[ k - Nr ] );\n\t\tdrp[k] = GSM_ADD( erp[k], drpp );\n\t}\n\n\t/*\n\t *  Update of the reconstructed short term residual signal\n\t *  drp[ -1..-120 ]\n\t */\n\n\tfor (k = 0; k <= 119; k++) drp[ -120 + k ] = drp[ -80 + k ];\n}\n"
  },
  {
    "path": "src/audio/gsm/src/lpc.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/lpc.c,v 1.5 1994/12/30 23:14:54 jutta Exp $ */\n\n#include <stdio.h>\n#include <assert.h>\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\n#undef\tP\n\n/*\n *  4.2.4 .. 4.2.7 LPC ANALYSIS SECTION\n */\n\n/* 4.2.4 */\n\n\nstatic void Autocorrelation P2((s, L_ACF),\n\tword     * s,\t\t/* [0..159]\tIN/OUT  */\n \tlongword * L_ACF)\t/* [0..8]\tOUT     */\n/*\n *  The goal is to compute the array L_ACF[k].  The signal s[i] must\n *  be scaled in order to avoid an overflow situation.\n */\n{\n\tregister int\tk, i;\n\n\tword\t\ttemp, smax, scalauto;\n\n#ifdef\tUSE_FLOAT_MUL\n\tfloat\t\tfloat_s[160];\n#endif\n\n\t/*  Dynamic scaling of the array  s[0..159]\n\t */\n\n\t/*  Search for the maximum.\n\t */\n\tsmax = 0;\n\tfor (k = 0; k <= 159; k++) {\n\t\ttemp = GSM_ABS( s[k] );\n\t\tif (temp > smax) smax = temp;\n\t}\n\n\t/*  Computation of the scaling factor.\n\t */\n\tif (smax == 0) scalauto = 0;\n\telse {\n\t\tassert(smax > 0);\n\t\tscalauto = 4 - gsm_norm( (longword)smax << 16 );/* sub(4,..) */\n\t}\n\n\t/*  Scaling of the array s[0...159]\n\t */\n\n\tif (scalauto > 0) {\n\n# ifdef USE_FLOAT_MUL\n#   define SCALE(n)\t\\\n\tcase n: for (k = 0; k <= 159; k++) \\\n\t\t\tfloat_s[k] = (float)\t\\\n\t\t\t\t(s[k] = GSM_MULT_R(s[k], 16384 >> (n-1)));\\\n\t\tbreak;\n# else \n#   define SCALE(n)\t\\\n\tcase n: for (k = 0; k <= 159; k++) \\\n\t\t\ts[k] = GSM_MULT_R( s[k], 16384 >> (n-1) );\\\n\t\tbreak;\n# endif /* USE_FLOAT_MUL */\n\n\t\tswitch (scalauto) {\n\t\tSCALE(1)\n\t\tSCALE(2)\n\t\tSCALE(3)\n\t\tSCALE(4)\n\t\t}\n# undef\tSCALE\n\t}\n# ifdef\tUSE_FLOAT_MUL\n\telse for (k = 0; k <= 159; k++) float_s[k] = (float) s[k];\n# endif\n\n\t/*  Compute the L_ACF[..].\n\t */\n\t{\n# ifdef\tUSE_FLOAT_MUL\n\t\tregister float * sp = float_s;\n\t\tregister float   sl = *sp;\n\n#\t\tdefine STEP(k)\t L_ACF[k] += (longword)(sl * sp[ -(k) ]);\n# else\n\t\tword  * sp = s;\n\t\tword    sl = *sp;\n\n#\t\tdefine STEP(k)\t L_ACF[k] += ((longword)sl * sp[ -(k) ]);\n# endif\n\n#\tdefine NEXTI\t sl = *++sp\n\n\n\tfor (k = 9; k--; L_ACF[k] = 0) ;\n\n\tSTEP (0);\n\tNEXTI;\n\tSTEP(0); STEP(1);\n\tNEXTI;\n\tSTEP(0); STEP(1); STEP(2);\n\tNEXTI;\n\tSTEP(0); STEP(1); STEP(2); STEP(3);\n\tNEXTI;\n\tSTEP(0); STEP(1); STEP(2); STEP(3); STEP(4);\n\tNEXTI;\n\tSTEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5);\n\tNEXTI;\n\tSTEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6);\n\tNEXTI;\n\tSTEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7);\n\n\tfor (i = 8; i <= 159; i++) {\n\n\t\tNEXTI;\n\n\t\tSTEP(0);\n\t\tSTEP(1); STEP(2); STEP(3); STEP(4);\n\t\tSTEP(5); STEP(6); STEP(7); STEP(8);\n\t}\n\n\tfor (k = 9; k--; L_ACF[k] <<= 1) ; \n\n\t}\n\t/*   Rescaling of the array s[0..159]\n\t */\n\tif (scalauto > 0) {\n\t\tassert(scalauto <= 4); \n\t\tfor (k = 160; k--; *s++ <<= scalauto) ;\n\t}\n}\n\n#if defined(USE_FLOAT_MUL) && defined(FAST)\n\nstatic void Fast_Autocorrelation P2((s, L_ACF),\n\tword * s,\t\t/* [0..159]\tIN/OUT  */\n \tlongword * L_ACF)\t/* [0..8]\tOUT     */\n{\n\tregister int\tk, i;\n\tfloat f_L_ACF[9];\n\tfloat scale;\n\n\tfloat          s_f[160];\n\tregister float *sf = s_f;\n\n\tfor (i = 0; i < 160; ++i) sf[i] = s[i];\n\tfor (k = 0; k <= 8; k++) {\n\t\tregister float L_temp2 = 0;\n\t\tregister float *sfl = sf - k;\n\t\tfor (i = k; i < 160; ++i) L_temp2 += sf[i] * sfl[i];\n\t\tf_L_ACF[k] = L_temp2;\n\t}\n\tscale = MAX_LONGWORD / f_L_ACF[0];\n\n\tfor (k = 0; k <= 8; k++) {\n\t\tL_ACF[k] = f_L_ACF[k] * scale;\n\t}\n}\n#endif\t/* defined (USE_FLOAT_MUL) && defined (FAST) */\n\n/* 4.2.5 */\n\nstatic void Reflection_coefficients P2( (L_ACF, r),\n\tlongword\t* L_ACF,\t\t/* 0...8\tIN\t*/\n\tregister word\t* r\t\t\t/* 0...7\tOUT \t*/\n)\n{\n\tregister int\ti, m, n;\n\tregister word\ttemp;\n\tregister longword ltmp;\n\tword\t\tACF[9];\t/* 0..8 */\n\tword\t\tP[  9];\t/* 0..8 */\n\tword\t\tK[  9]; /* 2..8 */\n\n\t/*  Schur recursion with 16 bits arithmetic.\n\t */\n\n\tif (L_ACF[0] == 0) {\n\t\tfor (i = 8; i--; *r++ = 0) ;\n\t\treturn;\n\t}\n\n\tassert( L_ACF[0] != 0 );\n\ttemp = gsm_norm( L_ACF[0] );\n\n\tassert(temp >= 0 && temp < 32);\n\n\t/* ? overflow ? */\n\tfor (i = 0; i <= 8; i++) ACF[i] = SASR( L_ACF[i] << temp, 16 );\n\n\t/*   Initialize array P[..] and K[..] for the recursion.\n\t */\n\n\tfor (i = 1; i <= 7; i++) K[ i ] = ACF[ i ];\n\tfor (i = 0; i <= 8; i++) P[ i ] = ACF[ i ];\n\n\t/*   Compute reflection coefficients\n\t */\n\tfor (n = 1; n <= 8; n++, r++) {\n\n\t\ttemp = P[1];\n\t\ttemp = GSM_ABS(temp);\n\t\tif (P[0] < temp) {\n\t\t\tfor (i = n; i <= 8; i++) *r++ = 0;\n\t\t\treturn;\n\t\t}\n\n\t\t*r = gsm_div( temp, P[0] );\n\n\t\tassert(*r >= 0);\n\t\tif (P[1] > 0) *r = -*r;\t\t/* r[n] = sub(0, r[n]) */\n\t\tassert (*r != MIN_WORD);\n\t\tif (n == 8) return; \n\n\t\t/*  Schur recursion\n\t\t */\n\t\ttemp = GSM_MULT_R( P[1], *r );\n\t\tP[0] = GSM_ADD( P[0], temp );\n\n\t\tfor (m = 1; m <= 8 - n; m++) {\n\t\t\ttemp     = GSM_MULT_R( K[ m   ],    *r );\n\t\t\tP[m]     = GSM_ADD(    P[ m+1 ],  temp );\n\n\t\t\ttemp     = GSM_MULT_R( P[ m+1 ],    *r );\n\t\t\tK[m]     = GSM_ADD(    K[ m   ],  temp );\n\t\t}\n\t}\n}\n\n/* 4.2.6 */\n\nstatic void Transformation_to_Log_Area_Ratios P1((r),\n\tregister word\t* r \t\t\t/* 0..7\t   IN/OUT */\n)\n/*\n *  The following scaling for r[..] and LAR[..] has been used:\n *\n *  r[..]   = integer( real_r[..]*32768. ); -1 <= real_r < 1.\n *  LAR[..] = integer( real_LAR[..] * 16384 );\n *  with -1.625 <= real_LAR <= 1.625\n */\n{\n\tregister word\ttemp;\n\tregister int\ti;\n\n\n\t/* Computation of the LAR[0..7] from the r[0..7]\n\t */\n\tfor (i = 1; i <= 8; i++, r++) {\n\n\t\ttemp = *r;\n\t\ttemp = GSM_ABS(temp);\n\t\tassert(temp >= 0);\n\n\t\tif (temp < 22118) {\n\t\t\ttemp >>= 1;\n\t\t} else if (temp < 31130) {\n\t\t\tassert( temp >= 11059 );\n\t\t\ttemp -= 11059;\n\t\t} else {\n\t\t\tassert( temp >= 26112 );\n\t\t\ttemp -= 26112;\n\t\t\ttemp <<= 2;\n\t\t}\n\n\t\t*r = *r < 0 ? -temp : temp;\n\t\tassert( *r != MIN_WORD );\n\t}\n}\n\n/* 4.2.7 */\n\nstatic void Quantization_and_coding P1((LAR),\n\tregister word * LAR    \t/* [0..7]\tIN/OUT\t*/\n)\n{\n\tregister word\ttemp;\n\tlongword\tltmp;\n\n\n\t/*  This procedure needs four tables; the following equations\n\t *  give the optimum scaling for the constants:\n\t *  \n\t *  A[0..7] = integer( real_A[0..7] * 1024 )\n\t *  B[0..7] = integer( real_B[0..7] *  512 )\n\t *  MAC[0..7] = maximum of the LARc[0..7]\n\t *  MIC[0..7] = minimum of the LARc[0..7]\n\t */\n\n#\tundef STEP\n#\tdefine\tSTEP( A, B, MAC, MIC )\t\t\\\n\t\ttemp = GSM_MULT( A,   *LAR );\t\\\n\t\ttemp = GSM_ADD(  temp,   B );\t\\\n\t\ttemp = GSM_ADD(  temp, 256 );\t\\\n\t\ttemp = SASR(     temp,   9 );\t\\\n\t\t*LAR  =  temp>MAC ? MAC - MIC : (temp<MIC ? 0 : temp - MIC); \\\n\t\tLAR++;\n\n\tSTEP(  20480,     0,  31, -32 );\n\tSTEP(  20480,     0,  31, -32 );\n\tSTEP(  20480,  2048,  15, -16 );\n\tSTEP(  20480, -2560,  15, -16 );\n\n\tSTEP(  13964,    94,   7,  -8 );\n\tSTEP(  15360, -1792,   7,  -8 );\n\tSTEP(   8534,  -341,   3,  -4 );\n\tSTEP(   9036, -1144,   3,  -4 );\n\n#\tundef\tSTEP\n}\n\nvoid Gsm_LPC_Analysis P3((S, s,LARc),\n\tstruct gsm_state *S,\n\tword \t\t * s,\t\t/* 0..159 signals\tIN/OUT\t*/\n        word \t\t * LARc)\t/* 0..7   LARc's\tOUT\t*/\n{\n\tlongword\tL_ACF[9];\n\n#if defined(USE_FLOAT_MUL) && defined(FAST)\n\tif (S->fast) Fast_Autocorrelation (s,\t  L_ACF );\n\telse\n#endif\n\tAutocorrelation\t\t\t  (s,\t  L_ACF\t);\n\tReflection_coefficients\t\t  (L_ACF, LARc\t);\n\tTransformation_to_Log_Area_Ratios (LARc);\n\tQuantization_and_coding\t\t  (LARc);\n}\n"
  },
  {
    "path": "src/audio/gsm/src/preprocess.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/preprocess.c,v 1.2 1994/05/10 20:18:45 jutta Exp $ */\n\n#include\t<stdio.h>\n#include\t<assert.h>\n\n#include \"private.h\"\n\n#include\t\"gsm.h\"\n#include \t\"proto.h\"\n\n/*\t4.2.0 .. 4.2.3\tPREPROCESSING SECTION\n *  \n *  \tAfter A-law to linear conversion (or directly from the\n *   \tAto D converter) the following scaling is assumed for\n * \tinput to the RPE-LTP algorithm:\n *\n *      in:  0.1.....................12\n *\t     S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.*\n *\n *\tWhere S is the sign bit, v a valid bit, and * a \"don't care\" bit.\n * \tThe original signal is called sop[..]\n *\n *      out:   0.1................... 12 \n *\t     S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0\n */\n\n\nvoid Gsm_Preprocess P3((S, s, so),\n\tstruct gsm_state * S,\n\tword\t\t * s,\n\tword \t\t * so )\t\t/* [0..159] \tIN/OUT\t*/\n{\n\n\tword       z1 = S->z1;\n\tlongword L_z2 = S->L_z2;\n\tword \t   mp = S->mp;\n\n\tword \t   \ts1;\n\tlongword      L_s2;\n\n\tlongword      L_temp;\n\n\tword\t\tmsp, lsp;\n\tword\t\tSO;\n\n\tlongword\tltmp;\t\t/* for   ADD */\n\tulongword\tutmp;\t\t/* for L_ADD */\n\n\tregister int\t\tk = 160;\n\n\twhile (k--) {\n\n\t/*  4.2.1   Downscaling of the input signal\n\t */\n\t\tSO = SASR( *s, 3 ) << 2;\n\t\ts++;\n\n\t\tassert (SO >= -0x4000);\t/* downscaled by     */\n\t\tassert (SO <=  0x3FFC);\t/* previous routine. */\n\n\n\t/*  4.2.2   Offset compensation\n\t * \n\t *  This part implements a high-pass filter and requires extended\n\t *  arithmetic precision for the recursive part of this filter.\n\t *  The input of this procedure is the array so[0...159] and the\n\t *  output the array sof[ 0...159 ].\n\t */\n\t\t/*   Compute the non-recursive part\n\t\t */\n\n\t\ts1 = SO - z1;\t\t\t/* s1 = gsm_sub( *so, z1 ); */\n\t\tz1 = SO;\n\n\t\tassert(s1 != MIN_WORD);\n\n\t\t/*   Compute the recursive part\n\t\t */\n\t\tL_s2 = s1;\n\t\tL_s2 <<= 15;\n\n\t\t/*   Execution of a 31 bv 16 bits multiplication\n\t\t */\n\n\t\tmsp = SASR( L_z2, 15 );\n\t\tlsp = L_z2-((longword)msp<<15); /* gsm_L_sub(L_z2,(msp<<15)); */\n\n\t\tL_s2  += GSM_MULT_R( lsp, 32735 );\n\t\tL_temp = (longword)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/\n\t\tL_z2   = GSM_L_ADD( L_temp, L_s2 );\n\n\t\t/*    Compute sof[k] with rounding\n\t\t */\n\t\tL_temp = GSM_L_ADD( L_z2, 16384 );\n\n\t/*   4.2.3  Preemphasis\n\t */\n\n\t\tmsp   = GSM_MULT_R( mp, -28180 );\n\t\tmp    = SASR( L_temp, 15 );\n\t\t*so++ = GSM_ADD( mp, msp );\n\t}\n\n\tS->z1   = z1;\n\tS->L_z2 = L_z2;\n\tS->mp   = mp;\n}\n"
  },
  {
    "path": "src/audio/gsm/src/rpe.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/rpe.c,v 1.3 1994/05/10 20:18:46 jutta Exp $ */\n\n#include <stdio.h>\n#include <assert.h>\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\n/*  4.2.13 .. 4.2.17  RPE ENCODING SECTION\n */\n\n/* 4.2.13 */\n\nstatic void Weighting_filter P2((e, x),\n\tregister word\t* e,\t\t/* signal [-5..0.39.44]\tIN  */\n\tword\t\t* x\t\t/* signal [0..39]\tOUT */\n)\n/*\n *  The coefficients of the weighting filter are stored in a table\n *  (see table 4.4).  The following scaling is used:\n *\n *\tH[0..10] = integer( real_H[ 0..10] * 8192 ); \n */\n{\n\t/* word\t\t\twt[ 50 ]; */\n\n\tregister longword\tL_result;\n\tregister int\t\tk /* , i */ ;\n\n\t/*  Initialization of a temporary working array wt[0...49]\n\t */\n\n\t/* for (k =  0; k <=  4; k++) wt[k] = 0;\n\t * for (k =  5; k <= 44; k++) wt[k] = *e++;\n\t * for (k = 45; k <= 49; k++) wt[k] = 0;\n\t *\n\t *  (e[-5..-1] and e[40..44] are allocated by the caller,\n\t *  are initially zero and are not written anywhere.)\n\t */\n\te -= 5;\n\n\t/*  Compute the signal x[0..39]\n\t */ \n\tfor (k = 0; k <= 39; k++) {\n\n\t\tL_result = 8192 >> 1;\n\n\t\t/* for (i = 0; i <= 10; i++) {\n\t\t *\tL_temp   = GSM_L_MULT( wt[k+i], gsm_H[i] );\n\t\t *\tL_result = GSM_L_ADD( L_result, L_temp );\n\t\t * }\n\t\t */\n\n#undef\tSTEP\n#define\tSTEP( i, H )\t(e[ k + i ] * (longword)H)\n\n\t\t/*  Every one of these multiplications is done twice --\n\t\t *  but I don't see an elegant way to optimize this. \n\t\t *  Do you?\n\t\t */\n\n#ifdef\tSTUPID_COMPILER\n\t\tL_result += STEP(\t0, \t-134 ) ;\n\t\tL_result += STEP(\t1, \t-374 )  ;\n\t               /* + STEP(\t2, \t0    )  */\n\t\tL_result += STEP(\t3, \t2054 ) ;\n\t\tL_result += STEP(\t4, \t5741 ) ;\n\t\tL_result += STEP(\t5, \t8192 ) ;\n\t\tL_result += STEP(\t6, \t5741 ) ;\n\t\tL_result += STEP(\t7, \t2054 ) ;\n\t \t       /* + STEP(\t8, \t0    )  */\n\t\tL_result += STEP(\t9, \t-374 ) ;\n\t\tL_result += STEP(\t10, \t-134 ) ;\n#else\n\t\tL_result +=\n\t\t  STEP(\t0, \t-134 ) \n\t\t+ STEP(\t1, \t-374 ) \n\t     /* + STEP(\t2, \t0    )  */\n\t\t+ STEP(\t3, \t2054 ) \n\t\t+ STEP(\t4, \t5741 ) \n\t\t+ STEP(\t5, \t8192 ) \n\t\t+ STEP(\t6, \t5741 ) \n\t\t+ STEP(\t7, \t2054 ) \n\t     /* + STEP(\t8, \t0    )  */\n\t\t+ STEP(\t9, \t-374 ) \n\t\t+ STEP(10, \t-134 )\n\t\t;\n#endif\n\n\t\t/* L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x2) *)\n\t\t * L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x4) *)\n\t\t *\n\t\t * x[k] = SASR( L_result, 16 );\n\t\t */\n\n\t\t/* 2 adds vs. >>16 => 14, minus one shift to compensate for\n\t\t * those we lost when replacing L_MULT by '*'.\n\t\t */\n\n\t\tL_result = SASR( L_result, 13 );\n\t\tx[k] =  (  L_result < MIN_WORD ? MIN_WORD\n\t\t\t: (L_result > MAX_WORD ? MAX_WORD : L_result ));\n\t}\n}\n\n/* 4.2.14 */\n\nstatic void RPE_grid_selection P3((x,xM,Mc_out),\n\tword\t\t* x,\t\t/* [0..39]\t\tIN  */ \n\tword\t\t* xM,\t\t/* [0..12]\t\tOUT */\n\tword\t\t* Mc_out\t/*\t\t\tOUT */\n)\n/*\n *  The signal x[0..39] is used to select the RPE grid which is\n *  represented by Mc.\n */\n{\n\t/* register word\ttemp1;\t*/\n\tregister int\t\t/* m, */  i;\n\tregister longword\tL_result, L_temp;\n\tlongword\t\tEM;\t/* xxx should be L_EM? */\n\tword\t\t\tMc;\n\n\tlongword\t\tL_common_0_3;\n\n\tEM = 0;\n\tMc = 0;\n\n\t/* for (m = 0; m <= 3; m++) {\n\t *\tL_result = 0;\n\t *\n\t *\n\t *\tfor (i = 0; i <= 12; i++) {\n\t *\n\t *\t\ttemp1    = SASR( x[m + 3*i], 2 );\n\t *\n\t *\t\tassert(temp1 != MIN_WORD);\n\t *\n\t *\t\tL_temp   = GSM_L_MULT( temp1, temp1 );\n\t *\t\tL_result = GSM_L_ADD( L_temp, L_result );\n\t *\t}\n\t * \n\t *\tif (L_result > EM) {\n\t *\t\tMc = m;\n\t *\t\tEM = L_result;\n\t *\t}\n\t * }\n\t */\n\n#undef\tSTEP\n#define\tSTEP( m, i )\t\tL_temp = SASR( x[m + 3 * i], 2 );\t\\\n\t\t\t\tL_result += L_temp * L_temp;\n\n\t/* common part of 0 and 3 */\n\n\tL_result = 0;\n\tSTEP( 0, 1 ); STEP( 0, 2 ); STEP( 0, 3 ); STEP( 0, 4 );\n\tSTEP( 0, 5 ); STEP( 0, 6 ); STEP( 0, 7 ); STEP( 0, 8 );\n\tSTEP( 0, 9 ); STEP( 0, 10); STEP( 0, 11); STEP( 0, 12);\n\tL_common_0_3 = L_result;\n\n\t/* i = 0 */\n\n\tSTEP( 0, 0 );\n\tL_result <<= 1;\t/* implicit in L_MULT */\n\tEM = L_result;\n\n\t/* i = 1 */\n\n\tL_result = 0;\n\tSTEP( 1, 0 );\n\tSTEP( 1, 1 ); STEP( 1, 2 ); STEP( 1, 3 ); STEP( 1, 4 );\n\tSTEP( 1, 5 ); STEP( 1, 6 ); STEP( 1, 7 ); STEP( 1, 8 );\n\tSTEP( 1, 9 ); STEP( 1, 10); STEP( 1, 11); STEP( 1, 12);\n\tL_result <<= 1;\n\tif (L_result > EM) {\n\t\tMc = 1;\n\t \tEM = L_result;\n\t}\n\n\t/* i = 2 */\n\n\tL_result = 0;\n\tSTEP( 2, 0 );\n\tSTEP( 2, 1 ); STEP( 2, 2 ); STEP( 2, 3 ); STEP( 2, 4 );\n\tSTEP( 2, 5 ); STEP( 2, 6 ); STEP( 2, 7 ); STEP( 2, 8 );\n\tSTEP( 2, 9 ); STEP( 2, 10); STEP( 2, 11); STEP( 2, 12);\n\tL_result <<= 1;\n\tif (L_result > EM) {\n\t\tMc = 2;\n\t \tEM = L_result;\n\t}\n\n\t/* i = 3 */\n\n\tL_result = L_common_0_3;\n\tSTEP( 3, 12 );\n\tL_result <<= 1;\n\tif (L_result > EM) {\n\t\tMc = 3;\n\t \tEM = L_result;\n\t}\n\n\t/**/\n\n\t/*  Down-sampling by a factor 3 to get the selected xM[0..12]\n\t *  RPE sequence.\n\t */\n\tfor (i = 0; i <= 12; i ++) xM[i] = x[Mc + 3*i];\n\t*Mc_out = Mc;\n}\n\n/* 4.12.15 */\n\nstatic void APCM_quantization_xmaxc_to_exp_mant P3((xmaxc,exp_out,mant_out),\n\tword\t\txmaxc,\t\t/* IN \t*/\n\tword\t\t* exp_out,\t/* OUT\t*/\n\tword\t\t* mant_out )\t/* OUT  */\n{\n\tword\texp, mant;\n\n\t/* Compute exponent and mantissa of the decoded version of xmaxc\n\t */\n\n\texp = 0;\n\tif (xmaxc > 15) exp = SASR(xmaxc, 3) - 1;\n\tmant = xmaxc - (exp << 3);\n\n\tif (mant == 0) {\n\t\texp  = -4;\n\t\tmant = 7;\n\t}\n\telse {\n\t\twhile (mant <= 7) {\n\t\t\tmant = mant << 1 | 1;\n\t\t\texp--;\n\t\t}\n\t\tmant -= 8;\n\t}\n\n\tassert( exp  >= -4 && exp <= 6 );\n\tassert( mant >= 0 && mant <= 7 );\n\n\t*exp_out  = exp;\n\t*mant_out = mant;\n}\n\nstatic void APCM_quantization P5((xM,xMc,mant_out,exp_out,xmaxc_out),\n\tword\t\t* xM,\t\t/* [0..12]\t\tIN\t*/\n\n\tword\t\t* xMc,\t\t/* [0..12]\t\tOUT\t*/\n\tword\t\t* mant_out,\t/* \t\t\tOUT\t*/\n\tword\t\t* exp_out,\t/*\t\t\tOUT\t*/\n\tword\t\t* xmaxc_out\t/*\t\t\tOUT\t*/\n)\n{\n\tint\ti, itest;\n\n\tword\txmax, xmaxc, temp, temp1, temp2;\n\tword\texp, mant;\n\n\n\t/*  Find the maximum absolute value xmax of xM[0..12].\n\t */\n\n\txmax = 0;\n\tfor (i = 0; i <= 12; i++) {\n\t\ttemp = xM[i];\n\t\ttemp = GSM_ABS(temp);\n\t\tif (temp > xmax) xmax = temp;\n\t}\n\n\t/*  Qantizing and coding of xmax to get xmaxc.\n\t */\n\n\texp   = 0;\n\ttemp  = SASR( xmax, 9 );\n\titest = 0;\n\n\tfor (i = 0; i <= 5; i++) {\n\n\t\titest |= (temp <= 0);\n\t\ttemp = SASR( temp, 1 );\n\n\t\tassert(exp <= 5);\n\t\tif (itest == 0) exp++;\t\t/* exp = add (exp, 1) */\n\t}\n\n\tassert(exp <= 6 && exp >= 0);\n\ttemp = exp + 5;\n\n\tassert(temp <= 11 && temp >= 0);\n\txmaxc = gsm_add( SASR(xmax, temp), exp << 3 );\n\n\t/*   Quantizing and coding of the xM[0..12] RPE sequence\n\t *   to get the xMc[0..12]\n\t */\n\n\tAPCM_quantization_xmaxc_to_exp_mant( xmaxc, &exp, &mant );\n\n\t/*  This computation uses the fact that the decoded version of xmaxc\n\t *  can be calculated by using the exponent and the mantissa part of\n\t *  xmaxc (logarithmic table).\n\t *  So, this method avoids any division and uses only a scaling\n\t *  of the RPE samples by a function of the exponent.  A direct \n\t *  multiplication by the inverse of the mantissa (NRFAC[0..7]\n\t *  found in table 4.5) gives the 3 bit coded version xMc[0..12]\n\t *  of the RPE samples.\n\t */\n\n\n\t/* Direct computation of xMc[0..12] using table 4.5\n\t */\n\n\tassert( exp <= 4096 && exp >= -4096);\n\tassert( mant >= 0 && mant <= 7 ); \n\n\ttemp1 = 6 - exp;\t\t/* normalization by the exponent */\n\ttemp2 = gsm_NRFAC[ mant ];  \t/* inverse mantissa \t\t */\n\n\tfor (i = 0; i <= 12; i++) {\n\n\t\tassert(temp1 >= 0 && temp1 < 16);\n\n\t\ttemp = xM[i] << temp1;\n\t\ttemp = GSM_MULT( temp, temp2 );\n\t\ttemp = SASR(temp, 12);\n\t\txMc[i] = temp + 4;\t\t/* see note below */\n\t}\n\n\t/*  NOTE: This equation is used to make all the xMc[i] positive.\n\t */\n\n\t*mant_out  = mant;\n\t*exp_out   = exp;\n\t*xmaxc_out = xmaxc;\n}\n\n/* 4.2.16 */\n\nstatic void APCM_inverse_quantization P4((xMc,mant,exp,xMp),\n\tregister word\t* xMc,\t/* [0..12]\t\t\tIN \t*/\n\tword\t\tmant,\n\tword\t\texp,\n\tregister word\t* xMp)\t/* [0..12]\t\t\tOUT \t*/\n/* \n *  This part is for decoding the RPE sequence of coded xMc[0..12]\n *  samples to obtain the xMp[0..12] array.  Table 4.6 is used to get\n *  the mantissa of xmaxc (FAC[0..7]).\n */\n{\n\tint\ti;\n\tword\ttemp, temp1, temp2, temp3;\n\tlongword\tltmp;\n\n\tassert( mant >= 0 && mant <= 7 ); \n\n\ttemp1 = gsm_FAC[ mant ];\t/* see 4.2-15 for mant */\n\ttemp2 = gsm_sub( 6, exp );\t/* see 4.2-15 for exp  */\n\ttemp3 = gsm_asl( 1, gsm_sub( temp2, 1 ));\n\n\tfor (i = 13; i--;) {\n\n\t\tassert( *xMc <= 7 && *xMc >= 0 ); \t/* 3 bit unsigned */\n\n\t\t/* temp = gsm_sub( *xMc++ << 1, 7 ); */\n\t\ttemp = (*xMc++ << 1) - 7;\t        /* restore sign   */\n\t\tassert( temp <= 7 && temp >= -7 ); \t/* 4 bit signed   */\n\n\t\ttemp <<= 12;\t\t\t\t/* 16 bit signed  */\n\t\ttemp = GSM_MULT_R( temp1, temp );\n\t\ttemp = GSM_ADD( temp, temp3 );\n\t\t*xMp++ = gsm_asr( temp, temp2 );\n\t}\n}\n\n/* 4.2.17 */\n\nstatic void RPE_grid_positioning P3((Mc,xMp,ep),\n\tword\t\tMc,\t\t/* grid position\tIN\t*/\n\tregister word\t* xMp,\t\t/* [0..12]\t\tIN\t*/\n\tregister word\t* ep\t\t/* [0..39]\t\tOUT\t*/\n)\n/*\n *  This procedure computes the reconstructed long term residual signal\n *  ep[0..39] for the LTP analysis filter.  The inputs are the Mc\n *  which is the grid position selection and the xMp[0..12] decoded\n *  RPE samples which are upsampled by a factor of 3 by inserting zero\n *  values.\n */\n{\n\tint\ti = 13;\n\n\tassert(0 <= Mc && Mc <= 3);\n\n        switch (Mc) {\n                case 3: *ep++ = 0;\n                case 2:  do {\n                                *ep++ = 0;\n                case 1:         *ep++ = 0;\n                case 0:         *ep++ = *xMp++;\n                         } while (--i);\n        }\n        while (++Mc < 4) *ep++ = 0;\n\n\t/*\n\n\tint i, k;\n\tfor (k = 0; k <= 39; k++) ep[k] = 0;\n\tfor (i = 0; i <= 12; i++) {\n\t\tep[ Mc + (3*i) ] = xMp[i];\n\t}\n\t*/\n}\n\n/* 4.2.18 */\n\n/*  This procedure adds the reconstructed long term residual signal\n *  ep[0..39] to the estimated signal dpp[0..39] from the long term\n *  analysis filter to compute the reconstructed short term residual\n *  signal dp[-40..-1]; also the reconstructed short term residual\n *  array dp[-120..-41] is updated.\n */\n\n#if 0\t/* Has been inlined in code.c */\nvoid Gsm_Update_of_reconstructed_short_time_residual_signal P3((dpp, ep, dp),\n\tword\t* dpp,\t\t/* [0...39]\tIN\t*/\n\tword\t* ep,\t\t/* [0...39]\tIN\t*/\n\tword\t* dp)\t\t/* [-120...-1]  IN/OUT \t*/\n{\n\tint \t\tk;\n\n\tfor (k = 0; k <= 79; k++) \n\t\tdp[ -120 + k ] = dp[ -80 + k ];\n\n\tfor (k = 0; k <= 39; k++)\n\t\tdp[ -40 + k ] = gsm_add( ep[k], dpp[k] );\n}\n#endif\t/* Has been inlined in code.c */\n\nvoid Gsm_RPE_Encoding P5((S,e,xmaxc,Mc,xMc),\n\n\tstruct gsm_state * S,\n\n\tword\t* e,\t\t/* -5..-1][0..39][40..44\tIN/OUT  */\n\tword\t* xmaxc,\t/* \t\t\t\tOUT */\n\tword\t* Mc,\t\t/* \t\t\t  \tOUT */\n\tword\t* xMc)\t\t/* [0..12]\t\t\tOUT */\n{\n\tword\tx[40];\n\tword\txM[13], xMp[13];\n\tword\tmant, exp;\n\n\tWeighting_filter(e, x);\n\tRPE_grid_selection(x, xM, Mc);\n\n\tAPCM_quantization(\txM, xMc, &mant, &exp, xmaxc);\n\tAPCM_inverse_quantization(  xMc,  mant,  exp, xMp);\n\n\tRPE_grid_positioning( *Mc, xMp, e );\n\n}\n\nvoid Gsm_RPE_Decoding P5((S, xmaxcr, Mcr, xMcr, erp),\n\tstruct gsm_state\t* S,\n\n\tword \t\txmaxcr,\n\tword\t\tMcr,\n\tword\t\t* xMcr,  /* [0..12], 3 bits \t\tIN\t*/\n\tword\t\t* erp\t /* [0..39]\t\t\tOUT \t*/\n)\n{\n\tword\texp, mant;\n\tword\txMp[ 13 ];\n\n\tAPCM_quantization_xmaxc_to_exp_mant( xmaxcr, &exp, &mant );\n\tAPCM_inverse_quantization( xMcr, mant, exp, xMp );\n\tRPE_grid_positioning( Mcr, xMp, erp );\n\n}\n"
  },
  {
    "path": "src/audio/gsm/src/short_term.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/short_term.c,v 1.2 1994/05/10 20:18:47 jutta Exp $ */\n\n#include <stdio.h>\n#include <assert.h>\n\n#include \"private.h\"\n\n#include \"gsm.h\"\n#include \"proto.h\"\n\n/*\n *  SHORT TERM ANALYSIS FILTERING SECTION\n */\n\n/* 4.2.8 */\n\nstatic void Decoding_of_the_coded_Log_Area_Ratios P2((LARc,LARpp),\n\tword \t* LARc,\t\t/* coded log area ratio\t[0..7] \tIN\t*/\n\tword\t* LARpp)\t/* out: decoded ..\t\t\t*/\n{\n\tregister word\ttemp1 /* , temp2 */;\n\tregister long\tltmp;\t/* for GSM_ADD */\n\n\t/*  This procedure requires for efficient implementation\n\t *  two tables.\n \t *\n\t *  INVA[1..8] = integer( (32768 * 8) / real_A[1..8])\n\t *  MIC[1..8]  = minimum value of the LARc[1..8]\n\t */\n\n\t/*  Compute the LARpp[1..8]\n\t */\n\n\t/* \tfor (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) {\n\t *\n\t *\t\ttemp1  = GSM_ADD( *LARc, *MIC ) << 10;\n\t *\t\ttemp2  = *B << 1;\n\t *\t\ttemp1  = GSM_SUB( temp1, temp2 );\n\t *\n\t *\t\tassert(*INVA != MIN_WORD);\n\t *\n\t *\t\ttemp1  = GSM_MULT_R( *INVA, temp1 );\n\t *\t\t*LARpp = GSM_ADD( temp1, temp1 );\n\t *\t}\n\t */\n\n#undef\tSTEP\n#define\tSTEP( B, MIC, INVA )\t\\\n\t\ttemp1    = GSM_ADD( *LARc++, MIC ) << 10;\t\\\n\t\ttemp1    = GSM_SUB( temp1, B << 1 );\t\t\\\n\t\ttemp1    = GSM_MULT_R( INVA, temp1 );\t\t\\\n\t\t*LARpp++ = GSM_ADD( temp1, temp1 );\n\n\tSTEP(      0,  -32,  13107 );\n\tSTEP(      0,  -32,  13107 );\n\tSTEP(   2048,  -16,  13107 );\n\tSTEP(  -2560,  -16,  13107 );\n\n\tSTEP(     94,   -8,  19223 );\n\tSTEP(  -1792,   -8,  17476 );\n\tSTEP(   -341,   -4,  31454 );\n\tSTEP(  -1144,   -4,  29708 );\n\n\t/* NOTE: the addition of *MIC is used to restore\n\t * \t the sign of *LARc.\n\t */\n}\n\n/* 4.2.9 */\n/* Computation of the quantized reflection coefficients \n */\n\n/* 4.2.9.1  Interpolation of the LARpp[1..8] to get the LARp[1..8]\n */\n\n/*\n *  Within each frame of 160 analyzed speech samples the short term\n *  analysis and synthesis filters operate with four different sets of\n *  coefficients, derived from the previous set of decoded LARs(LARpp(j-1))\n *  and the actual set of decoded LARs (LARpp(j))\n *\n * (Initial value: LARpp(j-1)[1..8] = 0.)\n */\n\nstatic void Coefficients_0_12 P3((LARpp_j_1, LARpp_j, LARp),\n\tregister word * LARpp_j_1,\n\tregister word * LARpp_j,\n\tregister word * LARp)\n{\n\tregister int \ti;\n\tregister longword ltmp;\n\n\tfor (i = 1; i <= 8; i++, LARp++, LARpp_j_1++, LARpp_j++) {\n\t\t*LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 ));\n\t\t*LARp = GSM_ADD( *LARp,  SASR( *LARpp_j_1, 1));\n\t}\n}\n\nstatic void Coefficients_13_26 P3((LARpp_j_1, LARpp_j, LARp),\n\tregister word * LARpp_j_1,\n\tregister word * LARpp_j,\n\tregister word * LARp)\n{\n\tregister int i;\n\tregister longword ltmp;\n\tfor (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) {\n\t\t*LARp = GSM_ADD( SASR( *LARpp_j_1, 1), SASR( *LARpp_j, 1 ));\n\t}\n}\n\nstatic void Coefficients_27_39 P3((LARpp_j_1, LARpp_j, LARp),\n\tregister word * LARpp_j_1,\n\tregister word * LARpp_j,\n\tregister word * LARp)\n{\n\tregister int i;\n\tregister longword ltmp;\n\n\tfor (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) {\n\t\t*LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 ));\n\t\t*LARp = GSM_ADD( *LARp, SASR( *LARpp_j, 1 ));\n\t}\n}\n\n\nstatic void Coefficients_40_159 P2((LARpp_j, LARp),\n\tregister word * LARpp_j,\n\tregister word * LARp)\n{\n\tregister int i;\n\n\tfor (i = 1; i <= 8; i++, LARp++, LARpp_j++)\n\t\t*LARp = *LARpp_j;\n}\n\n/* 4.2.9.2 */\n\nstatic void LARp_to_rp P1((LARp),\n\tregister word * LARp)\t/* [0..7] IN/OUT  */\n/*\n *  The input of this procedure is the interpolated LARp[0..7] array.\n *  The reflection coefficients, rp[i], are used in the analysis\n *  filter and in the synthesis filter.\n */\n{\n\tregister int \t\ti;\n\tregister word\t\ttemp;\n\tregister longword\tltmp;\n\n\tfor (i = 1; i <= 8; i++, LARp++) {\n\n\t\t/* temp = GSM_ABS( *LARp );\n\t         *\n\t\t * if (temp < 11059) temp <<= 1;\n\t\t * else if (temp < 20070) temp += 11059;\n\t\t * else temp = GSM_ADD( temp >> 2, 26112 );\n\t\t *\n\t\t * *LARp = *LARp < 0 ? -temp : temp;\n\t\t */\n\n\t\tif (*LARp < 0) {\n\t\t\ttemp = *LARp == MIN_WORD ? MAX_WORD : -(*LARp);\n\t\t\t*LARp = - ((temp < 11059) ? temp << 1\n\t\t\t\t: ((temp < 20070) ? temp + 11059\n\t\t\t\t:  GSM_ADD( temp >> 2, 26112 )));\n\t\t} else {\n\t\t\ttemp  = *LARp;\n\t\t\t*LARp =    (temp < 11059) ? temp << 1\n\t\t\t\t: ((temp < 20070) ? temp + 11059\n\t\t\t\t:  GSM_ADD( temp >> 2, 26112 ));\n\t\t}\n\t}\n}\n\n\n/* 4.2.10 */\nstatic void Short_term_analysis_filtering P4((S,rp,k_n,s),\n\tstruct gsm_state * S,\n\tregister word\t* rp,\t/* [0..7]\tIN\t*/\n\tregister int \tk_n, \t/*   k_end - k_start\t*/\n\tregister word\t* s\t/* [0..n-1]\tIN/OUT\t*/\n)\n/*\n *  This procedure computes the short term residual signal d[..] to be fed\n *  to the RPE-LTP loop from the s[..] signal and from the local rp[..]\n *  array (quantized reflection coefficients).  As the call of this\n *  procedure can be done in many ways (see the interpolation of the LAR\n *  coefficient), it is assumed that the computation begins with index\n *  k_start (for arrays d[..] and s[..]) and stops with index k_end\n *  (k_start and k_end are defined in 4.2.9.1).  This procedure also\n *  needs to keep the array u[0..7] in memory for each call.\n */\n{\n\tregister word\t\t* u = S->u;\n\tregister int\t\ti;\n\tregister word\t\tdi, zzz, ui, sav, rpi;\n\tregister longword \tltmp;\n\n\tfor (; k_n--; s++) {\n\n\t\tdi = sav = *s;\n\n\t\tfor (i = 0; i < 8; i++) {\t\t/* YYY */\n\n\t\t\tui    = u[i];\n\t\t\trpi   = rp[i];\n\t\t\tu[i]  = sav;\n\n\t\t\tzzz   = GSM_MULT_R(rpi, di);\n\t\t\tsav   = GSM_ADD(   ui,  zzz);\n\n\t\t\tzzz   = GSM_MULT_R(rpi, ui);\n\t\t\tdi    = GSM_ADD(   di,  zzz );\n\t\t}\n\n\t\t*s = di;\n\t}\n}\n\n#if defined(USE_FLOAT_MUL) && defined(FAST)\n\nstatic void Fast_Short_term_analysis_filtering P4((S,rp,k_n,s),\n\tstruct gsm_state * S,\n\tregister word\t* rp,\t/* [0..7]\tIN\t*/\n\tregister int \tk_n, \t/*   k_end - k_start\t*/\n\tregister word\t* s\t/* [0..n-1]\tIN/OUT\t*/\n)\n{\n\tregister word\t\t* u = S->u;\n\tregister int\t\ti;\n\n\tfloat \t  uf[8],\n\t\t rpf[8];\n\n\tregister float scalef = 3.0517578125e-5;\n\tregister float\t\tsav, di, temp;\n\n\tfor (i = 0; i < 8; ++i) {\n\t\tuf[i]  = u[i];\n\t\trpf[i] = rp[i] * scalef;\n\t}\n\tfor (; k_n--; s++) {\n\t\tsav = di = *s;\n\t\tfor (i = 0; i < 8; ++i) {\n\t\t\tregister float rpfi = rpf[i];\n\t\t\tregister float ufi  = uf[i];\n\n\t\t\tuf[i] = sav;\n\t\t\ttemp  = rpfi * di + ufi;\n\t\t\tdi   += rpfi * ufi;\n\t\t\tsav   = temp;\n\t\t}\n\t\t*s = di;\n\t}\n\tfor (i = 0; i < 8; ++i) u[i] = uf[i];\n}\n#endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */\n\nstatic void Short_term_synthesis_filtering P5((S,rrp,k,wt,sr),\n\tstruct gsm_state * S,\n\tregister word\t* rrp,\t/* [0..7]\tIN\t*/\n\tregister int\tk,\t/* k_end - k_start\t*/\n\tregister word\t* wt,\t/* [0..k-1]\tIN\t*/\n\tregister word\t* sr\t/* [0..k-1]\tOUT\t*/\n)\n{\n\tregister word\t\t* v = S->v;\n\tregister int\t\ti;\n\tregister word\t\tsri, tmp1, tmp2;\n\tregister longword\tltmp;\t/* for GSM_ADD  & GSM_SUB */\n\n\twhile (k--) {\n\t\tsri = *wt++;\n\t\tfor (i = 8; i--;) {\n\n\t\t\t/* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], v[i] ) );\n\t\t\t */\n\t\t\ttmp1 = rrp[i];\n\t\t\ttmp2 = v[i];\n\t\t\ttmp2 =  ( tmp1 == MIN_WORD && tmp2 == MIN_WORD\n\t\t\t\t? MAX_WORD\n\t\t\t\t: 0x0FFFF & (( (longword)tmp1 * (longword)tmp2\n\t\t\t\t\t     + 16384) >> 15)) ;\n\n\t\t\tsri  = GSM_SUB( sri, tmp2 );\n\n\t\t\t/* v[i+1] = GSM_ADD( v[i], gsm_mult_r( rrp[i], sri ) );\n\t\t\t */\n\t\t\ttmp1  = ( tmp1 == MIN_WORD && sri == MIN_WORD\n\t\t\t\t? MAX_WORD\n\t\t\t\t: 0x0FFFF & (( (longword)tmp1 * (longword)sri\n\t\t\t\t\t     + 16384) >> 15)) ;\n\n\t\t\tv[i+1] = GSM_ADD( v[i], tmp1);\n\t\t}\n\t\t*sr++ = v[0] = sri;\n\t}\n}\n\n\n#if defined(FAST) && defined(USE_FLOAT_MUL)\n\nstatic void Fast_Short_term_synthesis_filtering P5((S,rrp,k,wt,sr),\n\tstruct gsm_state * S,\n\tregister word\t* rrp,\t/* [0..7]\tIN\t*/\n\tregister int\tk,\t/* k_end - k_start\t*/\n\tregister word\t* wt,\t/* [0..k-1]\tIN\t*/\n\tregister word\t* sr\t/* [0..k-1]\tOUT\t*/\n)\n{\n\tregister word\t\t* v = S->v;\n\tregister int\t\ti;\n\n\tfloat va[9], rrpa[8];\n\tregister float scalef = 3.0517578125e-5, temp;\n\n\tfor (i = 0; i < 8; ++i) {\n\t\tva[i]   = v[i];\n\t\trrpa[i] = (float)rrp[i] * scalef;\n\t}\n\twhile (k--) {\n\t\tregister float sri = *wt++;\n\t\tfor (i = 8; i--;) {\n\t\t\tsri -= rrpa[i] * va[i];\n\t\t\tif     (sri < -32768.) sri = -32768.;\n\t\t\telse if (sri > 32767.) sri =  32767.;\n\n\t\t\ttemp = va[i] + rrpa[i] * sri;\n\t\t\tif     (temp < -32768.) temp = -32768.;\n\t\t\telse if (temp > 32767.) temp =  32767.;\n\t\t\tva[i+1] = temp;\n\t\t}\n\t\t*sr++ = va[0] = sri;\n\t}\n\tfor (i = 0; i < 9; ++i) v[i] = va[i];\n}\n\n#endif /* defined(FAST) && defined(USE_FLOAT_MUL) */\n\nvoid Gsm_Short_Term_Analysis_Filter P3((S,LARc,s),\n\n\tstruct gsm_state * S,\n\n\tword\t* LARc,\t\t/* coded log area ratio [0..7]  IN\t*/\n\tword\t* s\t\t/* signal [0..159]\t\tIN/OUT\t*/\n)\n{\n\tword\t\t* LARpp_j\t= S->LARpp[ S->j      ];\n\tword\t\t* LARpp_j_1\t= S->LARpp[ S->j ^= 1 ];\n\n\tword\t\tLARp[8];\n\n#undef\tFILTER\n#if \tdefined(FAST) && defined(USE_FLOAT_MUL)\n# \tdefine\tFILTER \t(* (S->fast\t\t\t\\\n\t\t\t   ? Fast_Short_term_analysis_filtering\t\\\n\t\t    \t   : Short_term_analysis_filtering\t))\n\n#else\n# \tdefine\tFILTER\tShort_term_analysis_filtering\n#endif\n\n\tDecoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j );\n\n\tCoefficients_0_12(  LARpp_j_1, LARpp_j, LARp );\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 13, s);\n\n\tCoefficients_13_26( LARpp_j_1, LARpp_j, LARp);\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 14, s + 13);\n\n\tCoefficients_27_39( LARpp_j_1, LARpp_j, LARp);\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 13, s + 27);\n\n\tCoefficients_40_159( LARpp_j, LARp);\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 120, s + 40);\n}\n\nvoid Gsm_Short_Term_Synthesis_Filter P4((S, LARcr, wt, s),\n\tstruct gsm_state * S,\n\n\tword\t* LARcr,\t/* received log area ratios [0..7] IN  */\n\tword\t* wt,\t\t/* received d [0..159]\t\t   IN  */\n\n\tword\t* s\t\t/* signal   s [0..159]\t\t  OUT  */\n)\n{\n\tword\t\t* LARpp_j\t= S->LARpp[ S->j     ];\n\tword\t\t* LARpp_j_1\t= S->LARpp[ S->j ^=1 ];\n\n\tword\t\tLARp[8];\n\n#undef\tFILTER\n#if \tdefined(FAST) && defined(USE_FLOAT_MUL)\n\n# \tdefine\tFILTER \t(* (S->fast\t\t\t\\\n\t\t\t   ? Fast_Short_term_synthesis_filtering\t\\\n\t\t    \t   : Short_term_synthesis_filtering\t))\n#else\n#\tdefine\tFILTER\tShort_term_synthesis_filtering\n#endif\n\n\tDecoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j );\n\n\tCoefficients_0_12( LARpp_j_1, LARpp_j, LARp );\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 13, wt, s );\n\n\tCoefficients_13_26( LARpp_j_1, LARpp_j, LARp);\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 14, wt + 13, s + 13 );\n\n\tCoefficients_27_39( LARpp_j_1, LARpp_j, LARp);\n\tLARp_to_rp( LARp );\n\tFILTER( S, LARp, 13, wt + 27, s + 27 );\n\n\tCoefficients_40_159( LARpp_j, LARp );\n\tLARp_to_rp( LARp );\n\tFILTER(S, LARp, 120, wt + 40, s + 40);\n}\n"
  },
  {
    "path": "src/audio/gsm/src/table.cpp",
    "content": "/*\n * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische\n * Universitaet Berlin.  See the accompanying file \"COPYRIGHT\" for\n * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n */\n\n/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/table.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */\n\n/*  Most of these tables are inlined at their point of use.\n */\n\n/*  4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP\n *      CODER AND DECODER\n *\n *\t(Most of them inlined, so watch out.)\n */\n\n#define\tGSM_TABLE_C\n#include \"private.h\"\n#include\t\"gsm.h\"\n\n/*  Table 4.1  Quantization of the Log.-Area Ratios\n */\n/* i \t\t     1      2      3        4      5      6        7       8 */\nword gsm_A[8]   = {20480, 20480, 20480,  20480,  13964,  15360,   8534,  9036};\nword gsm_B[8]   = {    0,     0,  2048,  -2560,     94,  -1792,   -341, -1144};\nword gsm_MIC[8] = { -32,   -32,   -16,    -16,     -8,     -8,     -4,    -4 };\nword gsm_MAC[8] = {  31,    31,    15,     15,      7,      7,      3,     3 };\n\n\n/*  Table 4.2  Tabulation  of 1/A[1..8]\n */\nword gsm_INVA[8]={ 13107, 13107,  13107, 13107,  19223, 17476,  31454, 29708 };\n\n\n/*   Table 4.3a  Decision level of the LTP gain quantizer\n */\n/*  bc\t\t      0\t        1\t  2\t     3\t\t\t*/\nword gsm_DLB[4] = {  6554,    16384,\t26214,\t   32767\t};\n\n\n/*   Table 4.3b   Quantization levels of the LTP gain quantizer\n */\n/* bc\t\t      0          1        2          3\t\t\t*/\nword gsm_QLB[4] = {  3277,    11469,\t21299,\t   32767\t};\n\n\n/*   Table 4.4\t Coefficients of the weighting filter\n */\n/* i\t\t    0      1   2    3   4      5      6     7   8   9    10  */\nword gsm_H[11] = {-134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 };\n\n\n/*   Table 4.5 \t Normalized inverse mantissa used to compute xM/xmax \n */\n/* i\t\t \t0        1    2      3      4      5     6      7   */\nword gsm_NRFAC[8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 };\n\n\n/*   Table 4.6\t Normalized direct mantissa used to compute xM/xmax\n */\n/* i                  0      1       2      3      4      5      6      7   */\nword gsm_FAC[8]\t= { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 };\n"
  },
  {
    "path": "src/audio/media_buffer.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"media_buffer.h\"\n#include <cstring>\n#include \"audits/memman.h\"\n\n////////////\n// PUBLIC\n////////////\n\nt_media_buffer::t_media_buffer(int size) {\n\tbuf_size = size;\n\tempty = true;\n\tbuffer = new unsigned char[size];\n\tMEMMAN_NEW_ARRAY(buffer);\n\tpos_start = 0;\n\tpos_end = 0;\n}\n\nt_media_buffer::~t_media_buffer() {\n\tMEMMAN_DELETE_ARRAY(buffer);\n\tdelete [] buffer;\n}\n\nvoid t_media_buffer::add(unsigned char *data, int len) {\n\tint data_start, data_end;\n\n\tmtx.lock();\n\n\t// The amount of data should fit in the buffer.\n\tif (len > buf_size) {\n\t\tmtx.unlock();\n\t\treturn;\n\t}\n\n\tint current_size_content = size_content();\n\tif (empty) {\n\t\tdata_start = 0;\n\t\tdata_end = len - 1;\n\t\tpos_start = 0;\n\t\tempty = false;\n\t} else {\n\t\tdata_start = (pos_end + 1) % buf_size;\n\t\tdata_end = (data_start + len - 1) % buf_size;\n\t}\n\n\t// Copy the data into the buffer\n\tif (data_end >= data_start) {\n\t\tmemcpy(buffer + data_start, data, len);\n\t} else {\n\t\t// The data wraps around the end of the buffer\n\t\tmemcpy(buffer + data_start, data, buf_size - data_start);\n\t\tmemcpy(buffer, data + buf_size - data_start, data_end + 1);\n\t}\n\n\t// Check if the new data wrapped over the start of the old data.\n\t// If so, then advance the start of the old data behind the end of the new\n\t// data as new data has erased the oldest data.\n\tif (buf_size - current_size_content < len) {\n\t\tpos_start =  (data_end + 1) % buf_size;\n\t}\n\n\tpos_end = data_end;\n\n\tmtx.unlock();\n}\n\nbool t_media_buffer::get(unsigned char *data, int len) {\n\tmtx.lock();\n\n\tif (len > size_content()) {\n\t\tmtx.unlock();\n\t\treturn false;\n\t}\n\n\t// Retrieve the data from the buffer\n\tif (pos_start + len <= buf_size) {\n\t\tmemcpy(data, buffer + pos_start, len);\n\t} else {\n\t\t// The data to be retrieved wraps around the end of\n\t\t// the buffer.\n\t\tmemcpy(data, buffer + pos_start, buf_size - pos_start);\n\t\tmemcpy(data + buf_size - pos_start, buffer, len - buf_size + pos_start);\n\t}\n\n\tpos_start = (pos_start + len) % buf_size;\n\n\t// Check if buffer is empty\n\tif (pos_start == (pos_end + 1) % buf_size) {\n\t\tempty = true;\n\t}\n\n\tmtx.unlock();\n\treturn true;\n}\n\nint t_media_buffer::size_content(void) {\n\tint len;\n\n\tmtx.lock();\n\n\tif (empty) {\n\t\tlen = 0;\n\t} else if (pos_end >= pos_start) {\n\t\tlen = pos_end - pos_start + 1;\n\t} else {\n\t\tlen = pos_end + buf_size - pos_start + 1;\n\t}\n\n\tmtx.unlock();\n\treturn len;\n}\n"
  },
  {
    "path": "src/audio/media_buffer.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _MEDIA_BUFFER_H\n#define _MEDIA_BUFFER_H\n\n#include \"threads/mutex.h\"\n\n// A buffer for buffering media streams.\n// Used for conference calls to buffer one stream that needs to be\n// mixed with the main stream.\n\nclass t_media_buffer {\nprivate:\n\t// The buffer. It is used as a ring buffer\n\tunsigned char\t*buffer;\n\n\t// Size of the buffer\n\tint\t\tbuf_size;\n\n\t// Begin and end position of the buffer content.\n\t// pos_end points to the last byte of content.\n\tint\t\tpos_start;\n\tint\t\tpos_end;\n\n\t// Inidicates if buffer is empty\n\tbool\t\tempty;\n\n\t// Mutex to protect operations on the buffer\n\tt_recursive_mutex mtx;\n\n\t// Prevent this constructor from being used.\n\tt_media_buffer() {};\n\npublic:\n\t// Create a media buffer of size size.\n\tt_media_buffer(int size);\n\t~t_media_buffer();\n\n\t// Add data to buffer. If there is more data than buffer\n\t// space left, then old content will be removed from the\n\t// buffer.\n\t// - data is the data to be added\n\t// - len is the number of bytes to be added\n\tvoid add(unsigned char *data, int len);\n\n\t// Get data from the buffer. If there is not enough data\n\t// in the buffer, then no data is retrieved at all.\n\t// False is returned if data cannot be retrieved.\n\t// - data must point to a buffer of at least size len\n\t// - len is the amount of bytes to be retrieved\n\tbool get(unsigned char *data, int len);\n\n\t// Return the number of bytes contained by the buffer.\n\tint size_content(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/rtp_telephone_event.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"cassert\"\n#include \"rtp_telephone_event.h\"\n#include <netinet/in.h>\n\nvoid t_rtp_telephone_event::set_event(unsigned char _event) {\n\tevent = _event;\n}\n\nvoid t_rtp_telephone_event::set_volume(unsigned char _volume) {\n\tvolume = _volume;\n}\n\nvoid t_rtp_telephone_event::set_reserved(bool _reserved) {\n\treserved = _reserved;\n}\n\nvoid t_rtp_telephone_event::set_end(bool _end) {\n\tend = _end;\n}\n\nvoid t_rtp_telephone_event::set_duration(unsigned short _duration) {\n\tduration = htons(_duration);\n}\n\nunsigned char t_rtp_telephone_event::get_event(void) const {\n\treturn event;\n}\n\nunsigned char t_rtp_telephone_event::get_volume(void) const {\n\treturn volume;\n}\n\nbool t_rtp_telephone_event::get_reserved(void) const {\n\treturn reserved;\n}\n\nbool t_rtp_telephone_event::get_end(void) const {\n\treturn end;\n}\n\nunsigned short t_rtp_telephone_event::get_duration(void) const {\n\treturn ntohs(duration);\n}\n\nt_dtmf_ev char2dtmf_ev(char sym) {\n\tif (sym >= '0' && sym <= '9') return (sym - '0' + TEL_EV_DTMF_0);\n\tif (sym >= 'A' && sym <= 'D') return (sym - 'A' + TEL_EV_DTMF_A);\n\tif (sym >= 'a' && sym <= 'd') return (sym-  'a' + TEL_EV_DTMF_A);\n\tif (sym == '*') return TEL_EV_DTMF_STAR;\n\tif (sym == '#') return TEL_EV_DTMF_POUND;\n\n\tassert(false);\n\treturn TEL_EV_DTMF_INVALID;\n}\n\nchar dtmf_ev2char(t_dtmf_ev ev) {\n\tif (ev <= TEL_EV_DTMF_9) {\n\t\treturn ev + '0' - TEL_EV_DTMF_0;\n\t}\n\tif (ev >= TEL_EV_DTMF_A && ev <= TEL_EV_DTMF_D) {\n\t  \treturn ev + 'A' - TEL_EV_DTMF_A;\n\t}\n\tif (ev == TEL_EV_DTMF_STAR) return '*';\n\tif (ev == TEL_EV_DTMF_POUND) return '#';\n\n\tassert(false);\n\treturn '?';\n}\n\n"
  },
  {
    "path": "src/audio/rtp_telephone_event.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 2833\n// RTP payload format for DTMF telephone events\n\n#ifndef _RTP_TELEPHONE_EVENT_H\n#define _RTP_TELEPHONE_EVENT_H\n\n\n// RFC 2833 3.10\n// DTMF events\ntypedef unsigned char t_dtmf_ev;\n\n#define\tTEL_EV_DTMF_0\t\t0\n#define\tTEL_EV_DTMF_1\t\t1\n#define\tTEL_EV_DTMF_2\t\t2\n#define\tTEL_EV_DTMF_3\t\t3\n#define\tTEL_EV_DTMF_4\t\t4\n#define\tTEL_EV_DTMF_5\t\t5\n#define\tTEL_EV_DTMF_6\t\t6\n#define\tTEL_EV_DTMF_7\t\t7\n#define\tTEL_EV_DTMF_8\t\t8\n#define\tTEL_EV_DTMF_9\t\t9\n#define\tTEL_EV_DTMF_STAR\t10\n#define\tTEL_EV_DTMF_POUND\t11\n#define\tTEL_EV_DTMF_A\t\t12\n#define\tTEL_EV_DTMF_B\t\t13\n#define\tTEL_EV_DTMF_C\t\t14\n#define\tTEL_EV_DTMF_D\t\t15\n#define\tTEL_EV_DTMF_INVALID\t((t_dtmf_ev) 0xff)\n\nstatic inline bool is_valid_dtmf_ev(t_dtmf_ev ev)\n{\n\treturn (ev <= TEL_EV_DTMF_D);\n}\n\nstatic inline bool is_valid_dtmf_sym(char s)\n{\n\tif (s >= '0' && s <= '9')\n\t\treturn true;\n\tif (s >= 'a' && s <= 'd')\n\t\treturn true;\n\tif (s >= 'A' && s <= 'D')\n\t\treturn true;\n\tif (s == '*' || s == '#')\n\t\treturn true;\n\treturn false;\n}\n\n// RFC 2833 3.5\n// Payload format (in network order!!)\nstruct t_rtp_telephone_event {\nprivate:\n\tunsigned char\tevent : 8;\n\tunsigned char\tvolume : 6;\n\tbool\t\treserved : 1;\n\tbool\t\tend : 1;\n\tunsigned short\tduration : 16;\n\npublic:\n\t// Values set/get are in host order\n\tvoid set_event(unsigned char _event);\n\tvoid set_volume(unsigned char _volume);\n\tvoid set_reserved(bool _reserved);\n\tvoid set_end(bool _end);\n\tvoid set_duration(unsigned short _duration);\n\n\tunsigned char get_event(void) const;\n\tunsigned char get_volume(void) const;\n\tbool get_reserved(void) const;\n\tbool get_end(void) const;\n\tunsigned short get_duration(void) const;\n};\n\nunsigned char char2dtmf_ev(char sym);\nchar dtmf_ev2char(unsigned char ev);\n\n#endif\n"
  },
  {
    "path": "src/audio/tone_gen.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/ioctl.h>\n#include <sys/time.h>\n#include <cstdlib>\n#include <cstdio>\n#include <cstring>\n#include <sys/soundcard.h>\n#include <iostream>\n#include \"tone_gen.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"audio_device.h\"\n\n// Number of samples read at once from the wav file\n#define NUM_SAMPLES_PER_TURN\t1024\n\n// Duration of one turn in ms\n#define DURATION_TURN\t(NUM_SAMPLES_PER_TURN * 1000 / wav_info.samplerate)\n\n\n// Main function for play thread\nvoid *tone_gen_play(void *arg) {\n\tui->add_prohibited_thread();\n\t\n\tt_tone_gen *tg = (t_tone_gen *)arg;\n\ttg->play();\n\t\n\tui->remove_prohibited_thread();\n\t\n\treturn NULL;\n}\n\nt_tone_gen::t_tone_gen(const string &filename, const t_audio_device &_dev_tone) :\n\t\tdev_tone(_dev_tone),\n\t\tsema_finished(0)\n{\n\tstring f;\n\n\twav_file = NULL;\n\taio = 0;\n\tvalid = false;\n\tdata_buf = NULL;\n\tthr_play = NULL;\n\tloop = false;\n\tpause = 0;\n\n\tif (filename.size() == 0) return;\n\n\t// Add share directory to filename\n\tif (filename[0] != '/') {\n\t\tf = sys_config->get_dir_share();\n\t\tf += \"/\";\n\t\tf += filename;\n\t} else {\n\t\tf = filename;\n\t}\n\n\twav_filename = f;\n\n\tmemset(&wav_info, 0, sizeof(SF_INFO));\n\twav_file = sf_open(f.c_str(), SFM_READ, &wav_info);\n\tif (!wav_file) {\n\t\tstring msg(\"Cannot open \");\n\t\tmsg += f;\n\t\tlog_file->write_report(msg, \"t_tone_gen::t_tone_gen\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tui->cb_display_msg(msg, MSG_WARNING);\n\t\treturn;\n\t}\n\n\tlog_file->write_header(\"t_tone_gen::t_tone_gen\");\n\tlog_file->write_raw(\"Opened \");\n\tlog_file->write_raw(f);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\n\tvalid = true;\n\tstop_playing = false;\n}\n\nt_tone_gen::~t_tone_gen() {\n\tif (wav_file) {\n\t\tsf_close(wav_file);\n\t}\n\tif (aio) {\n\t\tMEMMAN_DELETE(aio);\n\t\tdelete aio;\n\t}\n\taio = 0;\n\tif (data_buf) {\n\t\tMEMMAN_DELETE_ARRAY(data_buf);\n\t\tdelete [] data_buf;\n\t}\n\tif (thr_play) {\n\t\tMEMMAN_DELETE(thr_play);\n\t\tdelete thr_play;\n\t}\n\t\n\tlog_file->write_report(\"Deleted tone generator.\",\n\t\t\"t_tone_gen::~t_tone_gen\");\n}\n\nbool t_tone_gen::is_valid(void) const {\n\treturn valid;\n}\n\nvoid t_tone_gen::play(void) {\n\tif (!valid) {\n\t\tlog_file->write_report(\n\t\t\t\"Tone generator is invalid. Cannot play tone\",\n\t\t\t\"t_tone_gen::play\", LOG_NORMAL, LOG_WARNING);\n\t\tsema_finished.up();\n\t\treturn;\n\t}\n\n\taio = t_audio_io::open(dev_tone, true, false, true, wav_info.channels,\n\t\tSAMPLEFORMAT_S16, wav_info.samplerate, false);\n\tif (!aio) {\n\t\tstring msg(\"Failed to open sound card: \");\n\t\tmsg += get_error_str(errno);\n\t\tlog_file->write_report(msg, \"t_tone_gen::play\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tui->cb_display_msg(msg, MSG_WARNING);\n\t\tsema_finished.up();\n\t\treturn;\n\t}\n\t\n\tlog_file->write_report(\"Start playing tone.\",\n\t\t\"t_tone_gen::play\");\n\n\tdo {\n\t\t// Each samples consists of #channels shorts\n\t\tdata_buf = new short[NUM_SAMPLES_PER_TURN * wav_info.channels];\n\t\tMEMMAN_NEW_ARRAY(data_buf);\n\n\t\tsf_count_t frames_read = NUM_SAMPLES_PER_TURN;\n\t\twhile (frames_read == NUM_SAMPLES_PER_TURN) {\n\t\t\tif (stop_playing) break;\n\n\t\t\t// Play sample\n\t\t\tframes_read = sf_readf_short(wav_file, data_buf, NUM_SAMPLES_PER_TURN);\n\t\t\tif (frames_read > 0) {\n\t\t\t\taio->write((unsigned char*)data_buf, \n\t\t\t\t\tframes_read * wav_info.channels * 2);\n\t\t\t}\n\t\t}\n\n\t\tMEMMAN_DELETE_ARRAY(data_buf);\n\t\tdelete [] data_buf;\n\t\tdata_buf = NULL;\n\n\t\tif (stop_playing) break;\n\n\t\t// Pause between repetitions\n\t\tif (loop) {\n\t\t\t// Play silence\n\t\t\tif (pause > 0) {\n\t\t\t\tdata_buf = new short[NUM_SAMPLES_PER_TURN * wav_info.channels];\n\t\t\t\tMEMMAN_NEW_ARRAY(data_buf);\n\t\t\t\tmemset(data_buf, 0, NUM_SAMPLES_PER_TURN * wav_info.channels * 2);\n\n\t\t\t\tfor (int i = 0; i < pause; i += DURATION_TURN) {\n\t\t\t\t\taio->write((unsigned char*)data_buf, \n\t\t\t\t\t\tNUM_SAMPLES_PER_TURN * wav_info.channels * 2);\n\t\t\t\t\tif (stop_playing) break;\n\t\t\t\t}\n\n\t\t\t\tMEMMAN_DELETE_ARRAY(data_buf);\n\t\t\t\tdelete [] data_buf;\n\t\t\t\tdata_buf = NULL;\n\t\t\t}\n\n\t\t\tif (stop_playing) break;\n\n\t\t\t// Set file pointer back to start of data\n\t\t\tsf_seek(wav_file, 0, SEEK_SET);\n\t\t}\n\t} while (loop);\n\t\n\tlog_file->write_report(\"Tone ended.\",\n\t\t\"t_tone_gen::play_tone\");\n\n\tsema_finished.up();\n}\n\nvoid t_tone_gen::start_play_thread(bool _loop, int _pause) {\n\tloop = _loop;\n\tpause = _pause;\n\tthr_play = new t_thread(tone_gen_play, this);\n\tMEMMAN_NEW(thr_play);\n\tthr_play->detach();\n}\n\nvoid t_tone_gen::stop(void) {\n\tlog_file->write_report(\"Stopping tone.\",\n\t\t\"t_tone_gen::stop\");\n\n\tif (stop_playing) {\n\t\tlog_file->write_report(\"Tone has stopped already.\",\n\t\t\t\"t_tone_gen::stop\");\n\t\treturn;\n\t}\n\n\t// This will stop the playing thread.\n\tstop_playing = true;\n\t\n\t// The semaphore will be upped by the playing thread as soon \n\t// as playing finishes.\n\tsema_finished.down();\n\t\n\tlog_file->write_report(\"Tone stopped.\",\n\t\t\"t_tone_gen::stop\");\n\n\tif (aio) {\n\t\tMEMMAN_DELETE(aio);\n\t\tdelete aio;\n\t\taio = 0;\n\t}\n}\n\n\n"
  },
  {
    "path": "src/audio/tone_gen.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TONE_GEN_H\n#define _TONE_GEN_H\n\n#include <string>\n#include <fstream>\n#include <sndfile.h>\n#include \"sys_settings.h\"\n#include \"threads/mutex.h\"\n#include \"threads/thread.h\"\n#include \"threads/sema.h\"\n\n#ifndef _AUDIO_DEVICE_H\nclass t_audio_io;\n#endif\n\nusing namespace std;\n\nclass t_tone_gen {\nprivate:\n\tstring\t\twav_filename;\t// name of wav file\n\tSNDFILE\t\t*wav_file;\t// SNDFILE pointer to wav file\n\tSF_INFO \twav_info;\t// Information about format of the wav file\n\tt_audio_device\tdev_tone;\t// device to play tone\n\tt_audio_io*\taio;\t\t// soundcard\n\tbool\t\tvalid;\t\t// wav file is in a valid format\n\tbool\t\tstop_playing;\t// indicates if playing should stop\n\tt_thread\t*thr_play;\t// playing thread\n\tbool\t\tloop;\t\t// repeat playing\n\tint\t\tpause;\t\t// pause (ms) between repetitions\n\tshort\t\t*data_buf;\t// buffer for reading sound samples\n\tt_semaphore\tsema_finished;\t// indicates if playing finished\n\npublic:\n\tt_tone_gen(const string &filename, const t_audio_device &_dev_tone);\n\t~t_tone_gen();\n\n\tbool is_valid(void) const;\n\n\t// Play the wav file\n\t// loop = true -> repeat playing\n\t// pause is pause in ms between repetitions\n\tvoid start_play_thread(bool _loop, int _pause);\n\tvoid play(void);\n\n\t// Stop playing\n\tvoid stop(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/twinkle_rtp_session.cpp",
    "content": "\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <unistd.h>\n#include \"twinkle_rtp_session.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n\n#define TWINKLE_ZID_FILE \t\".twinkle.zid\"\n\nt_twinkle_rtp_session::~t_twinkle_rtp_session() {}\n\n#ifdef HAVE_ZRTP\nvoid t_twinkle_rtp_session::init_zrtp(void) {\n\tstring zid_filename = sys_config->get_dir_user();\n\tzid_filename += '/';\n\tzid_filename += TWINKLE_ZID_FILE;\n\t\n\tif (initialize(zid_filename.c_str()) >=0) {\n\t\tzrtp_initialized = true;\n\t\treturn;\n\t}\n\t\n\t// ZID file initialization failed. Maybe the ZID file\n\t// is corrupt. Try to remove it\n\tif (unlink(zid_filename.c_str()) < 0) {\n\t\tstring msg = \"Failed to remove \";\n\t\tmsg += zid_filename;\n\t\tlog_file->write_report(msg,\n\t\t\t\"t_twinkle_rtp_session::init_zrtp\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\t// Try to initialize once more\n\tif (initialize(zid_filename.c_str()) >= 0) {\n\t\tzrtp_initialized = true;\n\t} else {\n\t\tstring msg = \"Failed to initialize ZRTP - \";\n\t\tmsg += zid_filename;\n\t\tlog_file->write_report(msg,\n\t\t\t\"t_twinkle_rtp_session::init_zrtp\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t}\n}\n\nbool t_twinkle_rtp_session::is_zrtp_initialized(void) const {\n\treturn zrtp_initialized;\n}\n\nt_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host) :\n\tSymmetricZRTPSession(host),\n\tzrtp_initialized(false) \n{\n\tinit_zrtp();\n}\n\nt_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host, unsigned short port) : \n\tSymmetricZRTPSession(host, port) ,\n\tzrtp_initialized(false)\n{\n\tinit_zrtp();\n}\n#else\nt_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host) :\n\tSymmetricRTPSession(host) \n{\n}\n\nt_twinkle_rtp_session::t_twinkle_rtp_session(const InetHostAddress &host, unsigned short port) : \n\tSymmetricRTPSession(host, port) \n{\n}\n#endif\n\nuint32 t_twinkle_rtp_session::getLastTimestamp(const SyncSource *src) const {\n\tif ( src && !isMine(*src) ) return 0L;\n\t\n\trecvLock.readLock();\n\n\tuint32 ts = 0;\t\n\tif (src != NULL) {\n\t\tSyncSourceLink* srcm = getLink(*src);\n\t\tIncomingRTPPktLink* l = srcm->getFirst();\n\t\t\n\t\twhile (l) {\n\t\t\tts = l->getTimestamp();\n\t\t\tl = l->getSrcNext();\n\t\t}\n\t} else {\n\t\tIncomingRTPPktLink* l = recvFirst;\n\t\t\n\t\twhile (l) {\n\t\t\tts = l->getTimestamp();\n\t\t\tl = l->getNext();\n\t\t}\n\t}\n\t\n\trecvLock.unlock();\n\treturn ts;\n}\n"
  },
  {
    "path": "src/audio/twinkle_rtp_session.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef TWINKLE_RTP_SESSION_H\n#define TWINKLE_RTP_SESSION_H\n\n#include \"twinkle_config.h\"\n\n#include <string>\n\n#ifdef HAVE_ZRTP\n#include <libzrtpcpp/zrtpccrtp.h>\n#else\n#include <ccrtp/rtp.h>\n#endif\n\nusing namespace std;\nusing namespace ost;\n\n#ifdef HAVE_ZRTP\nclass t_twinkle_rtp_session : public SymmetricZRTPSession {\nprivate:\n\tbool zrtp_initialized;\n\tvoid init_zrtp(void);\npublic:\n\tbool is_zrtp_initialized(void) const;\n#else\nclass t_twinkle_rtp_session : public SymmetricRTPSession {\n#endif\npublic:\n\tvirtual ~t_twinkle_rtp_session();\n\t \n\tt_twinkle_rtp_session(const InetHostAddress &host);\n\tt_twinkle_rtp_session(const InetHostAddress &host, unsigned short port);\n\tuint32 getLastTimestamp(const SyncSource *src=NULL) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/audio/twinkle_zrtp_ui.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Author: Werner Dittmann <Werner.Dittmann@t-online.de>, (C) 2006\n//         Michel de Boer <michel@twinklephone.com>\n#include \"twinkle_zrtp_ui.h\"\n\n#ifdef HAVE_ZRTP\n\n#include \"phone.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nusing namespace std;\nusing namespace GnuZrtpCodes;\n\nextern t_phone *phone;\n\n// Initialize static data\nmap<int32, std::string>TwinkleZrtpUI::infoMap;\nmap<int32, std::string>TwinkleZrtpUI::warningMap;\nmap<int32, std::string>TwinkleZrtpUI::severeMap;\nmap<int32, std::string>TwinkleZrtpUI::zrtpMap;\n\nbool TwinkleZrtpUI::mapsDone = false;\nstd::string TwinkleZrtpUI::unknownCode = \"Unknown error code\";\n\nTwinkleZrtpUI::TwinkleZrtpUI(t_audio_session* session) :\n                             audioSession(session) \n{\n        if (mapsDone) {\n            return;\n        }\n        \n        // Initialize error mapping\n        infoMap.insert(pair<int32, std::string>(InfoHelloReceived,\n\t\t\t\t\t\t string(\"Hello received, preparing a Commit\")));\n        infoMap.insert(pair<int32, std::string>(InfoCommitDHGenerated, \n\t\t\t\t\t\t string(\"Commit: Generated a public DH key\")));\n        infoMap.insert(pair<int32, std::string>(InfoRespCommitReceived, \n\t\t\t\t\t\t string(\"Responder: Commit received, preparing DHPart1\")));\n        infoMap.insert(pair<int32, std::string>(InfoDH1DHGenerated, \n\t\t\t\t\t\t string(\"DH1Part: Generated a public DH key\")));\n        infoMap.insert(pair<int32, std::string>(InfoInitDH1Received,\n\t\t\t\t\t\t string(\"Initiator: DHPart1 received, preparing DHPart2\")));\n        infoMap.insert(pair<int32, std::string>(InfoRespDH2Received,\n\t\t\t\t\t\t string(\"Responder: DHPart2 received, preparing Confirm1\")));\n        infoMap.insert(pair<int32, std::string>(InfoInitConf1Received,\n\t\t\t\t\t\t string(\"Initiator: Confirm1 received, preparing Confirm2\")));\n        infoMap.insert(pair<int32, std::string>(InfoRespConf2Received,\n\t\t\t\t\t\t string(\"Responder: Confirm2 received, preparing Conf2Ack\")));\n        infoMap.insert(pair<int32, std::string>(InfoRSMatchFound,\n\t\t\t\t\t\t string(\"At least one retained secrets matches - security OK\")));\n        infoMap.insert(pair<int32, std::string>(InfoSecureStateOn,\n\t\t\t\t\t\t string(\"Entered secure state\")));\n        infoMap.insert(pair<int32, std::string>(InfoSecureStateOff,\n\t\t\t\t\t\t string(\"No more security for this session\")));\n\n        warningMap.insert(pair<int32, std::string>(WarningDHAESmismatch,\n                          string(\"Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096\")));\n        warningMap.insert(pair<int32, std::string>(WarningGoClearReceived,\n\t\t\t\t\t\t    string(\"Received a GoClear message\")));\n        warningMap.insert(pair<int32, std::string>(WarningDHShort,\n                          string(\"Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096\")));\n        warningMap.insert(pair<int32, std::string>(WarningNoRSMatch,\n\t\t\t\t\t\t    string(\"No retained shared secrets available - must verify SAS\")));\n        warningMap.insert(pair<int32, std::string>(WarningCRCmismatch,\n\t\t\t\t\t\t    string(\"Internal ZRTP packet checksum mismatch - packet dropped\")));\n        warningMap.insert(pair<int32, std::string>(WarningSRTPauthError,\n\t\t\t\t\t\t    string(\"Dropping packet because SRTP authentication failed!\")));\n        warningMap.insert(pair<int32, std::string>(WarningSRTPreplayError,\n\t\t\t\t\t\t    string(\"Dropping packet because SRTP replay check failed!\")));\n\twarningMap.insert(pair<int32, std::string>(WarningNoExpectedRSMatch,\n\t\t\t\t\t\t    string(\"Valid retained shared secrets availabe but no matches found - must verify SAS\")));\n\n        severeMap.insert(pair<int32, std::string>(SevereHelloHMACFailed,\n\t\t\t\t\t\t   string(\"Hash HMAC check of Hello failed!\")));\n        severeMap.insert(pair<int32, std::string>(SevereCommitHMACFailed,\n\t\t\t\t\t\t   string(\"Hash HMAC check of Commit failed!\")));\n        severeMap.insert(pair<int32, std::string>(SevereDH1HMACFailed,\n\t\t\t\t\t\t   string(\"Hash HMAC check of DHPart1 failed!\")));\n        severeMap.insert(pair<int32, std::string>(SevereDH2HMACFailed,\n\t\t\t\t\t\t   string(\"Hash HMAC check of DHPart2 failed!\")));\n        severeMap.insert(pair<int32, std::string>(SevereCannotSend,\n\t\t\t\t\t\t   string(\"Cannot send data - connection or peer down?\")));\n        severeMap.insert(pair<int32, std::string>(SevereProtocolError,\n\t\t\t\t\t\t   string(\"Internal protocol error occured!\")));\n        severeMap.insert(pair<int32, std::string>(SevereNoTimer, \n\t\t\t\t\t\t   string(\"Cannot start a timer - internal resources exhausted?\")));\n        severeMap.insert(pair<int32, std::string>(SevereTooMuchRetries,\n\t\t\t\t\t string(\"Too much retries during ZRTP negotiation - connection or peer down?\")));\n\n        zrtpMap.insert(pair<int32, std::string>(MalformedPacket,\n\t\t\t\t\t\t string(\"Malformed packet (CRC OK, but wrong structure)\")));\n        zrtpMap.insert(pair<int32, std::string>(CriticalSWError, \n\t\t\t\t\t\t string(\"Critical software error\")));\n        zrtpMap.insert(pair<int32, std::string>(UnsuppZRTPVersion, \n\t\t\t\t\t\t string(\"Unsupported ZRTP version\")));\n        zrtpMap.insert(pair<int32, std::string>(HelloCompMismatch, \n\t\t\t\t\t\t string(\"Hello components mismatch\")));\n        zrtpMap.insert(pair<int32, std::string>(UnsuppHashType, \n\t\t\t\t\t\t string(\"Hash type not supported\")));\n        zrtpMap.insert(pair<int32, std::string>(UnsuppCiphertype, \n\t\t\t\t\t\t string(\"Cipher type not supported\")));\n        zrtpMap.insert(pair<int32, std::string>(UnsuppPKExchange, \n\t\t\t\t\t\t string(\"Public key exchange not supported\")));\n        zrtpMap.insert(pair<int32, std::string>(UnsuppSRTPAuthTag, \n\t\t\t\t\t\t string(\"SRTP auth. tag not supported\")));\n        zrtpMap.insert(pair<int32, std::string>(UnsuppSASScheme, \n\t\t\t\t\t\t string(\"SAS scheme not supported\")));\n        zrtpMap.insert(pair<int32, std::string>(NoSharedSecret, \n\t\t\t\t\t\t string(\"No shared secret available, DH mode required\")));\n        zrtpMap.insert(pair<int32, std::string>(DHErrorWrongPV, \n\t\t\t\t\t\t string(\"DH Error: bad pvi or pvr ( == 1, 0, or p-1)\")));\n        zrtpMap.insert(pair<int32, std::string>(DHErrorWrongHVI, \n\t\t\t\t\t\t string(\"DH Error: hvi != hashed data\")));\n        zrtpMap.insert(pair<int32, std::string>(SASuntrustedMiTM, \n\t\t\t\t\t\t string(\"Received relayed SAS from untrusted MiTM\")));\n        zrtpMap.insert(pair<int32, std::string>(ConfirmHMACWrong, \n\t\t\t\t\t\t string(\"Auth. Error: Bad Confirm pkt HMAC\")));\n        zrtpMap.insert(pair<int32, std::string>(NonceReused, \n\t\t\t\t\t\t string(\"Nonce reuse\")));\n        zrtpMap.insert(pair<int32, std::string>(EqualZIDHello, \n\t\t\t\t\t\t string(\"Equal ZIDs in Hello\")));\n        zrtpMap.insert(pair<int32, std::string>(GoCleatNotAllowed, \n\t\t\t\t\t\tstring(\"GoClear packet received, but not allowed\")));\n\n        mapsDone = true;\n\n}\n\nvoid TwinkleZrtpUI::secureOn(std::string cipher) {\n\taudioSession->set_is_encrypted(true);\n\taudioSession->set_srtp_cipher_mode(cipher);\n\t\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\n\tlog_file->write_header(\"TwinkleZrtpUI::secureOn\");\n\tlog_file->write_raw(\"Line \");\n\tlog_file->write_raw(lineno + 1);\n\tlog_file->write_raw(\": audio encryption enabled: \");\n\tlog_file->write_raw(cipher);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tui->cb_async_line_encrypted(lineno, true);\n\tui->cb_async_line_state_changed();\n}\n\nvoid TwinkleZrtpUI::secureOff() {\n\taudioSession->set_is_encrypted(false);\n\t\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\n\tlog_file->write_header(\"TwinkleZrtpUI::secureOff\");\n\tlog_file->write_raw(\"Line \");\n\tlog_file->write_raw(lineno + 1);\n\tlog_file->write_raw(\": audio encryption disabled.\\n\");\n\tlog_file->write_footer();\n\t\n\tui->cb_async_line_encrypted(lineno, false);\n\tui->cb_async_line_state_changed();\n}\n\nvoid TwinkleZrtpUI::showSAS(std::string sas, bool verified) {\n\taudioSession->set_zrtp_sas(sas);\n\taudioSession->set_zrtp_sas_confirmed(verified);\n\t\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\n\tlog_file->write_header(\"TwinkleZrtpUI::showSAS\");\n\tlog_file->write_raw(\"Line \");\n\tlog_file->write_raw(lineno + 1);\n\tlog_file->write_raw(\": SAS =\");\n\tlog_file->write_raw(sas);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\n\tif (!verified) {\n\t  ui->cb_async_show_zrtp_sas(lineno, sas);\n\t}\n\tui->cb_async_line_state_changed();\n}\n\nvoid TwinkleZrtpUI::confirmGoClear() {\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\t\n\tui->cb_async_zrtp_confirm_go_clear(lineno);\n}\n\nvoid TwinkleZrtpUI::showMessage(MessageSeverity sev, int subCode) {\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\t\n\tstring msg = \"Line \";\n\tmsg += int2str(lineno + 1);\n\tmsg += \": \";\n\tmsg += *mapCodesToString(sev, subCode);\n\t\n\tswitch (sev) {\n\tcase Info:\n\t\tlog_file->write_report(msg, \"TwinkleZrtpUI::showMessage\", LOG_NORMAL,\n\t\t\tLOG_INFO);\n\t\tbreak;\n\tcase Warning:\n\t\tlog_file->write_report(msg, \"TwinkleZrtpUI::showMessage\", LOG_NORMAL,\n\t\t\tLOG_WARNING);\n\t\tbreak;\n\tdefault:\n\t\tlog_file->write_report(msg, \"TwinkleZrtpUI::showMessage\", LOG_NORMAL,\n\t\t\tLOG_CRITICAL);\n\t}\n}\n\nvoid TwinkleZrtpUI::zrtpNegotiationFailed(MessageSeverity severity, int subCode) {\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\t\n\tstring m = \"Line \";\n\tm += int2str(lineno + 1);\n\tm += \": ZRTP negotiation failed.\\n\";\n\tm += *mapCodesToString(severity, subCode);\n\t\n\tswitch (severity) {\n\tcase Info:\n\t\tlog_file->write_report(m, \"TwinkleZrtpUI::zrtpNegotiationFailed\", LOG_NORMAL,\n\t\t\tLOG_INFO);\n\t\tbreak;\n\tcase Warning:\n\t\tlog_file->write_report(m, \"TwinkleZrtpUI::zrtpNegotiationFailed\", LOG_NORMAL,\n\t\t\tLOG_WARNING);\n\t\tbreak;\n\tdefault:\n\t\tlog_file->write_report(m, \"TwinkleZrtpUI::zrtpNegotiationFailed\", LOG_NORMAL,\n\t\t\tLOG_CRITICAL);\n\t}\n}\n\nvoid TwinkleZrtpUI::zrtpNotSuppOther() {\n\tt_line *line = audioSession->get_line();\n\tint lineno = line->get_line_number();\n\t\n\tstring msg = \"Line \";\n\tmsg += int2str(lineno + 1);\n\tmsg += \": remote party does not support ZRTP.\";\n\tlog_file->write_report(msg, \"TwinkleZrtpUI::zrtpNotSuppOther\");\n}\n\nconst string *const TwinkleZrtpUI::mapCodesToString(MessageSeverity severity, int subCode) {\n  \tstring *m = &unknownCode;\n\n\tswitch (severity) {\n\tcase Info:\n\t\tm = &infoMap[subCode];\n\t\tbreak;\n\tcase Warning:\n\t\tm = &warningMap[subCode];\n\t\tbreak;\n\tcase Severe:\n\t\tm = &severeMap[subCode];\n\t\tbreak;\n\tcase ZrtpError:\n\t\tif (subCode < 0) {\n\t\t\tsubCode *= -1;\n\t\t}\n\t\tm = &zrtpMap[subCode];\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\treturn m;\n}\n\n#endif\n\n"
  },
  {
    "path": "src/audio/twinkle_zrtp_ui.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Author: Werner Dittmann <Werner.Dittmann@t-online.de>, (C) 2006\n//         Michel de Boer <michel@twinklephone.com>\n\n/**\n * @file\n * User interface call back functions for libzrtpcpp.\n */\n\n#ifndef __TWINKLEZRTPUI_H_\n#define __TWINKLEZRTPUI_H_\n\n#include \"twinkle_config.h\"\n\n#ifdef HAVE_ZRTP\n\n#include <iostream>\n#include <libzrtpcpp/ZrtpQueue.h>\n#include <libzrtpcpp/ZrtpUserCallback.h>\n#include \"audio_session.h\"\n#include \"userintf.h\"\n\nusing namespace GnuZrtpCodes;\n\n/** User interface for libzrtpcpp. */\nclass TwinkleZrtpUI : public ZrtpUserCallback {\n\n    public:\n    \t/**\n    \t * Constructor.\n    \t * @param session [in] The audio session that is encrypted by ZRTP.\n    \t */\n    \tTwinkleZrtpUI(t_audio_session* session);\n    \tvirtual ~TwinkleZrtpUI() {};\n    \n    \t//@{\n        /** @name ZRTP call back functions called from the ZRTP thread */\n        virtual void secureOn(std::string cipher);\n        virtual void secureOff();\n        virtual void showSAS(std::string sas, bool verified); \n        virtual void confirmGoClear();\n        virtual void showMessage(MessageSeverity sev, int subCode);\n        virtual void zrtpNegotiationFailed(MessageSeverity severity, int subCode);\n        virtual void zrtpNotSuppOther();\n        //}\n\n    private:\n    \t/** Audio session associated with this user interface. */\n        t_audio_session* audioSession;\n        \n        //@{\n        /** @name Message mappings for libzrtpcpp */\n        static map<int32, std::string> infoMap;\t\t/**< Info messages */\n        static map<int32, std::string> warningMap;\t/**< Warnings */\n        static map<int32, std::string> severeMap;\t/**< Severe errors */\n        static map<int32, std::string> zrtpMap;\t\t/**< ZRTP errors */\n\tstatic bool mapsDone;\t\t\t\t/**< Flag to indicate that maps are initialized */\n\tstatic std::string unknownCode;\t\t\t/**< Unknown error code */\n\t//@}\n\t\n\t/**\n\t * Map a message code returned by libzrtpcpp to a message text.\n\t * @param severity [in] The severity of the message.\n\t * @param subCode [in] The message code.\n\t * @return The message text.\n\t */\n\tconst string *const mapCodesToString(MessageSeverity severity, int subCode);\n\n};\n\n#endif // HAVE_ZRTP\n#endif // __TWINKLEZRTPUI_H_\n\n"
  },
  {
    "path": "src/audits/CMakeLists.txt",
    "content": "project(libtwinkle-audits)\n\nset(LIBTWINKLE_AUDITS-SRCS\n\tmemman.cpp\n)\n\nadd_library(libtwinkle-audits OBJECT ${LIBTWINKLE_AUDITS-SRCS})\n"
  },
  {
    "path": "src/audits/memman.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"memman.h\"\n#include \"log.h\"\n#include \"util.h\"\n\n//////////////////////\n// class t_ptr_info\n//////////////////////\n\nt_ptr_info::t_ptr_info(const string &_filename, int _lineno, bool _is_array) :\n\t\tfilename(_filename)\n{\n\tlineno = _lineno;\n\tis_array = _is_array;\n}\n\n//////////////////////\n// class t_memman\n//////////////////////\n\nt_memman::t_memman() {\n\tnum_new = 0;\n\tnum_new_duplicate = 0;\n\tnum_delete = 0;\n\tnum_delete_mismatch = 0;\n\tnum_array_mixing = 0;\n}\n\nvoid t_memman::trc_new(void *p, const string &filename, int lineno,\n\t\tbool is_array)\n{\n\tmtx_memman.lock();\n\n\tnum_new++;\n\n\t// Check if pointer already exists\n\tmap<void *, t_ptr_info>::iterator i;\n\ti = pointer_map.find(p);\n\tif (i != pointer_map.end()) {\n\t\t// Most likely this is an error in the usage of the\n\t\t// MEMMAN_NEW. A wrong pointer has been passed.\n\t\tnum_new_duplicate++;\n\t\t\n\t\t// Unlock now. If memman gets called again via the log,\n\t\t// there will be no dead lock.\n\t\tmtx_memman.unlock();\n\t\t\n\t\tlog_file->write_header(\"t_memman::trc_new\",\n\t\t\tLOG_MEMORY, LOG_WARNING);\n\t\tlog_file->write_raw(filename);\n\t\tlog_file->write_raw(\", line \");\n\t\tlog_file->write_raw(lineno);\n\t\tlog_file->write_raw(\": pointer to \");\n\t\tlog_file->write_raw(ptr2str(p));\n\t\tlog_file->write_raw(\" has already been allocated.\\n\");\n\t\tlog_file->write_raw(\"It was allocated here: \");\n\t\tlog_file->write_raw(i->second.filename);\n\t\tlog_file->write_raw(\", line \");\n\t\tlog_file->write_raw(i->second.lineno);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\n\tt_ptr_info pinfo(filename, lineno, is_array);\n\tpointer_map[p] = pinfo;\n\n\tmtx_memman.unlock();\n}\n\nvoid t_memman::trc_delete(void *p, const string &filename, int lineno,\n\t\tbool is_array)\n{\n\tmtx_memman.lock();\n\n\tnum_delete++;\n\n\tmap<void *, t_ptr_info>::iterator i;\n\ti = pointer_map.find(p);\n\n\t// Check if the pointer allocation has been reported\n\tif (i == pointer_map.end()) {\n\t\tnum_delete_mismatch++;\n\t\tmtx_memman.unlock();\n\t\t\n\t\tlog_file->write_header(\"t_memman::trc_delete\",\n\t\t\tLOG_MEMORY, LOG_WARNING);\n\t\tlog_file->write_raw(filename);\n\t\tlog_file->write_raw(\", line \");\n\t\tlog_file->write_raw(lineno);\n\t\tlog_file->write_raw(\": pointer to \");\n\t\tlog_file->write_raw(ptr2str(p));\n\t\tlog_file->write_raw(\" is deleted.\\n\");\n\t\tlog_file->write_raw(\"This pointer is not allocated however.\\n\");\n\t\tlog_file->write_footer();\n\t\t\n\t\treturn;\n\t}\n\n\n\tbool array_mismatch = (is_array != i->second.is_array);\n\n\t// Check mixing of array new/delete\n\t// NOTE: after the pointer has been erased from pointer_map, the\n\t//       iterator i is invalid.\n\t//       The mutex mtx_memman should be unlocked before logging to\n\t//       avoid dead locks.\n\tif (array_mismatch) {\n\t\tnum_array_mixing++;\n\t\tstring allocation_filename = i->second.filename;\n\t\tint allocation_lineno = i->second.lineno;\n\t\tbool allocation_is_array = i->second.is_array;\n\t\tpointer_map.erase(p);\n\t\tmtx_memman.unlock();\n\t\t\n\t\tlog_file->write_header(\"t_memman::trc_delete\",\n\t\t\tLOG_MEMORY, LOG_WARNING);\n\t\tlog_file->write_raw(filename);\n\t\tlog_file->write_raw(\", line \");\n\t\tlog_file->write_raw(lineno);\n\t\tlog_file->write_raw(\": pointer to \");\n\t\tlog_file->write_raw(ptr2str(p));\n\t\tlog_file->write_raw(\" is deleted \");\n\t\tif (is_array) {\n\t\t\tlog_file->write_raw(\"as array (delete []).\\n\");\n\t\t} else {\n\t\t\tlog_file->write_raw(\"normally (delete).\\n\");\n\t\t}\n\t\tlog_file->write_raw(\"But it was allocated \");\n\t\tif (allocation_is_array) {\n\t\t\tlog_file->write_raw(\"as array (new []) \\n\");\n\t\t} else {\n\t\t\tlog_file->write_raw(\"normally (new) \\n\");\n\t\t}\n\t\tlog_file->write_raw(allocation_filename);\n\t\tlog_file->write_raw(\", line \");\n\t\tlog_file->write_raw(allocation_lineno);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t} else {\n\t\tpointer_map.erase(p);\n\t\tmtx_memman.unlock();\n\t}\n}\n\nvoid t_memman::report_leaks(void) {\n\tmtx_memman.lock();\n\n\tif (pointer_map.empty()) {\n\t\tif (num_array_mixing == 0) {\n\t\t\tlog_file->write_report(\n\t\t\t\t\"All pointers have correctly been deallocated.\",\n\t\t\t\t\"t_memman::report_leaks\",\n\t\t\t\tLOG_MEMORY, LOG_INFO);\n\t\t} else {\n\t\t\tlog_file->write_header(\"t_memman::report_leaks\",\n\t\t\t\tLOG_MEMORY, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"All pointers have been deallocated.\"),\n\t\t\tlog_file->write_raw(\n\t\t\t\t\"Mixing of array/non-array caused memory loss though.\");\n\t\t\tlog_file->write_footer();\n\t\t}\n\n\t\tmtx_memman.unlock();\n\t\treturn;\n\t}\n\n\tlog_file->write_header(\"t_memman::report_leaks\", LOG_MEMORY, LOG_WARNING);\n\tlog_file->write_raw(\"The following pointers were never deallocated:\\n\");\n\n\tfor (map<void *, t_ptr_info>::const_iterator i = pointer_map.begin();\n\t     i != pointer_map.end(); i++)\n\t{\n\t\tlog_file->write_raw(ptr2str(i->first));\n\t\tlog_file->write_raw(\" allocated from \");\n\t\tlog_file->write_raw(i->second.filename);\n\t\tlog_file->write_raw(\", line \");\n\t\tlog_file->write_raw(i->second.lineno);\n\t\tlog_file->write_endl();\n\t}\n\n\tlog_file->write_footer();\n\n\tmtx_memman.unlock();\n}\n\nvoid t_memman::report_stats(void) {\n\tmtx_memman.lock();\n\n\tlog_file->write_header(\"t_memman::report_stats\", LOG_MEMORY, LOG_INFO);\n\n\tlog_file->write_raw(\"Number of allocations: \");\n\tlog_file->write_raw(num_new);\n\tlog_file->write_endl();\n\n\tlog_file->write_raw(\"Number of duplicate allocations: \");\n\tlog_file->write_raw(num_new_duplicate);\n\tlog_file->write_endl();\n\n\tlog_file->write_raw(\"Number of de-allocations: \");\n\tlog_file->write_raw(num_delete);\n\tlog_file->write_endl();\n\n\tlog_file->write_raw(\"Number of mismatched de-allocations: \");\n\tlog_file->write_raw(num_delete_mismatch);\n\tlog_file->write_endl();\n\n\tlog_file->write_raw(\"Number of array/non-array mixed operations: \");\n\tlog_file->write_raw(num_array_mixing);\n\tlog_file->write_endl();\n\n\tunsigned long num_unalloc = num_new - num_new_duplicate -\n\t\t\t\t    num_delete + num_delete_mismatch;\n\tlog_file->write_raw(\"Number of unallocated pointers: \");\n\tlog_file->write_raw(num_unalloc);\n\tlog_file->write_endl();\n\n\tlog_file->write_footer();\n\n\tmtx_memman.unlock();\n}\n"
  },
  {
    "path": "src/audits/memman.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _MEMMAN_H\n#define _MEMMAN_H\n\n#include <string>\n#include <map>\n#include \"threads/mutex.h\"\n\n#define MEMMAN_NEW(ptr) \t memman->trc_new((ptr), __FILE__, __LINE__)\n#define MEMMAN_NEW_ARRAY(ptr) \t memman->trc_new((ptr), __FILE__, __LINE__, true)\n#define MEMMAN_DELETE(ptr)\t memman->trc_delete((ptr), __FILE__, __LINE__)\n#define MEMMAN_DELETE_ARRAY(ptr) memman->trc_delete((ptr), __FILE__, __LINE__, true)\n#define MEMMAN_REPORT \t\t { memman->report_stats(); memman->report_leaks(); }\n\nusing namespace std;\n\n// Memory manager\n// Trace memory allocations and deallocations\n\nclass t_ptr_info {\npublic:\n\t// Src file from which pointer has been allocated\n\tstring\t\tfilename;\n\n\t// Line number of memman trace command tracing this pointer\n\tint\t\tlineno;\n\n\t// Indicates if the pointer points to an array\n\tbool\t\tis_array;\n\n\tt_ptr_info() {};\n\tt_ptr_info(const string &_filename, int _lineno, bool _is_array);\n};\n\nclass t_memman {\nprivate:\n\t// Map of allocated pointers\n\tmap<void *, t_ptr_info>\tpointer_map;\n\n\t// Statistics\n\tunsigned long num_new; // number of new's\n\tunsigned long num_new_duplicate; // number of duplicate new's\n\tunsigned long num_delete; // number of delete's\n\tunsigned long num_delete_mismatch; // number of delete's for without a new\n\tunsigned long num_array_mixing; // number of array/non-array mixes\n\n\t// Mutex to protect operations on the memory manager\n\tt_mutex mtx_memman;\n\npublic:\n\tt_memman();\n\n\t// Report pointer allocation\n\tvoid trc_new(void *p, const string &filename, int lineno,\n\t\t\tbool is_array = false);\n\n\t// Report pointer deallocation\n\tvoid trc_delete(void *p, const string &filename, int lineno,\n\t\t\tbool is_array = false);\n\n\t// Write a memory leak report to log\n\tvoid report_leaks(void);\n\n\t// Write statistics to log\n\tvoid report_stats(void);\n};\n\nextern t_memman *memman;\n\n#endif\n"
  },
  {
    "path": "src/auth.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include \"auth.h\"\n#include \"log.h\"\n#include \"protocol.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n\nextern string\t\tuser_host;\n\nt_cr_cache_entry:: t_cr_cache_entry(const t_url &_to, const t_credentials &_cr,\n\tconst string &_passwd, bool _proxy) :\n\t\tto(_to)\n{\n\tcredentials = _cr;\n\tpasswd = _passwd;\n\tproxy = _proxy;\n}\n\n\nlist<t_cr_cache_entry>::iterator t_auth::find_cache_entry(\n\tconst t_url &_to, const string &realm, bool proxy)\n{\n\tfor (list<t_cr_cache_entry>::iterator i = cache.begin();\n\t     i != cache.end(); i++)\n\t{\n\t\t// RFC 3261 22.1\n\t\t// Only the realm determines the protection space.\n\t\t// So the to-uri must not need to be compared as for HTTP\n\t\t// i.e. check i->to == _to must not be done.\n\t\t// As realm strings are globally unique there is no need\n\t\t// to check if the credentials are for a 407 or 401 response.\n\t\t// i.e. check i->proxy == proxy is not needed.\n\t\tif (i->credentials.digest_response.realm == realm)\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t}\n\n\treturn cache.end();\n}\n\nvoid t_auth::update_cache(const t_url &to, const t_credentials &cr,\n\tconst string &passwd, bool proxy)\n{\n\tlist<t_cr_cache_entry>::iterator i, j;\n\n\ti = find_cache_entry(to, cr.digest_response.realm, proxy);\n\n\tif (i == cache.end()) {\n\t\tif (cache.size() > AUTH_CACHE_SIZE) {\n\t\t\tcache.erase(cache.begin());\n\t\t}\n\t\tcache.push_back(t_cr_cache_entry(to, cr, passwd, proxy));\n\t} else {\n\t\ti->credentials = cr;\n\t\ti->passwd = passwd;\n\n\t\t// Move cache entry to end of the cache.\n\t\t// TODO: this can be more efficient by checking if the\n\t\t//       entry is already at the end.\n\t\tt_cr_cache_entry e = *i;\n\t\tcache.erase(i);\n\t\tcache.push_back(e);\n\t}\n}\n\nbool t_auth::auth_failed(t_request *r, const t_challenge &c,\n\tbool proxy) const\n{\n\tif (c.digest_challenge.stale) {\n\t\tlog_file->write_report(\"Stale nonce value.\", \"t_auth::auth_failed\");\n\t\treturn false;\n\t}\n\n\tif (proxy) {\n\t\treturn r->hdr_proxy_authorization.contains(\n\t\t\tc.digest_challenge.realm, r->uri);\n\t} else {\n\t\treturn r->hdr_authorization.contains(\n\t\t\tc.digest_challenge.realm, r->uri);\n\t}\n}\n\nvoid t_auth::remove_credentials(t_request *r, const t_challenge &c,\n\tbool proxy) const\n{\n\tif (proxy) {\n\t\tr->hdr_proxy_authorization.remove_credentials(\n\t\t\tc.digest_challenge.realm, r->uri);\n\t} else {\n\t\tr->hdr_authorization.remove_credentials(\n\t\t\tc.digest_challenge.realm, r->uri);\n\t}\n}\n\nt_auth::t_auth() {\n\tre_register = false;\n}\n\nbool t_auth::authorize(t_user *user_config, t_request *r, t_response *resp) {\n\tstring username;\n\tstring passwd;\n\tlist<t_cr_cache_entry>::iterator i;\n\tt_challenge c;\n\tbool proxy;\n\n\tassert(resp->must_authenticate());\n\n\tif (resp->code == R_401_UNAUTHORIZED) {\n\t\tc = resp->hdr_www_authenticate.challenge;\n\t\tproxy = false;\n\t} else {\n\t\tc = resp->hdr_proxy_authenticate.challenge;\n\t\tproxy = true;\n\t}\n\n\t// Only DIGEST is supported\n\tif (cmp_nocase(c.auth_scheme, AUTH_DIGEST) != 0) {\n\t\tlog_file->write_header(\"t_auth::authorize\");\n\t\tlog_file->write_raw(\"Unsupported authentication scheme: \");\n\t\tlog_file->write_raw(c.auth_scheme);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn false;\n\t}\n\n\tconst t_digest_challenge &dc = c.digest_challenge;\n\ti = find_cache_entry(r->uri, dc.realm, proxy);\n\n\t// remove_credentials() will invalidate this condition, so keep a copy\n\tbool _auth_failed = auth_failed(r, c, proxy);\n\tif (_auth_failed) {\n\t\t// The current credentials are wrong. Remove them and\n\t\t// ask the user for a username and password.\n\t\tremove_credentials(r, c, proxy);\n\t}\n\n\t// Determine user name and password\n\tif (i != cache.end()) {\n\t\tusername = i->credentials.digest_response.username;\n\t\tpasswd = i->passwd;\n\t} else if (dc.realm == user_config->get_auth_realm() ||\n\t\t   user_config->get_auth_realm() == \"\") {\n\t\tusername = user_config->get_auth_name();\n\t\tpasswd = user_config->get_auth_pass();\n\t}\n\n\tif (dc.stale) {\n\t\t// The current credentials are stale. Remove them.\n\t\tremove_credentials(r, c, proxy);\n\t}\n\n\t// Ask user for username/password\n\tif ((_auth_failed || username == \"\" || passwd == \"\") && !re_register) {\n\t\tif (!ui->cb_ask_credentials(user_config, dc.realm, username, passwd)) {\n\t\t\tlog_file->write_report(\"Asking user name and password failed.\",\n\t\t\t\t\t\t\"t_auth::authorize\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// No valid username/passwd\n\tif (username == \"\" && passwd == \"\") {\n\t\tlog_file->write_report(\"Incorrect user name and/or password.\",\n\t\t\t\t\t\t\"t_auth::authorize\");\n\t\treturn false;\n\t}\n\t\n\tbool auth_success;\n\tstring fail_reason;\n\tif (!proxy) {\n\t\tt_credentials cr;\n\t\tauth_success = r->www_authorize(c, user_config,\n\t\t\tusername, passwd, 1, NEW_CNONCE, cr, fail_reason);\n\n\t\tif (auth_success) {\n\t\t\tupdate_cache(r->uri, cr, passwd, proxy);\n\t\t}\n\t} else {\n\t\tt_credentials cr;\n\t\tauth_success = r->proxy_authorize(c, user_config,\n\t\t\tusername, passwd, 1, NEW_CNONCE, cr, fail_reason);\n\n\t\tif (auth_success) {\n\t\t\tupdate_cache(r->uri, cr, passwd, proxy);\n\t\t}\n\t}\n\n\tif (!auth_success) {\n\t\tlog_file->write_report(fail_reason, \"t_auth::authorize\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid t_auth::remove_from_cache(const string &realm) {\n\tif (realm.empty()) {\n\t\tcache.clear();\n\t} else {\t\n\t\tlist<t_cr_cache_entry>::iterator i = find_cache_entry(t_url(), realm);\n\t\tif (i != cache.end()) {\n\t\t\tcache.erase(i);\n\t\t}\n\t}\n}\n\nvoid t_auth::set_re_register(bool on) {\n\tre_register = on;\n}\n\nbool t_auth::get_re_register(void) const {\n\treturn re_register;\n}\n"
  },
  {
    "path": "src/auth.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * SIP authentication\n */\n\n#ifndef _AUTH_H\n#define _AUTH_H\n\n#include \"parser/credentials.h\"\n#include \"parser/request.h\"\n#include \"sockets/url.h\"\n#include <list>\n\nusing namespace std;\n\n/** Size of the credentials cache. */\n#define AUTH_CACHE_SIZE\t50\n\n/** Credentials cache entry. */\nclass t_cr_cache_entry {\npublic:\n\t/**\n\t * Destination for which credentials are cached.\n\t * This is not used for the SIP authentication itself.\n\t */\n\tt_url\t\tto;\n\t\n\t/** The credentials. */\n\tt_credentials\tcredentials;\n\t\n\t/** Password. */\n\tstring \t\tpasswd;\n\t\n\t/** Indicates if proxy authentication was requested. */\n\tbool\t\tproxy;\n\n\t/** Constructor. */\n\tt_cr_cache_entry(const t_url &_to, const t_credentials &_cr,\n\t\tconst string &_passwd, bool _proxy);\n};\n\n\n/** An object of this class authorizes a request given some credentials. */\nclass t_auth {\nprivate:\n\t/** Indicates if the current registration request is a re-REGISTER. */\n\tbool re_register;\n\n\t/**\n\t * LRU cache credentials for a destination.\n\t * The first entry in the list is the least recently used.\n\t */\n\tlist<t_cr_cache_entry>\tcache;\n\n\t/**\n\t * Find a cache entry that matches the realm.\n\t * @param _to [in] Destination for which authentication is needed.\n\t * @param realm [in] The authentication realm.\n\t * @param proxy [in] Indicates if proxy authentication was requested.\n\t * @return An iterator to the cached credentials if found.\n\t * @return The end iterator if not found.\n\t */\n\tlist<t_cr_cache_entry>::iterator find_cache_entry(const t_url &_to, \n\t\tconst string &realm, bool proxy=false);\n\n\t/**\n\t * Update cached credentials.\n\t * If the cache does not contain the credentials already\n\t * then it will be added to the end of the list. If the cache\n\t * already contains the maximum number of entries, then the least\n\t * recently used entry will be removed.\n\t * If the cache already contains an entry for credentials, then\n\t * this entry will be moved to the end of the list.\n\t * @param to [in] Destination for which authentication is needed.\n\t * @param cr [in] Credentials to update.\n\t * @param passwd [in] The password to store.\n\t * @param proxy Indicates if proxy authentication was requested.\n\t */\n\tvoid update_cache(const t_url &to, const t_credentials &cr,\n\t\tconst string &passwd, bool proxy);\n\n\t/**\n\t * Check if authorization failed.\n\t * Authorization failed if the challenge is for a realm for which\n\t * the request already contains an authorization header and the\n\t * challenge is not stale.\n\t * @return true, if authorization failed.\n\t * @return false, otherwise.\n\t */\n\tbool auth_failed(t_request *r, const t_challenge &c,\n\t\tbool proxy=false) const;\n\n\t/**\n\t * Remove existing credentials for this challenge from the\n\t * authorization or proxy-authorization header.\n\t * @param r [in] The request from which the credentials must be removed.\n\t * @param c [in] The challenge for which the credentials must be removed.\n\t * @param proxy [in] Indicates if proxy authentication was requested.\n\t */\n\tvoid remove_credentials(t_request *r, const t_challenge &c,\n\t\tbool proxy=false) const;\n\npublic:\n\t/** Constructor. */\n\tt_auth();\n\t\n\t/**\n\t * Authorize the request based on the challenge in the response\n\t * @param user_config [in] The user profile.\n\t * @param r [in] The request to be authorized.\n\t * @param resp [in] The response containing the challenge.\n\t * @return true, if authorization succeeds.\n\t * @return false, if authorization fails.\n\t * @post On successful authorization, the credentials has been added to\n\t * the request in the proper header (Authorization or Proxy-Authorization).\n\t */\n\tbool authorize(t_user *user_config, t_request *r, t_response *resp);\n\t\n\t/**\n\t * Remove credentials for a particular realm from cache.\n\t * @param realm [in] The authentication realm.\n\t */\n\tvoid remove_from_cache(const string &realm);\n\t\n\t/**\n\t * Set the re-REGISTER indication.\n\t * @param on [in] Value to set.\n\t */\n\tvoid set_re_register(bool on);\n\t\n\t/** Get the re-REGISTER indication. */\n\tbool get_re_register(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/call_history.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <cstdlib>\n#include <sys/stat.h>\n#include <iostream>\n#include <fstream>\n#include <vector>\n#include \"call_history.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n\n// Call history file\n#define CALL_HISTORY_FILE\t\"twinkle.ch\";\n\n// Field seperator in call history file\n#define REC_SEPARATOR\t\t'|'\n\n////////////////////////\n// class t_call_record\n////////////////////////\n\nt_mutex t_call_record::mtx_class;\nunsigned short t_call_record::next_id = 1;\n\nt_call_record::t_call_record() {\n\tmtx_class.lock();\n\tid = next_id++;\n\tif (next_id == 65535) next_id = 1;\n\tmtx_class.unlock();\n\n\ttime_start = 0;\n\ttime_answer = 0;\n\ttime_end = 0;\n\tinvite_resp_code = 0;\n}\n\nvoid t_call_record::renew() {\n\tt_mutex_guard x(mutex);\n\n\tmtx_class.lock();\n\tid = next_id++;\n\tif (next_id == 65535) next_id = 1;\n\tmtx_class.unlock();\n\n\ttime_start = 0;\n\ttime_answer = 0;\n\ttime_end = 0;\n\tdirection = DIR_IN;\n\tfrom_display.clear();\n\tfrom_uri.set_url(\"\");\n\tfrom_organization.clear();\n\tto_display.clear();\n\tto_uri.set_url(\"\");\n\tto_organization.clear();\n\treply_to_display.clear();\n\treply_to_uri.set_url(\"\");\n\treferred_by_display.clear();\n\treferred_by_uri.set_url(\"\");\n\tsubject.clear();\n\trel_cause = CS_LOCAL_USER;\n\tinvite_resp_code = 0;\n\tinvite_resp_reason.clear();\n\tfar_end_device.clear();\n\tuser_profile.clear();\n}\n\nvoid t_call_record::start_call(const t_request *invite, t_direction dir, \n\t\tconst string &_user_profile) \n{\n\tassert(invite->method == INVITE);\n\n\tt_mutex_guard x(mutex);\n\tstruct timeval t;\n\t\n\tgettimeofday(&t, NULL);\n\ttime_start = t.tv_sec;\n\t\n\tfrom_display = invite->hdr_from.get_display_presentation();\n\tfrom_uri = invite->hdr_from.uri;\n\t\n\tif (invite->hdr_organization.is_populated()) {\n\t\tfrom_organization = invite->hdr_organization.name;\n\t}\n\t\n\tto_display = invite->hdr_to.display;\n\tto_uri = invite->hdr_to.uri;\n\n\tif (invite->hdr_reply_to.is_populated()) {\n\t\treply_to_display = invite->hdr_reply_to.display;\n\t\treply_to_uri = invite->hdr_reply_to.uri;\n\t}\n\t\n\tif (invite->hdr_referred_by.is_populated()) {\n\t\treferred_by_display = invite->hdr_referred_by.display;\n\t\treferred_by_uri = invite->hdr_referred_by.uri;\n\t}\n\t\t\n\tif (invite->hdr_subject.is_populated()) {\n\t\tsubject = invite->hdr_subject.subject;\n\t}\n\t\n\tdirection = dir;\n\tuser_profile = _user_profile;\n\t\n\tif (direction == DIR_IN && invite->hdr_user_agent.is_populated()) {\n\t\tfar_end_device = invite->hdr_user_agent.get_ua_info();\n\t}\n}\n\nvoid t_call_record::fail_call(const t_response *resp) {\n\tassert(resp->get_class() >= 3);\n\tassert(resp->hdr_cseq.method == INVITE);\n\t\n\tt_mutex_guard x(mutex);\n\tstruct timeval t;\n\t\n\tgettimeofday(&t, NULL);\n\ttime_end = t.tv_sec;\n\trel_cause = CS_FAILURE;\n\tinvite_resp_code = resp->code;\n\tinvite_resp_reason = resp->reason;\n\t\n\tif (resp->hdr_organization.is_populated()) {\n\t\tto_organization = resp->hdr_organization.name;\n\t}\n\t\n\tif (direction == DIR_OUT && resp->hdr_server.is_populated()) {\n\t\tfar_end_device = resp->hdr_server.get_server_info();\n\t}\n}\n\nvoid t_call_record::answer_call(const t_response *resp) {\n\tassert(resp->is_success());\n\t\n\tt_mutex_guard x(mutex);\n\tstruct timeval t;\n\t\n\tgettimeofday(&t, NULL);\n\ttime_answer = t.tv_sec;\n\tinvite_resp_code = resp->code;\n\tinvite_resp_reason = resp->reason;\n\t\n\tif (resp->hdr_organization.is_populated()) {\n\t\tto_organization = resp->hdr_organization.name;\n\t}\n\t\n\tif (direction == DIR_OUT && resp->hdr_server.is_populated()) {\n\t\tfar_end_device = resp->hdr_server.get_server_info();\n\t}\n}\n\nvoid t_call_record::end_call(t_rel_cause cause) {\n\tstruct timeval t;\n\tt_mutex_guard x(mutex);\n\t\n\tgettimeofday(&t, NULL);\n\ttime_end = t.tv_sec;\n\trel_cause = cause;\n}\n\nvoid t_call_record::end_call(bool far_end) {\n\tif (far_end) {\n\t\tend_call(CS_REMOTE_USER);\n\t} else {\n\t\tend_call(CS_LOCAL_USER);\n\t}\n}\n\nstring t_call_record::get_rel_cause(void) const {\n\tswitch (rel_cause) {\n\tcase CS_LOCAL_USER:\n\t\treturn TRANSLATE2(\"CoreCallHistory\", \"local user\");\n\tcase CS_REMOTE_USER:\n\t\treturn TRANSLATE2(\"CoreCallHistory\", \"remote user\");\n\tcase CS_FAILURE:\n\t\treturn TRANSLATE2(\"CoreCallHistory\", \"failure\");\n\t}\n\t\n\treturn TRANSLATE2(\"CoreCallHistory\", \"unknown\");\n}\n\nstring t_call_record::get_rel_cause_internal(void) const {\n\tswitch (rel_cause) {\n\tcase CS_LOCAL_USER:\n\t\treturn \"local user\";\n\tcase CS_REMOTE_USER:\n\t\treturn \"remote user\";\n\tcase CS_FAILURE:\n\t\treturn \"failure\";\n\t}\n\t\n\treturn \"unknown\";\n}\n\nstring t_call_record::get_direction(void) const {\n\tswitch (direction) {\n\tcase DIR_IN:\n\t\treturn TRANSLATE2(\"CoreCallHistory\", \"in\");\n\tcase DIR_OUT:\n\t\treturn TRANSLATE2(\"CoreCallHistory\", \"out\");\n\t}\n\t\n\treturn TRANSLATE2(\"CoreCallHistory\", \"unknown\");\n}\n\nstring t_call_record::get_direction_internal(void) const {\n\tswitch (direction) {\n\tcase DIR_IN:\n\t\treturn \"in\";\n\tcase DIR_OUT:\n\t\treturn \"out\";\n\t}\n\t\n\treturn \"unknown\";\n}\n\nbool t_call_record::set_rel_cause(const string &cause) {\n\t// NOTE: caller and callee were used before version 0.7\n\t// They are still checked here for backward compatibility\n\n\tif (cause == \"caller\" || cause == \"local user\") {\n\t\trel_cause = CS_LOCAL_USER;\n\t} else if (cause == \"callee\" || cause == \"remote user\") {\n\t\trel_cause = CS_REMOTE_USER;\n\t} else if (cause == \"failure\") {\n\t\trel_cause = CS_FAILURE;\n\t} else {\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nbool t_call_record::set_direction(const string &dir) {\n\tif (dir == \"in\") {\n\t\tdirection = DIR_IN;\n\t} else if (dir == \"out\") {\n\t\tdirection = DIR_OUT;\n\t} else {\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nbool t_call_record::create_file_record(vector<string> &v) const {\n\tv.clear();\n\t\n\tv.push_back(ulong2str(time_start));\n\tv.push_back(ulong2str(time_answer));\n\tv.push_back(ulong2str(time_end));\n\tv.push_back(get_direction_internal());\n\tv.push_back(from_display);\n\tv.push_back(from_uri.encode());\n\tv.push_back(from_organization);\n\tv.push_back(to_display);\n\tv.push_back(to_uri.encode());\n\tv.push_back(to_organization);\n\tv.push_back(reply_to_display);\n\tv.push_back(reply_to_uri.encode());\n\tv.push_back(referred_by_display);\n\tv.push_back(referred_by_uri.encode());\n\tv.push_back(subject);\n\tv.push_back(get_rel_cause_internal());\n\tv.push_back(int2str(invite_resp_code));\n\tv.push_back(invite_resp_reason);\n\tv.push_back(far_end_device);\n\tv.push_back(user_profile);\n\t\n\treturn true;\n}\n\nbool t_call_record::populate_from_file_record(const vector<string> &v) {\n\tt_mutex_guard x(mutex);\n\n\t// Check number of fields\n\tif (v.size() != 20) return false;\n\t\n    time_start = std::stoul(v[0], NULL, 10);\n    time_answer = std::stoul(v[1], NULL, 10);\n    time_end = std::stoul(v[2], NULL, 10);\n\t\n\tif (!set_direction(v[3])) return false;\n\t\n\tfrom_display = v[4];\n\tfrom_uri.set_url(v[5]);\n\tif (!from_uri.is_valid()) return false;\n\tfrom_organization = v[6];\n\t\n\tto_display = v[7];\n\tto_uri.set_url(v[8]);\n\tif (!to_uri.is_valid()) return false;\n\tto_organization = v[9];\n\t\n\treply_to_display = v[10];\n\treply_to_uri.set_url(v[11]);\n\t\n\treferred_by_display = v[12];\n\treferred_by_uri.set_url(v[13]);\n\t\n\tsubject = v[14];\n\t\n\tif (!set_rel_cause(v[15])) return false;\n\t\n\tinvite_resp_code = atoi(v[16].c_str());\n\tinvite_resp_reason = v[17];\n\tfar_end_device = v[18];\n\tuser_profile = v[19];\n\t\n\treturn true;\n}\n\nbool t_call_record::is_valid(void) const {\n\tif (time_start == 0 || time_end == 0) return false;\n\tif (time_answer > 0 && rel_cause == CS_FAILURE) return false;\n\t\n\treturn true;\n}\n\nunsigned short t_call_record::get_id(void) const {\n\treturn id;\n}\n\nt_call_record::t_call_record(const t_call_record& that) {\n\t*this = that;\n}\n\nt_call_record& t_call_record::operator=(const t_call_record& that) {\n\tt_mutex_guard x1(that.mutex);\n\tt_mutex_guard x2(this->mutex);\n\n\tid = that.id;\n\n\ttime_start = that.time_start;\n\ttime_answer = that.time_answer;\n\ttime_end = that.time_end;\n\tdirection = that.direction;\n\tfrom_display = that.from_display;\n\tfrom_uri = that.from_uri;\n\tfrom_organization = that.from_organization;\n\tto_display = that.to_display;\n\tto_uri = that.to_uri;\n\tto_organization = that.to_organization;\n\treply_to_display = that.reply_to_display;\n\treply_to_uri = that.reply_to_uri;\n\treferred_by_display = that.referred_by_display;\n\treferred_by_uri = that.referred_by_uri;\n\tsubject = that.subject;\n\trel_cause = that.rel_cause;\n\tinvite_resp_code = that.invite_resp_code;\n\tinvite_resp_reason = that.invite_resp_reason;\n\tfar_end_device = that.far_end_device;\n\tuser_profile = that.user_profile;\n\n\treturn *this;\n}\n\n////////////////////////\n// class t_call_history\n////////////////////////\n\nt_call_history::t_call_history() : utils::t_record_file<t_call_record>() {\n\tset_header(\"time_start|time_answer|time_end|direction|from_display|from_uri|\"\n\t      \"from_organization|to_display|to_uri|to_organization|\"\n\t      \"reply_to_display|reply_to_uri|referred_by_display|referred_by_uri|\"\n\t      \"subject|rel_cause|invite_resp_code|invite_resp_reason|\"\n\t      \"far_end_device|user_profile\");\n\t      \n\tset_separator(REC_SEPARATOR);\n\t\n\tstring s(DIR_HOME);\n\ts += \"/\";\n\ts += USER_DIR;\n\ts += \"/\";\n\ts += CALL_HISTORY_FILE;\n\tset_filename(s);\n\t\n\tnum_missed_calls = 0;\n}\n\nvoid t_call_history::add_call_record(const t_call_record &call_record, bool write) {\n\tif (!call_record.is_valid()) {\n\t\tlog_file->write_report(\"Call history record is not valid.\",\n\t\t\t\"t_call_history::add_call_record\", LOG_NORMAL, LOG_WARNING);\n\t\treturn;\n\t}\n\t\n\tmtx_records.lock();\n\n\trecords.push_back(call_record);\n\t\n\twhile (records.size() > (size_t)sys_config->get_ch_max_size()) {\n\t\trecords.pop_front();\n\t}\n\t\n\t// Increment missed calls counter\n\tif (call_record.rel_cause == t_call_record::CS_FAILURE && \n\t    call_record.direction == t_call_record::DIR_IN)\n\t{\n\t\t++num_missed_calls;\n\t\tui->cb_missed_call(num_missed_calls);\n\t}\n\t\n\tmtx_records.unlock();\n\t\n\tif (write) {\n\t\tstring msg;\n\t\tif (!save(msg)) {\n\t\t\tlog_file->write_report(msg, \"t_call_history::add_call_record\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t}\n\t}\n\t\n\t// Update call history in user interface.\n\tui->cb_call_history_updated();\n}\n\nvoid t_call_history::delete_call_record(unsigned short id, bool write) {\n\tmtx_records.lock();\n\tfor (list<t_call_record>::iterator i = records.begin();\n\t     i != records.end(); i++)\n\t{\n\t\tif (i->get_id() == id) {\n\t\t\trecords.erase(i);\n\t\t\tbreak;\n\t\t}\n\t}\n\tmtx_records.unlock();\n\t\n\tif (write) {\n\t\tstring msg;\n\t\tif (!save(msg)) {\n\t\t\tlog_file->write_report(msg, \"t_call_history::delete_call_record\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t}\n\t}\n\t\n\t// Update call history in user interface.\n\tui->cb_call_history_updated();\n}\n\nvoid t_call_history::get_history(list<t_call_record> &history) {\n\tmtx_records.lock();\n\thistory = records;\n\tmtx_records.unlock();\n}\n\nvoid t_call_history::clear(bool write) {\n\tmtx_records.lock();\n\trecords.clear();\n\tmtx_records.unlock();\n\t\n\tif (write) {\n\t\tstring msg;\n\t\tif (!save(msg)) {\n\t\t\tlog_file->write_report(msg, \"t_call_history::clear\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t}\n\t}\n\t\n\t// Update call history in user interface.\n\tui->cb_call_history_updated();\t\n\t\n\tclear_num_missed_calls();\t\n}\n\nint t_call_history::get_num_missed_calls(void) const {\n\treturn num_missed_calls;\n}\n\nvoid t_call_history::clear_num_missed_calls(void) {\n\tmtx_records.lock();\n\tnum_missed_calls = 0;\n\tmtx_records.unlock();\n\t\n\tui->cb_missed_call(0);\n}\n"
  },
  {
    "path": "src/call_history.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Call history\n */\n\n#ifndef _CALL_HISTORY_H\n#define _CALL_HISTORY_H\n\n#include <list>\n#include <string>\n#include <sys/time.h>\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include \"utils/record_file.h\"\n\nusing namespace std;\n\n/** Call detail record. */\nclass t_call_record : public utils::t_record {\npublic:\n\n/** Release cause of a call. */\nenum t_rel_cause {\n\tCS_LOCAL_USER,\t/**< Released by the local user. */\n\tCS_REMOTE_USER, /**< Released by the remote user. */\n\tCS_FAILURE\t/**< Call ended due to failure. */\n};\n\n/** Direction of the call as seen from the user. */\nenum t_direction {\n\tDIR_IN,\t\t/**< Incoming call. */\n\tDIR_OUT\t\t/**< Outgoing call. */\n};\n\nprivate:\n\tstatic t_mutex\t\tmtx_class; \t/**< Protect static members. */\n\tstatic unsigned short\tnext_id;   \t/**< Next id to be used. */\n\t\n\tunsigned short\t\tid; \t\t/**< Record id. */\n\npublic:\n\ttime_t\t\ttime_start;\t\t/**< Timestamp of start of call. */\n\ttime_t\t\ttime_answer;\t\t/**< Timestamp when call got answered. */\n\ttime_t\t\ttime_end;\t\t/**< Timestamp of end of call. */\n\tt_direction\tdirection;\n\tstring\t\tfrom_display;\n\tt_url\t\tfrom_uri;\n\tstring\t\tfrom_organization;\n\tstring\t\tto_display;\n\tt_url\t\tto_uri;\n\tstring\t\tto_organization;\n\tstring\t\treply_to_display;\n\tt_url\t\treply_to_uri;\n\tstring\t\treferred_by_display;\n\tt_url\t\treferred_by_uri;\n\tstring\t\tsubject;\n\tt_rel_cause\trel_cause;\n\tint\t\tinvite_resp_code;\t/**< Response code sent/received on INVITE. */\n\tstring\t\tinvite_resp_reason;\t/**< Response reason sent/received on INVITE. */\n\tstring\t\tfar_end_device;\t\t/**< User-agent/Server description of device. */\n\tstring\t\tuser_profile;\n\t\n\t/** Constructor. */\n\tt_call_record();\n\n\t/** Copy constructor */\n\tt_call_record(const t_call_record& that);\n\n\t/** Assignment operator */\n\tt_call_record& operator=(const t_call_record& that);\n\t\n\t/**\n\t * Clear current settings and get a new record id.\n\t * So this action creates a brand new call record.\n\t */\n\tvoid renew();\n\t\n\t/**\n\t * Record call start.\n\t * @param invite [in] The INVITE request starting the call.\n\t * @param dir [in] Call direction.\n\t * @param _user_profile [in] The user profile.\n\t */\n\tvoid start_call(const t_request *invite, t_direction dir, const string &_user_profile);\n\t\n\t/**\n\t * Record call failure. This is also the end of the call.\n\t * @param resp [in] The failure response.\n\t */\n\tvoid fail_call(const t_response *resp);\n\t\n\t/**\n\t * Record successful call answer.\n\t * @param resp [in] The 2XX INVITE response.\n\t */\n\tvoid answer_call(const t_response *resp);\n\t\n\t/**\n\t * Record end of a successful call with an explicit cause.\n\t * @param cause [in] The release cause.\n\t */\n\tvoid end_call(t_rel_cause cause);\n\t\n\t/**\n\t * Record end of a successful call.\n\t * If far_end is true, then the far-end ended the call, otherwise\n\t * the near-end ended the call. This indication together with the\n\t * direction determines the correct cause of the call end.\n\t * @param far_end [in] Indicates if the far end released the call.\n\t */\n\tvoid end_call(bool far_end);\n\t\n\t/**\n\t * Get user presentable release cause description.\n\t * The release cause is returned in the language of the user.\n\t * @return Release cause description.\n\t */\n\tstring get_rel_cause(void) const;\n\t\n\t/**\n\t * Get release cause description for internal use.\n\t * This description is written to file.\n\t * @return Release cause description.\n\t */\n\tstring get_rel_cause_internal(void) const;\n\t\n\t/**\n\t * Get user presentable direction description.\n\t * The description is returned in the language of the user.\n\t * @return Direction description.\n\t */\n\tstring get_direction(void) const;\n\t\n\t/**\n\t * Get direction description for internal use.\n\t * This description is written to file.\n\t * @return Direction description.\n\t */\n\tstring get_direction_internal(void) const;\n\t\n\t/**\n\t * Set the release cause from an internal description.\n\t * @param cause [in] Internal release cause description.\n\t * @return Indication if operation succeeded.\n\t */\n\tbool set_rel_cause(const string &cause);\n\t\n\t/**\n\t * Set the direction from an internal description.\n\t * @param cause [in] Internal direction description.\n\t * @return Indication if operation succeeded.\n\t */\n\tbool set_direction(const string &dir);\n\t\n\tvirtual bool create_file_record(vector<string> &v) const;\n\tvirtual bool populate_from_file_record(const vector<string> &v);\n\t\n\t/**\n\t * Check if this call record represents a valid call.\n\t * @return Indication if call record is valid.\n\t */\n\tbool is_valid(void) const;\n\t\n\t/** Get the record id. */\n\tunsigned short get_id(void) const;\nprivate:\n\t// Guarded by a mutex, because contents of this class are updated from other threads.\n\t// The main thread always creates a copy (snapshot) of current state.\n\tmutable t_mutex mutex;\n};\n\n/** History of calls. */\nclass t_call_history : public utils::t_record_file<t_call_record> {\nprivate:\n\t/** Number of missed calls since this counter was cleared. */\n\tint\t\tnum_missed_calls;\n\t\npublic:\n\t/** Constructor. */\n\tt_call_history();\n\t\n\t/**\n\t * Add a call record to the history.\n\t * @param call_record [in] The call record to be added.\n\t * @param write [in] Indicates if history must be written to file after adding.\n\t */\n\tvoid add_call_record(const t_call_record &call_record, bool write = true);\n\t\n\t/**\n\t * Delete record with a given id.\n\t * @param id [in] The record id that must be deleted.\n\t * @param write [in] Indicates if history must be written to file after deleting.\n\t */\n\tvoid delete_call_record(unsigned short id, bool write = true);\n\t\n\t/** \n\t * Get list of historic call records.\n\t * @param history [out] List of historic call records.\n\t */\n\tvoid get_history(list<t_call_record> &history);\n\t\n\t/** \n\t * Clear call history file.\n\t * @param write [in] Indicates if history must be written to file after adding.\n\t */\n\tvoid clear(bool write = true);\n\t\n\t/** Get number of missed calls. */\n\tint get_num_missed_calls(void) const;\n\t\n\t/** Clear number of missed calls. */\n\tvoid clear_num_missed_calls(void);\n};\n\nextern t_call_history *call_history;\n\n#endif\n"
  },
  {
    "path": "src/call_script.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdio>\n#include <cstring>\n#include <iostream>\n#include <unistd.h>\n#include \"call_script.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"phone_user.h\"\n#include \"service.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n\nextern char **environ;\n\nextern t_phone *phone;\n\n// Maximum length of the reason value\n#define MAX_LEN_REASON\t\t50\n\n// Script result fields\n#define SCR_ACTION\t\t\"action\"\n#define SCR_REASON\t\t\"reason\"\n#define SCR_CONTACT\t\t\"contact\"\n#define SCR_CALLER_NAME\t\t\"caller_name\"\n#define SCR_RINGTONE\t\t\"ringtone\"\n#define SCR_DISPLAY_MSG\t\t\"display_msg\"\n#define SCR_INTERNAL_ERROR\t\"internal_error\"\n\n// Script triggers\n#define SCR_TRIGGER_IN_CALL\t\t\"in_call\"\n#define SCR_TRIGGER_IN_CALL_ANSWERED\t\"in_call_answered\"\n#define SCR_TRIGGER_IN_CALL_FAILED\t\"in_call_failed\"\n#define SCR_TRIGGER_OUT_CALL\t\t\"out_call\"\n#define SCR_TRIGGER_OUT_CALL_ANSWERED\t\"out_call_answered\"\n#define SCR_TRIGGER_OUT_CALL_FAILED\t\"out_call_failed\"\n#define SCR_TRIGGER_LOCAL_RELEASE\t\"local_release\"\n#define SCR_TRIGGER_REMOTE_RELEASE\t\"remote_release\"\n\n/////////////////////////\n// class t_script_result\n/////////////////////////\n\nt_script_result::t_script_result() {\n\tclear();\n}\n\nt_script_result::t_action t_script_result::str2action(const string action_string) {\n\tstring s = tolower(action_string);\n\t\n\tt_action result;\t\n\tif (s == \"continue\") {\n\t\tresult = ACTION_CONTINUE;\n\t} else if (s == \"reject\") {\n\t\tresult = ACTION_REJECT;\n\t} else if (s == \"dnd\") {\n\t\tresult = ACTION_DND;\n\t} else if (s == \"redirect\") {\n\t\tresult = ACTION_REDIRECT;\n\t} else if (s == \"autoanswer\") {\n\t\tresult = ACTION_AUTOANSWER;\n\t} else {\n\t\t// Unknown action\n\t\tresult = ACTION_ERROR;\n\t}\n\t\n\treturn result;\n}\n\nvoid t_script_result::clear(void) {\n\taction = ACTION_CONTINUE;\n\treason.clear();\n\tcontact.clear();\n\tcaller_name.clear();\n\tringtone.clear();\n\tdisplay_msgs.clear();\n}\n\nvoid t_script_result::set_parameter(const string &parameter, const string &value) {\n\tif (parameter == SCR_ACTION) {\n\t\taction = str2action(value);\n\t} else if (parameter == SCR_REASON) {\n\t\tif (value.size() <= MAX_LEN_REASON) {\n\t\t\treason = value;\n\t\t} else {\n\t\t\treason = value.substr(0, MAX_LEN_REASON);\n\t\t}\n\t} else if (parameter == SCR_CONTACT) {\n\t\tcontact = value;\n\t} else if (parameter == SCR_CALLER_NAME) {\n\t\tcaller_name = value;\n\t} else if (parameter == SCR_RINGTONE) {\n\t\tringtone = value;\n\t} else if (parameter == SCR_DISPLAY_MSG) {\n\t\tdisplay_msgs.push_back(value);\n\t}\n\t// Unknown parameters are ignored\n}\n\n/////////////////////////\n// class t_call_script\n/////////////////////////\n\nstring t_call_script::trigger2str(t_trigger t) const {\n\tswitch (t) {\n\tcase TRIGGER_IN_CALL:\n\t\treturn SCR_TRIGGER_IN_CALL;\n\tcase TRIGGER_IN_CALL_ANSWERED:\n\t\treturn SCR_TRIGGER_IN_CALL_ANSWERED;\n\tcase TRIGGER_IN_CALL_FAILED:\n\t\treturn SCR_TRIGGER_IN_CALL_FAILED;\n\tcase TRIGGER_OUT_CALL:\n\t\treturn SCR_TRIGGER_OUT_CALL;\n\tcase TRIGGER_OUT_CALL_ANSWERED:\n\t\treturn SCR_TRIGGER_OUT_CALL_ANSWERED;\n\tcase TRIGGER_OUT_CALL_FAILED:\n\t\treturn SCR_TRIGGER_OUT_CALL_FAILED;\n\tcase TRIGGER_LOCAL_RELEASE:\n\t\treturn SCR_TRIGGER_LOCAL_RELEASE;\n\tcase TRIGGER_REMOTE_RELEASE:\n\t\treturn SCR_TRIGGER_REMOTE_RELEASE;\n\tdefault:\n\t\treturn \"unknown\";\n\t}\n}\n\nstring t_call_script::cf_dest2str(const list<t_display_url> &cf_dest) const {\n\tstring s;\n\n\tfor (list<t_display_url>::const_iterator i = cf_dest.begin();\n\t     i != cf_dest.end(); i++)\n\t{\n\t\tif (i != cf_dest.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nchar **t_call_script::create_env(t_sip_message *m) const {\n\t\tstring var_twinkle;\n\n\t\t// Number of existing environment variables\n\t\tint environ_size = 0;\n\t\tfor (int i = 0; environ[i] != NULL; i++) {\n\t\t\tenviron_size++;\n\t\t}\n\t\t\n\t\t// Number of SIP environment variables\n\t\tint start_sip_env = environ_size; // Position of SIP variables\n\t\tlist<string> l = m->encode_env();\n\t\t\n\t\tvar_twinkle = \"SIP_FROM_USER=\";\n\t\tvar_twinkle += m->hdr_from.uri.get_user();\n\t\tl.push_back(var_twinkle);\n\t\t\n\t\tvar_twinkle = \"SIP_FROM_HOST=\";\n\t\tvar_twinkle += m->hdr_from.uri.get_host();\n\t\tl.push_back(var_twinkle);\n\t\t\n\t\tvar_twinkle = \"SIP_FROM_DISPLAY=\";\n\t\tvar_twinkle += m->hdr_from.display;\n\t\tl.push_back(var_twinkle);\n\t\t\n\t\tvar_twinkle = \"SIP_TO_USER=\";\n\t\tvar_twinkle += m->hdr_to.uri.get_user();\n\t\tl.push_back(var_twinkle);\n\t\t\n\t\tvar_twinkle = \"SIP_TO_HOST=\";\n\t\tvar_twinkle += m->hdr_to.uri.get_host();\n\t\tl.push_back(var_twinkle);\n\t\t\n\t\tvar_twinkle = \"SIP_TO_DISPLAY=\";\n\t\tvar_twinkle += m->hdr_to.display;\n\t\tl.push_back(var_twinkle);\n\t\t\n\t\t// Add Twinkle specific environment variables\n\t\tvar_twinkle = \"TWINKLE_USER_PROFILE=\";\n\t\tvar_twinkle += user_config->get_profile_name();\n\t\tl.push_back(var_twinkle);\n\n\t\tvar_twinkle = \"TWINKLE_TRIGGER=\";\n\t\tvar_twinkle += trigger2str(trigger);\n\t\tl.push_back(var_twinkle);\n\n\t\tvar_twinkle = \"TWINKLE_LINE=\";\n\t\tvar_twinkle += ulong2str(line_number);\n\t\tl.push_back(var_twinkle);\n\n\t\t// Add service-based environment variables\n\t\tt_phone_user *pu = phone->find_phone_user(user_config->get_profile_name());\n\t\tif (pu) {\n\t\t\tvar_twinkle = \"TWINKLE_DO_NOT_DISTURB=\";\n\t\t\tif (pu->service->is_dnd_active()) {\n\t\t\t\tvar_twinkle += \"1\";\n\t\t\t}\n\t\t\tl.push_back(var_twinkle);\n\n\t\t\tvar_twinkle = \"TWINKLE_AUTO_ANSWER=\";\n\t\t\tif (pu->service->is_auto_answer_active()) {\n\t\t\t\tvar_twinkle += \"1\";\n\t\t\t}\n\t\t\tl.push_back(var_twinkle);\n\n\t\t\tlist<t_display_url> cf_dest; // call forwarding destinations\n\n\t\t\tvar_twinkle = \"TWINKLE_REDIRECT_ALWAYS=\";\n\t\t\tif (pu->service->get_cf_active(CF_ALWAYS, cf_dest)) {\n\t\t\t\tvar_twinkle += cf_dest2str(cf_dest);\n\t\t\t}\n\t\t\tl.push_back(var_twinkle);\n\n\t\t\tvar_twinkle = \"TWINKLE_REDIRECT_BUSY=\";\n\t\t\tif (pu->service->get_cf_active(CF_BUSY, cf_dest)) {\n\t\t\t\tvar_twinkle += cf_dest2str(cf_dest);\n\t\t\t}\n\t\t\tl.push_back(var_twinkle);\n\n\t\t\tvar_twinkle = \"TWINKLE_REDIRECT_NO_ANSWER=\";\n\t\t\tif (pu->service->get_cf_active(CF_NOANSWER, cf_dest)) {\n\t\t\t\tvar_twinkle += cf_dest2str(cf_dest);\n\t\t\t}\n\t\t\tl.push_back(var_twinkle);\n\t\t}\n\t\t\n\t\tenviron_size += l.size();\n\t\t\n\t\t// MEMMAN not called on purpose\n\t\tchar **env = new char *[environ_size + 1];\n\t\t\n\t\t// Copy current environment to child\n\t\tfor (int i = 0; environ[i] != NULL; i++) {\n\t\t\tenv[i] = strdup(environ[i]);\n\t\t}\n\t\t\n\t\t// Add environment variables for SIP request\n\t\tint j = start_sip_env;\n\t\tfor (list<string>::iterator i = l.begin(); i != l.end(); i++, j++) {\n\t\t\tenv[j] = strdup(i->c_str());\n\t\t}\n\t\t\n\t\t// Terminate array with NULL\n\t\tenv[environ_size] = NULL;\n\t\t\n\t\treturn env;\n}\n\nchar **t_call_script::create_argv(void) const {\n\t\t// Determine script agument list\n\t\tvector<string> arg_list = split_ws(script_command, true);\n\t\t\n\t\t// MEMMAN not called on purpose\n\t\tchar **argv = new char *[arg_list.size() + 1];\n\t\t\n\t\tint idx = 0;\n\t\tfor (vector<string>::iterator i = arg_list.begin(); \n\t\t     i != arg_list.end(); i++, idx++) \n\t\t{\n\t\t\targv[idx] = strdup(i->c_str());\n\t\t}\n\t\targv[arg_list.size()] = NULL;\n\t\t\n\t\treturn argv;\n}\n\nt_call_script::t_call_script(t_user *_user_config, t_trigger _trigger, uint16 _line_number) :\n\tuser_config(_user_config),\n\ttrigger(_trigger),\n\tline_number(_line_number)\n{\n\tswitch (trigger) {\n\tcase TRIGGER_IN_CALL:\n\t\tscript_command = user_config->get_script_incoming_call();\n\t\tbreak;\n\tcase TRIGGER_IN_CALL_ANSWERED:\n\t\tscript_command = user_config->get_script_in_call_answered();\n\t\tbreak;\n\tcase TRIGGER_IN_CALL_FAILED:\n\t\tscript_command = user_config->get_script_in_call_failed();\n\t\tbreak;\n\tcase TRIGGER_OUT_CALL:\n\t\tscript_command = user_config->get_script_outgoing_call();\n\t\tbreak;\n\tcase TRIGGER_OUT_CALL_ANSWERED:\n\t\tscript_command = user_config->get_script_out_call_answered();\n\t\tbreak;\n\tcase TRIGGER_OUT_CALL_FAILED:\n\t\tscript_command = user_config->get_script_out_call_failed();\n\t\tbreak;\n\tcase TRIGGER_LOCAL_RELEASE:\n\t\tscript_command = user_config->get_script_local_release();\n\t\tbreak;\n\tcase TRIGGER_REMOTE_RELEASE:\n\t\tscript_command = user_config->get_script_remote_release();\n\t\tbreak;\n\tdefault:\n\t\tscript_command.clear();\n\t\tbreak;\n\t}\n}\n\nvoid t_call_script::exec_action(t_script_result &result, t_sip_message *m) const \n{\n\tresult.clear();\n\t\n\tif (script_command.empty()) return;\n\t\n\tlog_file->write_header(\"t_call_script::exec_action\");\n\tlog_file->write_raw(\"Execute script: \");\n\tlog_file->write_raw(script_command);\n\tlog_file->write_raw(\"\\nTrigger: \");\n\tlog_file->write_raw(trigger2str(trigger));\n\tlog_file->write_raw(\"\\nLine: \");\n\tlog_file->write_raw(line_number);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\t// Create pipe for communication with child process\n\tint fds[2];\n\tif (pipe(fds) == -1) {\n\t\t// Failed to create pipe\n\t\tlog_file->write_header(\"t_call_script::exec_action\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Failed to create pipe: \");\n\t\tlog_file->write_raw(get_error_str(errno));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\t\n\t// Fork child process\n\tpid_t pid = fork();\n\tif (pid == -1) {\n\t\t// Failed to fork child process\n\t\tlog_file->write_header(\"t_call_script::exec_action\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Failed to fork child process: \");\n\t\tlog_file->write_raw(get_error_str(errno));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tclose(fds[0]);\n\t\tclose(fds[1]);\n\t\treturn;\n\t} else if (pid == 0) {\n\t\t// Child process\n\t\t\n\t\t// Close the read end of the pipe\n\t\tclose(fds[0]);\n\t\t\n\t\t// Redirect stdout to the write end of the pipe\n\t\tdup2(fds[1], STDOUT_FILENO);\n\t\t\n\t\t// NOTE: MEMMAN audits are not called as all pointers will be deleted\n\t\t//       automatically when the child process dies\n\t\t//\t Also, the child process has a copy of the MEMMAN object\n\t\tchar **argv = create_argv();\n\t\t\n\t\t// Determine environment\n\t\tchar **env = create_env(m);\n\t\t\n\t\t// Replace the child process by the script\n\t\tif (execve(argv[0], argv, env) == -1) {\n\t\t\t// Failed to execute script. Report error to parent.\n\t\t\tstring err_msg;\n\t\t\terr_msg = get_error_str(errno);\n\t\t\terr_msg += \": \";\n\t\t\terr_msg += argv[0];\n\t\t\tcout << SCR_INTERNAL_ERROR << '=' << err_msg << endl;\n\t\t\texit(0);\n\t\t}\n\t} else {\n\t\t// Parent process\n\t\tlog_file->write_header(\"t_call_script::exec_action\");\n\t\tlog_file->write_raw(\"Child process spawned, pid = \");\n\t\tlog_file->write_raw((int)pid);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\t// Close the write end of the pipe\n\t\tclose(fds[1]);\n\t\t\n\t\t// Read the script results\n\t\tFILE *fp_result = fdopen(fds[0], \"r\");\n\t\tif (!fp_result) {\n\t\t\tlog_file->write_header(\"t_call_script::exec_action\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Failed to open pipe to child: \");\n\t\t\tlog_file->write_raw(get_error_str(errno));\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\t// Child will be cleaned up by phone_sigwait\n\n\t\t\tclose(fds[0]);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tchar *line_buf = NULL;\n\t\tsize_t line_buf_len = 0;\n\t\tssize_t num_read;\n\t\t\n\t\t// Read and parse script results.\n\t\twhile ((num_read = getline(&line_buf, &line_buf_len, fp_result)) != -1) {\n\t\t\t// Strip newline if present\n\t\t\tif (line_buf[num_read - 1] == '\\n') {\n\t\t\t\tline_buf[num_read - 1] = 0;\n\t\t\t}\n\n\t\t\t// Convert the read line to a C++ string\n\t\t\tstring line(line_buf);\t\n\t\t\tline = trim(line);\n\t\t\t\n\t\t\t// Stop reading on end command\n\t\t\tif (line == \"end\") break;\n\t\n\t\t\t// Skip empty lines\n\t\t\tif (line.empty()) continue;\n\t\n\t\t\t// Skip comment lines\n\t\t\tif (line[0] == '#') continue;\n\t\n\t\t\tvector<string> v = split_on_first(line, '=');\n\t\t\t\n\t\t\t// SKip invalid lines\n\t\t\tif (v.size() != 2) continue;\n\t\n\t\t\tstring parameter = trim(v[0]);\n\t\t\tstring value = trim(v[1]);\n\t\t\t\n\t\t\tif (parameter == SCR_INTERNAL_ERROR) {\n\t\t\t\tlog_file->write_report(value,\n\t\t\t\t\t\"t_call_script::exec_action\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tui->cb_display_msg(value, MSG_WARNING);\n\t\t\t\tresult.clear();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tresult.set_parameter(parameter, value);\n\t\t}\n\t\t\n\t\tif (line_buf) free(line_buf);\n\t\tfclose(fp_result);\n\t\tclose(fds[0]);\n\t\t\n\t\t// Child will be cleaned up by phone_sigwait\n\t}\n}\n\nvoid t_call_script::exec_notify(t_sip_message *m) const \n{\n\tif (script_command.empty()) return;\n\t\n\tlog_file->write_header(\"t_call_script::exec_notify\");\n\tlog_file->write_raw(\"Execute script: \");\n\tlog_file->write_raw(script_command);\n\tlog_file->write_raw(\"\\nTrigger: \");\n\tlog_file->write_raw(trigger2str(trigger));\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\t// Fork child process\n\tpid_t pid = fork();\n\tif (pid == -1) {\n\t\t// Failed to fork child process\n\t\tlog_file->write_header(\"t_call_script::exec_notify\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Failed to fork child process: \");\n\t\tlog_file->write_raw(get_error_str(errno));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\n\t\treturn;\n\t} else if (pid == 0) {\n\t\t// Child process\n\t\t\t\n\t\t// NOTE: MEMMAN audits are not called as all pointers will be deleted\n\t\t//       automatically when the child process dies\n\t\t//\t Also, the child process has a copy of the MEMMAN object\n\t\tchar **argv = create_argv();\n\t\t\n\t\t// Determine environment\n\t\tchar **env = create_env(m);\n\t\t\n\t\t// Replace the child process by the script\n\t\tif (execve(argv[0], argv, env) == -1) {\n\t\t\t// Failed to execute script.\n\t\t\texit(0);\n\t\t}\n\t} else {\n\t\t// Parent process\n\t\tlog_file->write_header(\"t_call_script::exec_notify\");\n\t\tlog_file->write_raw(\"Child process spawned, pid = \");\n\t\tlog_file->write_raw((int)pid);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\t// No interaction with child needed.\n\t\t// Child will be cleaned up by phone_sigwait\n\t}\n}\n"
  },
  {
    "path": "src/call_script.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Call scripting interface.\n * A call script is called by Twinkle during call processing.\n * Currently only when a call comes in (INVITE received).\n * Twinkle calls the script and based of the output of the script, the\n * call is further handled.\n * \n * The following environment variables are passed to the script:\n * \n@verbatim\n   TWINKLE_USER_PROFILE=<user profile name>\n   TWINKLE_TRIGGER=<trigger type>\n   TWINKLE_LINE=<line number (starting at 1) associated with the call>\n   TWINKLE_DO_NOT_DISTURB=<\"1\" if \"Do not disturb\" is enabled>\n   TWINKLE_AUTO_ANSWER=<\"1\" if \"Auto answer\" is enabled>\n   TWINKLE_REDIRECT_ALWAYS=<destination list for \"Unconditional\" call redirection>\n   TWINKLE_REDIRECT_BUSY=<destination list for \"When busy\" call redirection>\n   TWINKLE_REDIRECT_NO_ANSWER=<destination list for \"No answer\" call redirection>\n   SIPREQUEST_METHOD=<method>\n   SIPREQUEST_URI=<request uri>\n   SIPSTATUS_CODE=<status code of a response>\n   SIPSTATUS_REASON=<reason phrase of a response>\n   SIP_FROM_USER=<user name of From header>\n   SIP_FROM_HOST=<host part of From header>\n   SIP_FROM_DISPLAY=<display name of From header>\n   SIP_TO_USER=<user name of To header>\n   SIP_TO_HOST=<host part of To header>\n   SIP_TO_DISPLAY=<display name of To header>\n   SIP_<header_name>=<header value>\n@endverbatim\n * \n * The header name is in capitals and dashed are replaced by underscores\n * \n * The script can return on stdout how the call should be further\n * processed. The following output parameters are recognized:\n * \n@verbatim\n   action=[continue|reject|dnd|redirect]\n   reason=<reason phrase>, for reject and dnd actions\n   contact=<sip uri>, for redirect ation\n   ringtone=<name of wav file>, for continue action\n   caller_name=<name to override the display name of the caller>\n   display_msg=<msg to show in Twinkle's display> (may occur multiple times)\n   end This parameter makes Twinkle stop waiting for the script to complete.\n@endverbatim\n * \n * If no action is returned, the \"continue\" action is performed.\n * Invalid output will be skipped.\n */\n\n#ifndef _H_CALL_SCRIPT\n#define _H_CALL_SCRIPT\n\n#include <vector>\n#include <string>\n#include \"sockets/url.h\"\n#include \"user.h\"\n#include \"parser/request.h\"\n\nusing namespace std;\n\n/** Results of the incoming call script. */\nclass t_script_result {\npublic:\n\t/** Action to perform. */\n\tenum t_action {\n\t\tACTION_CONTINUE, \t/**< Continue with incoming call */\n\t\tACTION_REJECT, \t\t/**< Reject incoming call with 603 response */\n\t\tACTION_DND,\t\t/**< Do not disturb, send 480 response */\n\t\tACTION_REDIRECT,\t/**< Redirect call (302 response) */\n\t\tACTION_AUTOANSWER,\t/**< Auto answer incoming call */\n\t\tACTION_ERROR\t\t/**< Fail call due to error (500 response) */\n\t};\n\t\n\t/** @name Output parameters */\n\t//@{\n\tt_action\taction;\t\t/**< How to proceed with call */\n\tstring\t\treason;\t\t/**< Reason if call is not continued */\n\tstring\t\tcontact;\t/**< Redirect destination for redirect action */\n\tstring\t\tcaller_name;\t/**< Name of caller (can be used to override display name) */\n\tstring\t\tringtone;\t/**< Wav file for ring tone */\n\tvector<string>\tdisplay_msgs;\t/**< Message (multi line) to show on display */\n\t//@}\n\t\n\t/** Constructor. */\n\tt_script_result();\n\t\n\t/**\n\t * Convert string representation to an action.\n\t * @param action_string [in] String representation of an action.\n\t * @return The action.\n\t */\n\tstatic t_action str2action(const string action_string);\n\t\n\t/** Clear the results. */\n\tvoid clear(void);\n\t\n\t/**\n\t * Set output parameter from values read from the result output of a script.\n\t * @param parameter [in] Name of the parameter to set,\n\t * @param value [in] The value to set.\n\t */\n\tvoid set_parameter(const string &parameter, const string &value);\n};\n\n/** Call script definition. */\nclass t_call_script {\npublic:\n\t/** Trigger type. */\n\tenum t_trigger {\n\t\tTRIGGER_IN_CALL,\t\t/**< Incoming call. */\n\t\tTRIGGER_IN_CALL_ANSWERED,\t/**< Incoming call answered. */\n\t\tTRIGGER_IN_CALL_FAILED,\t\t/**< Incoming call failed. */\n\t\tTRIGGER_OUT_CALL,\t\t/**< Outgoing call made. */\n\t\tTRIGGER_OUT_CALL_ANSWERED,\t/**< Outgoing call answered. */\n\t\tTRIGGER_OUT_CALL_FAILED,\t/**< Outgoing call failed. */\n\t\tTRIGGER_LOCAL_RELEASE,\t\t/**< Call released by local party. */\n\t\tTRIGGER_REMOTE_RELEASE\t\t/**< Call released by remotre party. */\n\t};\n\t\nprivate:\n\tt_user\t\t*user_config;\t\t/**< The user profile. */\n\tstring\t\tscript_command;\t\t/**< The script to execute. */\n\tt_trigger\ttrigger;\t\t/**< Trigger point for this script. */\n\t\n\t/**\n\t * Number of the line associated with the call causing the trigger.\n\t * The line numbers start at 1. For some triggers a line number does not\n\t * apply, e.g. incoming call and all lines are busy. In that case the\n\t * line number is 0.\n\t */\n\tuint16\t\tline_number;\n\t\n\t/**\n\t * Convert a trigger type value to a string.\n\t * @param t [in] Trigger\n\t * @return String representation for the trigger.\n\t */\n\tstring trigger2str(t_trigger t) const;\n\t\n\t/**\n\t * Converts a list of call forwarding destinations to a single string,\n\t * with multiple destinations separated by commas.\n\t * @param cf_dest [in] List of call forwarding destinations\n\t * @return String representation of the destinations list\n\t */\n\tstring cf_dest2str(const list<t_display_url> &cf_dest) const;\n\t\n\t/**\n\t * Create environment for the process running the script.\n\t * The environment contains the header values of a SIP message.\n\t * @param m [in] The SIP message.\n\t * @return The environment.\n\t * @note This function creates the env array without registering\n\t *       the memory allocation to MEMMAN.\n\t */\n\tchar **create_env(t_sip_message *m) const;\n\t\n\t/**\n\t * Create script command argument list.\n\t * @return The argument list.\n\t * @note This function creates the argv array without registering\n\t *       the memory allocation to MEMMAN.\n\t */\n\tchar **create_argv(void) const;\n\t\nprotected:\n\t/** Cannot use this constructor. */\n\tt_call_script() {};\n\t\npublic:\n\t/** \n\t * Constructor. \n\t * @param _user_config [in] User profile associated with the trigger.\n\t * @param _trigger [in] The trigger type.\n\t * @param _line_number [in] Line associated with the trigger (0 if no line\n\t * is associated).\n\t */\n\tt_call_script(t_user *_user_config, t_trigger _trigger, uint16 _line_number);\n\t\n\t/**\n\t * Execute call script resulting in an action.\n\t * @param result [out] Contains the result on return.\n\t * @param m [in] The SIP message triggering this call script.\n\t */\n\tvoid exec_action(t_script_result &result, t_sip_message *m) const;\n\t\n\t/**\n\t * Execute notification call script.\n\t * @param m [in] The SIP message triggering this call script.\n\t */\n\tvoid exec_notify(t_sip_message *m) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/client_request.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"client_request.h\"\n#include \"audits/memman.h\"\n\nt_mutex t_client_request::mtx_next_tuid;\nt_tuid t_client_request::next_tuid = 1;\n\nt_client_request::t_client_request(t_user *user, t_request *r, const t_tid _tid) :\n\t\tredirector(r->uri, user->get_max_redirections())\n{\n\trequest = (t_request *)r->copy();\n\tstun_request = NULL;\n\ttid = _tid;\n\tref_count = 1;\n\n\tmtx_next_tuid.lock();\n\ttuid = next_tuid++;\n\tif (next_tuid == 65535) next_tuid = 1;\n\tmtx_next_tuid.unlock();\n}\n\nt_client_request::t_client_request(t_user *user, StunMessage *r, const t_tid _tid) :\n\t\tredirector(t_url(), user->get_max_redirections())\n{\n\trequest = NULL;\n\tstun_request = new StunMessage(*r);\n\tMEMMAN_NEW(stun_request);\n\ttid = _tid;\n\tref_count = 1;\n\n\tmtx_next_tuid.lock();\n\ttuid = next_tuid++;\n\tif (next_tuid == 65535) next_tuid = 1;\n\tmtx_next_tuid.unlock();\n}\n\nt_client_request::~t_client_request() {\n\tif (request) {\n\t\tMEMMAN_DELETE(request);\n\t\tdelete request;\n\t}\n\t\n\tif (stun_request) {\n\t\tMEMMAN_DELETE(stun_request);\n\t\tdelete stun_request;\n\t}\n}\n\nt_client_request *t_client_request::copy(void) {\n\tt_client_request *cr = new t_client_request(*this);\n\tMEMMAN_NEW(cr);\n\t\n\tif (request) {\n\t\tcr->request = (t_request *)request->copy();\n\t}\n\t\n\tif (stun_request) {\n\t\tcr->stun_request = new StunMessage(*stun_request);\n\t\tMEMMAN_NEW(cr->stun_request);\n\t}\n\t\n\tcr->ref_count = 1;\n\treturn cr;\n}\n\nt_request *t_client_request::get_request(void) const {\n\treturn request;\n}\n\nStunMessage *t_client_request::get_stun_request(void) const {\n\treturn stun_request;\n}\n\nt_tuid t_client_request::get_tuid(void) const {\n\treturn tuid;\n}\n\nt_tid t_client_request::get_tid(void) const {\n\treturn tid;\n}\n\nvoid t_client_request::set_tid(t_tid _tid) {\n\ttid = _tid;\n}\n\nvoid t_client_request::renew(t_tid _tid) {\n\tmtx_next_tuid.lock();\n\ttuid = next_tuid++;\n\tif (next_tuid == 65535) next_tuid = 1;\n\tmtx_next_tuid.unlock();\n\n\ttid = _tid;\n}\n\nint t_client_request::get_ref_count(void) const {\n\treturn ref_count;\n}\n\nint t_client_request::inc_ref_count(void) {\n\tref_count++;\n\treturn ref_count;\n}\n\nint t_client_request::dec_ref_count(void) {\n\tref_count--;\n\treturn ref_count;\n}\n"
  },
  {
    "path": "src/client_request.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/** @file\n * Bind request with TU and transaction\n */\n\n#ifndef _CLIENT_REQUEST_H\n#define _CLIENT_REQUEST_H\n\n#include \"protocol.h\"\n#include \"redirect.h\"\n#include \"user.h\"\n#include \"transaction_layer.h\"\n#include \"threads/mutex.h\"\n#include \"parser/request.h\"\n#include \"stun/stun.h\"\n\nusing namespace std;\n\n/** Object for storing a request together with its Transaction User id and transaction id. */\nclass t_client_request {\nprivate:\n\tstatic t_mutex\tmtx_next_tuid; \t/**< Protect updates on @ref next_tuid */\n\tstatic t_tuid\tnext_tuid;     \t/**< Next transaction user id to handout. */\n\n\t// A client request is either a SIP or a STUN request\n\tt_request\t*request;\t/**< SIP request. */\n\tStunMessage\t*stun_request;\t/**< STUN request. */\n\t\n\tt_tuid\t\ttuid;\t\t/**< Transaction user id. */\n\tt_tid\t\ttid;\t\t/**< Transaction id. */\n\n\t/** Number of references to this object (#dialogs). */\n\tint\t\tref_count;\n\npublic:\n\t/** Redirector for 3XX redirections. */\n\tt_redirector\tredirector;\n\n\t/**\n\t * Constructor.\n\t * A copy of the request is stored in the client_request object.\n\t * @param user The user profile of the user sending the request.\n\t * @param r SIP request.\n\t * @param _tid Transaction id.\n\t */\n\tt_client_request(t_user *user, t_request *r, const t_tid _tid);\n\t\n\t/**\n\t * Constructor.\n\t * A copy of the request is stored in the client_request object.\n\t * @param user The user profile of the user sending the request.\n\t * @param r STUN request.\n\t * @param _tid Transaction id.\n\t */\t\n\tt_client_request(t_user *user, StunMessage *r, const t_tid _tid);\n\t\n\t/** Destructor. */\n\t~t_client_request();\n\n\t/**\n\t * Create a copy of the client request.\n\t * @return Copy of the client request.\n\t * @note: The request inside the client request is copied.\n\t */\n\tt_client_request *copy(void);\n\n\t/**\n\t * Get a pointer to the SIP request.\n\t * @return Pointer to the SIP request.\n\t */\n\tt_request *get_request(void) const;\n\t\n\t/**\n\t * Get a pointer to the STUN request.\n\t * @return Pointer to the STUN request.\n\t */\n\tStunMessage *get_stun_request(void) const;\n\n\t/** Get the transaction user id. */\n\tt_tuid get_tuid(void) const;\n\t\n\t/** Get the transaction id. */\n\tt_tid get_tid(void) const;\n\t\n\t/** Set the transaction id. */\n\tvoid set_tid(t_tid _tid);\n\n\t/** \n\t * Create a new tuid and set tid.\n\t * @param _tid The new tid to set.\n\t */\n\tvoid renew(t_tid _tid);\n\n\t/** Get the reference count. */\n\tint get_ref_count(void) const;\n\n\t/**\n\t * Increment reference count. \n\t * @return The reference count after increment.\n\t */\n\tint inc_ref_count(void);\n\n\t/**\n\t * Decrement reference count. \n\t * @returns The reference count after decrement.\n\t */\n\tint dec_ref_count(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/cmd_socket.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdlib>\n#include <cstring>\n#include <iostream>\n#include \"cmd_socket.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"sockets/socket.h\"\n\nnamespace cmdsocket {\n\n/** Command opcodes */\nenum t_cmd_code {\n\tCMD_CALL,\t/**< Call */\n\tCMD_CLI,\t/**< Any CLI command */\n\tCMD_SHOW,\t/**< Show Twinkle */\n\tCMD_HIDE\t/**< Hide Twinkle */\n};\n\nstring cmd_code2str(t_cmd_code opcode) {\n\tswitch (opcode) {\n\tcase CMD_CALL:\n\t\treturn \"CALL\";\n\tcase CMD_CLI:\n\t\treturn \"CLI\";\n\tcase CMD_SHOW:\n\t\treturn \"SHOW\";\n\tcase CMD_HIDE:\n\t\treturn \"HIDE\";\n\tdefault:\n\t\treturn \"UNKNOWN\";\n\t}\n}\n\nvoid exec_cmd(t_socket_local &sock_client) {\n\tt_cmd_code opcode;\n\tbool immediate;\n\tint len;\n\tstring log_msg;\n\n\ttry {\n\t\tif (sock_client.read(&opcode, sizeof(opcode)) != sizeof(opcode)) {\n\t\t\tlog_file->write_report(\"Failed to read opcode from socket.\",\n\t\t\t\t\"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_WARNING);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (sock_client.read(&immediate, sizeof(immediate)) != sizeof(immediate)) {\n\t\t\tlog_file->write_report(\"Failed to read immediate mode from socket.\",\n\t\t\t\t\"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_WARNING);\n\t\t\treturn;\n\t\t}\n\t\n\t\tif (sock_client.read(&len, sizeof(len)) != sizeof(len)) {\n\t\t\tlog_file->write_report(\"Failed to read length from socket.\",\n\t\t\t\t\"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_WARNING);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tchar args[len];\n\t\t\n\t\tif (sock_client.read(args, len) != len) {\n\t\t\tlog_file->write_report(\"Failed to read arguments from socket.\",\n\t\t\t\t\"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_WARNING);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tlog_file->write_header(\"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"External command received:\\n\");\n\t\tlog_file->write_raw(\"Opcode: \");\n\t\tlog_file->write_raw(cmd_code2str(opcode));\n\t\tlog_file->write_raw(\"\\nImmediate: \");\n\t\tlog_file->write_raw(bool2yesno(immediate));\n\t\tlog_file->write_raw(\"\\nArguments: \");\n\t\tlog_file->write_raw(args);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tswitch (opcode) {\n\t\tcase CMD_CALL:\n\t\t\tui->cmd_call(args, immediate);\n\t\t\tbreak;\n\t\tcase CMD_CLI:\n\t\t\tui->cmd_cli(args, immediate);\n\t\t\tbreak;\n\t\tcase CMD_SHOW:\n\t\t\tui->cmd_show();\n\t\t\tbreak;\n\t\tcase CMD_HIDE:\n\t\t\tui->cmd_hide();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Discard unknown commands\n\t\t\tlog_file->write_header(\"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unknown external command received:\\n\");\n\t\t\tlog_file->write_raw(\"Opcode: \");\n\t\t\tlog_file->write_raw(cmd_code2str(opcode));\n\t\t\tlog_file->write_raw(\"\\nImmediate: \");\n\t\t\tlog_file->write_raw(bool2yesno(immediate));\n\t\t\tlog_file->write_raw(\"\\nArguments: \");\n\t\t\tlog_file->write_raw(args);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\tbreak;\n\t\t}\n\t}\n\tcatch (int e) {\n\t\tlog_msg = \"Failed to read from socket.\\n\";\n\t\tlog_msg += get_error_str(e);\n\t\tlog_msg += \"\\n\";\n\t\tlog_file->write_report(log_msg, \"cmdsocket::exec_cmd\", LOG_NORMAL, LOG_WARNING);\n\t}\n}\n\nvoid *listen_cmd(void *arg) {\n\tt_socket_local *sock_cmd = (t_socket_local *)arg;\n\tstring log_msg;\n\t\n\twhile (true) {\n\t\ttry {\n\t\t\tint fd = sock_cmd->accept();\n\t\t\tt_socket_local sock_client(fd);\n\t\t\texec_cmd(sock_client);\n\t\t}\n\t\tcatch (int e) {\n\t\t\tlog_msg = \"Accept failed on socket.\\n\";\n\t\t\tlog_msg += get_error_str(e);\n\t\t\tlog_msg += \"\\n\";\n\t\t\tlog_file->write_report(log_msg, \"cmdsocket::listen_cmd\", LOG_NORMAL, \n\t\t\t\tLOG_WARNING);\n\t\t\treturn NULL;\n\t\t}\n\t}\n}\n\nvoid write_cmd_to_socket(t_cmd_code opcode, bool immediate, const string &args) {\n\tstring name = sys_config->get_dir_user();\n\tname += '/';\n\tname += CMD_SOCKNAME;\n\n\ttry {\n\t\tt_socket_local sock_cmd;\n\t\tsock_cmd.connect(name);\n\t\tsock_cmd.write(&opcode, sizeof(opcode));\n\t\tsock_cmd.write(&immediate, sizeof(immediate));\n\t\tint len = args.size() + 1;\n\t\tsock_cmd.write(&len, sizeof(len));\n\t\tchar *buf = strdup(args.c_str());\n\t\tMEMMAN_NEW(buf);\n\t\tsock_cmd.write(buf, len);\n\t\tMEMMAN_DELETE(buf);\n\t\tfree(buf);\n\t}\n\tcatch (int e) {\n\t\t// This function will be called from Twinkle when it\n\t\t// notices another Twinkle is already running. In that\n\t\t// case this process does not have a log file. So write\n\t\t// errors to stderr\n\t\tcerr << \"Failed to send \" << cmd_code2str(opcode) << \" command to \" << name << endl;\n\t\tcerr << get_error_str(e) << endl;\n\t}\n}\n\nvoid cmd_call(const string &destination, bool immediate) {\n\twrite_cmd_to_socket(CMD_CALL, immediate, destination);\n}\n\nvoid cmd_cli(const string &cli_command, bool immediate) {\n\twrite_cmd_to_socket(CMD_CLI, immediate, cli_command);\n}\n\nvoid cmd_show(void) {\n\twrite_cmd_to_socket(CMD_SHOW, true, \"\");\n}\n\nvoid cmd_hide(void) {\n\twrite_cmd_to_socket(CMD_HIDE, true, \"\");\n}\n\n}\n"
  },
  {
    "path": "src/cmd_socket.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Twinkle listens on a local socket for external commands.\n */\n\n#ifndef _H_CMD_SOCKET\n#define _H_CMD_SOCKET\n\n#include <string>\n\n/** Name of the local socket. */\n#define CMD_SOCKNAME\t\".cmdsock\"\n\nusing namespace std;\n\nnamespace cmdsocket {\n\n/**\n * Listen on local socket for commands.\n * @param arg A local socket (@ref t_socket_local)\n */\nvoid *listen_cmd(void *arg);\n\n/**\n * Send call command to the local socket.\n * @param destination The SIP destination to call.\n * @param immediate Indicates if the call should be made immediately\n * without asking the user for confirmation.\n */\nvoid cmd_call(const string &destination, bool immediate);\n\n/**\n * Send a CLI command to the local socket.\n * @param  cli_command The CLI command to send.\n * @param immediate Indicates if the call should be made immediately\n * without asking the user for confirmation.\n */\nvoid cmd_cli(const string &cli_command, bool immediate);\n\n/** Send show command to the local socket. */\nvoid cmd_show(void);\n\n/** Send hide command to the local socket. */\nvoid cmd_hide(void);\n\n}\n\n#endif\n"
  },
  {
    "path": "src/dialog.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdlib>\n#include <assert.h>\n#include <iostream>\n#include \"call_history.h\"\n#include \"call_script.h\"\n#include \"dialog.h\"\n#include \"exceptions.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"phone_user.h\"\n#include \"sub_refer.h\"\n#include \"util.h\"\n#include \"userintf.h\"\n#include \"audio/rtp_telephone_event.h\"\n#include \"audits/memman.h\"\n#include \"im/im_iscomposing_body.h\"\n#include \"sdp/sdp.h\"\n#include \"sockets/ipaddr.h\"\n#include \"sockets/socket.h\"\n#include \"stun/stun_transaction.h\"\n\nextern t_event_queue\t*evq_sender;\nextern t_event_queue\t*evq_trans_mgr;\nextern string\t\tuser_host;\nextern string\t\tlocal_hostname;\nextern t_phone\t\t*phone;\n\n// Protected\n\n// Create a request within a dialog\n// RFC 3261 12.2.1.1\nt_request *t_dialog::create_request(t_method m) {\n\tassert(state != DS_NULL);\n\tt_user *user_config = phone_user->get_user_profile();\n\n\t// RFC 3261 9.1\n\tif (m == CANCEL) {\n\t\tt_request *r = new t_request(m);\n\t\tMEMMAN_NEW(r);\n\t\t\n\t\tassert(req_out_invite);\n\t\tt_request *orig_req = req_out_invite->get_request();\n\t\tr->hdr_to = orig_req->hdr_to;\n\t\tr->hdr_from = orig_req->hdr_from;\n\t\tr->hdr_call_id = orig_req->hdr_call_id;\n\t\tr->hdr_cseq.set_seqnr(orig_req->hdr_cseq.seqnr);\n\t\tr->hdr_cseq.set_method(CANCEL);\n\t\tr->hdr_via = orig_req->hdr_via; // RFC 3261 8.1.1.7\n\t\tr->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);\n\t\tr->hdr_route = orig_req->hdr_route;\n\t\tSET_HDR_USER_AGENT(r->hdr_user_agent);\n\t\tr->uri = orig_req->uri;\n\t\t\n\t\t// RFC 3263 4\n\t\t// CANCEL for a particular SIP request MUST be sent to the same SIP\n\t\t// server that the SIP request was delivered to.\n\t\tt_ip_port ip_port;\n\t\torig_req->get_destination(ip_port, *user_config);\n\t\tr->set_destination(ip_port);\n\t\treturn r;\n\t}\n\t\n\tt_request *r = t_abstract_dialog::create_request(m);\n\n\t// CSeq header\n\tif (m == ACK) {\n\t\tassert(req_out_invite);\n\t\t\n\t\t// Local sequence number was incremented by t_abstract_dialog.\n\t\t// Decrement as it ACK does not take a new sequence number.\n\t\tlocal_seqnr--;\n\n\t\t// ACK has the same sequence number\n\t\t// as the INVITE.\n\t\tr->hdr_cseq.set_seqnr(req_out_invite->get_request()->hdr_cseq.seqnr);\n\n\t\t// RFC 3261 22.1\n\t\t// Authorization and Proxy-Authorization headers in INVITE\n\t\t// must be repeated in ACK\n\t\tr->hdr_authorization = req_out_invite->get_request()->\n\t\t\t\t\thdr_authorization;\n\t\tr->hdr_proxy_authorization = req_out_invite->get_request()->\n\t\t\t\t\thdr_proxy_authorization;\n\t}\n\n\t// Contact header\n\tt_contact_param contact;\n\tswitch (m) {\n\tcase REFER:\n\tcase SUBSCRIBE:\n\tcase NOTIFY:\n\t\t// RFC 3265 7.1, RFC 3515 2.2\n\t\t// Contact header is mandatory\n\t\tcontact.uri.set_url(line->create_user_contact(h_ip2str(r->get_local_ip())));\n\t\tr->hdr_contact.add_contact(contact);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\t// Privacy header\n\tif (line->get_hide_user()) {\n\t\tr->hdr_privacy.add_privacy(PRIVACY_ID);\n\t}\n\n\t// Session-Expires header\n\tif (m == INVITE) {\n\t\tset_session_expires_headers(r);\n\t}\n\n\treturn r;\n}\n\n// NULL state. Waiting for incoming INVITE\nvoid t_dialog::state_null(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (r->method != INVITE) {\n\t\tstate = DS_TERMINATED;\n\t\treturn;\n\t}\n\t\n\t// Set local tag\n\tif (r->hdr_to.tag.size() == 0) {\n\t\tlocal_tag = NEW_TAG;\n\t} else {\n\t\tlocal_tag = r->hdr_to.tag;\n\t}\n\t\n\t// If STUN is enabled, then first send a STUN binding request to\n\t// discover the IP adderss and port for media.\n\tif (phone->use_stun(user_config)) {\n\t\t// The STUN transaction may take a while.\n\t\t// Send 100 Trying\n\t\tresp = r->create_response(R_100_TRYING);\n\t\tresp->hdr_to.set_tag(\"\");\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tif (!stun_bind_media()) {\n\t\t\t\t// STUN request failed. Send a 500 on the INVITE.\n\t\t\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\t\t\tresp->hdr_to.set_tag(local_tag);\n\t\t\t\tline->send_response(resp, tuid, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\t\n\t\t\t\tstate = DS_TERMINATED;\n\t\t\t\treturn;\n\t\t}\n\t}\n\t\n\tcall_id = r->hdr_call_id.call_id;\n\n\t// Initialize local seqnr\n\tlocal_seqnr = NEW_SEQNR;\n\tlocal_resp_nr = NEW_SEQNR;\n\n\tremote_tag = r->hdr_from.tag;\n\tlocal_uri = r->hdr_to.uri;\n\tlocal_display = r->hdr_to.display;\n\tremote_uri = r->hdr_from.uri;\n\tremote_display = r->hdr_from.display;\n\n\t// Set remote target URI and display name\n\tremote_target_uri = r->hdr_contact.contact_list.front().uri;\n\tremote_target_display = r->\n\t\t\thdr_contact.contact_list.front().display;\n\n\t// Set route set\n\tif (r->hdr_record_route.is_populated()) {\n\t\troute_set = r->hdr_record_route.route_list;\n\t}\n\t\n\t// RFC 3261 13.2.1\n\t// An initial INVITE should list all supported extensions.\n\t// Set supported extensions\n\tif (r->hdr_supported.is_populated()) {\n\t\tremote_extensions.insert(r->hdr_supported.features.begin(),\n\t\t\t\tr->hdr_supported.features.end());\n\t}\n\n\t// Media information\n\tint warn_code;\n\tstring warn_text;\n\tif (r->body) {\n\t\tswitch(r->body->get_type()) {\n\t\tcase BODY_SDP:\n\t\t\tif (session->process_sdp_offer((t_sdp*)r->body,\n\t\t\t\t\twarn_code, warn_text)) {\n\t\t\t\tsession->recvd_offer = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Unsupported media\n\t\t\tresp = r->create_response(\n\t\t\t\t\tR_488_NOT_ACCEPTABLE_HERE);\n\t\t\tresp->hdr_to.set_tag(local_tag);\n\t\t\tresp->hdr_warning.add_warning(t_warning(LOCAL_HOSTNAME,\n\t\t\t\t\t0, warn_code, warn_text));\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\t\n\t\t\t// Create call history record\n\t\t\tline->call_hist_record.start_call(r, t_call_record::DIR_IN,\n\t\t\t\tuser_config->get_profile_name());\n\t\t\tline->call_hist_record.fail_call(resp);\n\t\t\t\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\tstate = DS_TERMINATED;\n\t\t\treturn;\n\t\tdefault:\n\t\t\t// Unsupported body type. Reject call.\n\t\t\tresp = r->create_response(\n\t\t\t\t\tR_415_UNSUPPORTED_MEDIA_TYPE);\n\t\t\tresp->hdr_to.set_tag(local_tag);\n\n\t\t\t// RFC 3261 21.4.13\n\t\t\tSET_HDR_ACCEPT(resp->hdr_accept);\n\t\t\t\n\t\t\t// Create call history record\n\t\t\tline->call_hist_record.start_call(r, t_call_record::DIR_IN,\n\t\t\t\tuser_config->get_profile_name());\n\t\t\tline->call_hist_record.fail_call(resp);\n\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\tstate = DS_TERMINATED;\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\tresp = r->create_response(R_180_RINGING);\n\tresp->hdr_to.set_tag(local_tag);\n\n\t// RFC 3261 13.3.1.1\n\t// A provisional response creates an early dialog, so\n\t// copy the Record-Route header and add a Contact\n\t// header.\n\n\t// Copy the Record-Route header from request to response\n\tif (r->hdr_record_route.is_populated()) {\n\t\tresp->hdr_record_route = r->hdr_record_route;\n\t}\n\n\t// Set Contact header\n\tt_contact_param contact;\n\tcontact.uri.set_url(line->create_user_contact(h_ip2str(resp->get_local_ip())));\n\tresp->hdr_contact.add_contact(contact);\n\n\t// RFC 3262 3\n\t// Send 180 response reliable if needed\n\tif (r->hdr_require.contains(EXT_100REL) ||\n\t    (r->hdr_supported.contains(EXT_100REL) &&\n\t     (user_config->get_ext_100rel() == EXT_PREFERRED ||\n\t      user_config->get_ext_100rel() == EXT_REQUIRED)))\n\t{\n\t\tresp->hdr_require.add_feature(EXT_100REL);\n\t\tresp->hdr_rseq.set_resp_nr(++local_resp_nr);\n\n\t\t// RFC 3262 5\n\t\t// Create SDP offer in first reliable response if no offer\n\t\t// was received in INVITE.\n\t\t// This implentation does not create an answer in an\n\t\t// reliable 1xx response if an offer was received.\n\t\tif (!session->recvd_offer) {\n\t\t\tsession->create_sdp_offer(resp, SDP_O_USER);\n\t\t}\n\n\t\t// Keep a copy of the response for retransmission\n\t\tresp_1xx_invite = (t_response *)resp->copy();\n\n\t\t// Start 100rel timeout and guard timers\n\t\tline->start_timer(LTMR_100REL_GUARD, get_object_id());\n\t\tline->start_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\t}\n\n\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\treq_in_invite->get_tid());\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\t\n\tui->cb_incoming_call(user_config, line->get_line_number(), r);\n\tline->call_hist_record.start_call(r, t_call_record::DIR_IN,\n\t\tuser_config->get_profile_name());\n\t\n\tstate = DS_W4ANSWER;\n}\n\n// A provisional answer has been sent. Waiting for user to answer.\nvoid t_dialog::state_w4answer(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\tbool tear_down = false;\n\tbool answer_call = false;\n\t\n\tt_call_script script_in_call_failed(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch (r->method) {\n\tcase CANCEL:\n\t\t// Cancel the request and terminate the dialog.\n\t\t// A response on the CANCEL is already given by dialog::recvd_cancel\n\t\tresp = req_in_invite->get_request()->\n\t\t\t\tcreate_response(R_487_REQUEST_TERMINATED);\n\t\tresp->hdr_to.set_tag(local_tag);\n\t\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\t\treq_in_invite->get_tid());\n\t\tline->call_hist_record.fail_call(resp);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\tui->cb_call_cancelled(line->get_line_number(),\n\t\t\t\tr->hdr_reason.get_display_text());\n\t\tstate = DS_TERMINATED;\n\t\tbreak;\n\tcase BYE:\n\t\t// Send 200 on the BYE request\n\t\tresp = r->create_response(R_200_OK);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\t// Send a 487 response to terminate the pending request\n\t\tresp = req_in_invite->get_request()->create_response(\n\t\t\t\t\t\tR_487_REQUEST_TERMINATED);\n\t\tresp->hdr_to.set_tag(local_tag);\n\t\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\t\t\t\treq_in_invite->get_tid());\n\t\tline->call_hist_record.fail_call(resp);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\tui->cb_far_end_hung_up(line->get_line_number(),\n\t\t\t\tr->hdr_reason.get_display_text());\n\t\tstate = DS_TERMINATED;\n\t\tbreak;\n\tcase PRACK:\n\t\t// RFC 3262 3\n\t\tif (respond_prack(r, tuid, tid)) {\n\t\t\tanswer_call = answer_after_prack;\n\n\t\t\t// RFC 3262 5\n\t\t\t// If an offer was sent in the 1xx response, then PRACK must\n\t\t\t// contain an answer\n\t\t\tif (session->sent_offer && r->body) {\n\t\t\t\tint warn_code;\n\t\t\t\tstring warn_text;\n\t\t\t\tif (r->body->get_type() != BODY_SDP) {\n\t\t\t\t\t// Only SDP bodies are supported\n\t\t\t\t\tui->cb_unsupported_content_type(\n\t\t\t\t\t\tline->get_line_number(), r);\n\t\t\t\t\ttear_down = true;\n\t\t\t\t} else if (session->process_sdp_answer((t_sdp *)r->body,\n\t\t\t\t\t\twarn_code, warn_text))\n\t\t\t\t{\n\t\t\t\t\tsession->recvd_answer = true;\n\t\t\t\t\tsession->start_rtp();\n\t\t\t\t} else {\n\t\t\t\t\t// SDP answer is not supported.\n\t\t\t\t\t// Tear down the call.\n\t\t\t\t\tui->cb_sdp_answer_not_supported(\n\t\t\t\t\t\tline->get_line_number(), warn_text);\n\t\t\t\t\ttear_down = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (session->sent_offer && !r->body) {\n\t\t\t\tui->cb_sdp_answer_missing(line->get_line_number());\n\t\t\t\ttear_down = true;\n\t\t\t}\n\t\t}\n\n\t\tif (tear_down) {\n\t\t\tresp = req_in_invite->get_request()->create_response(\n\t\t\t\tR_400_BAD_REQUEST,\n\t\t\t\t\"SDP answer in PRACK missing or unsupported\");\n\t\t\tresp->hdr_to.set_tag(local_tag);\t\t\n\t\t\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\t\t\t\treq_in_invite->get_tid());\n\t\t\tline->call_hist_record.fail_call(resp);\n\t\t\t\n\t\t\t// Trigger call script\n\t\t\tt_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,\n\t\t\t\t\tline->get_line_number() + 1);\n\t\t\tscript.exec_notify(resp);\n\t\t\t\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\tstate = DS_TERMINATED;\n\t\t} else if (answer_call) {\n\t\t\tanswer();\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\t// INVITE transaction has not been completed. Deny\n\t\t// other requests within the dialog.\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR,\n\t\t\t\"Session not yet established\");\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_w4answer(t_line_timer timer) {\n\tt_ip_port ip_port;\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tt_call_script script_in_call_failed(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\n\t// RFC 3262 3\n\tswitch(timer) {\n\tcase LTMR_100REL_TIMEOUT:\n\t\t// Retransmit 1xx response.\n\t\t// Send the response directly to the sender thread\n\t\t// bypassing the transaction layer. As this is a retransmission\n\t\t// from the TU, the transaction layer does not need to know.\n\t\tresp_1xx_invite->get_destination(ip_port);\n\t\tif (ip_port.ipaddr == 0) {\n\t\t\t// This should not happen. The response has been\n\t\t\t// sent before so it should be possible to sent\n\t\t\t// it again. Ignore the timeout. When the 100rel\n\t\t\t// guard timer expires, the dialog will be\n\t\t\t// cleaned up.\n\t\t\tbreak;\n\t\t}\n\t\tevq_sender->push_network(resp_1xx_invite, ip_port);\n\t\tline->start_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\t\tbreak;\n\tcase LTMR_100REL_GUARD:\n\t\tline->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\n\t\t// PRACK was not received in time. Tear down the call.\n\t\tresp = req_in_invite->get_request()->create_response(\n\t\t\tR_500_INTERNAL_SERVER_ERROR, \"100rel timeout\");\n\t\tresp->hdr_to.set_tag(local_tag);\n\t\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\t\t\t\treq_in_invite->get_tid());\n\t\tline->call_hist_record.fail_call(resp);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\tremove_client_request(&req_in_invite);\n\t\tMEMMAN_DELETE(resp_1xx_invite);\n\t\tdelete resp_1xx_invite;\n\t\tresp_1xx_invite = NULL;\n\n\t\tstate = DS_TERMINATED;\n\t\tlog_file->write_report(\"LTMR_100REL_GUARD expired.\",\n\t\t\t\t\"t_dialog::state_w4answer\");\n\n\t\tui->cb_100rel_timeout(line->get_line_number());\n\t\tbreak;\n\tdefault:\n\t\t// Other timeouts are not expected. Ignore.\n\t\tbreak;\n\t}\n}\n\n// 200 OK has been sent. Waiting for ACK\nvoid t_dialog::state_w4ack(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\tbool tear_down = false;\n\tt_client_request *cr;\n\t\n\tt_call_script script_out_call_failed(user_config, t_call_script::TRIGGER_OUT_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch(r->method) {\n\tcase ACK:\n\t\t// Dialog is established now.\n\t\tline->stop_timer(LTMR_ACK_TIMEOUT, get_object_id());\n\t\tline->stop_timer(LTMR_ACK_GUARD, get_object_id());\n\t\tremove_client_request(&req_in_invite);\n\t\tMEMMAN_DELETE(resp_invite);\n\t\tdelete resp_invite;\n\t\tresp_invite = NULL;\n\n\t\t// If no offer was received in INVITE, then an offer\n\t\t// has been sent in 200 OK or reliable 1xx (RFC 3262).\n\t\t// Therefor an answer must be present in ACK\n\t\tif (!session->recvd_offer && r->body) {\n\t\t\tint warn_code;\n\t\t\tstring warn_text;\n\t\t\tif (r->body->get_type() != BODY_SDP) {\n\t\t\t\t// Only SDP bodies are supported\n\t\t\t\tui->cb_unsupported_content_type(\n\t\t\t\t\tline->get_line_number(), r);\n\t\t\t\ttear_down = true;\n\t\t\t} else if (session->process_sdp_answer((t_sdp *)r->body,\n\t\t\t\t\twarn_code, warn_text))\n\t\t\t{\n\t\t\t\tsession->recvd_answer = true;\n\t\t\t\tsession->start_rtp();\n\t\t\t} else {\n\t\t\t\t// SDP answer is not supported.\n\t\t\t\t// Tear down the call.\n\t\t\t\tui->cb_sdp_answer_not_supported(\n\t\t\t\t\t\tline->get_line_number(), warn_text);\n\t\t\t\ttear_down = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!session->recvd_offer && !r->body) {\n\t\t\tui->cb_sdp_answer_missing(line->get_line_number());\n\t\t\ttear_down = true;\n\t\t}\n\n\t\tif (end_after_ack) {\n\t\t\tui->cb_far_end_hung_up(line->get_line_number(),\n\t\t\t\t\tend_after_ack_reason);\n\t\t\tstate = DS_TERMINATED;\n\t\t} else {\n\t\t\tstate = DS_CONFIRMED;\n\t\t\tif (tear_down) {\n\t\t\t\tsend_bye();\n\t\t\t}\n\t\t}\n\n\t\tui->cb_call_established(line->get_line_number());\n\t\tbreak;\n\tcase BYE:\n\t\t// Send 200 on the BYE request\n\t\tresp = r->create_response(R_200_OK);\n\t\tline->send_response(resp, tuid, tid);\n\t\t\n\t\t// Trigger call script\n\t\tscript_out_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tline->call_hist_record.end_call(true);\n\n\t\t// The session will be ended when an ACK has been\n\t\t// received.\n\t\tend_after_ack = true;\n\t\tend_after_ack_reason = r->hdr_reason.get_display_text();\n\t\tbreak;\n\tcase PRACK:\n\t\t// RFC 3262 3\n\t\t// This is a late PRACK as the call is answered already.\n\t\t// Respond in a normal way to the PRACK\n\t\trespond_prack(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\t// Queue the request as ACK needs to be received first.\n\t\t// Note that the tuid value is not stored in the queue.\n\t\t// For an incoming request tuid is always 0.\n\t\tcr = new t_client_request(user_config, r, tid);\n\t\tMEMMAN_NEW(cr);\n\t\tinc_req_queue.push_back(cr);\n\t\tlog_file->write_header(\"t_dialog::state_w4ack\", \n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Waiting for ACK.\\n\");\n\t\tlog_file->write_raw(\"Queue incoming \");\n\t\tlog_file->write_raw(method2str(r->method, r->unknown_method));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_w4ack_re_invite(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\tbool tear_down = false;\n\t\n\tt_call_script script_out_call_failed(user_config, t_call_script::TRIGGER_OUT_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch(r->method) {\n\tcase ACK:\n\t\t// re_INVITE is finished now\n\t\tline->stop_timer(LTMR_ACK_TIMEOUT, get_object_id());\n\t\tline->stop_timer(LTMR_ACK_GUARD, get_object_id());\n\t\tremove_client_request(&req_in_invite);\n\t\tMEMMAN_DELETE(resp_invite);\n\t\tdelete resp_invite;\n\t\tresp_invite = NULL;\n\n\t\t// If no offer was received in INVITE, then an offer\n\t\t// has been sent in 200 OK or reliable 1xx (RFC 3262).\n\t\t// Therefor an answer must be present in ACK\n\t\tif (!session_re_invite->recvd_offer && r->body) {\n\t\t\tint warn_code;\n\t\t\tstring warn_text;\n\n\t\t\tif (r->body->get_type() != BODY_SDP) {\n\t\t\t\t// Only SDP bodies are supported\n\t\t\t\tui->cb_unsupported_content_type(\n\t\t\t\t\tline->get_line_number(), r);\n\t\t\t\ttear_down = true;\n\t\t\t} else if (session_re_invite->process_sdp_answer(\n\t\t\t\t(t_sdp *)r->body, warn_code, warn_text))\n\t\t\t{\n\t\t\t\tsession_re_invite->recvd_answer = true;\n\t\t\t} else {\n\t\t\t\t// SDP answer is not supported.\n\t\t\t\t// Tear down the call.\n\t\t\t\tui->cb_sdp_answer_not_supported(\n\t\t\t\t\t\tline->get_line_number(), warn_text);\n\t\t\t\ttear_down = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!session_re_invite->recvd_offer && !r->body) {\n\t\t\tui->cb_sdp_answer_missing(line->get_line_number());\n\t\t\ttear_down = true;\n\t\t}\n\n\t\tif (end_after_ack) {\n\t\t\tui->cb_far_end_hung_up(line->get_line_number(),\n\t\t\t\t\tend_after_ack_reason);\n\t\t\tstate = DS_TERMINATED;\n\t\t} else {\n\t\t\tstate = DS_CONFIRMED;\n\t\t\tif (tear_down) {\n\t\t\t\tsend_bye();\n\t\t\t} else {\n\t\t\t\t// Make the new session description current\n\t\t\t\tactivate_new_session();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase BYE:\n\t\t// Send 200 on the BYE request\n\t\tresp = r->create_response(R_200_OK);\n\t\tline->send_response(resp, tuid, tid);\n\t\t\n\t\t// Trigger call script\n\t\tscript_out_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tline->call_hist_record.end_call(true);\n\n\t\t// The session will be ended when an ACK has been\n\t\t// received.\n\t\tend_after_ack = true;\n\t\tend_after_ack_reason = r->hdr_reason.get_display_text();\n\t\tbreak;\n\tdefault:\n\t\t// ACK has not been received. Handle other incoming request\n\t\t// as if we are in the confirmed state. These incoming requests\n\t\t// should not change state.\n\t\tstate_confirmed(r, tuid, tid);\n\t\tassert(state == DS_W4ACK_RE_INVITE);\n\t\tbreak;\n\t}\n}\n\n// RFC 3261 13.3.1.4\nvoid t_dialog::state_w4ack(t_line_timer timer) {\n\tt_ip_port ip_port;\n\n\t// NOTE: this code is also executed for re-INVITE ACK time-outs\n\t//       timeout handling for INVITE/re-INVITE is the same\n\n\tswitch(timer) {\n\tcase LTMR_ACK_TIMEOUT:\n\t\t// Retransmit 2xx response.\n\t\t// Send the response directly to the sender thread\n\t\t// as the INVITE transaction completed already.\n\t\t// (see RFC 3261 17.2.1)\n\t\tif (!resp_invite) break; // there is no response to send\n\t\tresp_invite->get_destination(ip_port);\n\t\tif (ip_port.ipaddr == 0) {\n\t\t\t// This should not happen. The response has been\n\t\t\t// sent before so it should be possible to sent\n\t\t\t// it again. Ignore the timeout. When the ACK\n\t\t\t// guard timer expires, the dialog will be\n\t\t\t// cleaned up.\n\t\t\tbreak;\n\t\t}\n\t\tevq_sender->push_network(resp_invite, ip_port);\n\t\tline->start_timer(LTMR_ACK_TIMEOUT, get_object_id());\n\t\tbreak;\n\tcase LTMR_ACK_GUARD:\n\t\tline->stop_timer(LTMR_ACK_TIMEOUT, get_object_id());\n\t\t// Consider dialog as established and tear down call\n\t\tremove_client_request(&req_in_invite);\n\t\tMEMMAN_DELETE(resp_invite);\n\t\tdelete resp_invite;\n\t\tresp_invite = NULL;\n\t\tstate = DS_CONFIRMED;\n\t\tlog_file->write_report(\"LTMR_ACK_GUARD expired.\",\n\t\t\t\t\"t_dialog::state_w4ack\");\n\t\tif (end_after_ack) {\n\t\t\tstate = DS_TERMINATED;\n\t\t} else {\n\t\t\tsend_bye();\n\t\t}\n\t\tui->cb_ack_timeout(line->get_line_number());\n\t\tbreak;\n\tdefault:\n\t\t// Other timeouts are not expected. Ignore.\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_w4ack_re_invite(t_line_timer timer) {\n\tstate_w4ack(timer);\n}\n\nvoid t_dialog::state_w4re_invite_resp(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tt_call_script script_remote_release(user_config, t_call_script::TRIGGER_REMOTE_RELEASE,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch(r->method) {\n\tcase BYE:\n\t\tresp = r->create_response(R_200_OK);\t\t\n\t\tline->send_response(resp, tuid, tid);\n\t\t\n\t\t// Trigger call script\n\t\tscript_remote_release.exec_notify(r);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tui->cb_far_end_hung_up(line->get_line_number(),\n\t\t\t\tr->hdr_reason.get_display_text());\n\t\tline->call_hist_record.end_call(true);\n\n\t\tif (!sub_refer) {\n\t\t\tstate = DS_TERMINATED;\n\t\t} else {\n\t\t\tstate = DS_CONFIRMED_SUB;\n\t\t\tif (sub_refer->get_role() == SR_SUBSCRIBER) {\n\t\t\t\t// End subscription\n\t\t\t\tsub_refer->unsubscribe();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ACK:\n\t\t// Ignore ACK\n\t\tbreak;\n\tcase OPTIONS:\n\t\tresp = line->create_options_response(r, true);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\tcase PRACK:\n\t\t// RFC 3262 3\n\t\t// This is a late PRACK. Respond in a normal way.\n\t\trespond_prack(r, tuid, tid);\n\t\tbreak;\n\tcase SUBSCRIBE:\n\t\tprocess_subscribe(r, tuid, tid);\n\t\tbreak;\n\tcase NOTIFY:\n\t\tprocess_notify(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR,\n\t\t\t\"Waiting for re-INVITE response\");\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n}\n\n// In the confirmed state, requests will be responded.\nvoid t_dialog::state_confirmed(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tt_call_script script_remote_release(user_config, t_call_script::TRIGGER_REMOTE_RELEASE,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch(r->method) {\n\tcase INVITE:\n\t\t// re-INVITE\n\t\tprocess_re_invite(r, tuid, tid);\n\t\tbreak;\n\tcase BYE:\n\t\tresp = r->create_response(R_200_OK);\t\t\n\t\tline->send_response(resp, tuid, tid);\n\t\t\n\t\t// Trigger call script\n\t\tscript_remote_release.exec_notify(r);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tui->cb_far_end_hung_up(line->get_line_number(),\n\t\t\t\tr->hdr_reason.get_display_text());\n\t\tline->call_hist_record.end_call(true);\n\n\t\tif (!sub_refer) {\n\t\t\tstate = DS_TERMINATED;\n\t\t} else {\n\t\t\tstate = DS_CONFIRMED_SUB;\n\t\t\tif (sub_refer->get_role() == SR_SUBSCRIBER) {\n\t\t\t\t// End subscription\n\t\t\t\tsub_refer->unsubscribe();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ACK:\n\t\t// Ignore ACK\n\t\tbreak;\n\tcase OPTIONS:\n\t\tresp = line->create_options_response(r, true);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\tcase PRACK:\n\t\t// RFC 3262 3\n\t\t// This is a late PRACK. Respond in a normal way.\n\t\trespond_prack(r, tuid, tid);\n\t\tbreak;\n\tcase REFER:\n\t\tprocess_refer(r, tuid, tid);\n\t\tbreak;\n\tcase SUBSCRIBE:\n\t\tprocess_subscribe(r, tuid, tid);\n\t\tbreak;\n\tcase NOTIFY:\n\t\tprocess_notify(r, tuid, tid);\n\t\tbreak;\n\tcase INFO:\n\t\tprocess_info(r, tuid, tid);\n\t\tbreak;\n\tcase MESSAGE:\n\t\tprocess_message(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_confirmed(t_line_timer timer) {\n\tswitch(timer) {\n\tcase LTMR_GLARE_RETRY:\n\t\tswitch(reinvite_purpose) {\n\t\tcase REINVITE_HOLD:\n\t\t\thold();\n\t\t\tbreak;\n\t\tcase REINVITE_RETRIEVE:\n\t\t\tretrieve();\n\t\t\tline->retry_retrieve_succeeded();\n\t\t\t// Note that the re-INVITE is not completed here yet.\n\t\t\t// If re-INVITE fails then line->failed_retrieve will\n\t\t\t// be called later.\n\t\t\tbreak;\n\t\tcase REINVITE_REFRESH:\n\t\t\tsend_session_refresh_request();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\t// Other timeouts are not exepcted. Ignore.\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_confirmed_sub(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\n\tswitch(r->method) {\n\tcase OPTIONS:\n\t\tresp = line->create_options_response(r, true);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\tcase SUBSCRIBE:\n\t\tprocess_subscribe(r, tuid, tid);\n\t\tbreak;\n\tcase NOTIFY:\n\t\tprocess_notify(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n\n\tif (!sub_refer) {\n\t\t// The subscription has been terminated already.\n\t\tstate = DS_TERMINATED;\n\t} else if (sub_refer->get_state() == SS_TERMINATED) {\n\t\tMEMMAN_DELETE(sub_refer);\n\t\tdelete sub_refer;\n\t\tsub_refer = NULL;\n\t\tstate = DS_TERMINATED;\n\t}\n}\n\nvoid t_dialog::process_re_invite(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tsession_re_invite = session->create_clean_copy();\n\n\t// Media information\n\tint warn_code;\n\tstring warn_text;\n\tif (r->body) {\n\t\tswitch(r->body->get_type()) {\n\t\tcase BODY_SDP:\n\t\t\tif (session_re_invite->\n\t\t\t\t\tprocess_sdp_offer((t_sdp*)r->body,\n\t\t\t\t\t\twarn_code, warn_text))\n\t\t\t{\n\t\t\t\tsession_re_invite->recvd_offer = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Unsupported media\n\t\t\tresp = r->create_response(\n\t\t\t\t\tR_488_NOT_ACCEPTABLE_HERE);\n\t\t\tresp->hdr_warning.add_warning(t_warning(LOCAL_HOSTNAME,\n\t\t\t\t\t0, warn_code, warn_text));\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\t\n\t\t\tMEMMAN_DELETE(session_re_invite);\n\t\t\tdelete session_re_invite;\n\t\t\tsession_re_invite = NULL;\n\n\t\t\t// Stay in the confirmed state. The sender of the\n\t\t\t// request has to determine if the dialog needs to\n\t\t\t// be torn down by sending a BYE.\n\t\t\treturn;\n\t\tdefault:\n\t\t\t// Unsupported body type. Reject call.\n\t\t\tresp = r->create_response(\n\t\t\t\t\tR_415_UNSUPPORTED_MEDIA_TYPE);\n\n\t\t\t// RFC 3261 21.4.13\n\t\t\tSET_HDR_ACCEPT(resp->hdr_accept);\n\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\t\n\t\t\tMEMMAN_DELETE(session_re_invite);\n\t\t\tdelete session_re_invite;\n\t\t\tsession_re_invite = NULL;\n\n\t\t\t// Stay in the confirmed state.\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// If STUN is enabled, then first send a STUN binding request to\n\t// discover the IP adderss and port for media if no RTP stream\n\t// is currently active.\n\tif (phone->use_stun(user_config) && !session->is_rtp_active()) {\n\t\t// The STUN transaction may take a while.\n\t\t// Send 100 Trying\n\t\tresp = r->create_response(R_100_TRYING);\n\t\tresp->hdr_to.set_tag(\"\");\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tif (!stun_bind_media()) {\n\t\t\t// STUN request failed. Send a 500 on the INVITE.\n\t\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\t\tresp->hdr_to.set_tag(local_tag);\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\t\t\n\t\t\tstate = DS_TERMINATED;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Refresh target\n\tif (r->hdr_contact.is_populated() &&\n\t    r->hdr_contact.contact_list.size() > 0)\n\t{\n\t\tremote_target_uri = r->hdr_contact.contact_list.front().uri;\n\t\tremote_target_display = r->\n\t\t\thdr_contact.contact_list.front().display;\n\t}\n\n\t// Send 200 OK\n\tresp_invite = r->create_response(R_200_OK);\n\tresp_invite->hdr_to.set_tag(local_tag);\n\n\t// Set Contact header\n\tt_contact_param contact;\n\tcontact.uri.set_url(line->create_user_contact(h_ip2str(resp_invite->get_local_ip())));\n\tresp_invite->hdr_contact.add_contact(contact);\n\n\t// A re-INVITE acts as session refresh request\n\tprocess_session_refresh_request(r, resp_invite, true);\n\n\t// Set Allow and Supported headers\n\tSET_HDR_ALLOW(resp_invite->hdr_allow, user_config);\n\tSET_HDR_SUPPORTED(resp_invite->hdr_supported, user_config);\n\n\t// RFC 3261 13.3.1.4\n\t// Create SDP offer if no offer was received in INVITE and no offer\n\t// was sent in a reliable 1xx response (RFC 3262 5).\n\t// Otherwise create an SDP answer.\n\tif (!session_re_invite->recvd_offer && !session_re_invite->sent_offer) {\n\t\tsession_re_invite->create_sdp_offer(resp_invite, SDP_O_USER);\n\t} else {\n\t\tsession_re_invite->create_sdp_answer(resp_invite, SDP_O_USER);\n\t}\n\n\tline->send_response(resp_invite, tuid, tid);\n\tline->start_timer(LTMR_ACK_GUARD, get_object_id());\n\tline->start_timer(LTMR_ACK_TIMEOUT, get_object_id());\n\n\tstate = DS_W4ACK_RE_INVITE;\n}\n\nvoid t_dialog::process_refer(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_contact_param contact;\n\t\n\trefer_accepted = true;\n\n\t// RFC 3515\n\tif (sub_refer || !user_config->get_allow_refer()) {\n\t\t// A reference is already in progress or REFER is not\n\t\t// allowed.\n\t\tresp = r->create_response(R_603_DECLINE);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\trefer_accepted = false;\n\t\treturn;\n\t}\n\n\t// Check if the URI scheme is supported\n\tif (r->hdr_refer_to.uri.get_scheme() != \"sip\") {\n\t\tresp = r->create_response(R_416_UNSUPPORTED_URI_SCHEME);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\trefer_accepted = false;\n\t\treturn;\n\t}\n\n\tresp = r->create_response(R_202_ACCEPTED);\n\n\t// RFC 3515 2.2\n\t// Contact header is mandatory\n\tcontact.uri.set_url(line->create_user_contact(h_ip2str(resp->get_local_ip())));\n\tresp->hdr_contact.add_contact(contact);\n\n\tif (r->hdr_refer_sub.is_populated() && !r->hdr_refer_sub.create_refer_sub) {\n\t\t// RFC 4488 4\n\t\tresp->hdr_refer_sub.set_create_refer_sub(false);\n\t}\n\tline->send_response(resp, tuid, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\tif (r->hdr_refer_sub.is_populated() && !r->hdr_refer_sub.create_refer_sub) {\n\t\t// RFC 4488\n\t\t// The REFER-issuer requested not to create an implicit refer\n\t\t// subscription.\n\t\tlog_file->write_report(\n\t\t\t\"REFER-issuer requested not to create a refer subscription.\",\n\t\t\t\"t_dialog::process_refer\");\n\t} else {\n\t\t// RFC 3515\n\t\t// The event header of a NOTIFY to a first REFER MAY\n\t\t// include the id paramter. NOTIFY's to subsequent\n\t\t// REFERs MUST include the id parameter (CSeq from REFER).\n\t\tsub_refer = new t_sub_refer(this, SR_NOTIFIER,\n\t\t\tulong2str(r->hdr_cseq.seqnr));\n\t\tMEMMAN_NEW(sub_refer);\n\t\n\t\t// Send immediate NOTIFY\n\t\tresp = new t_response(R_100_TRYING);\n\t\tMEMMAN_NEW(resp);\n\t\tif (user_config->get_ask_user_to_refer()) {\n\t\t\t// If the user has to grant permission, then the\n\t\t\t// subscription is pending.\n\t\t\tsub_refer->send_notify(resp, SUBSTATE_PENDING);\n\t\t} else {\n\t\t\tsub_refer->send_notify(resp, SUBSTATE_ACTIVE);\n\t\t}\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t}\n\n\t// Ask permission to refer\n\tif (user_config->get_ask_user_to_refer()) {\n\t\tif (r->hdr_referred_by.is_populated()) {\n\t\t\tui->cb_ask_user_to_refer(user_config,\n\t\t\t\tr->hdr_refer_to.uri,\n\t\t\t\tr->hdr_refer_to.display,\n\t\t\t\tr->hdr_referred_by.uri,\n\t\t\t\tr->hdr_referred_by.display);\n\t\t} else {\n\t\t\tui->cb_ask_user_to_refer(user_config,\n\t\t\t\tr->hdr_refer_to.uri,\n\t\t\t\tr->hdr_refer_to.display,\n\t\t\t\tt_url(), \"\");\n\t\t}\n\t} else {\n\t\tui->send_refer_permission(true);\n\t}\n\t\n\t// NOTE: refer_accepted = true, though the answer to permission\n\t//       is not given yet. So this means, that the refer is not\n\t//       rejected at this moment. It may be rejected by the user.\n}\n\nvoid t_dialog::recvd_refer_permission(bool permission, t_request *r) {\n\tt_response *resp;\n\t\n\t// NOTE: if the REFER-issuer requested not to create a refer\n\t// subscription (RFC 4488), then no NOTIFY can be sent to signal\n\t// the rejection.\n\tif (!permission && sub_refer) {\n\t\t// User denied REFER\n\t\t// RFC 3515 2.4.5\n\t\tresp = new t_response(R_603_DECLINE);\n\t\tMEMMAN_NEW(resp);\n\t\tsub_refer->send_notify(resp, SUBSTATE_TERMINATED,\n\t\t\t\tEV_REASON_REJECTED);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t}\n\t\n\trefer_accepted = permission;\n}\n\nvoid t_dialog::process_subscribe(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\n\tif (sub_refer && sub_refer->match(r)) {\n\t\tsub_refer->recv_subscribe(r, tuid, tid);\n\t\tif (sub_refer->get_state() == SS_TERMINATED) {\n\t\t\tMEMMAN_DELETE(sub_refer);\n\t\t\tdelete sub_refer;\n\t\t\tsub_refer = NULL;\n\t\t}\n\n\t\treturn;\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST,\n\t\t\tREASON_481_SUBSCRIPTION_NOT_EXIST);\n\tline->send_response(resp, tuid, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_dialog::process_notify(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\n\tif (!sub_refer &&\n\t    (refer_state == REFST_W4RESP || refer_state == REFST_W4NOTIFY))\n\t{\n\t\t// First NOTIFY after sending a REFER\n\t\tsub_refer = new t_sub_refer(this, SR_SUBSCRIBER, r->hdr_event.id);\n\t\tMEMMAN_NEW(sub_refer);\n\t\trefer_state = REFST_PENDING;\n\t}\n\n\tif (sub_refer && sub_refer->match(r)) {\n\t\tsub_refer->recv_notify(r, tuid, tid);\n\t\tif (sub_refer->get_state() == SS_TERMINATED) {\n\t\t\t// Set the refer state to NULL before calling the UI\n\t\t\t// call back functions as the user interface might use\n\t\t\t// the refer state to render the correct status to the\n\t\t\t// user.\n\t\t\trefer_state = REFST_NULL;\n\t\t\n\t\t\t// Determine outcome of the reference\n\t\t\tswitch(sub_refer->get_sr_result()) {\n\t\t\tcase SRR_INPROG:\n\t\t\t\t// The outcome of the reference is unknown.\n\t\t\t\t// Treat it as a success as no new info will\n\t\t\t\t// come to the referrer.\n\t\t\t\trefer_succeeded = true;\n\t\t\t\tui->cb_refer_result_inprog(line->get_line_number());\n\t\t\t\tbreak;\n\t\t\tcase SRR_FAILED:\n\t\t\t\trefer_succeeded = false;\n\t\t\t\tui->cb_refer_result_failed(line->get_line_number());\n\t\t\t\tbreak;\n\t\t\tcase SRR_SUCCEEDED:\n\t\t\t\trefer_succeeded = true;\n\t\t\t\tui->cb_refer_result_success(line->get_line_number());\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\n\t\t\tMEMMAN_DELETE(sub_refer);\n\t\t\tdelete sub_refer;\n\t\t\tsub_refer = NULL;\n\t\t} else if (!sub_refer->is_pending()) {\n\t\t\trefer_state = REFST_ACTIVE;\n\t\t}\n\n\t\treturn;\n\t}\n\n\t// RFC 3265 3.2.4\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST,\n\t\t\tREASON_481_SUBSCRIPTION_NOT_EXIST);\n\tline->send_response(resp, tuid, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_dialog::process_info(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\t\n\t// RFC 2976 2.2\n\t// A 200 OK response MUST be sent by a UAS for an INFO request with\n        // no message body if the INFO request was successfully received for\n        // an existing call.\n\tif (!r->body) {\n\t\tresp = r->create_response(R_200_OK);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\tif (r->body->get_type() != BODY_DTMF_RELAY) {\n\t\tresp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);\n\t\tresp->hdr_accept.add_media(t_media(\"application\", \"dtmf-relay\"));\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\tchar dtmf_signal = ((t_sip_body_dtmf_relay *)r->body)->signal;\n\tif (!is_valid_dtmf_sym(dtmf_signal)) {\n\t\tresp = r->create_response(R_400_BAD_REQUEST, \"Invalid DTMF signal\");\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\tresp = r->create_response(R_200_OK);\n\tline->send_response(resp, tuid, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\t\n\tui->cb_dtmf_detected(line->get_line_number(), char2dtmf_ev(dtmf_signal));\n}\n\nvoid t_dialog::process_message(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tlog_file->write_report(\"Received in-dialog MESSAGE.\",\n\t\t\"t_dialog::process_message\", LOG_NORMAL, LOG_DEBUG);\n\t\t\n\tif (!r->body || !MESSAGE_CONTENT_TYPE_SUPPORTED(*r)) {\n\t\tresp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);\n\t\t// RFC 3261 21.4.13\n\t\tSET_MESSAGE_HDR_ACCEPT(resp->hdr_accept);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\tif (r->body && r->body->get_type() == BODY_IM_ISCOMPOSING_XML) {\n\t\t// Message composing indication\n\t\tt_im_iscomposing_xml_body *sb = dynamic_cast<t_im_iscomposing_xml_body *>(r->body);\n\t\tim::t_composing_state state = im::string2composing_state(sb->get_state());\n\t\ttime_t refresh = sb->get_refresh();\n\t\t\n\t\tui->cb_im_iscomposing_request(line->get_user(), r, state, refresh);\n\t\tresp = r->create_response(R_200_OK);\n\t} else {\n\t\t// Instant message\n\t\tbool accepted = ui->cb_message_request(line->get_user(), r);\n\t\tif (accepted) {\n\t\t\tresp = r->create_response(R_200_OK);\n\t\t} else {\n\t\t\tif (user_config->get_im_max_sessions() == 0) {\n\t\t\t\tresp = r->create_response(R_603_DECLINE);\n\t\t\t} else {\n\t\t\t\tresp = r->create_response(R_486_BUSY_HERE);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tline->send_response(resp, tuid, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\n// INVITE sent. Waiting for a first non-100 response.\nvoid t_dialog::state_w4invite_resp(t_response *r, t_tuid tuid, t_tid tid) {\n\tif (r->hdr_cseq.method != INVITE) return;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\t// 1XX (except 100) and 2XX establish the dialog.\n\t// Update the state for dialog establishment.\n\t// RFC 3261 12.1.2\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\tif (r->code == R_100_TRYING) break;\n\t\t\n\t\t// RFC 3262 4\n\t\t// Discard retransmissions and out-of-sequence reliable\n\t\t// provisional responses.\n\t\tif (must_discard_100rel(r)) return;\n\n\t\t// fall thru\n\tcase R_2XX:\n\t\t// Set remote tag\n\t\tremote_tag = r->hdr_to.tag;\n\n\t\tcreate_route_set(r);\n\t\tcreate_remote_target(r);\n\n\t\t// Set remote URI and display name\n\t\tremote_uri = r->hdr_to.uri;\n\t\tremote_display = r->hdr_to.display;\n\n\t\tprocess_1xx_2xx_invite_resp(r);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// RFC 3262\n\t// Send PRACK if required\n\tsend_prack_if_required(r);\n\t\n\tt_call_script script_out_call_answered(user_config,\n\t\t\tt_call_script::TRIGGER_OUT_CALL_ANSWERED,\n\t\t\tline->get_line_number() + 1);\n\tt_call_script script_out_call_failed(user_config, \n\t\t\tt_call_script::TRIGGER_OUT_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\t// Provisional response received.\n\t\tline->ci_set_last_provisional_reason(r->reason);\n\t\tui->cb_provisional_resp_invite(line->get_line_number(), r);\n\t\tif (r->code > R_100_TRYING && r->hdr_to.tag.size() > 0) {\n\t\t\tstate = DS_EARLY;\n\t\t} else {\n\t\t\tstate = DS_W4INVITE_RESP2;\n\t\t}\n\n\t\t// User indicated that the request should be cancelled.\n\t\t// Now that the first provisional response has been received,\n\t\t// a CANCEL can be sent.\n\t\tif (request_cancelled) {\n\t\t\tsend_cancel(true);\n\t\t}\n\n\t\tbreak;\n\tcase R_2XX:\n\t\t// Stop cancel guard timer if it was running\n\t\tline->stop_timer(LTMR_CANCEL_GUARD, get_object_id());\n\t\n\t\t// Success received.\n\t\tack_2xx_invite(r);\n\n\t\t// Check for REFER support\n\t\t// If the Allow header is not present then assume REFER\n\t\t// is supported.\n\t\tif (!r->hdr_allow.is_populated() ||\n\t\t    r->hdr_allow.contains_method(REFER))\n\t\t{\n\t\t\tline->ci_set_refer_supported(true);\n\t\t}\n\t\t\n\t\t// This was a response to a session refresh request (INVITE)\n\t\tprocess_session_refresh_response(r);\n\n\t\t// Trigger call script\n\t\tscript_out_call_answered.exec_notify(r);\n\n\t\tui->cb_call_answered(user_config, line->get_line_number(), r);\n\t\tline->call_hist_record.answer_call(r);\n\t\tstate = DS_CONFIRMED;\n\n\t\tif (request_cancelled) {\n\t\t\t// User indicated that the request should be cancelled,\n\t\t\t// but no response was received yet. A final response\n\t\t\t// has been received. Instead of CANCEL a BYE will be\n\t\t\t// sent now.\n\t\t\tsend_bye();\n\t\t} else if (end_after_2xx_invite) {\n\t\t\t// Or user cancelled the request already, but the 2XX\n\t\t\t// glared with CANCEL.\n\t\t\tlog_file->write_report(\"CANCEL / 2XX INVITE glare.\",\n\t\t\t\t\"t_dialog::state_w4invite_resp\");\n\t\t\tsend_bye();\n\t\t}\n\n\t\tbreak;\n\tcase R_3XX:\n\tcase R_4XX:\n\tcase R_5XX:\n\tcase R_6XX:\n\tdefault:\n\t\t// Stop cancel guard timer if it was running\n\t\tline->stop_timer(LTMR_CANCEL_GUARD, get_object_id());\n\t\n\t\t// Final response (failure) received.\n\t\t// Treat unknown response classes as failure.\n\t\t\n\t\t// Trigger call script\n\t\tscript_out_call_failed.exec_notify(r);\n\t\n\t\tui->cb_stop_call_notification(line->get_line_number());\n\t\tui->cb_call_failed(user_config, line->get_line_number(), r);\n\t\tline->call_hist_record.fail_call(r);\n\t\tremove_client_request(&req_out_invite);\n\t\tstate = DS_TERMINATED;\n\t\tbreak;\n\t}\n\n\t// Notify progress to the referror if this is a referred call\n\tif (is_referred_call) {\n\t\tget_phone()->notify_refer_progress(r, line->get_line_number());\n\t}\n}\n\nvoid t_dialog::state_w4invite_resp(t_line_timer timer) {\n\tswitch (timer) {\n\tcase LTMR_CANCEL_GUARD:\n\t\tlog_file->write_report(\"Timer LTMR_CANCEL_GUARD expired.\",\n\t\t\t\t\"t_dialog::state_w4invite_resp\", LOG_NORMAL, LOG_WARNING);\n\t\n\t\t// CANCEL has been responded to, but 487 on INVITE was never\n\t\t// received. Abort the INVITE transaction.\n\t\tif (req_out_invite) {\n\t\t\tt_tid _tid = req_out_invite->get_tid();\n\t\t\tif (_tid > 0) {\n\t\t\t\tevq_trans_mgr->push_abort_trans(_tid);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Ignore other timeouts\n\t\tbreak;\n\t}\n}\n\n// INVITE response sent. At least 1 provisional response (not 100 Trying)\n// received.\nvoid t_dialog::state_early(t_response *r, t_tuid tuid, t_tid tid) {\n\tif (r->hdr_cseq.method != INVITE) return;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\t// RFC 3262 4\n\t\t// Discard retransmissiona and out-of-sequence reliable\n\t\t// provisional responses.\n\t\tif (must_discard_100rel(r)) return;\n\n\t\t// fall thru\n\tcase R_2XX:\n\t\tcreate_route_set(r);\n\t\tcreate_remote_target(r);\n\t\tprocess_1xx_2xx_invite_resp(r);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// RFC 3262\n\t// Send PRACK if required\n\tsend_prack_if_required(r);\n\t\n\tt_call_script script_out_call_answered(user_config, \n\t\t\tt_call_script::TRIGGER_OUT_CALL_ANSWERED,\n\t\t\tline->get_line_number() + 1);\n\tt_call_script script_out_call_failed(user_config, \n\t\t\tt_call_script::TRIGGER_OUT_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\t// Provisional response received.\n\t\tline->ci_set_last_provisional_reason(r->reason);\n\t\tui->cb_provisional_resp_invite(line->get_line_number(), r);\n\n\t\tif (request_cancelled) {\n\t\t\tsend_cancel(true);\n\t\t}\n\n\t\tbreak;\n\tcase R_2XX:\n\t\t// Stop cancel guard timer if it was running\n\t\tline->stop_timer(LTMR_CANCEL_GUARD, get_object_id());\n\t\t\n\t\t// Success received.\n\t\tack_2xx_invite(r);\n\n\t\t// Check for REFER support\n\t\t// If the Allow header is not present then assume REFER\n\t\t// is supported.\n\t\tif (!r->hdr_allow.is_populated() ||\n\t\t    r->hdr_allow.contains_method(REFER))\n\t\t{\n\t\t\tline->ci_set_refer_supported(true);\n\t\t}\n\t\t\n\t\t// This was a response to a session refresh request (INVITE)\n\t\tprocess_session_refresh_response(r);\n\n\t\t// Trigger call script\n\t\tscript_out_call_answered.exec_notify(r);\n\n\t\tui->cb_call_answered(user_config, line->get_line_number(), r);\n\t\tline->call_hist_record.answer_call(r);\n\t\tstate = DS_CONFIRMED;\n\n\t\tif (request_cancelled) {\n\t\t\t// User indicated that the request should be cancelled,\n\t\t\t// but no response was received yet. A final response\n\t\t\t// has been received. Instead of CANCEL a BYE will be\n\t\t\t// sent now.\n\t\t\tsend_bye();\n\t\t} else if (end_after_2xx_invite) {\n\t\t\t// Or user cancelled the request already, but the 2XX\n\t\t\t// glared with CANCEL.\n\t\t\tlog_file->write_report(\"CANCEL / 2XX INVITE glare.\",\n\t\t\t\t\"t_dialog::state_w4invite_resp\");\n\t\t\tsend_bye();\n\t\t}\n\n\t\tbreak;\n\tcase R_3XX:\n\tcase R_4XX:\n\tcase R_5XX:\n\tcase R_6XX:\n\tdefault:\n\t\t// Stop cancel guard timer if it was running\n\t\tline->stop_timer(LTMR_CANCEL_GUARD, get_object_id());\n\t\t\n\t\t// Final response (failure) received.\n\t\t// Treat unknown response classes as failure.\n\n\t\t// Trigger call script\n\t\tscript_out_call_failed.exec_notify(r);\n\t\t\n\t\tui->cb_stop_call_notification(line->get_line_number());\n\t\tui->cb_call_failed(user_config, line->get_line_number(), r);\n\t\tline->call_hist_record.fail_call(r);\n\t\tremove_client_request(&req_out_invite);\n\t\tstate = DS_TERMINATED;\n\t\tbreak;\n\t}\n\n\t// Notify progress to the referror if this is a referred call\n\tif (is_referred_call) {\n\t\tget_phone()->notify_refer_progress(r, line->get_line_number());\n\t}\n}\n\nvoid t_dialog::state_early(t_line_timer timer) {\n\tswitch (timer) {\n\tcase LTMR_CANCEL_GUARD:\n\t\tlog_file->write_report(\"Timer LTMR_CANCEL_GUARD expired.\",\n\t\t\t\t\"t_dialog::state_early\", LOG_NORMAL, LOG_WARNING);\n\t\n\t\t// CANCEL has been responded to, but 487 on INVITE was never\n\t\t// received. Abort the INVITE transaction.\n\t\tif (req_out_invite) {\n\t\t\tt_tid _tid = req_out_invite->get_tid();\n\t\t\tif (_tid > 0) {\n\t\t\t\tevq_trans_mgr->push_abort_trans(_tid);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Ignore other timeouts\n\t\tbreak;\n\t}\n}\n\n// BYE sent. Waiting for response.\nvoid t_dialog::state_w4bye_resp(t_response *r, t_tuid tuid, t_tid tid) {\n\tif (r->hdr_cseq.method != BYE) return;\n\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\t// Provisional response received. Wait for final response.\n\t\tbreak;\n\tdefault:\n\t\t// All final responses terminate the dialog.\n\t\tremove_client_request(&req_out);\n\t\tif (!sub_refer) {\n\t\t\tstate = DS_TERMINATED;\n\t\t} else {\n\t\t\tstate = DS_CONFIRMED_SUB;\n\t\t\tif (sub_refer->get_role() == SR_SUBSCRIBER) {\n\t\t\t\t// End subscription\n\t\t\t\tsub_refer->unsubscribe();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_w4bye_resp(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\n\tswitch(r->method) {\n\tcase BYE:\n\t\tresp = r->create_response(R_200_OK);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\t// A BYE glare situation. Keep waiting for the BYE\n\t\t// response.\n\t\tbreak;\n\tdefault:\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n}\n\n// Confirmed dialog. Responses are for mid-dialog requests.\nvoid t_dialog::state_confirmed_resp(t_response *r, t_tuid tuid, t_tid tid) {\n\t// 1XX responses are not expected. If they are received\n\t// then simply ignore them.\n\tif (r->is_provisional()) return;\n\n\tswitch (r->hdr_cseq.method) {\n\tcase OPTIONS:\n\t\tui->cb_options_response(r);\n\t\tremove_client_request(&req_out);\n\t\tbreak;\n\tcase REFER:\n\t\tremove_client_request(&req_refer);\n\n\t\tif (refer_state != REFST_W4RESP) {\n\t\t\t// NOTIFY has already been received. No need to\n\t\t\t// process the REFER response anymore. Interesting\n\t\t\t// issue might be: what if NOTIFY has been received and\n\t\t\t// now a failure response comes in?\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!r->is_success()) {\n\t\t\t// REFER failed\n\t\t\trefer_state = REFST_NULL;\n\t\t\trefer_succeeded = false;\n\t\t\t\n\t\t\t// KLUDGE: only signal REFER failure in case of\n\t\t\t//         non-408/481 responses. These responses\n\t\t\t//         clear the line, so the upper layers should not\n\t\t\t//         take action on the failed refer.\n\t\t\tif (r->code != R_408_REQUEST_TIMEOUT ||\n\t    \t\t    r->code == R_481_TRANSACTION_NOT_EXIST)\n\t    \t\t{\n\t\t\t\tout_refer_req_failed = true;\n\t\t\t}\n\t\t\t\n\t\t\tui->cb_refer_failed(line->get_line_number(), r);\n\t\t\tbreak;\n\t\t}\n\n\t\trefer_state = REFST_W4NOTIFY;\n\t\tbreak;\n\tcase INFO:\n\t\tremove_client_request(&req_info);\n\t\t\n\t\tif (!dtmf_queue.empty()) {\n\t\t\tchar digit = dtmf_queue.front();\n\t\t\tdtmf_queue.pop();\n\t\t\tsend_dtmf(digit, false, true);\n\t\t}\n\t\t\n\t\tbreak;\n\tdefault:\n\t\t// The received response should match the pending request.\n\t\t// So this point should never be reached.\n\t\tassert(false);\n\t\tbreak;\n\t}\n\n\t// RFC 3261 12.2.1.2\n\t// If a mid-dialog request is timed out, or the call/transaction\n\t// does not exist anymore at the server, then terminate the\n\t// dialog.\n\tif (r->code == R_408_REQUEST_TIMEOUT ||\n\t    r->code == R_481_TRANSACTION_NOT_EXIST)\n\t{\n\t\tsend_bye();\n\t}\n}\n\nvoid t_dialog::state_w4re_invite_resp(t_response *r, t_tuid tuid, t_tid tid) {\n\tif (r->hdr_cseq.method != INVITE) return;\n\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\tif (r->code == R_100_TRYING) break;\n\n\t\t// RFC 3262 4\n\t\t// Discard retransmissiona and out-of-sequence reliable\n\t\t// provisional responses.\n\t\tif (must_discard_100rel(r)) return;\n\n\t\tif (state == DS_W4RE_INVITE_RESP2) {\n\t\t\t// RFC 3262\n\t\t\t// Discard retransmissions and out-of-order\n\t\t\t// reliable provisional responses.\n\t\t\tif (must_discard_100rel(r)) return;\n\t\t}\n\n\t\t// RFC 3262\n\t\t// Send PRACK if required\n\t\tsend_prack_if_required(r);\n\n\t\t// fall thru\n\tcase R_2XX:\n\t\t// Process SDP answer if answer is present and no\n\t\t// answer has been received yet.\n\t\tif (!session_re_invite->recvd_answer && r->body) {\n\t\t\tint warn_code;\n\t\t\tstring warn_text;\n\n\t\t\tif (r->body->get_type() != BODY_SDP) {\n\t\t\t\t// Only SDP bodies are supported\n\t\t\t\tui->cb_unsupported_content_type(\n\t\t\t\t\tline->get_line_number(), r);\n\t\t\t\trequest_cancelled = true;\n\t\t\t} else if (session_re_invite->\n\t\t\t\t   process_sdp_answer((t_sdp *)r->body,\n\t\t\t\t   \twarn_code, warn_text))\n\t\t\t{\n\t\t\t\tsession_re_invite->recvd_answer = true;\n\t\t\t} else {\n\t\t\t\t// SDP answer is not supported. Cancel\n\t\t\t\t// the INVITE.\n\t\t\t\trequest_cancelled = true;\n\t\t\t\tui->cb_sdp_answer_not_supported(\n\t\t\t\t\t\tline->get_line_number(), warn_text);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// This implementation always sends an offer in\n\t\t// INVITE. So an answer must be in a 2XX response\n\t\t// as PRACK is not supported.\n\t\tif (r->get_class() == R_2XX && !r->body) {\n\t\t\trequest_cancelled = true;\n\t\t\tui->cb_sdp_answer_missing(line->get_line_number());\n\t\t\tbreak;\n\t\t}\n\n\t\t// Refresh target URI and display name\n\t\tif (r->get_class() == R_2XX &&\n\t\t    r->hdr_contact.is_populated() &&\n\t    \t    r->hdr_contact.contact_list.size() > 0)\n\t\t{\n\t\t\tremote_target_uri = r->\n\t\t\t\thdr_contact.contact_list.front().uri;\n\t\t\tremote_target_display = r->\n\t\t\t\thdr_contact.contact_list.front().display;\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tswitch (r->get_class()) {\n\tcase R_1XX:\n\t\t// Provisional response received.\n\t\tstate = DS_W4RE_INVITE_RESP2;\n\n\t\t// Start re-INVITE guard timer (no RFC requirement)\n\t\tline->start_timer(LTMR_RE_INVITE_GUARD, get_object_id());\n\n\t\t// User indicated that the request should be cancelled.\n\t\t// Now that the first provional response has been received,\n\t\t// a CANCEL can be sent.\n\t\tif (request_cancelled) {\n\t\t\tsend_cancel(true);\n\t\t}\n\n\t\tbreak;\n\tcase R_2XX:\n\t\t// Success received.\n\t\tline->stop_timer(LTMR_RE_INVITE_GUARD, get_object_id());\n\n\t\tack_2xx_invite(r);\n\t\tui->cb_reinvite_success(line->get_line_number(), r);\n\t\tstate = DS_CONFIRMED;\n\n\t\t// This was a response to a session refresh request (re-INVITE)\n\t\tprocess_session_refresh_response(r);\n\n\t\tif (request_cancelled) {\n\t\t\t// User indicated that the request should be cancelled,\n\t\t\t// but no response was received yet. A final response\n\t\t\t// has been received. Instead of CANCEL a BYE will be\n\t\t\t// sent now.\n\t\t\tsend_bye();\n\t\t} else if (end_after_2xx_invite) {\n\t\t\t// Or user cancelled the request already, but the 2XX\n\t\t\t// glared with CANCEL.\n\t\t\tlog_file->write_report(\"CANCEL / 2XX INVITE glare.\",\n\t\t\t\t\"t_dialog::state_w4invite_resp\");\n\t\t\tsend_bye();\n\t\t} else {\n\t\t\t// Make the re-INIVTE session info the current info\n\t\t\tactivate_new_session();\n\t\t}\n\n\t\tbreak;\n\tcase R_3XX:\n\tcase R_4XX:\n\tcase R_5XX:\n\tcase R_6XX:\n\tdefault:\n\t\t// Final response (failure) received.\n\t\t// Treat unknown response classes as failure.\n\t\tline->stop_timer(LTMR_RE_INVITE_GUARD, get_object_id());\n\t\tui->cb_reinvite_failed(line->get_line_number(), r);\n\t\tremove_client_request(&req_out_invite);\n\n\t\t// RFC 3261 14.1\n\t\t// delete re-INVITE session info. Old session info\n\t\t// stays as re-INVITE failed.\n\t\tMEMMAN_DELETE(session_re_invite);\n\t\tdelete session_re_invite;\n\t\tsession_re_invite = NULL;\n\n\t\tstate = DS_CONFIRMED;\n\n\t\tswitch(reinvite_purpose) {\n\t\tcase REINVITE_HOLD:\n\t\t\t// A call hold may not fail for the user as\n\t\t\t// this cause problems with soundcard access and\n\t\t\t// showing line status in the GUI. Even though re-INVITE\n\t\t\t// failed, the RTP still stopped. So simply indicated\n\t\t\t// that the hold failed, such that a subsequent retrieve\n\t\t\t// can simply restart the RTP.\n\t\t\thold_failed = true;\n\t\t\tbreak;\n\t\tcase REINVITE_RETRIEVE:\n\t\t\tline->failed_retrieve();\n\t\t\tif (r->code != R_491_REQUEST_PENDING) {\n\t\t\t\tui->cb_retrieve_failed(line->get_line_number(), r);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase REINVITE_REFRESH:\n\t\t\tif (r->code != R_491_REQUEST_PENDING) {\n\t\t\t\tlog_file->write_report(\"Session refresh failed.\",\n\t\t\t\t\t\t\"t_dialog::state_w4re_invite_resp\",\n\t\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\n\t\t// RFC 3261 14.1\n\t\t// Start wait timer before retrying a re-INVITE after a\n\t\t// glare.\n\t\tif (r->code == R_491_REQUEST_PENDING) {\n\t\t\tline->start_timer(LTMR_GLARE_RETRY, get_object_id());\n\t\t}\n\n\t\t// RFC 3261 14.1\n\t\tif (r->code == R_408_REQUEST_TIMEOUT ||\n\t\t    r->code == R_481_TRANSACTION_NOT_EXIST)\n\t\t{\n\t\t\tsend_bye();\n\t\t}\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::state_w4re_invite_resp(t_line_timer timer) {\n\tswitch(timer) {\n\tcase LTMR_RE_INVITE_GUARD:\n\t\t// Abort the INVITE as the user cannot terminate\n\t\t// it in a normal way.\n\t\tif (req_out_invite) {\n\t\t\tt_tid _tid = req_out_invite->get_tid();\n\t\t\tif (_tid > 0) {\n\t\t\t\tevq_trans_mgr->push_abort_trans(_tid);\n\t\t\t}\n\t\t} else {\n\t\t\t// Consider this as if a 408 Timeout response has\n\t\t\t// been received. Terminate the dialog.\n\t\t\tsend_bye();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::activate_new_session(void) {\n\tif (session->equal_audio(*session_re_invite)) {\n\t\tlog_file->write_report(\"SDP in re-INVITE is a noop.\",\n\t\t\t\"t_dialog::activate_new_session\");\n\t\n\t\tMEMMAN_DELETE(session_re_invite);\n\t\tdelete session_re_invite;\n\t\tsession_re_invite = NULL;\n\t\treturn;\n\t}\n\t\n\tlog_file->write_report(\"Renew session as specified by SDP in re-INVITE.\",\n\t\t\"t_dialog::activate_new_session\");\n\n\t// Stop current session\n\tMEMMAN_DELETE(session);\n\tdelete session;\n\n\t// Create new session\n\tsession = session_re_invite;\n\tsession_re_invite = NULL;\n\tsession->start_rtp();\n}\n\nvoid t_dialog::process_1xx_2xx_invite_resp(t_response *r) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\t// Process SDP answer if answer is present and no\n\t// answer has been received yet.\n\tif (r->body) {\n\t\tint warn_code;\n\t\tstring warn_text;\n\n\t\tif (r->body->get_type() != BODY_SDP) {\n\t\t\t// Only SDP bodies are supported\n\t\t\tui->cb_unsupported_content_type(line->get_line_number(), r);\n\t\t\trequest_cancelled = true;\n\t\t} else if (!session->recvd_answer || \n\t\t           (user_config->get_allow_sdp_change() && \n\t\t            ((t_sdp *)r->body)->origin.session_version !=\n\t\t            session->dst_sdp_version))\n\t\t{\n\t\t\t// Only process SDP if no SDP was received yet (RFC 3261\n\t\t\t// 13.3.1. Or process SDP if overridden by the\n\t\t\t// allow_sdp_change setting in the user profile.\n\t\t\t// A changed SDP must have a new version number (RFC 3264)\n\t\t\tif (session->process_sdp_answer((t_sdp *)r->body,\n\t\t\t\t\twarn_code, warn_text))\n\t\t\t{\n\t\t\t\t// If this is a changed SDP, then stop the\n\t\t\t\t// current RTP stream based on the previous SDP.\n\t\t\t\tif (session->recvd_answer) session->stop_rtp();\n\t\t\t\t\n\t\t\t\tsession->recvd_answer = true;\n\t\n\t\t\t\t// The following code part handles the ugly interaction\n\t\t\t\t// between forking and early media (Vonage uses this).\n\t\t\t\t// In case of forking 1xx responses with SDP may com\n\t\t\t\t// from different destinations. Only the first 1xx will\n\t\t\t\t// create a media stream. Media streams on other legs cannot\n\t\t\t\t// be created as that would give sound conflicts.\n\t\t\t\t// When a 2xx response with SDP is received, an early media\n\t\t\t\t// stream on another leg must be killed.\n\t\t\t\t// Due to forking multiple 2xx repsonses from different\n\t\t\t\t// destinations may be received. Only the first 2xx response\n\t\t\t\t// will create a media session. The other dialogs receiving\n\t\t\t\t// a 2xx will be released immediately anyway (see line.cpp).\n\t\t\t\tbool start_media = true;\n\t\t\t\tt_dialog *d = line->get_dialog_with_active_session();\n\t\t\t\tif (d != NULL) {\n\t\t\t\t\tif (r->get_class() == R_2XX &&\n\t\t\t\t\t    d->get_state() != DS_CONFIRMED)\n\t\t\t\t\t{\n\t\t\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\t\t\"t_dialog::process_1xx_2xx_invite_resp\");\n\t\t\t\t\t\tlog_file->write_raw(\n\t\t\t\t\t\t\t\"Kill early media on another dialog, id=\");\n\t\t\t\t\t\tlog_file->write_raw(d->get_object_id());\n\t\t\t\t\t\tlog_file->write_endl();\n\t\t\t\t\t\tlog_file->write_footer();\n\t\t\t\t\t\t\n\t\t\t\t\t\td->kill_rtp();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\t\t\"t_dialog::process_1xx_2xx_invite_resp\");\n\t\t\t\t\t\tlog_file->write_raw(\n\t\t\t\t\t\t\t\"Cannot start media as another dialog (id=\");\n\t\t\t\t\t\tlog_file->write_raw(d->get_object_id());\n\t\t\t\t\t\tlog_file->write_raw(\") already has media.\\n\");\n\t\t\t\t\t\tlog_file->write_footer();\n\t\t\t\t\t\n\t\t\t\t\t\tstart_media = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (start_media) {\n\t\t\t\t\tif (r->is_provisional()) {\n\t\t\t\t\t\tlog_file->write_report(\"Starting early media.\",\n\t\t\t\t\t\t\t\"t_dialog::process_1xx_2xx_invite_resp\");\n\t\t\t\t\t}\n\t\t\n\t\t\t\t\t// Stop locally played tones to free the soundcard\n\t\t\t\t\t// for the voice stream\n\t\t\t\t\tui->cb_stop_call_notification(line->get_line_number());\n\t\t\n\t\t\t\t\tsession->start_rtp();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// SDP answer is not supported. Cancel\n\t\t\t\t// the INVITE.\n\t\t\t\trequest_cancelled = true;\n\t\t\t\tui->cb_sdp_answer_not_supported(\n\t\t\t\t\t\tline->get_line_number(), warn_text);\n\t\t\t}\n\t\t}\n\t} else if (r->code == R_180_RINGING &&\n\t           !ringing_received && !session->recvd_answer)\n\t{\n\t\t// There is no SDP and far-end indicated that it is ringing\n\t\t// so generate ring back tone locally.\n\t\tui->cb_play_ringback(user_config);\n\t\tringing_received = true;\n\t}\n\n\t// This implementation always sends an offer in\n\t// INVITE. So an answer must be in a 2XX response if\n\t// no answer has been received in a provisional response.\n\tif (!session->recvd_answer && r->get_class() == R_2XX && !r->body) {\n\t\trequest_cancelled = true;\n\t\tui->cb_sdp_answer_missing(line->get_line_number());\n\t}\n\t\n\t// RFC 3261 13.3.1.4\n\t// A 2XX response to an INVITE should contain a Supported header\n\t// listing all supported extensions.\n\t// Set extensions supported by remote party\n\tif (r->get_class() == R_2XX && r->hdr_supported.is_populated()) {\n\t\tremote_extensions.insert(r->hdr_supported.features.begin(),\n\t\t\t\tr->hdr_supported.features.end());\n\t}\n}\n\nvoid t_dialog::process_session_refresh_request(t_request *req, t_response *resp,\n\t\tbool is_reinvite) {\n\tif (req->hdr_session_expires.is_populated()) {\n\t\t// If no refresher was specified by the UAC, then we must\n\t\t// choose one ourselves (RFC 4028 9)\n\t\tif (req->hdr_session_expires.refresher == t_hdr_session_expires::REFRESHER_NONE) {\n\t\t\t// If possible, let the UAC handle the refreshing,\n\t\t\t// since they will probably do a better job than us.\n\t\t\t// We put this value back into the request headers, so\n\t\t\t// that process_session_refresh() can act on it.\n\t\t\treq->hdr_session_expires.refresher =\n\t\t\t\treq->hdr_supported.contains(EXT_TIMER) ?\n\t\t\t\t\tt_hdr_session_expires::REFRESHER_UAC :\n\t\t\t\t\tt_hdr_session_expires::REFRESHER_UAS;\n\t\t}\n\t}\n\n\t// Remember the largest value among all Min-SE headers received within\n\t// this dialog (i.e. excluding the INVITE request) (RFC 4028 7.4)\n\tif (req->hdr_min_se.is_populated() && is_reinvite) {\n\t\tif (req->hdr_min_se.time > session_min_se) {\n\t\t\tsession_min_se = req->hdr_min_se.time;\n\t\t}\n\t}\n\n\tprocess_session_refresh(req);\n\tset_session_expires_headers(resp);\n\n\tif (session_interval) {\n\t\t// Add a Require header if necessary/suggested (RFC 4028 9)\n\t\tif (is_session_refresher) {\n\t\t\t// Refresher is UAS, so require 'timer' if possible\n\t\t       if (req->hdr_supported.contains(EXT_TIMER)) {\n\t\t\t\tresp->hdr_require.add_feature(EXT_TIMER);\n\t\t       }\n\t\t} else {\n\t\t\t// Refresher is UAC, so 'timer' is always required\n\t\t\tresp->hdr_require.add_feature(EXT_TIMER);\n\t\t}\n\t}\n}\n\nvoid t_dialog::process_session_refresh_response(t_response *resp) {\n\tprocess_session_refresh(resp);\n}\n\nvoid t_dialog::process_session_refresh(t_sip_message *r) {\n\t// stop timers\n\tline->stop_timer(LTMR_SESSION_REFRESH, get_object_id());\n\tline->stop_timer(LTMR_SESSION_EXPIRE, get_object_id());\n\n\t// Copy the Session-Expires value as-is (RFC 4028 7.2, 9)\n\t// (If the header is missing, then session expiration is turned off.)\n\tsession_interval = r->hdr_session_expires.time;\n\n\tif (session_interval) {\n\t\t// Figure out whether the designated refresher is actually us\n\t\tt_hdr_session_expires::t_refresher our_role =\n\t\t\t(r->get_type() == MSG_REQUEST) ?\n\t\t\t\tt_hdr_session_expires::REFRESHER_UAS :\n\t\t\t\tt_hdr_session_expires::REFRESHER_UAC;\n\t\tis_session_refresher = (r->hdr_session_expires.refresher == our_role);\n\n\t\t// Prevent a rogue peer from tricking us into refreshing too\n\t\t// fast.  (RFC 4028 s. 9 forbids us from raising this value,\n\t\t// but such a value was already illegal to beging with.)\n\t\tif (is_session_refresher && (session_interval < MIN_SESSION_INTERVAL)) {\n\t\t\tlog_file->write_header(\"t_dialog::process_session_refresh\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Session-Expires value of \");\n\t\t\tlog_file->write_raw(session_interval);\n\t\t\tlog_file->write_raw(\" seconds is below minimum value of \");\n\t\t\tlog_file->write_raw(MIN_SESSION_INTERVAL);\n\t\t\tlog_file->write_raw(\" seconds.\\n\");\n\t\t\tlog_file->write_footer();\n\n\t\t\tsession_interval = MIN_SESSION_INTERVAL;\n\t\t}\n\n\t\tlog_file->write_header(\"t_dialog::process_session_refresh\",\n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Session refresh interval set to \");\n\t\tlog_file->write_raw(session_interval);\n\t\tlog_file->write_raw(\" seconds.\\n\");\n\n\t\t// Start timers\n\t\tline->start_timer(LTMR_SESSION_EXPIRE, get_object_id());\n\t\tif (is_session_refresher) {\n\t\t\tlog_file->write_raw(\"Refreshes will be performed by us.\\n\");\n\t\t\tline->start_timer(LTMR_SESSION_REFRESH, get_object_id());\n\t\t}\n\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_dialog::set_session_expires_headers(t_sip_message *r) {\n\tif (session_interval) {\n\t\t// Figure our whether the refresher is the UAC or UAS\n\t\tbool is_refresher_uac = (r->get_type() == MSG_REQUEST) ?\n\t\t\tis_session_refresher : !is_session_refresher;\n\n\t\tr->hdr_session_expires.set_time(session_interval);\n\t\tr->hdr_session_expires.set_refresher(\n\t\t\tis_refresher_uac ?\n\t\t\t\tt_hdr_session_expires::REFRESHER_UAC :\n\t\t\t\tt_hdr_session_expires::REFRESHER_UAS\n\t\t\t);\n\t\t// Only set Min-SE on request, not 2xx (RFC 4028 5)\n\t\tif (session_min_se && (r->get_type() == MSG_REQUEST)) {\n\t\t\tr->hdr_min_se.set_time(session_min_se);\n\t\t}\n\t}\n}\n\nvoid t_dialog::ack_2xx_invite(t_response *r) {\n\tt_ip_port ip_port;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (ack) {\n\t\t// delete previous cached ACK\n\t\tMEMMAN_DELETE(ack);\n\t\tdelete ack;\n\t}\n\tack = create_request(ACK);\n\tack->get_destination(ip_port, *user_config);\n\n\t// If for some strange reason the destination could\n\t// not be computed then wait for a retransmission of\n\t// 2XX.\n\tif (ip_port.ipaddr != 0 && ip_port.port != 0) {\n\t\tevq_sender->push_network(ack, ip_port);\n\t} else {\n\t\tlog_file->write_header(\"t_dialog::ack_2xx_invite\", LOG_SIP, LOG_CRITICAL);\n\t\tlog_file->write_raw(\"Cannot determine destination IP address for ACK.\\n\\n\");\n\t\tlog_file->write_raw(ack->encode());\n\t\tlog_file->write_footer();\n\t}\n\n\tremove_client_request(&req_out_invite);\n}\n\nvoid t_dialog::send_prack_if_required(t_response *r) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\t// RFC 3262\n\t// Send PRACK if needed\n\tif (r->get_class() == R_1XX && r->code != R_100_TRYING) {\n\t\t// RFC 3262 4\n\t\t// Send PRACK if the 1xx response is sent reliable and 100rel\n\t\t// is enabled.\n\t\tif (r->hdr_to.tag.size() > 0 &&\n\t\t    r->hdr_require.contains(EXT_100REL) &&\n\t\t    r->hdr_rseq.is_populated() &&\n\t\t    remote_target_uri.is_valid() &&\n\t\t    user_config->get_ext_100rel() != EXT_DISABLED)\n\t\t{\n\t\t\tt_request *prack = create_request(PRACK);\n\t\t\tprack->hdr_rack.set_method(r->hdr_cseq.method);\n\t\t\tprack->hdr_rack.set_cseq_nr(r->hdr_cseq.seqnr);\n\t\t\tprack->hdr_rack.set_resp_nr(r->hdr_rseq.resp_nr);\n\n\t\t\t// Delete previous PRACK request if it is still pending\n\t\t\tif (req_prack) {\n\t\t\t\tlog_file->write_report(\"Previous PRACK still pending.\",\n\t\t\t\t\t\"t_dialog::send_prack_if_needed\");\n\t\t\t\tremove_client_request(&req_prack);\n\t\t\t}\n\n\t\t\treq_prack = new t_client_request(user_config, prack, 0);\n\t\t\tMEMMAN_NEW(req_prack);\n\t\t\tline->send_request(prack, req_prack->get_tuid());\n\t\t\tMEMMAN_DELETE(prack);\n\t\t\tdelete prack;\n\t\t}\n\t}\n}\n\nbool t_dialog::must_discard_100rel(t_response *r) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\t// RFC 3262 4\n\t// Discard retransmissiona and out-of-sequence reliable\n\t// provisional responses.\n\tif (r->code > R_100_TRYING && r->hdr_to.tag.size() > 0 &&\n\t    r->hdr_require.contains(EXT_100REL) &&\n\t    r->hdr_rseq.is_populated() &&\n\t    user_config->get_ext_100rel() != EXT_DISABLED)\n\t{\n\t\tif (remote_resp_nr == 0) {\n\t\t\t// This is the first response with a repsonse nr.\n\t\t\t// Initialize the remote response nr\n\t\t\tremote_resp_nr = r->hdr_rseq.resp_nr;\n\t\t\treturn false;\n\t\t}\n\t\n\t\tif (r->hdr_rseq.resp_nr <= remote_resp_nr) {\n\t\t\t// This is a retransmission.\n\t\t\t// PRACK has already been sent. The transaction\n\t\t\t// layer takes care of retransmitting PRACK\n\t\t\t// if PRACK got lost.\n\t\t\tlog_file->write_report(\"Discard 1xx retransmission.\",\n\t\t\t\t\"t_dialog::must_discard_100rel\");\n\t\t\treturn true;\n\t\t}\n\n\t\tif (r->hdr_rseq.resp_nr != remote_resp_nr + 1) {\n\t\t\t// A provisional response has been lost.\n\t\t\t// Discard this response and wait for a retransmission\n\t\t\t// of the lost response.\n\t\t\tlog_file->write_report(\"Discard out-of-order 1xx\",\n\t\t\t\t\"t_dialog::must_discard_100rel\");\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tremote_resp_nr = r->hdr_rseq.resp_nr;\n\treturn false;\n}\n\nbool t_dialog::respond_prack(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\n\t// RFC 3262 3\n\tif (resp_1xx_invite &&\n\t    r->hdr_rack.method == resp_1xx_invite->hdr_cseq.method &&\n\t    r->hdr_rack.cseq_nr == resp_1xx_invite->hdr_cseq.seqnr &&\n\t    r->hdr_rack.resp_nr == resp_1xx_invite->hdr_rseq.resp_nr)\n\t{\n\t\t// The provisional response has been delivered now.\n\t\tline->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\t\tline->stop_timer(LTMR_100REL_GUARD, get_object_id());\n\t\tMEMMAN_DELETE(resp_1xx_invite);\n\t\tdelete resp_1xx_invite;\n\t\tresp_1xx_invite = NULL;\n\n\t\t// Send 200 on the PRACK request\n\t\tresp = r->create_response(R_200_OK);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\treturn true;\n\t} else {\n\t\t// PRACK does not match pending 1xx response\n\t\t// Send a 481 on the PRACK request\n\t\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\treturn false;\n\t}\n}\n\nvoid t_dialog::send_request(t_request *r, t_tuid tuid) {\n\tline->send_request(r, tuid);\n}\n\n////////////\n// Public\n////////////\n\nt_dialog::t_dialog(t_line *_line) :\n\tt_abstract_dialog(_line->get_phone_user()),\n\tsession_interval(0),\n\tsession_min_se(0),\n\tis_session_refresher(false)\n{\n\tline = _line;\n\t\n\treq_out = NULL;\n\treq_out_invite = NULL;\n\treq_in_invite = NULL;\n\treq_cancel = NULL;\n\treq_prack = NULL;\n\treq_refer = NULL;\n\treq_info = NULL;\n\treq_stun = NULL;\n\n\trequest_cancelled = false;\n\tend_after_ack = false;\n\tend_after_ack_reason = \"\";\n\tend_after_2xx_invite = false;\n\tanswer_after_prack = false;\n\tringing_received = false;\n\t\n\tresp_invite = NULL;\n\tresp_1xx_invite = NULL;\n\tack = NULL;\n\n\tstate = DS_NULL;\n\n\t// Timers\n\tdur_ack_timeout = 0;\n\tid_ack_timeout = 0;\n\tid_ack_guard = 0;\n\tid_re_invite_guard = 0;\n\tid_glare_retry = 0;\n\tid_cancel_guard = 0;\n\n\t// RFC 3262\n\t// Timers\n\tdur_100rel_timeout = 0;\n\tid_100rel_timeout = 0;\n\tid_100rel_guard = 0;\n\t\n\tt_user *user_config = phone_user->get_user_profile();\n\n\t// Create session\n\tsession = new t_session(this, USER_HOST(user_config, AUTO_IP4_ADDRESS), line->get_rtp_port());\n\tMEMMAN_NEW(session);\n\tsession_re_invite = NULL;\n\n\t// Subscription\n\tsub_refer = NULL;\n\tis_referred_call = false;\n\trefer_state = REFST_NULL;\n\trefer_accepted = false;\n\trefer_succeeded = false;\n\tout_refer_req_failed = false;\n}\n\nt_dialog::~t_dialog() {\n\tif (req_out) remove_client_request(&req_out);\n\tif (req_out_invite) remove_client_request(&req_out_invite);\n\tif (req_in_invite) remove_client_request(&req_in_invite);\n\tif (req_cancel) remove_client_request(&req_cancel);\n\tif (req_prack) remove_client_request(&req_prack);\n\tif (req_refer) remove_client_request(&req_refer);\n\tif (req_info) remove_client_request(&req_info);\n\tif (req_stun) remove_client_request(&req_stun);\n\tif (resp_invite) { MEMMAN_DELETE(resp_invite); delete resp_invite; }\n\tif (resp_1xx_invite) {\n\t\tMEMMAN_DELETE(resp_1xx_invite);\n\t\tdelete resp_1xx_invite;\n\t}\n\tif (ack) { MEMMAN_DELETE(ack); delete ack; }\n\tif (session) { MEMMAN_DELETE(session); delete session; }\n\tif (session_re_invite) {\n\t\tMEMMAN_DELETE(session_re_invite);\n\t\tdelete session_re_invite;\n\t}\n\tif (sub_refer) { MEMMAN_DELETE(sub_refer); delete sub_refer; }\n\t\n\tfor (list<t_client_request *>::iterator i = inc_req_queue.begin();\n\t     i != inc_req_queue.end(); i++)\n\t{\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n}\n\n// Copy will only be used on the open dialog.\nt_dialog *t_dialog::copy(void) {\n\tt_dialog *d = new t_dialog(*this);\n\tMEMMAN_NEW(d);\n\n\td->generate_new_id();\n\n\t// Increment reference count on client request\n\tif (req_out) d->req_out->inc_ref_count();\n\tif (req_out_invite) d->req_out_invite->inc_ref_count();\n\tif (req_in_invite) d->req_in_invite->inc_ref_count();\n\tif (req_prack) d->req_prack->inc_ref_count();\n\tif (req_refer) d->req_refer->inc_ref_count();\n\tif (req_stun) d->req_stun->inc_ref_count();\n\n\t// The open dialog will handle the CANCEL, so delete it\n\t// from the copy.\n\tif (req_cancel) d->req_cancel = NULL;\n\n\tif (resp_invite) d->resp_invite = (t_response *)resp_invite->copy();\n\tif (resp_1xx_invite) d->resp_1xx_invite = (t_response *)resp_1xx_invite->copy();\n\tif (ack) d->ack = (t_request *)ack->copy();\n\tdur_ack_timeout = 0;\n\tid_ack_timeout = 0;\n\tid_ack_guard = 0;\n\tdur_100rel_timeout = 0;\n\tid_100rel_timeout = 0;\n\tid_100rel_guard = 0;\n\n\tif (session) {\n\t\td->session = new t_session(*session);\n\t\tMEMMAN_NEW(d->session);\n\t\td->session->set_owner(d);\n\n\t\t// If an audio session was already created for early media\n\t\t// then the audio session will be moved to the copy of the\n\t\t// dialog. Only 1 dialog can have an audio session.\n\t\t// See process_1xx_2xx_invite_resp for more information on\n\t\t// early media problems.\n\t\t// Clear a possible audio session in the open dialog.\n\t\tt_audio_session *as = session->get_audio_session();\n\t\tif (as) {\n\t\t\tas->set_session(d->session);\n\t\t\tsession->set_audio_session(NULL);\n\t\t\tlog_file->write_report(\n\t\t\t\t\"An audio session was created on an open dialog.\",\n\t\t\t\t\"t_dialog::copy\",\n\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t}\n\t}\n\t\n\tlog_file->write_header(\"t_dialog::copy\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Created dialog through copy, id=\");\n\tlog_file->write_raw(d->get_object_id());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\n\treturn d;\n}\n\nvoid t_dialog::send_invite(const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, const t_hdr_referred_by &hdr_referred_by,\n\t\tconst t_hdr_replaces &hdr_replaces, \n\t\tconst t_hdr_require &hdr_require, \n\t\tconst t_hdr_request_disposition &hdr_request_disposition,\n\t\tbool anonymous)\n{\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (state != DS_NULL) {\n\t\tthrow X_DIALOG_ALREADY_ESTABLISHED;\n\t}\n\n\t// If STUN is enabled, then first send a STUN binding request to\n\t// discover the IP adderss and port for media.\n\tif (phone->use_stun(user_config)) {\n\t\tif (!stun_bind_media()) {\n\t\t\tui->cb_stun_failed_call_ended(line->get_line_number());\n\t\t\tstate = DS_TERMINATED;\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\tt_request invite(INVITE);\n\t\n\t// RFC 3261 12.2.1.1\n\t// Request URI and Route header\n\tinvite.set_route(to_uri, phone_user->get_service_route());\n\n\t// Set Call-ID header\n\tcall_id = NEW_CALL_ID(user_config);\n\tinvite.hdr_call_id.set_call_id(call_id);\n\tcall_id_owner = true;\n\n\t// Set To header\n\tinvite.hdr_to.set_uri(to_uri);\n\tinvite.hdr_to.set_display(to_display);\n\n\t// Set From header\n\tlocal_tag = NEW_TAG;\n\tlocal_uri.set_url(line->create_user_uri());\n\tlocal_display = user_config->get_display(anonymous);\n\tinvite.hdr_from.set_uri(local_uri);\n\tinvite.hdr_from.set_display(local_display);\n\tinvite.hdr_from.set_tag(local_tag);\n\t\n\t// Privacy header\n\tif (line->get_hide_user()) {\n\t\tinvite.hdr_privacy.add_privacy(PRIVACY_ID);\n\t}\n\t\n\t// Set P-Preferred-Identity header\n\tif (anonymous && user_config->get_send_p_preferred_id()) {\n\t\tt_identity identity;\n\t\tidentity.set_uri(user_config->create_user_uri(false));\n\t\tidentity.set_display(user_config->get_display(false));\n\t\tinvite.hdr_p_preferred_identity.add_identity(identity);\n\t}\n\t// Set P-Asserted-Identity header\n\tif (anonymous && user_config->get_send_p_asserted_id()) {\n\t\tt_identity identity;\n\t\tidentity.set_uri(user_config->create_user_uri(false));\n\t\tidentity.set_display(user_config->get_display(false));\n\t\tinvite.hdr_p_asserted_identity.add_identity(identity);\n\t}\n\n\t// Set CSeq header\n\tlocal_seqnr = rand() % 1000 + 1;\n\tinvite.hdr_cseq.set_method(INVITE);\n\tinvite.hdr_cseq.set_seqnr(local_seqnr);\n\n\t// Set Max-Forwards header\n\tinvite.hdr_max_forwards.set_max_forwards(MAX_FORWARDS);\n\n\t// User-Agent\n\tSET_HDR_USER_AGENT(invite.hdr_user_agent);\n\n\t// RFC 3261 13.2.1\n\t// Allow and Supported headers\n\tSET_HDR_ALLOW(invite.hdr_allow, user_config);\n\tSET_HDR_SUPPORTED(invite.hdr_supported, user_config);\n\n\t// Extensions specific for INVITE\n\tif (user_config->get_ext_100rel() != EXT_DISABLED) {\n\t\tinvite.hdr_supported.add_feature(EXT_100REL);\n\t}\n\n\t// Require header\n\tswitch (user_config->get_ext_100rel()) {\n\tcase EXT_PREFERRED:\n\tcase EXT_REQUIRED:\n\t\tinvite.hdr_require.add_feature(EXT_100REL);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// Subject header\n\tif (subject != \"\") {\n\t\tinvite.hdr_subject.set_subject(subject);\n\t}\n\n\t// Organization\n\tif (!anonymous) {\n\t\tSET_HDR_ORGANIZATION(invite.hdr_organization, user_config);\n\t}\n\n\t// RFC 3892 Referred-By header if a call is initated because\n\t// of an incoming REFER.\n\tinvite.hdr_referred_by = hdr_referred_by;\n\t\n\t// RFC 3891 Replaces header\n\tinvite.hdr_replaces = hdr_replaces;\n\t\n\t// Add required extension passed by the upper layer\n\tif (hdr_require.is_populated()) {\n\t\tinvite.hdr_require.add_features(hdr_require.features);\n\t}\n\t\n\t// RFC 3841 Request-Disposition header\n\tinvite.hdr_request_disposition = hdr_request_disposition;\n\t\n\t// Calculate destinations\n\t// See create_request() for more comments\n\tinvite.calc_destinations(*user_config);\n\t\n        // The Contatc, Via header and SDP can only be created after the destinations\n        // are calculated, because the destination deterimines which\n        // local IP address should be used.\n        \n\t// Create SDP offer\n\tsession->create_sdp_offer(&invite, SDP_O_USER);\n\t\n\t// Set Via header\n\tIPaddr local_ip = invite.get_local_ip();\n\tt_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));\n\tinvite.hdr_via.add_via(via);\n\t\n\t// Set Contact header\n\tt_contact_param contact;\n\tcontact.uri.set_url(line->create_user_contact(h_ip2str(local_ip)));\n\tinvite.hdr_contact.add_contact(contact);\n\n\t// Pre-trim the destination list to match what the transaction manager would\n\t// compute from the event copy.  calc_destinations() always adds both TCP and\n\t// UDP entries for SIP_TRANS_AUTO, but the transaction manager removes TCP for\n\t// messages small enough to fit in UDP.  If we skip this step the copy stored\n\t// in req_out_invite retains [tcp, udp], so next_destination() succeeds on a\n\t// 503 and failover_invite() re-sends the INVITE to the same destination\n\t// (RFC 3263 failover is only meaningful when there is a genuinely different\n\t// next hop).\n\t{\n\t\tt_ip_port discard;\n\t\tinvite.get_destination(discard, *user_config);\n\t}\n\n\t// Send INVITE\n\treq_out_invite = new t_client_request(user_config, &invite, 0);\n\tMEMMAN_NEW(req_out_invite);\n\t\n\t// Trigger call script\n\tt_call_script script(user_config, t_call_script::TRIGGER_OUT_CALL,\n\t\t\tline->get_line_number() + 1);\n\tscript.exec_notify(&invite);\n\t\n\tline->send_request(&invite, req_out_invite->get_tuid());\n\tline->call_hist_record.start_call(&invite, t_call_record::DIR_OUT, \n\t\tuser_config->get_profile_name());\n\n\tstate = DS_W4INVITE_RESP;\n}\n\nbool t_dialog::resend_invite_auth(t_response *resp) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tif (!req_out_invite) return false;\n\n\tassert(state == DS_W4INVITE_RESP || state == DS_W4INVITE_RESP2);\n\n\tt_request *req = req_out_invite->get_request();\n\n\t// Add authorization header, increment CSeq and create new branch id\n\tif (get_phone()->authorize(user_config, req, resp)) {\n\t\tresend_request(req_out_invite);\n\n\t\t// Reset state in case a 100 Trying was received\n\t\tstate = DS_W4INVITE_RESP;\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool t_dialog::resend_invite_unsupported(t_response *resp) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (!req_out_invite) return false;\n\tif (resp->code != R_420_BAD_EXTENSION) return false;\n\tif (!resp->hdr_unsupported.is_populated()) return false;\n\tif (resp->hdr_unsupported.features.empty()) return false;\n\n\tt_request *req = req_out_invite->get_request();\n\n\t// If no extensions were required then return.\n\tif (!req->hdr_require.is_populated()) return false;\n\tif (req->hdr_require.features.empty()) return false;\n\t\n\tbool removed_ext = false;\n\n\tfor (list<string>::iterator i = resp->hdr_unsupported.features.begin();\n\t     i != resp->hdr_unsupported.features.end(); i++)\n\t{\n\t\tif (req->hdr_require.contains(*i)) {\n\t\t\tif (*i == EXT_100REL) {\n\t\t\t\tif (user_config->get_ext_100rel() == EXT_PREFERRED) {\n\t\t\t\t\treq->hdr_require.del_feature(*i);\n\t\t\t\t} else {\n\t\t\t\t\t// The 100rel is required.\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// There is no specific requirement for\n\t\t\t\t// this extension so do not remove it.\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tremoved_ext = true;\n\t\t}\n\t}\n\n\t// Return if none of the unsupported extensions was required.\n\tif (!removed_ext) return false;\n\n\tif (req->hdr_require.features.empty()) {\n\t\t// There are no required features anymore\n\t\treq->hdr_require.unpopulate();\n\t}\n\n\tresend_request(req_out_invite);\n\n\t// Reset state in case a 100 Trying was received\n\tstate = DS_W4INVITE_RESP;\n\n\treturn true;\n}\n\nbool t_dialog::redirect_invite(t_response *resp) {\n\tt_contact_param contact;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (!req_out_invite) return false;\n\n\t// If the response is a 3XX response then add redirection contacts\n\tif (resp->get_class() == R_3XX  && resp->hdr_contact.is_populated()) {\n\t\treq_out_invite->redirector.add_contacts(\n\t\t\t\t\tresp->hdr_contact.contact_list);\n\t}\n\n\t// Get next destination\n\tif (!req_out_invite->redirector.get_next_contact(contact)) {\n\t\t// There is no next destination\n\t\treturn false;\n\t}\n\n\tassert(state == DS_W4INVITE_RESP || state == DS_W4INVITE_RESP2);\n\n\tt_request *req = req_out_invite->get_request();\n\n\t// Ask user for permission to redirect if indicated by user config\n\tif (user_config->get_ask_user_to_redirect()) {\n\t\tif(!ui->cb_ask_user_to_redirect_invite(user_config,\n\t\t\t\tcontact.uri, contact.display)) \n\t\t{\n\t\t\t// User did not permit to redirect\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Change the request URI to the new URI.\n\t// As the URI changes the destination set must be recalculated\n\treq->uri = contact.uri;\n\treq->calc_destinations(*user_config);\n\n\tui->cb_redirecting_request(user_config, line->get_line_number(), contact);\n\tresend_request(req_out_invite);\n\n\t// Reset state in case a 100 Trying was received\n\tstate = DS_W4INVITE_RESP;\n\n\treturn true;\n}\n\nbool t_dialog::failover_invite(void) {\n\tif (!req_out_invite) return false;\n\t\n\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\t\"t_dialog::failover_invite\");\n\t\n\tt_request *req = req_out_invite->get_request();\n\t\n\t// Get next destination\n\tif (!req->next_destination()) {\n\t\tlog_file->write_report(\"No next destination for failover.\",\n\t\t\t\t\"t_dialog::failover_invite\");\n\t\treturn false;\n\t}\n\t\n\tassert(state == DS_W4INVITE_RESP || state == DS_W4INVITE_RESP2);\n\tresend_request(req_out_invite);\n\n\t// Reset state in case a 100 Trying was received\n\tstate = DS_W4INVITE_RESP;\n\n\treturn true;\n}\n\nvoid t_dialog::send_bye(void) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tswitch (state) {\n\tcase DS_W4INVITE_RESP2:\n\tcase DS_EARLY:\n\tcase DS_CONFIRMED:\n\t\tbreak;\n\tcase DS_W4RE_INVITE_RESP:\n\tcase DS_W4RE_INVITE_RESP2:\n\t\t// send BYE after completion of re-INVITE\n\t\trequest_cancelled = true;\n\t\treturn;\n\tcase DS_W4ACK:\n\tcase DS_W4ACK_RE_INVITE:\n\t\t// send BYE after completion of re-INVITE\n\t\trequest_cancelled = true;\n\t\treturn;\n\tcase DS_TERMINATED:\n\t\t// Dialog has already been terminated. Do not send BYE.\n\t\treturn;\n\tdefault:\n\t\tlog_file->write_header(\"t_dialog::failover_invite\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Cannot send BYE on dialog in state \");\n\t\tlog_file->write_raw(state);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\n\t// If a previous request is still pending then remove it.\n\tif (req_out) { MEMMAN_DELETE(req_out); delete (req_out); }\n\n\tt_request *bye = create_request(BYE);\n\treq_out = new t_client_request(user_config, bye, 0);\n\tMEMMAN_NEW(req_out);\n\t\n\t// Trigger call script\n\tt_call_script script(user_config, t_call_script::TRIGGER_LOCAL_RELEASE,\n\t\t\tline->get_line_number() + 1);\n\tscript.exec_notify(bye);\n\t\n\tline->send_request(bye, req_out->get_tuid());\n\tline->call_hist_record.end_call(false);\t\n\tMEMMAN_DELETE(bye);\n\tdelete bye;\n\n\tstate = DS_W4BYE_RESP;\n\tui->cb_call_ended(line->get_line_number());\n}\n\nvoid t_dialog::send_options(void) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\t// Request can only be sent in a confirmed dialog.\n\tif (state != DS_CONFIRMED) return;\n\n\t// If a previous request is still pending then remove it.\n\tif (req_out) { MEMMAN_DELETE(req_out); delete (req_out); }\n\n\tt_request *r = create_request(OPTIONS);\n\n\t// Accept\n\tr->hdr_accept.add_media(t_media(\"application\",\"sdp\"));\n\treq_out = new t_client_request(user_config, r, 0);\n\tMEMMAN_NEW(req_out);\n\tline->send_request(r, req_out->get_tuid());\n\tMEMMAN_DELETE(r);\n\tdelete r;\n}\n\nvoid t_dialog::send_cancel(bool early_dialog_exists) {\n\tt_request *cancel;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tswitch (state) {\n\tcase DS_W4INVITE_RESP:\n\tcase DS_W4RE_INVITE_RESP:\n\t\tif (!early_dialog_exists) {\n\t\t\t// wait for first response then send CANCEL or BYE\n\t\t\trequest_cancelled = true;\n\t\t\tbreak;\n\t\t}\n\t\t// Fall through\n\tcase DS_W4INVITE_RESP2:\n\tcase DS_W4RE_INVITE_RESP2:\n\tcase DS_EARLY:\n\t\tif (req_cancel) {\n\t\t\t// CANCEL has been sent already\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tcancel = create_request(CANCEL);\n\t\treq_cancel = new t_client_request(user_config, cancel, 0);\n\t\tMEMMAN_NEW(req_cancel);\n\t\tline->send_request(cancel, req_cancel->get_tuid());\n\t\tMEMMAN_DELETE(cancel);\n\t\tdelete cancel;\n\t\t\n\t\t// Make sure dialog is terminated if CANCEL glares with\n\t\t// 2XX on INVITE.\n\t\tset_end_after_2xx_invite(true);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tui->cb_call_ended(line->get_line_number());\n}\n\nvoid t_dialog::set_end_after_2xx_invite(bool on) {\n\tend_after_2xx_invite = on;\n}\n\nvoid t_dialog::send_re_invite(void) {\n\tassert(session_re_invite);\n\tt_user *user_config = phone_user->get_user_profile();\n\n\t// Request can only be sent in a confirmed dialog.\n\tif (state != DS_CONFIRMED) return;\n\n\t// Do nothing if a re-INVITE is already in progress\n\tif (req_out_invite) return;\n\n\tt_request *r = create_request(INVITE);\n\n\t// Set Contact header\n\t// INVITE must contain a contact header\n\tt_contact_param contact;\n\tcontact.uri.set_url(line->create_user_contact(h_ip2str(r->get_local_ip())));\n\tr->hdr_contact.add_contact(contact);\n\n\t// RFC 3261 13.2.1\n\t// Allow and Supported headers\n\tSET_HDR_ALLOW(r->hdr_allow, user_config);\n\tSET_HDR_SUPPORTED(r->hdr_supported, user_config);\n\n\t// Extensions specific for INVITE\n\tif (user_config->get_ext_100rel() != EXT_DISABLED) {\n\t\t// If some weird far end implementation wants to send\n\t\t// a reliable provisional then support it.\n\t\t// As a provisional response not needed for a re-INVITE,\n\t\t// do not require the 100rel.\n\t\tr->hdr_supported.add_feature(EXT_100REL);\n\t}\n\n\t// Create SDP offer\n\tsession_re_invite->create_sdp_offer(r, SDP_O_USER);\n\n\t// Send INVITE\n\treq_out_invite = new t_client_request(user_config, r, 0);\n\tMEMMAN_NEW(req_out_invite);\n\tline->send_request(r, req_out_invite->get_tuid());\n\tMEMMAN_DELETE(r);\n\tdelete r;\n\n\tstate = DS_W4RE_INVITE_RESP;\n}\n\nbool t_dialog::resend_request_auth(t_response *resp) {\n\tt_client_request **current_cr;\n\n\tswitch (resp->hdr_cseq.method) {\n\tcase INVITE:\n\t\t// re-INVITE\n\t\tif (!req_out_invite) return false;\n\t\tassert(state == DS_W4RE_INVITE_RESP ||\n\t\t       state == DS_W4RE_INVITE_RESP2);\n\t\tcurrent_cr = &req_out_invite;\n\t\tbreak;\n\tcase PRACK:\n\t\tif (!req_prack) return false;\n\t\tcurrent_cr = &req_prack;\n\t\tbreak;\n\tcase REFER:\n\t\tif (!req_refer) return false;\n\t\tcurrent_cr = &req_refer;\n\t\tbreak;\n\tcase INFO:\n\t\tif (!req_info) return false;\n\t\tcurrent_cr = &req_info;\n\t\tbreak;\n\tcase SUBSCRIBE:\n\tcase NOTIFY:\n\t\tif (!sub_refer) return false;\n\t\tif (!sub_refer->req_out) return false;\n\t\tcurrent_cr = &(sub_refer->req_out);\n\t\tbreak;\n\tdefault:\n\t\t// other requests\n\t\tif (!req_out) return false;\n\t\tcurrent_cr = &req_out;\n\t}\n\t\n\tif (t_abstract_dialog::resend_request_auth(*current_cr, resp)) {\n\t\tif (resp->hdr_cseq.method == INVITE) {\n\t\t\t// Reset state in case a 100 Trying was received\n\t\t\tstate = DS_W4RE_INVITE_RESP;\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool t_dialog::redirect_request(t_response *resp) {\n\tt_client_request **current_cr;\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (resp->hdr_cseq.method == INVITE) {\n\t\t// re-INVITE\n\t\tif (!req_out_invite) return false;\n\t\tassert(state == DS_W4RE_INVITE_RESP ||\n\t\t       state == DS_W4RE_INVITE_RESP2);\n\t\tcurrent_cr = &req_out_invite;\n\t} else {\n\t\t// non-INVITE\n\t\tif (!req_out) return false;\n\t\tcurrent_cr = &req_out;\n\t}\n\t\n\tt_contact_param contact;\n\tif (!t_abstract_dialog::redirect_request(*current_cr, resp, contact)) return false;\n\n\t// Re-INVITE\n\tif (resp->hdr_cseq.method == INVITE) {\n\t\t// Reset state in case a 100 Trying was received\n\t\tstate = DS_W4RE_INVITE_RESP;\n\t}\n\t\n\tui->cb_redirecting_request(user_config, line->get_line_number(), contact);\n\treturn true;\n}\n\nbool t_dialog::failover_request(t_response *resp) {\n\tt_client_request **current_cr;\n\n\tif (resp->hdr_cseq.method == INVITE) {\n\t\t// re-INVITE\n\t\tif (!req_out_invite) return false;\n\t\tassert(state == DS_W4RE_INVITE_RESP ||\n\t\t       state == DS_W4RE_INVITE_RESP2);\n\t\tcurrent_cr = &req_out_invite;\n\t} else {\n\t\t// non-INVITE\n\t\tif (!req_out) return false;\n\t\tcurrent_cr = &req_out;\n\t}\n\t\n\tif (!t_abstract_dialog::failover_request(*current_cr)) return false;\n\n\t// Re-INVITE\n\tif (resp->hdr_cseq.method == INVITE) {\n\t\t// Reset state in case a 100 Trying was received\n\t\tstate = DS_W4RE_INVITE_RESP;\n\t}\n\n\treturn true;\t\n}\n\nvoid t_dialog::hold(bool rtponly) {\n\tassert(!session_re_invite);\n\n\t// Stop glare retry timer\n\tif (id_glare_retry) {\n\t\tline->stop_timer(LTMR_GLARE_RETRY, get_object_id());\n\t}\n\n\treinvite_purpose = REINVITE_HOLD;\n\n\tif (rtponly) {\n\t\tsession->stop_rtp();\n\t\tsession->hold();\n\n\t\t// Stopping the RTP only is like a full call hold where\n\t\t// the re-INVITE failed. By setting the hold_failed flag,\n\t\t// a subsequent retrieve will only start RTP.\n\t\thold_failed = true;\n\t\treturn;\n\t}\n\n\thold_failed = false;\n\tsession_re_invite = session->create_call_hold();\n\tsend_re_invite();\n\n\t// Stop the audio streams now. If we do not stop the stream now\n\t// the stream will be stopped when a 200 OK is received on the\n\t// re-INVITE. However, when the line is put on-hold because\n\t// the user switches to another line that already has a held call\n\t// a race condition might occur:\n\t//\n\t// 1. A re-INVITE on this line is sent to put it on-hold\n\t// 2. A re-INVITE on the other line is sent to retrieve the call\n\t// 3. If the 200 OK on the second re-INVITE comes in before the\n\t//    the 200 OK on the first re-INVITE, then the audio streams\n\t//    for the second line will be started already while the first\n\t//    line still has the audio device open. On some systems this\n\t//    causes a dead lock as the audio device may only be opened\n\t//    once.\n\t//\n\t// Also if the re-INVITE to put the line on-hold fails, the\n\t// audio might not be stopped at all. It must be stopped however\n\t// as the user has switched to the other line. So stopping the\n\t// audio now will make sure the audio device is idle when the\n\t// second call is retrieved.\n\tsession->stop_rtp();\n\n\t// Prevent RTP stream from getting started even if the signaling \n\t// for hold fails. After all the user has put the phone locally\n\t// on-hold, so RTP should never be started.\n\tsession->hold();\n}\n\nvoid t_dialog::retrieve(void) {\n\tassert(!session_re_invite);\n\tt_user *user_config = phone_user->get_user_profile();\n\n\t// Stop glare retry timer\n\tif (id_glare_retry) {\n\t\tline->stop_timer(LTMR_GLARE_RETRY, get_object_id());\n\t}\n\t\n\t// Allow RTP stream to be started again.\n\tsession->unhold();\n\n\t// If the previous call-hold failed, then only RTP needs to\n\t// be restarted. The session description did never change\n\t// because of the failure.\n\tif (hold_failed) {\n\t\tsession->start_rtp();\n\t\treturn;\n\t}\n\t\n\t// If STUN is enabled, then first send a STUN binding request to\n\t// discover the IP adderss and port for media.\n\tif (phone->use_stun(user_config)) {\n\t\tif (!stun_bind_media()) {\t\n\t\t\t// No re-INVITE can be sent. Simply return.\n\t\t\t// User will decide if the call should be\n\t\t\t// torn down.\n\t\t\treturn;\n\t\t}\n\t}\n\n\treinvite_purpose = REINVITE_RETRIEVE;\n\tsession_re_invite = session->create_call_retrieve();\n\tsend_re_invite();\n}\n\nvoid t_dialog::send_session_refresh_request(void) {\n\t// If we already sent another re-INVITE for any other purpose, it will\n\t// also have refreshed the session (RFC 4028 7.2)\n\tif (session_re_invite) {\n\t\treturn;\n\t}\n\n\t// Stop glare retry timer\n\tif (id_glare_retry) {\n\t\tline->stop_timer(LTMR_GLARE_RETRY, get_object_id());\n\t}\n\n\treinvite_purpose = REINVITE_REFRESH;\n\tsession_re_invite = session->create_session_refresh();\n\tsend_re_invite();\n}\n\nvoid t_dialog::kill_rtp(void){\n\tsession->kill_rtp();\n\tif (session_re_invite) session_re_invite->kill_rtp();\n}\n\nvoid t_dialog::send_refer(const t_url &uri, const string &display) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (state != DS_CONFIRMED) return;\n\n\tif (refer_state != REFST_NULL) return;\n\t\n\t// If a previous refer is still in progress, then do nothing\n\tif (req_refer) {\n\t\tlog_file->write_report(\"A REFER request is already in progress.\",\n\t\t\t\"t_dialog::send_refer\");\n\t\treturn;\n\t}\n\n\t// If a refer subscription already exists, then do nothing\n\tif (sub_refer) {\n\t\tlog_file->write_report(\"Refer subscription exists already.\",\n\t\t\t\"t_dialog::send_refer\");\n\t\treturn;\n\t}\n\n\tt_request *refer = create_request(REFER);\n\n\t// Refer-To header\n\trefer->hdr_refer_to.set_uri(uri);\n\trefer->hdr_refer_to.set_display(display);\n\n\t// Referred-By header\n\trefer->hdr_referred_by.set_uri(line->create_user_uri());\n\trefer->hdr_referred_by.set_display(user_config->get_display(line->get_hide_user()));\n\n\treq_refer = new t_client_request(user_config, refer, 0);\n\tMEMMAN_NEW(req_refer);\n\tline->send_request(refer, req_refer->get_tuid());\n\tMEMMAN_DELETE(refer);\n\tdelete refer;\n\n\trefer_succeeded = false;\n\tout_refer_req_failed = false;\n\trefer_state = REFST_W4RESP;\n}\n\nvoid t_dialog::send_dtmf(char digit, bool inband, bool info) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (info) {\n\t\tif (req_info) {\n\t\t\t// An INFO request is still in progress, put the\n\t\t\t// DTMF digit in the queue\n\t\t\tdtmf_queue.push(digit);\n\t\t} else {\n\t\t\tt_request *info_request = create_request(INFO);\n\t\t\t\n\t\t\t// Content-Type header\n\t\t\tinfo_request->hdr_content_type.set_media(t_media(\"application\", \"dtmf-relay\"));\n\t\t\t\n\t\t\t// application/dtmf-relay body\n\t\t\tinfo_request->body = new t_sip_body_dtmf_relay(digit,\n\t\t\t\t\tuser_config->get_dtmf_duration());\n\t\t\tMEMMAN_NEW(info_request->body);\n\t\t\t\n\t\t\treq_info = new t_client_request(user_config, info_request, 0);\n\t\t\tMEMMAN_NEW(req_info);\n\t\t\tline->send_request(info_request, req_info->get_tuid());\n\t\t\tMEMMAN_DELETE(info_request);\n\t\t\tdelete info_request;\n\t\t\t\n\t\t\tui->cb_send_dtmf(line->get_line_number(), char2dtmf_ev(digit));\n\t\t}\n\t} else {\n\t\tif (session) session->send_dtmf(digit, inband);\n\t}\n}\n\nbool t_dialog::stun_bind_media(void) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\ttry {\n\t\tIPaddr mapped_ip;\n\t\tunsigned short mapped_port;\n\t\tint stun_err_code;\n\t\tstring stun_err_reason;\n\t\tbool ret = get_stun_binding(user_config, line->get_rtp_port(),\n\t\t\tmapped_ip, mapped_port,\n\t\t\tstun_err_code, stun_err_reason);\n\t\t\t\n\t\tif (!ret) {\n\t\t\t// STUN request failed\n\t\t\tui->cb_stun_failed(user_config, stun_err_code, stun_err_reason);\n\t\t\t\n\t\t\tlog_file->write_header(\"t_dialog::stun_bind_media\", \n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tlog_file->write_raw(\"STUN bind request for media failed.\\n\");\n\t\t\tlog_file->write_raw(stun_err_code);\n\t\t\tlog_file->write_raw(\" \");\n\t\t\tlog_file->write_raw(stun_err_reason);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// STUN binding request succeeded.\n\t\tsession->receive_host = h_ip2str(mapped_ip);\n\t\tsession->receive_port = mapped_port;\n\t} catch (int err) {\n\t\t// STUN request failed\n\t\tui->cb_stun_failed(user_config);\n\t\t\n\t\tlog_file->write_header(\"t_dialog::stun_bind_media\", \n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tlog_file->write_raw(\"STUN bind request for media failed.\\n\");\n\t\tlog_file->write_raw(get_error_str(err));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nvoid t_dialog::recvd_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_abstract_dialog::recvd_response(r, tuid, tid);\n\n\tif (r->hdr_cseq.method == INVITE &&\n\t    tuid == 0 && tid == 0 && !req_out_invite)\n\t{\n\t\tt_ip_port ip_port;\n\n\t\t// Only a retransmission of a 2XX INVITE is allowed.\n\t\tif (r->get_class() != R_2XX) return;\n\t\tif (!ack) return;\n\t\tif (r->hdr_cseq.seqnr != ack->hdr_cseq.seqnr)\n\t\t{\n\t\t\t// The 2XX response does not match the ACK\n\t\t\treturn;\n\t\t}\n\n\t\tack->get_destination(ip_port, *user_config);\n\t\tif (ip_port.ipaddr != 0 && ip_port.port != 0) {\n\t\t\tevq_sender->push_network(ack, ip_port);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif (r->hdr_cseq.method == CANCEL) {\n\t\tif (!req_cancel) return;\n\t\tif (r->is_final()) {\n\t\t\tremove_client_request(&req_cancel);\n\t\t\tif (r->is_success()) {\n\t\t\t\tline->start_timer(LTMR_CANCEL_GUARD, get_object_id());\n\t\t\t} else {\n\t\t\t\t// CANCEL request failed.\n\t\t\t\tui->cb_cancel_failed(line->get_line_number(), r);\n\n\t\t\t\t// Abort the INVITE as the user cannot terminate\n\t\t\t\t// it in a normal way.\n\t\t\t\tif (req_out_invite) {\n\t\t\t\t\tt_tid _tid = req_out_invite->get_tid();\n\t\t\t\t\tif (_tid > 0) {\n\t\t\t\t\t\tevq_trans_mgr->push_abort_trans(_tid);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\t// No processing done for PRACK responses.\n\tif (r->hdr_cseq.method == PRACK) {\n\t\tif (!req_prack) return;\n\t\tt_request *prack = req_prack->get_request();\n\n\t\tif (r->hdr_cseq.seqnr != prack->hdr_cseq.seqnr) {\n\t\t\t// The response does not match the latest sent PRACK.\n\t\t\t// It might match a previous sent PRACK. However, when\n\t\t\t// a previous PRACK fails, then the latest PRACK will also\n\t\t\t// fail, so the failure will be handled in the end without\n\t\t\t// the overhead to keep a list of all pending PRACKs which\n\t\t\t// should be a rare case.\n\t\t\treturn;\n\t\t}\n\n\t\tif (r->is_final()) {\n\t\t\t// PRACK is finished, so remove request\n\t\t\tremove_client_request(&req_prack);\n\n\t\t\t// Tear down the call if PRACK failed and call is\n\t\t\t// not yet established.\n\t\t\tif (!r->is_success() && state == DS_EARLY) {\n\t\t\t\tlog_file->write_header(\"t_dialog::recvd_response\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tlog_file->write_raw(\"PRACK failed: \");\n\t\t\t\tlog_file->write_raw(r->code);\n\t\t\t\tlog_file->write_raw(\" \");\n\t\t\t\tlog_file->write_raw(r->reason);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_raw(\"Call will be cancelled.\\n\");\n\t\t\t\tlog_file->write_footer();\n\n\t\t\t\tui->cb_prack_failed(line->get_line_number(), r);\n\t\t\t\tsend_cancel(true);\n\n\t\t\t\t// Ignore the failure in other states.\n\t\t\t\t// The call has been setup, so all seems fine.\n\t\t\t}\n\t\t}\n\n\t\treturn;\n\t}\n\n\t// Determine if this is an INVITE or non-INVITE response\n\tt_client_request *req;\n\tbool send_to_sub_refer = false;\n\n\tswitch(r->hdr_cseq.method) {\n\tcase INVITE:\n\t\treq = req_out_invite;\n\t\tbreak;\n\tcase SUBSCRIBE:\n\tcase NOTIFY:\n\t\tif (!sub_refer) return;\n\t\treq = sub_refer->req_out;\n\t\tsend_to_sub_refer = true;\n\t\tbreak;\n\tcase REFER:\n\t\treq = req_refer;\n\t\tbreak;\n\tcase INFO:\n\t\treq = req_info;\n\t\tbreak;\n\tdefault:\n\t\treq = req_out;\n\t}\n\n\t// Discard response if no request is pending\n\tif (!req) {\n\t\treturn;\n\t}\n\n\t// Check cseq\n\tif (r->hdr_cseq.method != req->get_request()->method) {\n\t\treturn;\n\t}\n\tif (r->hdr_cseq.seqnr != req->get_request()->hdr_cseq.seqnr) return;\n\n\t// Set the transaction identifier. This identifier is needed if the\n\t// transaction must be aborted at a later time.\n\treq->set_tid(tid);\n\n\tif (send_to_sub_refer) {\n\t\tsub_refer->recv_response(r, tuid, tid);\n\t\tif (sub_refer->get_state() == SS_TERMINATED) {\n\t\t\tMEMMAN_DELETE(sub_refer);\n\t\t\tdelete sub_refer;\n\t\t\tsub_refer = NULL;\n\t\t\tif (state == DS_CONFIRMED_SUB) {\n\t\t\t\tstate = DS_TERMINATED;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tswitch (state) {\n\tcase DS_W4INVITE_RESP:\n\tcase DS_W4INVITE_RESP2:\n\t\tstate_w4invite_resp(r, tuid, tid);\n\t\tbreak;\n\tcase DS_EARLY:\n\t\tstate_early(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4BYE_RESP:\n\t\tstate_w4bye_resp(r, tuid, tid);\n\t\tbreak;\n\tcase DS_CONFIRMED:\n\t\tstate_confirmed_resp(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4RE_INVITE_RESP:\n\tcase DS_W4RE_INVITE_RESP2:\n\t\tstate_w4re_invite_resp(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\t// No response expected in other states. Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::recvd_request(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\t// CANCEL will be handled by recvd_cancel()\n\t\n\tt_abstract_dialog::recvd_request(r, tuid, tid);\n\n\tswitch (r->method) {\n\tcase ACK:\n\t\t// When ACK is received then the current incoming request\n\t\t// must be INVITE.\n\t\tif (!req_in_invite) return;\n\t\tif (req_in_invite->get_request()->hdr_cseq.seqnr !=\n\t\t\t\tr->hdr_cseq.seqnr)\n\t\t{\n\t\t\tlog_file->write_header(\"t_dialog::recvd_request\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"ACK does not match a pending INVITE.\\n\");\n\t\t\tlog_file->write_raw(\"Discard ACK.\\n\");\n\t\t\tlog_file->write_footer();\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase INVITE:\n\t\tif (remote_seqnr_set && r->hdr_cseq.seqnr <= remote_seqnr) {\n\t\t\t// Request received out of sequence. Discard.\n\t\t\tlog_file->write_header(\"t_dialog::recvd_request\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"INVITE is received out of order.\\n\");\n\t\t\tlog_file->write_raw(\"Remote seqnr = \");\n\t\t\tlog_file->write_raw(remote_seqnr);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Received seqnr = \");\n\t\t\tlog_file->write_raw(r->hdr_cseq.seqnr);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Discard INVITE.\\n\");\n\t\t\tlog_file->write_footer();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tremote_seqnr = r->hdr_cseq.seqnr;\n\t\tremote_seqnr_set = true;\n\n\t\tif (req_in_invite) {\n\t\t\t// RFC 3261 14.2\n\t\t\t// Another INVITE is received while the previous\n\t\t\t// one is not finished.\n\t\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR,\n\t\t\t\t\"Previous INVITE still in progress\");\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\treturn;\n\t\t} else if (req_out_invite) {\n\t\t\t// RFC 3261 14.2\n\t\t\t// re-INVITE glare\n\t\t\tresp = r->create_response(R_491_REQUEST_PENDING);\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\treturn;\n\t\t} else {\n\t\t\treq_in_invite = new t_client_request(user_config, r, tid);\n\t\t\tMEMMAN_NEW(req_in_invite);\n\t\t}\n\t\tbreak;\n\tcase REFER:\n\t\t// Reset refer_accepted indication.\n\t\trefer_accepted = false;\n\t\t// fall thru\n\tdefault:\n\t\t// Check cseq\n\t\t// RFC 3261 12.2.2\n\t\tif (remote_seqnr_set && r->hdr_cseq.seqnr <= remote_seqnr) {\n\t\t\t// Request received out of order.\t\t\n\t\t\tlog_file->write_header(\"t_dialog::recvd_request\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"CSeq seqnr is out of sequence.\\n\");\n\t\t\tlog_file->write_raw(\"Reveived seqnr: \");\n\t\t\tlog_file->write_raw(r->hdr_cseq.seqnr);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Remote seqnr: \");\n\t\t\tlog_file->write_raw(remote_seqnr);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR,\n\t\t\t\t\"Request received out of order\");\n\t\t\tline->send_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tremote_seqnr = r->hdr_cseq.seqnr;\n\t\tremote_seqnr_set = true;\n\t}\n\n\tt_dialog_state old_state = state;\n\t\n\tswitch (state) {\n\tcase DS_NULL:\n\t\tstate_null(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4ACK:\n\t\tstate_w4ack(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4ACK_RE_INVITE:\n\t\tstate_w4ack_re_invite(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4ANSWER:\n\t\tstate_w4answer(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4RE_INVITE_RESP:\n\tcase DS_W4RE_INVITE_RESP2:\n\t\tstate_w4re_invite_resp(r, tuid, tid);\n\t\tbreak;\n\tcase DS_W4BYE_RESP:\n\t\tstate_w4bye_resp(r, tuid, tid);\n\t\tbreak;\n\tcase DS_CONFIRMED:\n\t\tstate_confirmed(r, tuid, tid);\n\t\tbreak;\n\tcase DS_CONFIRMED_SUB:\n\t\tstate_confirmed_sub(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\t// No request expected in other states. Discard.\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\tline->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n\t\n\t// If the state has changed, then waiting requests needs to be\n\t// processed.\n\tif (state != old_state && !inc_req_queue.empty()) {\n\t\tt_client_request *queued_cr = inc_req_queue.front();\n\t\tinc_req_queue.pop_front();\n\t\t\n\t\tlog_file->write_header(\"t_dialog::recvd_request\", \n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Process queued \");\n\t\tlog_file->write_raw(method2str(r->method, r->unknown_method));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\n\t\trecvd_request(queued_cr->get_request(), 0, queued_cr->get_tid());\n\t\tMEMMAN_DELETE(queued_cr);\n\t\tdelete queued_cr;\n\t}\n}\n\n// RFC 3261 9.2\nvoid t_dialog::recvd_cancel(t_request *r, t_tid cancel_tid,\n\t\tt_tid target_tid)\n{\n\tt_response *resp;\n\n\tassert(r->method == CANCEL);\n\n\t// Send 200 as response to CANCEL\n\tresp = r->create_response(R_200_OK);\n\t// RFC 3261 9.2\n\t// The To-tag in the response to the CANCEL should be the same\n\t// as the To-tag in the original request.\n\tresp->hdr_to.set_tag(local_tag);\n\tline->send_response(resp, 0, cancel_tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\tswitch (state) {\n\tcase DS_W4ANSWER:\n\t\tstate_w4answer(r, 0, cancel_tid);\n\t\tbreak;\n\tdefault:\n\t\t// Ignore CANCEL in other states.\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid) {\n\t// Not used anymore.\n\t// STUN requests are performed in a synchronous way.\n}\n\n// RFC 3261 13.3.1.4\nvoid t_dialog::answer(void) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tif (!req_in_invite) return;\n\n\tt_request *invite_req = req_in_invite->get_request();\n\n\t// RFC 3262 3\n\t// Delay the final response if we are still waiting for a PRACK\n\t// on a 1xx response containing SDP\n\tif (resp_1xx_invite && resp_1xx_invite->body) {\n\t\tanswer_after_prack = true;\n\t\treturn;\n\t}\n\n\tif (state != DS_W4ANSWER) {\n\t\tthrow X_WRONG_STATE;\n\t}\n\t\n\tresp_invite = invite_req->create_response(R_200_OK);\n\tresp_invite->hdr_to.set_tag(local_tag);\n\n\t// Set Organization header\n\tSET_HDR_ORGANIZATION(resp_invite->hdr_organization, user_config);\n\n\t// RFC 3261 12.1.1\n\t// Copy the Record-Route header from request to response\n\tif (invite_req->hdr_record_route.is_populated()) {\n\t\tresp_invite->hdr_record_route = invite_req->hdr_record_route;\n\t}\n\n\t// Set Contact header\n\tt_contact_param contact;\n\tcontact.uri.set_url(line->create_user_contact(h_ip2str(resp_invite->get_local_ip())));\n\tresp_invite->hdr_contact.add_contact(contact);\n\n\t// An INVITE acts as initial session refresh request\n\tprocess_session_refresh_request(invite_req, resp_invite, false);\n\n\t// Set Allow and Supported headers\n\tSET_HDR_ALLOW(resp_invite->hdr_allow, user_config);\n\tSET_HDR_SUPPORTED(resp_invite->hdr_supported, user_config);\n\n\t// RFC 3261 13.3.1.4\n\t// Create SDP offer if no offer was received in INVITE and no offer\n\t// was sent in a reliable 1xx response (RFC 3262 5)\n\t// Otherwise if no offer was sent in a reliable 1xx, create an SDP answer.\n\tif (!session->sent_offer) {\n\t\tif (!session->recvd_offer && !session->sent_offer) {\n\t\t\tsession->create_sdp_offer(resp_invite, SDP_O_USER);\n\t\t} else {\n\t\t\tsession->create_sdp_answer(resp_invite, SDP_O_USER);\n\t\t\tsession->start_rtp();\n\t\t}\n\t}\n\t\n\t// Trigger call script\n\tt_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_ANSWERED,\n\t\t\tline->get_line_number() + 1);\n\tscript.exec_notify(resp_invite);\n\n\tline->call_hist_record.answer_call(resp_invite);\n\tline->send_response(resp_invite, req_in_invite->get_tuid(),\n\t\t\t\t\treq_in_invite->get_tid());\n\tline->start_timer(LTMR_ACK_GUARD, get_object_id());\n\tline->start_timer(LTMR_ACK_TIMEOUT, get_object_id());\n\n\t// Stop 100rel timers if they are running.\n\tline->stop_timer(LTMR_100REL_GUARD, get_object_id());\n\tline->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\n\tstate = DS_W4ACK;\n}\n\nvoid t_dialog::reject(int code, string reason) {\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (state != DS_W4ANSWER) {\n\t\tthrow X_WRONG_STATE;\n\t}\n\n\tassert(req_in_invite);\n\tassert(code >= 400);\n\n\tresp = req_in_invite->get_request()->create_response(code, reason);\n\tresp->hdr_to.set_tag(local_tag);\n\t\n\t// Trigger call script\n\tt_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\tscript.exec_notify(resp);\n\t\t\n\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\t\t\t\treq_in_invite->get_tid());\n\tline->call_hist_record.fail_call(resp);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\t// Stop 100rel timers if they are running.\n\tline->stop_timer(LTMR_100REL_GUARD, get_object_id());\n\tline->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\n\tstate = DS_TERMINATED;\n}\n\nvoid t_dialog::redirect(const list<t_display_url> &destinations, int code, string reason)\n{\n\tt_response *resp;\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (state != DS_W4ANSWER) {\n\t\tthrow X_WRONG_STATE;\n\t}\n\n\tassert(req_in_invite);\n\tassert(code >= 300 && code <= 399);\n\n\tresp = req_in_invite->get_request()->create_response(code, reason);\n\tresp->hdr_to.set_tag(local_tag);\n\n\tt_contact_param *contact;\n\tfloat q = 0.9;\n\tfor (list<t_display_url>::const_iterator i = destinations.begin();\n\t     i != destinations.end(); i++)\n\t{\n\t\tcontact = new t_contact_param();\n\t\tMEMMAN_NEW(contact);\n\t\tcontact->display = i->display;\n\t\tcontact->uri = i->url;\n\t\tcontact->set_qvalue(q);\n\t\tresp->hdr_contact.add_contact(*contact);\n\t\tMEMMAN_DELETE(contact);\n\t\tdelete contact;\n\t\tq = q - 0.1;\n\t\tif (q < 0.1) q = 0.1;\n\t}\n\t\n\t// Trigger call script\n\tt_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,\n\t\t\tline->get_line_number() + 1);\n\tscript.exec_notify(resp);\n\n\tline->send_response(resp, req_in_invite->get_tuid(),\n\t\t\t\t\t\treq_in_invite->get_tid());\n\tline->call_hist_record.fail_call(resp);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\t// Stop 100rel timers if they are running.\n\tline->stop_timer(LTMR_100REL_GUARD, get_object_id());\n\tline->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());\n\n\tstate = DS_TERMINATED;\n}\n\nbool t_dialog::match_response(t_response *r, t_tuid tuid) {\n\tif (tuid != 0) {\n  \t\tif (req_out && req_out->get_tuid() == tuid) return true;\n\t\tif (req_out_invite && req_out_invite->get_tuid() == tuid) {\n\t\t\treturn true;\n\t\t}\n\t\tif (req_cancel && req_cancel->get_tuid() == tuid) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t// The implementation sends CANCEL on the open dialog.\n\t// The tags of a CANCEL response will be identical to the tags of\n\t// the INVITE, so it matches all pending dialogs as well.\n\t// So a CANCEL should only match if the dialog has a CANCEL request\n\t// pending.\n\tif (r->hdr_cseq.method == CANCEL && !req_cancel) return false;\n\n\treturn t_abstract_dialog::match_response(r, tuid);\n}\n\nbool t_dialog::match_response(StunMessage *r, t_tuid tuid) {\n\tif (tuid == 0) return false;\n\tif (!req_stun) return false;\n\n\treturn (req_stun->get_tuid() == tuid);\n}\n\nbool t_dialog::match_cancel(t_request *r, t_tid target_tid) {\n\treturn (req_in_invite && req_in_invite->get_tid() == target_tid);\n}\n\nbool t_dialog::is_invite_retrans(t_request *r) {\n\tassert(r->method == INVITE);\n\n\t// An INVITE can only be a retransmission if an incoming INVITE is\n\t// still in progress.\n\tif (!req_in_invite) return false;\n\tt_request *request = req_in_invite->get_request();\n\n\t// RFC 3261 17.2.3\n\tt_via &orig_top_via = request->hdr_via.via_list.front();\n\tt_via &recv_top_via = r->hdr_via.via_list.front();\n\n\tif (recv_top_via.rfc3261_compliant()) {\n\t\tif (orig_top_via.branch != recv_top_via.branch) return false;\n\t\tif (orig_top_via.host != recv_top_via.host) return false;\n\t\tif (orig_top_via.port != recv_top_via.port) return false;\n\t\treturn (request->hdr_cseq.method == r->hdr_cseq.method);\n\t}\n\n\t// Matching rules for backward compatibiliy with RFC 2543\n\t// TODO: verify rules for matching via headers\n\treturn (request->uri.sip_match(r->uri) &&\n\t\trequest->hdr_to.tag == r->hdr_to.tag &&\n\t\trequest->hdr_from.tag == r->hdr_from.tag &&\n\t\trequest->hdr_call_id.call_id == r->hdr_call_id.call_id &&\n\t\trequest->hdr_cseq.seqnr == r->hdr_cseq.seqnr &&\n\t\torig_top_via.host == recv_top_via.host &&\n\t\torig_top_via.port == recv_top_via.port);\n}\n\nvoid t_dialog::process_invite_retrans(void) {\n\tt_ip_port ip_port;\n\t\n\t// Retransmit 2xx response.\n\t// Send the response directly to the sender thread\n\t// as the INVITE transaction completed already.\n\t// (see RFC 3261 17.2.1)\n\tif (!resp_invite) return; // there is no response to send\n\tresp_invite->get_destination(ip_port);\n\tif (ip_port.ipaddr == 0) {\n\t\t// This should not happen. The response has been\n\t\t// sent before so it should be possible to sent\n\t\t// it again. Ignore the timeout. When the ACK\n\t\t// guard timer expires, the dialog will be\n\t\t// cleaned up.\n\t\treturn;\n\t}\n\tevq_sender->push_network(resp_invite, ip_port);\n}\n\nt_dialog_state t_dialog::get_state(void) const {\n\treturn state;\n}\n\nvoid t_dialog::timeout(t_line_timer timer) {\n\t// Session timers don't have anything to do with state -- we're just\n\t// piggy-backing on the existing line timer architecture.\n\tswitch(timer) {\n\tcase LTMR_SESSION_REFRESH:\n\tcase LTMR_SESSION_EXPIRE:\n\t\treturn timeout_session_refresh(timer);\n\t}\n\n\tswitch(state) {\n\tcase DS_W4INVITE_RESP:\n\tcase DS_W4INVITE_RESP2:\n\t\tstate_w4invite_resp(timer);\n\t\tbreak;\n\tcase DS_EARLY:\n\t\tstate_early(timer);\n\t\tbreak;\n\tcase DS_W4ACK:\n\t\tstate_w4ack(timer);\n\t\tbreak;\n\tcase DS_W4ACK_RE_INVITE:\n\t\tstate_w4ack_re_invite(timer);\n\t\tbreak;\n\tcase DS_W4RE_INVITE_RESP2:\n\t\tstate_w4re_invite_resp(timer);\n\t\tbreak;\n\tcase DS_W4ANSWER:\n\t\tstate_w4answer(timer);\n\t\tbreak;\n\tcase DS_CONFIRMED:\n\t\tstate_confirmed(timer);\n\t\tbreak;\n\tdefault:\n\t\t// Timeout not expected in other states. Ignore.\n\t\tbreak;\n\t}\n}\n\nvoid t_dialog::timeout_sub(t_subscribe_timer timer, const string &event_type,\n\t\tconst string &event_id)\n{\n\tif (sub_refer &&\n\t    sub_refer->get_event_type() == event_type &&\n\t    sub_refer->get_event_id() == event_id)\n\t{\n\t\tsub_refer->timeout(timer);\n\t} else {\n\t\t// Timeout does not match with the current subscription.\n\t\t// Ignore.\n\t\treturn;\n\t}\n\n\tif (sub_refer->get_state() == SS_TERMINATED && state == DS_CONFIRMED_SUB) {\n\t\tMEMMAN_DELETE(sub_refer);\n\t\tdelete sub_refer;\n\t\tsub_refer = NULL;\n\t\tstate = DS_TERMINATED;\n\t}\n}\n\nvoid t_dialog::timeout_session_refresh(t_line_timer timer) {\n\tswitch (timer) {\n\tcase LTMR_SESSION_REFRESH:\n\t\tlog_file->write_report(\"Timer LTMR_SESSION_REFRESH expired, sending re-INVITE.\",\n\t\t\t\t\"t_dialog::timeout_session_refresh\", LOG_NORMAL, LOG_INFO);\n\t\tsend_session_refresh_request();\n\t\tbreak;\n\tcase LTMR_SESSION_EXPIRE:\n\t\tlog_file->write_report(\"Timer LTMR_SESSION_EXPIRE expired.\",\n\t\t\t\t\"t_dialog::timeout_session_refresh\", LOG_NORMAL, LOG_WARNING);\n\t\tsend_bye();\n\t\tui->cb_session_expired(line->get_line_number());\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nt_phone *t_dialog::get_phone(void) const {\n\treturn line->get_phone();\n}\n\nt_line *t_dialog::get_line(void) const {\n\treturn line;\n}\n\nt_session *t_dialog::get_session(void) const {\n\treturn session;\n}\n\nt_audio_session *t_dialog::get_audio_session(void) const {\n\tif (!session) return NULL;\n\n\treturn session->get_audio_session();\n}\n\nbool t_dialog::has_active_session(void) const {\n\tif (session) return session->is_rtp_active();\n\t\n\treturn false;\n}\n\n// RFC 3515\n// Send a NOTIFY with reference progress to the referror\nvoid t_dialog::notify_refer_progress(t_response *r) {\n\tif (!sub_refer) return;\n\n\tif (r->is_final()) {\n\t\tsub_refer->send_notify(r, SUBSTATE_TERMINATED, EV_REASON_NORESOURCE);\n\t} else {\n\t\tsub_refer->send_notify(r, SUBSTATE_ACTIVE);\n\t}\n}\n\nbool t_dialog::will_release(void) const {\n\treturn state == DS_W4BYE_RESP || request_cancelled || \n\t\tend_after_2xx_invite || end_after_ack;\n}\n"
  },
  {
    "path": "src/dialog.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * SIP dialog established by an INVITE transaction.\n */\n\n#ifndef _DIALOG_H\n#define _DIALOG_H\n\n#include <string>\n#include <list>\n#include <set>\n#include <queue>\n#include \"abstract_dialog.h\"\n#include \"client_request.h\"\n#include \"phone.h\"\n#include \"transaction_layer.h\"\n#include \"protocol.h\"\n#include \"redirect.h\"\n#include \"session.h\"\n#include \"user.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include \"parser/request.h\"\n#include \"sdp/sdp.h\"\n#include \"stun/stun.h\"\n\nusing namespace std;\n\n// Forward declarations\nclass t_phone;\nclass t_line;\nclass t_session;\nclass t_sub_refer;\n\n/** Dialog state */\nenum t_dialog_state {\n\tDS_NULL,\t\t/**< Initial state */\n\n\t// UAC states\n\tDS_W4INVITE_RESP,\t/**< INVITE sent, waiting for response */\n\tDS_W4INVITE_RESP2,\t/**< Provisional response received */\n\tDS_EARLY,\t\t/**< Provisional response with to-tag received */\n\tDS_W4BYE_RESP,\t\t/**< BYE sent, waiting for response */\n\n\t// UAS states\n\tDS_W4ACK,\t\t/**< Waiting for ACK on 2XX INVITE */\n\tDS_W4ANSWER,\t\t/**< INVITE received, waiting for user to answer */\n\n\t// UAS and UAC states\n\tDS_CONFIRMED,\t\t/**< Success received/sent */\n\tDS_W4ACK_RE_INVITE,\t/**< Waiting for ACK on re-INVITE */\n\tDS_W4RE_INVITE_RESP,\t/**< re-INVITE sent, waiting for response */\n\tDS_W4RE_INVITE_RESP2,\t/**< re-INVITE sent, provisional response recvd */\n\tDS_TERMINATED,\t\t/**< Dialog terminated */\n\n\t// Subscription states\n\tDS_CONFIRMED_SUB,\t/**< Confirmed refer-subscription dialog */\n};\n\n/** Purpose for sending a re-INVITE request */\nenum t_reinvite_purpose {\n\tREINVITE_HOLD,\t\t/**< Re-invite for call hold */\n\tREINVITE_RETRIEVE,\t/**< Re-invite for call retrieve */\n\tREINVITE_REFRESH,\t/**< Refresh session */\n};\n\n/**\n * SIP dialog established by an INVITE transaction.\n *\n * Dialog state diagrams:\n * @dot\n * digraph call {\n *   label=\"Call setup and tear down state transitions\"\n *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];\n *   edge [fontname=Helvetica, fontsize=9];\n *\n *   null [label=\"DS_NULL\" URL=\"\\ref DS_NULL\"];\n *   w4invite_resp [label=\"DS_W4INVITE_RESP\" URL=\"\\ref DS_W4INVITE_RESP\"]\n *   w4invite_resp2 [label=\"DS_W4INVITE_RESP2\" URL=\"\\ref DS_W4INVITE_RESP2\"]\n *   early [label=\"DS_EARLY\" URL=\"\\ref DS_EARLY\"]\n *   w4bye_resp [label=\"DS_W4BYE_RESP\" URL=\"\\ref DS_W4BYE_RESP\"]\n *   w4ack [label=\"DS_W4ACK\" URL=\"\\ref DS_W4ACK\"]\n *   w4answer [label=\"DS_W4ANSWER\" URL=\"\\ref DS_W4ANSWER\"]\n *   confirmed [label=\"DS_CONFIRMED\" URL=\"\\ref DS_CONFIRMED\"]\n *   terminated [label=\"DS_TERMINATED\" URL=\"\\ref DS_TERMINATED\"]\n *\n *   null -> w4invite_resp [label=\"send INVITE\"]\n *   null -> w4answer [label=\"receive INVITE\"]\n *   null -> terminated [label=\"receive INVITE\\nSTUN media\\nbind fails\"]\n *   null -> terminated [label=\"receive INVITE\\nunsupported\\nmedia or body\"]\n *   w4invite_resp -> w4invite_resp2 [label=\"receive 1XX without to-tag\"]\n *   w4invite_resp -> early [label=\"receive 1XX with to-tag\"]\n *   w4invite_resp -> confirmed [label=\"receive 2XX\"]\n *   w4invite_resp -> terminated [label=\"receive failure\\nresponse\"]\n *   w4invite_resp2 -> confirmed [label=\"receive 2XX\"]\n *   w4invite_resp2 -> early [label=\"receive 1XX with to-tag\"]\n *   w4invite_resp2 -> terminated [label=\"receive failure\\nresponse\"]\n *   early -> confirmed [label=\"receive 2XX\"]\n *   early -> terminated [label=\"receive failure\\nresponse\"]\n *   w4answer -> w4ack [label=\"user answered, send 2XX\"]\n *   w4answer -> terminated [label=\"user rejected\\nsend 4XX/6XX\"]\n *   w4answer -> terminated [label=\"receive CANCEL/BYE\\nsend 487\"]\n *   w4ack -> confirmed [label=\"receive ACK\"]\n *   confirmed -> w4bye_resp [label=\"send BYE\"]\n *   confirmed -> terminated [label=\"receive BYE\"]\n *   w4bye_resp -> terminated [label=\"receive BYE response\"]\n * }\n * @enddot\n *\n * @dot\n * digraph reinvite {\n *   label=\"re-INVITE state transitions\"\n *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];\n *   edge [fontname=Helvetica, fontsize=9];\n *\n *   confirmed [label=\"DS_CONFIRMED\" URL=\"\\ref DS_CONFIRMED\"]\n *   w4ack_re_invite [label=\"DS_W4ACK_RE_INVITE\" URL=\"\\ref DS_W4ACK_RE_INVITE\"]\n *   w4re_invite_resp [label=\"DS_W4RE_INVITE_RESP\" URL=\"\\ref DS_W4RE_INVITE_RESP\"]\n *   w4re_invite_resp2 [label=\"DS_W4RE_INVITE_RESP2\" URL=\"\\ref DS_W4RE_INVITE_RESP2\"]\n *   terminated [label=\"DS_TERMINATED\" URL=\"\\ref DS_TERMINATED\"]\n *\n *   confirmed -> w4ack_re_invite [label=\"receive re-INVITE, send 2XX\"]\n *   confirmed -> terminated [label=\"receive re-INVITE\\nSTUN fails\"]\n *   confirmed -> confirmed [label=\"receive re-INVITE, send failure response\"]\n *   confirmed -> w4re_invite_resp [label=\"send re-INVITE\"]\n *   w4ack_re_invite -> confirmed [label=\"reveive ACK\"]\n *   w4re_invite_resp -> w4re_invite_resp2 [label=\"receive 1XX\"]\n *   w4re_invite_resp -> confirmed [label=\"receive final response\"]\n *   w4re_invite_resp2 -> confirmed [label=\"receive final response\"]\n *   w4re_invite_resp -> terminated [label=\"receive BYE\"]\n *   w4re_invite_resp2 -> terminated [label=\"receive BYE\"]\n * }\n * @enddot\n *\n * @dot\n * digraph refer {\n *   label=\"State transitions when REFER subscription is active\"\n *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];\n *   edge [fontname=Helvetica, fontsize=9];\n *\n *   confirmed [label=\"DS_CONFIRMED\" URL=\"\\ref DS_CONFIRMED\"]\n *   w4bye_resp [label=\"DS_W4BYE_RESP\" URL=\"\\ref DS_W4BYE_RESP\"]\n *   w4re_invite_resp [label=\"DS_W4RE_INVITE_RESP\" URL=\"\\ref DS_W4RE_INVITE_RESP\"]\n *   w4re_invite_resp2 [label=\"DS_W4RE_INVITE_RESP2\" URL=\"\\ref DS_W4RE_INVITE_RESP2\"]\n *   terminated [label=\"DS_TERMINATED\" URL=\"\\ref DS_TERMINATED\"]\n *   confirmed_sub [label=\"DS_CONFIRMED_SUB\" URL=\"DS_CONFIRMED_SUB\"]\n *\n *   confirmed -> confirmed_sub [label=\"receive BYE\"]\n *   w4re_invite_resp -> confirmed_sub [label=\"receive BYE\"]\n *   w4re_invite_resp2 -> confirmed_sub [label=\"receive BYE\"]\n *   w4bye_resp -> confirmed_sub [label=\"receive BYE response\"]\n *   confirmed_sub -> terminated [label=\"terminate subscription\"]\n * }\n * @enddot\n *\n * @dot\n * digraph timeout {\n *   label=\"State transitions due to timeouts\"\n *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];\n *   edge [fontname=Helvetica, fontsize=9];\n *\n *   w4answer [label=\"DS_W4ANSWER\" URL=\"\\ref DS_W4ANSWER\"]\n *   w4ack [label=\"DS_W4ACK\" URL=\"\\ref DS_W4ACK\"]\n *   confirmed [label=\"DS_CONFIRMED\" URL=\"\\ref DS_CONFIRMED\"]\n *   terminated [label=\"DS_TERMINATED\" URL=\"\\ref DS_TERMINATED\"]\n *   confirmed_sub [label=\"DS_CONFIRMED_SUB\" URL=\"DS_CONFIRMED_SUB\"]\n *\n *   w4answer -> w4answer [label=\"LTMR_100REL_TIMEOUT\\nretransmit 1XX\" URL=\"LTMR_100REL_TIMEOUT\"]\n *   w4answer -> terminated [label=\"LTMR_100REL_GUARD\\nsend 500\" URL=\"LTMR_100REL_GUARD\"]\n *   w4ack -> w4ack [label=\"LTMR_ACK_TIMEOUT\\nretransmit 2XX\" URL=\"LTMR_ACK_TIMEOUT\"]\n *   w4ack -> confirmed [label=\"LTMR_ACK_GUARD\\ntear down call\" URL=\"LTMR_ACK_GUARD\"]\n *   confirmed_sub -> terminated [label=\"REFER subscription\\ntimeout\"]\n * }\n * @enddot\n */\nclass t_dialog : public t_abstract_dialog {\n\tfriend class t_phone;\n\t\nprotected:\n\tt_line\t\t\t*line; /**< Phone line owning this dialog. */\n\tt_dialog_state\t\tstate; /**< Dialog state. */\n\n\t/** Session established by this dialog. */\n\tt_session\t*session;\n\n\t/**\n\t * New session being established during re-invite.\n\t * When the re-INVITE transaction finishes successfully, then\n\t * this session information will override the general session.\n\t */\n\tt_session\t*session_re_invite;\n\n\t/** The purpose of an outgoing re-INVITE request */\n\tt_reinvite_purpose\treinvite_purpose;\n\n\t/** Indicates if the last call hold action failed. */\n\tbool\t\t\thold_failed;\n\n\tt_client_request\t*req_out;\t  /**< Pending outgoing non-INVITE request */\n\tt_client_request\t*req_out_invite;  /**< Pending outgoing INVITE */\n\tt_client_request\t*req_in_invite;   /**< Pending incoming INVITE */\n\tt_client_request\t*req_cancel;      /**< Pending outgoing CANCEL */\n\tt_client_request\t*req_refer;\t  /**< Pending outgoing REFER */\n\tt_client_request\t*req_info;\t  /**< Pending outgoing INFO */\n\n\t/**\n\t * Last outgoing PRACK. While a PRACK is still pending a new 1xx\n\t * response might come in. A PRACK will be sent for this 1xx without\n\t * waiting for the response for the previous PRACK.\n\t */\n\tt_client_request\t*req_prack;\n\t\n\t/** Pending STUN request */\n\tt_client_request\t*req_stun;\n\t\n\t/**\n\t * Incoming request queue. A request may come in when it cannot be\n\t * served yet. Such a request is stored in the queue to be served\n\t * later.\n\t */\n\tlist<t_client_request *>\tinc_req_queue;\n\t\n\t/** Indication if request must be cancelled */\n\tbool request_cancelled;\n\t\n\t/**\n\t * Indication that the dialog must be terminated after a 2XX\n\t * on an INVITE is received (e.g. when 2XX glares with CANCEL).\n\t */\n\tbool end_after_2xx_invite;\n\n\t/** Indication that the dialog must be terminated after ACK. */\n\tbool end_after_ack;\n\t/** Optional reason provided by the far end. */\n\tstd::string end_after_ack_reason;\n\n\t/**\n\t * Indication that the user wants to answer the call.\n\t * Sending the answer must be delayed as we are still waiting for\n\t * a PRACK to acknowledge a 1xx containing SDP from the\n\t * far end (RFC 3262 3).\n\t */\n\tbool answer_after_prack;\n\t\n\t/** Indication if 180 ringing has already been received */\n\tbool ringing_received;\n\n\t/** Cached success response to INVITE needed for retransmission */\n\tt_response\t\t*resp_invite;\n\n\t/**\n\t * Cached provisional response to INVITE needed for retransmission\n\t * when provisional responses are sent reliable (100rel)\n\t */\n\tt_response\t\t*resp_1xx_invite;\n\n\t/** Cached ack needed for retransmission */\n\tt_request\t\t*ack;\n\n\t/** Subscription created by REFER (RFC 3515) */\n\tt_sub_refer\t\t*sub_refer;\n\t\n\t/** Queue of DTMF digits to be sent via INFO requests */\n\tqueue<char>\t\tdtmf_queue;\n\n\t/** @name Process incoming responses */\n\t//@{\n\t/**\n\t * Process an incoming response in the @ref DS_W4INVITE_RESP state.\n\t * @param r The response\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4invite_resp(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming response in the @ref DS_EARLY state.\n\t * @param r The response\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_early(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming response in the @ref DS_W4BYE_RESP state.\n\t * @param r The response\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4bye_resp(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming response in the @ref DS_CONFIRMED state.\n\t * @param r The response\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_confirmed_resp(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming response in the @ref DS_W4RE_INVITE_RESP state.\n\t * @param r The response\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4re_invite_resp(t_response *r, t_tuid tuid, t_tid tid);\n\t//@}\n\n\t/** @name Process incoming requests */\n\t//@{\n\t/**\n\t * Process an incoming request in the @ref DS_NULL state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_null(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_W4ANSWER state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4answer(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_W4ACK state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4ack(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_W4ACK_RE_INVITE state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4ack_re_invite(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_W4RE_INVITE_RESP state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4re_invite_resp(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_W4BYE_RESP state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_w4bye_resp(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_CONFIRMED state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_confirmed(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process an incoming request in the @ref DS_CONFIRMED_SUB state.\n\t * @param r The request\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid state_confirmed_sub(t_request *r, t_tuid tuid, t_tid tid);\n\t//@}\n\t\n\t/** @name Process requests in the confirmed state */\n\t//@{\n\t/** Proces incoming re-INVITE. */\n\tvoid process_re_invite(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/** Proces incoming REFER. */\n\tvoid process_refer(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/** Process incoming SUBSCRIBE (refer subscription). */\n\tvoid process_subscribe(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/** Process incoming NOTIFY (refer subscription). */\n\tvoid process_notify(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/** Process incoming INFO. */\n\tvoid process_info(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/** Process incoming MESSAGE. */\n\tvoid process_message(t_request *r, t_tuid tuid, t_tid tid);\n\t//@}\n\n\t/** @name Process timeouts */\n\t//@{\n\t/**\n\t * Process timeout in @ref DS_W4INVITE_RESP state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_w4invite_resp(t_line_timer timer);\n\t\n\t/**\n\t * Process timeout in @ref DS_EARLY state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_early(t_line_timer timer);\n\t\n\t/**\n\t * Process timeout in @ref DS_W4ACK state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_w4ack(t_line_timer timer);\n\t\n\t/**\n\t * Process timeout in @ref DS_W4ACK_RE_INVITE state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_w4ack_re_invite(t_line_timer timer);\n\t\n\t/**\n\t * Process timeout in @ref DS_W4RE_INVITE_RESP state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_w4re_invite_resp(t_line_timer timer);\n\t\n\t/**\n\t * Process timeout in @ref DS_W4ANSWER state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_w4answer(t_line_timer timer);\n\t\n\t/**\n\t * Process timeout in @ref DS_CONFIRMED state.\n\t * @param timer The expired timer.\n\t */\n\tvoid state_confirmed(t_line_timer timer);\n\t//@}\n\n\t/** Make the re-INVITE session the current session. */\n\tvoid activate_new_session(void);\n\n\t/**\n\t * Process SDP answer in 1xx and 2xx responses if present.\n\t * Apply ringing tone for a 180 response.\n\t * Determine if call should be canceled due to unsupported\n\t * or missing SDP.\n\t * @param r The 1XX/2XX response.\n\t */\n\tvoid process_1xx_2xx_invite_resp(t_response *r);\n\n\t/** Process an incoming session refresh request (i.e. (re-)INVITE) */\n\tvoid process_session_refresh_request(t_request *req, t_response *resp,\n\t\t\tbool is_reinvite);\n\t/** Process an incoming 2xx response to our session refresh request */\n\tvoid process_session_refresh_response(t_response *resp);\n\t/** Generic portion common to both previous methods */\n\tvoid process_session_refresh(t_sip_message *r);\n\n\t/** Add headers to an outgoing (re-)INVITE request or a 2xx response */\n\tvoid set_session_expires_headers(t_sip_message *r);\n\n\t/**\n\t * Acknowledge a reveived 2xx response on an INVITE.\n\t * @param r The 2XX response.\n\t */\n\tvoid ack_2xx_invite(t_response *r);\n\n\t/**\n\t * Send PRACK if the response requires it.\n\t * @param r The response.\n\t */\n\tvoid send_prack_if_required(t_response *r);\n\n\t/**\n\t * Determine if a reliable provisional repsonse must be discarded.\n\t * A provisional response must be discarded because it is a retransmission \n\t * or received out of order.\n\t * Initializes the remote response nr if the response is the\n\t * first response.\n\t * @param r The provisional response.\n\t * @return true, discard response.\n\t * @return false, otherwise\n\t */\n\tbool must_discard_100rel(t_response *r);\n\n\t/**\n\t * Respond to an incoming PRACK.\n\t * @param r The incoming PRACK.\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t * @return true, if a success response was given.\n\t * @return false if an error response was given.\n\t */\n\tbool respond_prack(t_request *r, t_tuid tuid, t_tid tid);\n\n\tvirtual void send_request(t_request *r, t_tuid tuid);\npublic:\n\t/** @name Timer durations and timer id's */\n\t//@{\n\tunsigned long\t\tdur_ack_timeout;\t/**< @ref LTMR_ACK_TIMEOUT duration (ms) */\n\tt_object_id\t\tid_ack_timeout;\t\t/**< @ref LTMR_ACK_TIMEOUT timer id */\n\tt_object_id\t\tid_ack_guard;\t\t/**< @ref LTMR_ACK_GUARD timer id */\n\tt_object_id\t\tid_re_invite_guard;\t/**< @ref LTMR_RE_INVITE_GUARD timer id */\n\tt_object_id\t\tid_glare_retry;\t\t/**< @ref LTMR_GLARE_RETRY timer id */\n\tt_object_id\t\tid_cancel_guard;\t/**< @ref LTMR_CANCEL_GUARD timer id */\n\tt_object_id\t\tid_session_refresh;\t/**< @ref LTMR_SESSION_REFRESH timer id */\n\tt_object_id\t\tid_session_expire;\t/**< @ref LTMR_SESSION_EXPIRE timer id */\n\t//@}\n\n\t/** @name RFC 3262 100rel timers */\n\t//@{\n\tunsigned long\t\tdur_100rel_timeout;\t/**< @ref LTMR_100REL_TIMEOUT duration (ms) */\n\tt_object_id\t\tid_100rel_timeout;\t/**< @ref LTMR_100REL_TIMEOUT timer id */\n\tt_object_id\t\tid_100rel_guard;\t/**< @ref LTMR_100REL_GUARD timer id */\n\t//@}\n\n\t/** Session expiration (RFC 4028) */\n\tunsigned long\t\tsession_interval;\n\tunsigned long\t\tsession_min_se;\n\tbool\t\t\tis_session_refresher;\n\n\n\t/** Indicates if last incoming REFER was accepted. */\n\tbool\t\t\trefer_accepted;\n\t\n\t/** Indicates if the call transfer triggered by the last outgoing REFER succeeded. */\n\tbool\t\t\trefer_succeeded;\n\t\n\t/** Indicates if the last outgoing REFER request failed. */\n\tbool\t\t\tout_refer_req_failed;\n\n\t/**\n\t * Indicates if this dialog is setup because the user told to do\n\t * so by a REFER.\n\t */\n\tbool\t\t\tis_referred_call;\n\n\t/** State of an outgoing REFER. */\n\tt_refer_state\t\trefer_state;\n\n\t/**\n\t * Constructor.\n\t * @param _line The line owning this dialog.\n\t */\n\tt_dialog(t_line *_line);\n\t\n\t/** Destructor. */\n\tvirtual ~t_dialog();\n\n\tvirtual t_request *create_request(t_method m);\n\n\tvirtual t_dialog *copy(void);\n\n\t/**\n\t * Send INIVTE request.\n\t * @param to_uri The URI to be used a request-URI and To header URI\n\t * @param to_display Display name for To header.\n\t * @param subject If not empty, this string will go into the Subject header.\n\t * @param hdr_referred_by The Reffered-By header to be put in the INVITE.\n\t * @param hdr_replaces The Replaces header to be put in the INVITE.\n\t * @param hdr_require Required extensions to be put in the Require header.\n\t * @param hdr_request_disposition Request-Disposition header to be put in the INVITE.\n\t * @param anonymous Inidicates if the INVITE should be sent anonymous.\n\t *\n\t * @pre Dialog is in @ref DS_NULL state.\n\t */\n\tvoid send_invite(const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, const t_hdr_referred_by &hdr_referred_by,\n\t\tconst t_hdr_replaces &hdr_replaces, \n\t\tconst t_hdr_require &hdr_require, \n\t\tconst t_hdr_request_disposition &hdr_request_disposition,\n\t\tbool anonymous);\n\n\t/**\n\t * Resend the INVITE with an authorization header containing credentials\n\t * for the challenge in the response.\n\t * @param resp, the response on the INVITE.\n\t * @return true, if resending succeeded. \n\t * @return false, if credentials could not be determined.\n\t *\n\t * @pre The response must be a 401 or 407.\n\t */\n\tbool resend_invite_auth(t_response *resp);\n\n\t/**\n\t * Resend the INVITE because the far-end did not support all required\n\t * extensions. Extensions that could not be supported will not be\n\t * required this time.\n\t * @param resp The response in the INVITE.\n\t * @return true, if resending succeeded.\n\t * @return false, if far-end did not indicate which extensions are\n\t * unsupported. Or if a required extension could not be disabled.\n\t *\n\t * @pre The response must be a 420.\n\t */\n\tbool resend_invite_unsupported(t_response *resp);\n\n\t/**\n\t * Redirect INVITE to the next destination.\n\t * @param resp The response on the INVITE.\n\t * @return true, if INIVTE was redirected successfully.\n\t * @return false, if there is no next destination.\n\t *\n\t * @pre The response must be a 3XX.\n\t */\n\tbool redirect_invite(t_response *resp);\n\t\n\t/**\n\t * Failover INVITE to the next destination from DNS lookup.\n\t * @return true, if INIVTE was successfully sent.\n\t * @return false, if there is no next destination.\n\t */\n\tbool failover_invite(void);\n\n\t/** Send BYE request. */\n\tvoid send_bye(void);\n\t\n\t/** Send OPTIONS request. */\n\tvoid send_options(void);\n\n\t/**\n\t * Send CANCEL request.\n\t * If an early dialog exists, then the CANCEL can be sent\n\t * right away as a response has been received for the INVITE.\n\t * Otherwise, the CANCEL will be sent as soon as an early dialog\n\t * is created.\n\t * @param early_dialog_exists Indicates if an early dialog exists.\n\t */\n\tvoid send_cancel(bool early_dialog_exists);\n\t\n\t/**\n\t * Indicate that the dialog must be ended if a 2XX is received\n\t * on an INVITE.\n\t * @param on Set/clear indication.\n\t */\n\tvoid set_end_after_2xx_invite(bool on);\n\n\t/**\n\t * Send re-INVITE.\n\t * @pre session_re_invite attribute contains the session\n\t * information for the re-INVITE.\n\t */\n\tvoid send_re_invite(void);\n\n\tvirtual bool resend_request_auth(t_response *resp);\n\n\tvirtual bool redirect_request(t_response *resp);\n\t\n\tvirtual bool failover_request(t_response *resp);\n\n\t/**\n\t * Hold call.\n\t * If rtp_only is false, then a re-INVITE will be sent.\n\t * @param rtponly Indicates if only the RTP streams should be stopped and\n\t * the soundcard freed without any SIP signaling.\n\t */\n\tvoid hold(bool rtponly = false);\n\t\n\t/** Retrieve call (send re-INVITE if needed). */\n\tvoid retrieve(void);\n\n\t/** Send a re-INVITE to act as session refresh request */\n\tvoid send_session_refresh_request(void);\n\t\n\t/** Kill all RTP stream associated with this dialog. */\n\tvoid kill_rtp(void);\n\n\t/**\n\t * Refer a call (send REFER).\n\t * @param uri URI of the refer target.\n\t * @param display Display name of the refer target.\n\t */\n\tvoid send_refer(const t_url &uri, const string &display);\n\n\t/**\n\t * Send DTMF digit.\n\t * @param digit The digit.\n\t * @param inband Indicates if digit must be sent inband.\n\t * @param info Indicates if digit must be sent in a SIP INFO.\n\t *\n\t * @pre Either inband or info or none of the indicators is true.\n\t * @post If none of the indicators is true, then RFC 2833 is used.\n\t */\n\tvoid send_dtmf(char digit, bool inband, bool info);\n\t\n\t/**\n\t * Create a binding for the media port via STUN.\n\t * @return true, if binding is created.\n\t * @return false, if binding cannot be created.\n\t */\n\tbool stun_bind_media(void);\n\n\t/** @name Handle received events */\n\t//@{\n\tvoid recvd_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\tvoid recvd_request(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Handle incoming CANCEL request.\n\t * @param r The CANCEL request.\n\t * @param cancel_tid Transaction id of the CANCEL transaction.\n\t * @param target_tid Transaction id of the transaction to be cancellerd.\n\t */\n\tvoid recvd_cancel(t_request *r, t_tid cancel_tid, t_tid target_tid);\n\t\n\t/**\n\t * Handle incoming STUN response.\n\t * @param r The STUN response.\n\t * @param tuid Transaction user id\n\t * @param tid Transaction id\n\t */\n\tvoid recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Handle the response from the user on the question for refer\n\t * permission. This response is received on the dialog that received\n\t * the REFER before.\n\t * @param permission Permission response from the user.\n\t * @param r The REFER request that was received.\n\t */\n\tvoid recvd_refer_permission(bool permission, t_request *r);\n\t//@}\n\n\t/** Answer a call (send 200 OK). */\n\tvoid answer(void);\n\n\t/**\n\t * Reject a call.\n\t * @param code The response code to reject the call with.\n\t * @param reason A specific reason may be given to the error code. If no\n\t * reason is specified the default reason is used.\n\t *\n\t * @pre code >= 400\n\t */\n\tvoid reject(int code, string reason = \"\");\n\n\t/**\n\t * Redirect a call.\n\t * @param code The response code to redirect the call with.\n\t * @param A specific reason may be give to the error code. If no\n\t * reason is specified the default reason is used.\n\t * @param destinations The list of redirect destinations in order of\n\t * preference.\n\t *\n\t * @pre code is 3XX.\n\t */\n\tvoid redirect(const list<t_display_url> &destinations, int code, string reason = \"\");\n\n\tvirtual bool match_response(t_response *r, t_tuid tuid);\n\t\n\t/**\n\t * Match STUN response with dialog.\n\t * @param r STUN response.\n\t * @param tuid Transaction user id\n\t * @return true, if response matches.\n\t * @return false, otherwise.\n\t */\n\tbool match_response(StunMessage *r, t_tuid tuid);\n\n\t/**\n\t * Match CANCEL request with dialog.\n\t * @param r CANCEL request.\n\t * @param target_tid Transaction id of transaction to be cancelled.\n\t * @return true, if request matches.\n\t * @return false, otherwise.\n\t */\n\tbool match_cancel(t_request *r, t_tid target_tid);\n\n\t/**\n\t * Check if an incoming INVITE is a retransmission.\n\t * @param r The INVITE request.\n\t * @return true, if INVITE is a retransmission.\n\t * @return false, otherwise.\n\t */\n\tbool is_invite_retrans(t_request *r);\n\n\t/** Process a retransmission of an incoming INVITE. */\n\tvoid process_invite_retrans(void);\n\n\t/**\n\t * Get the state of the dialog.\n\t * @return The dialog state.\n\t */\n\tt_dialog_state get_state(void) const;\n\n\t/**\n\t * Process dialog timer timeout.\n\t * @param timer The timer that expired.\n\t */\n\tvoid timeout(t_line_timer timer);\n\n\t/**\n\t * Process subcribe timer timeout (REFER subscription).\n\t * @param timer The timer that expired.\n\t * @param event_type Event type of the subscription.\n\t * @param event_id Event id of the subscription.\n\t */\n\tvoid timeout_sub(t_subscribe_timer timer, const string &event_type,\n\t\tconst string &event_id);\n\n\t/** Process refresh or expiration session timer timeout */\n\tvoid timeout_session_refresh(t_line_timer timer);\n\n\t/**\n\t * Get the phone that belongs to this dialog.\n\t * @return The phone object.\n\t */\n\tt_phone *get_phone(void) const;\n\n\t/**\n\t * Get the line that belongs to this dialog.\n\t * @return The line object.\n\t */\n\tt_line *get_line(void) const;\n\n\t/**\n\t * Get the session belonging to this dialog.\n\t * @return The session belonging to this dialog.\n\t * @return NULL if there is no session.\n\t */\n\tt_session * get_session(void) const;\n\t\n\t/**\n\t * Get the audio session belonging to this dialog.\n\t * @return The audio session belonging to this dialog.\n\t * @return NULL if there is no audio session.\n\t */\t\n\tt_audio_session *get_audio_session(void) const;\n\t\n\t/**\n\t * Check if the dialog has an acitve session.\n\t * @return true, if the dialog has an active session.\n\t * @return false, otherwise.\n\t */\n\tbool has_active_session(void) const;\n\n\t/**\n\t * Notify the dialog of the progress of a reference.\n\t * @param r The response sent by the refer target.\n\t */\n\tvoid notify_refer_progress(t_response *r);\n\t\n\t/**\n\t * Check if a dialog will be released.\n\t * @return true, if the dialog will be released.\n\t * @return false, otherwise.\n\t */\n\tbool will_release(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/diamondcard.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"diamondcard.h\"\n\n#include <cassert>\n#include \"twinkle_config.h\"\n#include \"util.h\"\n#include \"sockets/url.h\"\n\n#define DIAMONDCARD_DISTSITE\t\"twinkle\"\n\n#define DIAMONDCARD_URL_SIGNUP\t\"https://www.diamondcard.us/exec/voip-login?act=sgn&spo=%DISTSITE\"\n#define DIAMONDCARD_URL_ACTION  \"https://www.diamondcard.us/exec/voip-login?\"\\\n\t\t\t\t\"accId=%ACCID&pinCode=%PIN&act=%ACT&spo=%DISTSITE\"\n\nstring diamondcard_url(t_dc_action action, const string &accountId, const string &pinCode)\n{\n\tstring url;\n\t\n\tif (action == DC_ACT_SIGNUP) {\n\t\turl = DIAMONDCARD_URL_SIGNUP;\n\t} else {\n\t\turl = DIAMONDCARD_URL_ACTION;\n\t\turl = replace_first(url, \"%ACCID\", t_url::escape_hnv(accountId));\n\t\turl = replace_first(url, \"%PIN\", t_url::escape_hnv(pinCode));\n\t\t\n\t\tswitch (action) {\n\t\tcase DC_ACT_BALANCE_HISTORY:\n\t\t\turl = replace_first(url, \"%ACT\", \"bh\");\n\t\t\tbreak;\n\t\tcase DC_ACT_RECHARGE:\n\t\t\turl = replace_first(url, \"%ACT\", \"rch\");\n\t\t\tbreak;\n\t\tcase DC_ACT_CALL_HISTORY:\n\t\t\turl = replace_first(url, \"%ACT\", \"ch\");\n\t\t\tbreak;\n\t\tcase DC_ACT_ADMIN_CENTER:\n\t\t\turl = replace_first(url, \"%ACT\", \"log\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t}\n\t\n\turl = replace_first(url, \"%DISTSITE\", DIAMONDCARD_DISTSITE);\n\t\n\treturn url;\n}\n\nvoid diamondcard_set_user_config(t_user &user, const string &displayName,\n                                 const string &accountId, const string &pinCode)\n{\n\t// User\n\tuser.set_display(displayName);\n\tuser.set_name(accountId);\n\t\n\t// The real domain name is \"diamondcard.us\", but Diamondcard\n\t// instructs users to use \"sip.diamondcard.us\" for the domain.\n\t// This latter name is resolvable by both a DNS SRV and A lookup.\n\t// So clients not capable of DNS SRV lookups van still work with\n\t// this domain name. To be consistent with other client settings\n\t// Twinkle uses this domain name too.\n\tuser.set_domain(\"sip.diamondcard.us\");\n\t\n\tuser.set_auth_name(accountId);\n\tuser.set_auth_pass(pinCode);\n\t\n\t// SIP server\n\tuser.set_use_outbound_proxy(true);\n\tuser.set_outbound_proxy(t_url(\"sip:sip.diamondcard.us\"));\n\t\n\t// Audio codecs\n\tlist<t_audio_codec> codecs;\n\tcodecs.push_back(CODEC_G711_ULAW);\n\tcodecs.push_back(CODEC_G711_ALAW);\n#ifdef HAVE_ILBC\n\tcodecs.push_back(CODEC_ILBC);\n#endif\n\tcodecs.push_back(CODEC_GSM);\n\tuser.set_codecs(codecs);\n\t\n\t// Voice mail\n\tuser.set_mwi_vm_address(\"80#\");\n\t\n\t// IM\n\tuser.set_im_send_iscomposing(false);\n\t\n\t// Presence\n\tuser.set_pres_publish_startup(false);\n\n\t// NAT\n\tuser.set_enable_nat_keepalive(true);\n\tuser.set_timer_nat_keepalive(20);\n\t\n\t// Address format\n\tuser.set_numerical_user_is_phone(true);\n}\n\nlist<t_user *>diamondcard_get_users(t_phone *phone) {\n\tlist<t_user *> users = phone->ref_users();\n\tlist<t_user *> diamond_users;\n\t\n\tfor (list<t_user *>::const_iterator it = users.begin(); it != users.end(); ++it) {\n\t\tif ((*it)->is_diamondcard_account()) {\n\t\t\tdiamond_users.push_back(*it);\n\t\t}\n\t}\n\t\n\treturn diamond_users;\n}\n"
  },
  {
    "path": "src/diamondcard.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/** @file \n * Diamondcard settings (www.diamondcard.us)\n */\n \n#ifndef _DIAMONDCARD_H\n#define _DIAMONDCARD_H\n\n#include <string>\n#include <list>\n#include \"user.h\"\n#include \"phone.h\"\n\n#define DIAMONDCARD_DOMAIN \"diamondcard.us\"\n\nusing namespace std;\n\n/** Actions that can be performed on the Diamondcard web site */\nenum t_dc_action {\n\tDC_ACT_SIGNUP,\n\tDC_ACT_BALANCE_HISTORY,\n\tDC_ACT_RECHARGE,\n\tDC_ACT_CALL_HISTORY,\n\tDC_ACT_ADMIN_CENTER\n};\n\n/** \n * Get the URL of a Diamondcard web page for an action. \n * @param action [in] Action for which the URL is requested.\n * @param displayName [in] The display name of the user.\n * @param accountId [in] Account ID of the user. N/A for signup.\n * @param pinCode [in] PIN code of the user. N/A for signup.\n * @return URL of the web page for the requested action.\n */\nstring diamondcard_url(t_dc_action action, const string &accountId, const string &pinCode);\n\n/** \n * Configure a user profile for a Diamondcard account.\n * @param user [inout] The user profile to configure.\n * @param accountId [in] Account ID of the user.\n * @param pinCode [in] PIN code of the user.\n */\nvoid diamondcard_set_user_config(t_user &user, const string &displayName,\n                                 const string &accountId, const string &pinCode);\n\n/**\n * Get all active Diamondcard users.\n * @return List of active Diamondcard users.\n */\nlist<t_user *>diamondcard_get_users(t_phone *phone);\n#endif\n"
  },
  {
    "path": "src/epa.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"epa.h\"\n\n#include \"log.h\"\n#include \"phone.h\"\n#include \"timekeeper.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nextern t_phone *phone;\nextern t_event_queue\t*evq_timekeeper;\nextern string local_hostname;\n\n/////////////\n// PRIVATE\n/////////////\n\nvoid t_epa::enqueue_request(t_request *r) {\n\tlog_file->write_header(\"t_epa::enqueue_request\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Enqueue request\\n\");\n\tlog_publication();\n\tlog_file->write_footer();\n\t\n\tqueue_publish.push(r);\n}\n\n\n/////////////\n// PROTECTED\n/////////////\n\nvoid t_epa::log_publication() const {\n\tlog_file->write_raw(\"Event: \");\n\tlog_file->write_raw(event_type);\n\tlog_file->write_raw(\", URI: \");\n\tlog_file->write_raw(request_uri.encode());\n\tlog_file->write_raw(\", SIP-ETag: \");\n\tlog_file->write_raw(etag);\n\tlog_file->write_endl();\n}\n\nvoid t_epa::remove_client_request(t_client_request **cr) {\n\tif ((*cr)->dec_ref_count() == 0) {\n\t\tMEMMAN_DELETE(*cr);\n\t\tdelete *cr;\n\t}\n\n\t*cr = NULL;\n}\n\nt_request *t_epa::create_publish(unsigned long expires, t_sip_body *body) const {\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_request *r = phone_user->create_request(PUBLISH, request_uri);\n\t\n\t// Call-ID\n\tr->hdr_call_id.set_call_id(NEW_CALL_ID(user_config));\n\t\n\t// CSeq\n\tr->hdr_cseq.set_method(PUBLISH);\n\tr->hdr_cseq.set_seqnr(NEW_SEQNR);\n\t\n\t// To\n\tr->hdr_to.set_uri(user_config->create_user_uri(false));\n\tr->hdr_to.set_display(user_config->get_display(false));\n\t\n\t// RFC 3903 4 Expires\n\tr->hdr_expires.set_time(expires);\n\t\n\t// RFC 3903 4 Event\n\tr->hdr_event.set_event_type(event_type);\n\t\n\t// SIP-If-Match\n\tif (!etag.empty()) {\n\t\tr->hdr_sip_if_match.set_etag(etag);\n\t}\n\t\n\t// Body\n\tif (body) {\n\t\tr->body = body;\n\t\tr->hdr_content_type.set_media(body->get_media());\n\t}\n\n\treturn r;\n}\n\nvoid t_epa::send_request(t_request *r, t_tuid tuid) const {\n\tphone->send_request(phone_user->get_user_profile(), r, tuid);\n}\n\nvoid t_epa::send_publish_from_queue(void) {\n\t// If there is a PUBLISH in the queue, then send it\n\twhile (!queue_publish.empty()) {\n\t\tlog_file->write_header(\"t_epa::send_publish_from_queue\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Get PUBLISH from queue.\\n\");\n\t\tlog_publication();\n\t\tlog_file->write_footer();\n\t\n\t\tt_request *req = queue_publish.front();\n\t\tqueue_publish.pop();\n\t\t\n\t\t// Update the SIP-If-Match header to the current entity tag\n\t\tif (!etag.empty()) {\n\t\t\treq->hdr_sip_if_match.set_etag(etag);\n\t\t} else {\n\t\t\treq->hdr_sip_if_match.clear();\n\t\t}\n\t\t\n\t\tif (req->hdr_expires.time == 0) {\n\t\t\tif (epa_state != EPA_PUBLISHED) {\n\t\t\t\tlog_file->write_header(\"t_epa::send_publish_from_queue\", LOG_NORMAL, LOG_DEBUG);\n\t\t\t\tlog_file->write_raw(\"Nothing published, discard unpublish\\n\");\n\t\t\t\tlog_publication();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\t\tMEMMAN_DELETE(req);\n\t\t\t\tdelete req;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tis_unpublishing = true;\n\t\t} else {\n\t\t\tis_unpublishing = false;\n\t\t}\n\t\t\n\t\tstop_timer(PUBLISH_TMR_PUBLICATION);\n\t\t\n\t\treq_out = new t_client_request(phone_user->get_user_profile(), req, 0);\n\t\tMEMMAN_NEW(req_out);\n\t\tsend_request(req, req_out->get_tuid());\n\t\tMEMMAN_DELETE(req);\n\t\tdelete req;\n\t\t\n\t\tbreak;\n\t}\n}\n\nvoid t_epa::start_timer(t_publish_timer timer, long duration) {\n\tt_tmr_publish *t = NULL;\n\n\tswitch(timer) {\n\tcase PUBLISH_TMR_PUBLICATION:\n\t\tt = new t_tmr_publish(duration, timer, event_type);\n\t\tMEMMAN_NEW(t);\n\t\tid_publication_timeout = t->get_object_id();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_epa::stop_timer(t_publish_timer timer) {\n\tunsigned short\t*id;\n\n\tswitch(timer) {\n\tcase PUBLISH_TMR_PUBLICATION:\n\t\tid = &id_publication_timeout;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tif (*id != 0) evq_timekeeper->push_stop_timer(*id);\n\t*id = 0;\n}\n\n//////////\n// PUBLIC\n//////////\n\nt_epa::t_epa(t_phone_user *pu, const string &_event_type, const t_url _request_uri) :\n\tphone_user(pu),\n\tepa_state(EPA_UNPUBLISHED),\n\tevent_type(_event_type),\n\trequest_uri(_request_uri),\n\tid_publication_timeout(0),\n\tpublication_expiry(3600),\n\tdefault_duration(3600),\n\tis_unpublishing(false),\n\tcached_body(NULL),\n\treq_out(NULL)\n{}\n\nt_epa::~t_epa() {\n\tclear();\n}\n\nt_epa::t_epa_state t_epa::get_epa_state(void) const {\n\treturn epa_state;\n}\n\nstring t_epa::get_failure_msg(void) const {\n\treturn failure_msg;\n}\n\nt_phone_user *t_epa::get_phone_user(void) const {\n\treturn phone_user;\n}\n\nt_user *t_epa::get_user_profile(void) const {\n\treturn phone_user->get_user_profile();\n}\n\nbool t_epa::recv_response(t_response *r, t_tuid tuid, t_tid tid) {\n\t// Discard response if it does not match a pending request\n\tif (!req_out) return true;\n\tt_request *req = req_out->get_request();\n\tif (r->hdr_cseq.method != req->method) return true;\n\n\t// Ignore provisional responses\n\tif (r->is_provisional()) return true;\n\t\n\tif (r->is_success()) {\n\t\t// RFC 3903 11.3\n\t\t// A 2XX response must contain a SIP-ETag header\n\t\tif (r->hdr_sip_etag.is_populated()) {\n\t\t\tetag = r->hdr_sip_etag.etag;\n\t\t} else {\n\t\t\tlog_file->write_report(\"SIP-ETag header missing from PUBLISH 2XX response.\",\n\t\t\t\t\"t_epa::recv_response\", LOG_NORMAL, LOG_WARNING);\n\t\t\tetag.clear();\n\t\t}\n\t\t\n\t\t// RFC 3903 1.1.1 says that the Expires header is mandatory\n\t\t// in a 2XX response. Some SIP servers do not include this\n\t\t// however. To interoperate with such servers, assume that\n\t\t// the granted expiry time equals the requested expiry time.\n\t\tif (!r->hdr_expires.is_populated()) {\n\t\t\tr->hdr_expires.set_time(\n\t\t\t\treq->hdr_expires.time);\n\t\t\t\t\n\t\t\tlog_file->write_header(\"t_epa::recv_response\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Mandatory Expires header missing.\\n\");\n\t\t\tlog_file->write_raw(\"Assuming expires = \");\n\t\t\tlog_file->write_raw(r->hdr_expires.time);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_publication();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t\t\n\t\t// If some faulty server sends a non-zero expiry time in\n\t\t// a response on an unsubscribe request, then ignore\n\t\t// the expiry time.\n\t\tif (r->hdr_expires.time == 0 || is_unpublishing) {\n\t\t\t// Unpublish succeeded.\n\t\t\tstop_timer(PUBLISH_TMR_PUBLICATION);\n\t\t\tetag.clear();\n\t\t\tis_unpublishing = false;\n\t\t\tepa_state = EPA_UNPUBLISHED;\n\t\t\t\n\t\t\tlog_file->write_header(\"t_epa::recv_response\", \n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unpublish successful.\\n\");\n\t\t\tlog_publication();\n\t\t\tlog_file->write_footer();\n\t\t} else {\n\t\t\tlog_file->write_header(\"t_epa::recv_response\");\n\t\t\tlog_file->write_raw(\"Publication successful.\\n\");\n\t\t\tlog_publication();\n\t\t\tlog_file->write_footer();\n\t\t\n\t\t\t// Start/refresh publish timer\n\t\t\tstop_timer(PUBLISH_TMR_PUBLICATION);\n\t\t\tunsigned long dur = r->hdr_expires.time;\n\t\t\tdur -= dur / 10;\n\t\t\tstart_timer(PUBLISH_TMR_PUBLICATION, dur * 1000);\n\t\t\tepa_state = EPA_PUBLISHED;\n\t\t}\n\t\n\t\tremove_client_request(&req_out);\n\t\tsend_publish_from_queue();\n\t\treturn true;\n\t}\n\t\n\t// Authentication\n\tif (r->must_authenticate()) {\n\t\tif (phone_user->authorize(req, r)) {\n\t\t\tphone_user->resend_request(req, req_out);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Authentication failed\n\t\t// Handle the 401/407 as a normal failure response\n\t}\n\t\n\t// PUBLISH failed\n\t\n\tif (is_unpublishing) {\n\t\t// Unpublish failed.\n\t\t// There is nothing we can do about that. Just clear\n\t\t// the internal publication.\n\t\tstop_timer(PUBLISH_TMR_PUBLICATION);\n\t\tetag.clear();\n\t\tis_unpublishing = false;\n\t\tepa_state = EPA_UNPUBLISHED;\n\t\t\n\t\tlog_file->write_header(\"t_epa::recv_response\", \n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Unpublish failed.\\n\");\n\t\tlog_publication();\n\t\tlog_file->write_footer();\n\t\t\n\t\tremove_client_request(&req_out);\n\t\treturn true;\n\t}\n\t\n\tif (r->code == R_423_INTERVAL_TOO_BRIEF) {\n\t\tif (!r->hdr_min_expires.is_populated()) {\n\t\t\t// Violation of RFC 3261 10.3 item 7\t\t\n\t\t\tlog_file->write_report(\"Min-Expires header missing from 423 response.\",\n\t\t\t\t\"t_epa::recv_response\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t} else if (r->hdr_min_expires.time <= publication_expiry) {\n\t\t\t// Wrong Min-Expires time\n\t\t\tstring s = \"Min-Expires (\";\n\t\t\ts += ulong2str(r->hdr_min_expires.time);\n\t\t\ts += \") is smaller than the requested \";\n\t\t\ts += \"time (\";\n\t\t\ts += ulong2str(publication_expiry);\n\t\t\ts += \")\";\n\t\t\tlog_file->write_report(s, \"t_epa::recv_response\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t} else {\n\t\t\t// Publish with the advised interval\n\t\t\tremove_client_request(&req_out);\n\t\t\tif (etag.empty()) {\n\t\t\t\t// Initial publication.\n\t\t\t\tpublish(r->hdr_min_expires.time, cached_body);\n\t\t\t} else {\n\t\t\t\tpublication_expiry = r->hdr_min_expires.time;\n\t\t\t\trefresh_publication();\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t} else if (r->code == R_412_CONDITIONAL_REQUEST_FAILED) {\n\t\tlog_file->write_header(\"t_epa::recv_response\");\n\t\tlog_file->write_raw(\"SIP-ETag mismatch, retry with initial publication.\\n\");\n\t\tlog_publication();\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\t// The state seems to be gone from the presence agent. Clear\n\t\t// the internal pubication state.\n\t\tremove_client_request(&req_out);\n\t\tetag.clear();\n\t\tepa_state = EPA_UNPUBLISHED;\n\t\t\n\t\t// Retry to publish state\n\t\tpublish(publication_expiry, cached_body);\n\t\treturn true;\n\t}\n\t\n\tremove_client_request(&req_out);\n\tepa_state = EPA_FAILED;\n\tfailure_msg = int2str(r->code);\n\tfailure_msg += ' ';\n\tfailure_msg += r->reason;\n\t\n\tlog_file->write_header(\"t_epa::recv_response\", \n\t\tLOG_NORMAL, LOG_WARNING);\n\tlog_file->write_raw(\"PUBLISH failure response.\\n\");\n\tlog_file->write_raw(r->code);\n\tlog_file->write_raw(\" \" + r->reason + \"\\n\");\n\tlog_publication();\n\tlog_file->write_footer();\n\n\tsend_publish_from_queue();\n\treturn true;\n}\n\nbool t_epa::match_response(t_response *r, t_tuid tuid) const {\n\treturn (req_out && req_out->get_tuid() == tuid);\n}\n\nbool t_epa::timeout(t_publish_timer timer) {\n\tswitch (timer) {\n\tcase PUBLISH_TMR_PUBLICATION:\n\t\tid_publication_timeout = 0;\n\t\t\t\n\t\tlog_file->write_header(\"t_epa::timeout\");\n\t\tlog_file->write_raw(\"Publication timed out.\\n\");\n\t\tlog_publication();\n\t\tlog_file->write_footer();\n\t\t\n\t\trefresh_publication();\n\t\treturn true;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn false;\n}\n\nbool t_epa::match_timer(t_publish_timer timer, t_object_id id_timer) const {\n\treturn id_timer == id_publication_timeout;\n}\n\nvoid t_epa::publish(unsigned long expires, t_sip_body *body) {\n\tt_request *r = create_publish(expires, body);\n\t\n\tif (req_out) {\n\t\t// A PUBLISH request is pending, queue this one.\n\t\t// Only 1 PUBLISH at a time may be sent.\n\t\t// RFC 3903 4\n\t\tenqueue_request(r);\n\t\treturn;\n\t}\n\t\n\t// If the body equals the cached body, then do not\n\t// delete the cached_body as that will delete the body!\n\tif (cached_body && body && body != cached_body) {\n\t\tMEMMAN_DELETE(cached_body);\n\t\tdelete cached_body;\n\t\tcached_body = NULL;\n\t}\n\t\n\tif (body) {\n\t\tcached_body = body->copy();\n\t}\n\t\n\tif (expires > 0) {\n\t\tpublication_expiry = expires;\n\t} else {\n\t\tpublication_expiry = default_duration;\n\t}\n\t\n\tis_unpublishing = false;\n\t\n\tstop_timer(PUBLISH_TMR_PUBLICATION);\n\t\n\treq_out = new t_client_request(phone_user->get_user_profile(), r, 0);\n\tMEMMAN_NEW(req_out);\n\tsend_request(r, req_out->get_tuid());\n\tMEMMAN_DELETE(r);\n\tdelete r;\n}\n\nvoid t_epa::unpublish(void) {\n\tif (!req_out && epa_state != EPA_PUBLISHED) {\n\t\tlog_file->write_header(\"t_epa::unpublish\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Nothing published, discard unpublish\\n\");\n\t\tlog_publication();\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\n\tt_request *r = create_publish(0, NULL);\n\t\n\tif (req_out) {\n\t\t// A PUBLISH request is pending, queue this one.\n\t\t// Only 1 PUBLISH at a time may be sent.\n\t\t// RFC 3903 4\n\t\tenqueue_request(r);\n\t\treturn;\n\t}\n\t\n\tif (cached_body) {\n\t\tMEMMAN_DELETE(cached_body);\n\t\tdelete cached_body;\n\t\tcached_body = NULL;\n\t}\n\t\t\n\tis_unpublishing = true;\n\t\n\tstop_timer(PUBLISH_TMR_PUBLICATION);\n\t\n\treq_out = new t_client_request(phone_user->get_user_profile(), r, 0);\n\tMEMMAN_NEW(req_out);\n\tsend_request(r, req_out->get_tuid());\n\tMEMMAN_DELETE(r);\n\tdelete r;\t\n}\n\nvoid t_epa::refresh_publication(void) {\n\tpublish(publication_expiry, NULL);\n}\n\nvoid t_epa::clear(void) {\n\tif (req_out) remove_client_request(&req_out);\n\tif (id_publication_timeout) stop_timer(PUBLISH_TMR_PUBLICATION);\n\t\n\tif (cached_body) {\n\t\tMEMMAN_DELETE(cached_body);\n\t\tdelete cached_body;\n\t\tcached_body = NULL;\n\t}\n\n\t// Cleanup list of unsent PUBLISH messages\n\twhile (!queue_publish.empty()) {\n\t\tt_request *r = queue_publish.front();\n\t\tqueue_publish.pop();\n\t\tMEMMAN_DELETE(r);\n\t\tdelete r;\n\t}\n}\n"
  },
  {
    "path": "src/epa.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Event Publication Agent (EPA) [RFC 3903]\n */\n \n#ifndef _EPA_H\n#define _EPA_H\n\n#include <queue>\n#include <string>\n\n#include \"id_object.h\"\n#include \"phone_user.h\"\n#include \"sockets/url.h\"\n#include \"parser/sip_body.h\"\n#include \"protocol.h\"\n\nusing namespace std;\n\n\n/** Event Publication Agent (EPA) [RFC 3903] */\nclass t_epa {\npublic:\n\t/** State of the EPA */\n\tenum t_epa_state {\n\t\tEPA_UNPUBLISHED, /**< The event has not been published. */\n\t\tEPA_PUBLISHED,\t /**< The event has been published. */\n\t\tEPA_FAILED,\t /**< Failed to publish the event. */\n\t};\n\t\nprivate:\n\t/**\n\t * Queue of pending outgoing PUBLISH requests. A next PUBLISH\n\t * will only be sent after the previous PUBLISH has been\n\t * answered.\n\t */\n\tqueue<t_request *>\tqueue_publish;\n\t\n\t/**\n\t * Enqueue a request.\n\t * @param r [in] Request to enqueue.\n\t */\n\tvoid\t\tenqueue_request(t_request *r);\n\nprotected:\n\t/** Phone user for whom publications are issued. */\n\tt_phone_user\t*phone_user;\n\t\n\t/** EPA state. */\n\tt_epa_state\tepa_state;\n\t\n\t/** Detailed failure message when @ref epa_state == @ref EPA_FAILED */\n\tstring\t\tfailure_msg;\n\n\t/** \n\t * Entity tag associated with the publication.\n\t * For an initial publication there is no entity tag yet.\n\t */\n\tstring\t\tetag;\n\n\t/** Event for which the event state is published. */\n\tstring\t\tevent_type;\n\t\n\t/** Request-URI for the publish request. */\n\tt_url\t\trequest_uri;\n\t\n\t/** Timer indicating when a publication must be refreshed. */\n\tt_object_id\tid_publication_timeout;\n\t\n\t/** Expiry duration (sec) of a publication. */\n\tunsigned long\tpublication_expiry;\n\t\n\t/** Default duration for a publication/ */\n\tunsigned long\tdefault_duration;\n\t\n\t/** Indicates if an unpublish is in progress. */\n\tbool\t\tis_unpublishing;\n\t\n\t/** Cached body of last publication. */\n\tt_sip_body\t*cached_body;\n\t\n\t/** Log the publication details */\n\tvoid log_publication(void) const;\n\t\n\t/**\n\t * Remove a pending request. Pass one of the client request pointers.\n\t * @param cr [in] Client request to remove.\n\t */\n\tvoid remove_client_request(t_client_request **cr);\n\t\n\t/**\n\t * Create a PUBLISH request.\n\t * @param expires [in] Expiry time in seconds.\n\t * @param body [in] Body for the request. The body will be destroyed when\n\t * the request will be destroyed.\n\t */\n\tvirtual t_request *create_publish(unsigned long expires, t_sip_body *body) const;\n\n\t/**\n\t * Send request.\n\t * @param r [in] Request to send.\n\t * @param tuid [in] Transaction user id.\n\t */\n\tvoid send_request(t_request *r, t_tuid tuid) const;\n\t\n\t/**\n\t * Send the next PUBLISH request from the queue.\n\t * If the queue is empty, then this method does nothing.\n\t */\n\tvoid send_publish_from_queue(void);\n\n\t/** \n\t * Start a publication timer.\n\t * @param timer [in] Type of publication timer.\n\t * @param duration [in] Duration of timer in ms\n\t */\n\tvoid start_timer(t_publish_timer timer, long duration);\n\t\n\t/**\n\t * Stop a publication timer.\n\t * @param timer [in] Type of publication timer.\n\t */\n\tvoid stop_timer(t_publish_timer timer);\n\t\npublic:\n\t/** Pending request */\n\tt_client_request\t*req_out;\n\n\t/** Constructor. */\n\tt_epa(t_phone_user *pu, const string &_event_type, const t_url _request_uri);\n\t\n\t/** Destructor. */\n\tvirtual ~t_epa();\n\t\n\t/** @name Getters */\n\t//@{\n\tt_epa_state get_epa_state(void) const;\n\tstring get_failure_msg(void) const;\n\tt_phone_user *get_phone_user(void) const;\n\t//@}\n\t\n\t/**\n\t * Get the user profile of the user.\n\t * @return The user profile.\n\t */\n\tt_user *get_user_profile(void) const;\n\t\n\t/**\n\t * Receive PUBLISH response.\n\t * @param r [in] Received response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return The return value indicates if processing is finished.\n\t */\n\tvirtual bool recv_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Match response with a pending publish.\n\t * @param r [in] The response.\n\t * @param tuid [in] Transaction user id.\n\t * @return True if the response matches, otherwise false.\n\t */\n\tvirtual bool match_response(t_response *r, t_tuid tuid) const;\n\t\n\t/**\n\t * Process timeouts\n\t * @param timer [in] Type of publication timer.\n\t * @return The return value indicates if processing is finished.\n\t */\n\tvirtual bool timeout(t_publish_timer timer);\n\t\n\t/**\n\t * Match timer id with a running timer.\n\t * @param timer [in] Type of publication timer.\n\t * @return True, if id matches, otherwise false.\n\t */\n\tvirtual bool match_timer(t_publish_timer timer, t_object_id id_timer) const;\n\t\n\t/**\n\t * Publish event state.\n\t * @param expired [in] Duration of publication in seconds.\n\t * @param body [in] Body for PUBLISH request.\n\t * @note The body will be deleted when the PUBLISH has been sent.\n\t * The caller of this method should *not* delete the body.\n\t */\n\tvirtual void publish(unsigned long expires, t_sip_body *body);\n\t\n\t/** Terminate publication. */\n\tvirtual void unpublish(void);\n\t\n\t/** Refresh publication. */\n\tvirtual void refresh_publication(void);\n\t\n\t/** Clear all state */\n\tvirtual void clear(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/events.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include \"events.h\"\n#include \"log.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nstring event_type2str(t_event_type t) {\n\tswitch(t) {\n\tcase EV_QUIT:\t\treturn \"EV_QUIT\";\n\tcase EV_NETWORK: \treturn \"EV_NETWORK\";\n\tcase EV_USER: \t\treturn \"EV_USER\";\n\tcase EV_TIMEOUT: \treturn \"EV_TIMEOUT\";\n\tcase EV_FAILURE: \treturn \"EV_FAILURE\";\n\tcase EV_START_TIMER: \treturn \"EV_START_TIMER\";\n\tcase EV_STOP_TIMER: \treturn \"EV_STOP_TIMER\";\n\tcase EV_ABORT_TRANS:\treturn \"EV_ABORT_TRANS\";\n\tcase EV_STUN_REQUEST:\treturn \"EV_STUN_REQUEST\";\n\tcase EV_STUN_RESPONSE:\treturn \"EV_STUN_RESPONSE\";\n\tcase EV_NAT_KEEPALIVE:\treturn \"EV_NAT_KEEPALIVE\";\n\tcase EV_ICMP:\t\treturn \"EV_ICMP\";\n\tcase EV_UI:\t\treturn \"EV_UI\";\n\tcase EV_ASYNC_RESPONSE:\treturn \"EV_ASYNC_RESPONSE\";\n\tcase EV_BROKEN_CONNECTION: return \"EV_BROKEN_CONNECTION\";\n\tcase EV_TCP_PING:\treturn \"EV_TCP_PING\";\n\tcase EV_FN_CALL:\treturn \"EV_FN_CALL\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_network\n///////////////////////////////////////////////////////////\n\nt_event_network::t_event_network(t_sip_message *m) : t_event() {\n\tmsg = m->copy();\n\tsrc_addr = 0;\n\tsrc_port = 0;\n\tdst_addr = 0;\n\tdst_port = 0;\n\ttransport.clear();\n}\n\nt_event_network::~t_event_network() {\n\tMEMMAN_DELETE(msg);\n\tdelete msg;\n}\n\nt_event_type t_event_network::get_type(void) const {\n\treturn EV_NETWORK;\n}\n\nt_sip_message *t_event_network::get_msg(void) const {\n\treturn msg;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_quit\n///////////////////////////////////////////////////////////\n\nt_event_quit::~t_event_quit() {}\n\nt_event_type t_event_quit::get_type(void) const {\n\treturn EV_QUIT;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_user\n///////////////////////////////////////////////////////////\n\nt_event_user::t_event_user(t_user *u, t_sip_message *m, unsigned short _tuid,\n\t\tunsigned short _tid) : t_event()\n{\n\tmsg = m->copy();\n\ttuid = _tuid;\n\ttid = _tid;\n\ttid_cancel_target = 0;\n\tif (u) {\n\t\tuser_config = u->copy();\n\t} else {\n\t\tuser_config = NULL;\n\t}\n}\n\nt_event_user::t_event_user(t_user *u, t_sip_message *m, unsigned short _tuid,\n\t\tunsigned short _tid, unsigned short _tid_cancel_target) :\n\t\t\tt_event()\n{\n\tmsg = m->copy();\n\ttuid = _tuid;\n\ttid = _tid;\n\ttid_cancel_target = _tid_cancel_target;\n\tif (u) {\n\t\tuser_config = u->copy();\n\t} else {\n\t\tuser_config = NULL;\n\t}\n}\n\nt_event_user::~t_event_user() {\n\tMEMMAN_DELETE(msg);\n\tdelete msg;\n\t\n\tif (user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\n}\n\nt_event_type t_event_user::get_type(void) const {\n\treturn EV_USER;\n}\n\nt_sip_message *t_event_user::get_msg(void) const {\n\treturn msg;\n}\n\nunsigned short t_event_user::get_tuid(void) const {\n\treturn tuid;\n}\n\nunsigned short t_event_user::get_tid(void) const {\n\treturn tid;\n}\n\nunsigned short t_event_user::get_tid_cancel_target(void) const {\n\treturn tid_cancel_target;\n}\n\nt_user *t_event_user::get_user_config(void) const {\n\treturn user_config;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_timeout\n///////////////////////////////////////////////////////////\n\nt_event_timeout::t_event_timeout(t_timer *t) : t_event() {\n\ttimer = t->copy();\n}\n\nt_event_timeout::~t_event_timeout() {\n\tMEMMAN_DELETE(timer);\n\tdelete timer;\n}\n\nt_event_type t_event_timeout::get_type(void) const {\n\treturn EV_TIMEOUT;\n}\n\nt_timer *t_event_timeout::get_timer(void) const {\n\treturn timer;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_failure\n///////////////////////////////////////////////////////////\nt_event_failure::t_event_failure(t_failure f, unsigned short _tid) :\n\tt_event(),\n\tfailure(f),\n\ttid_populated(true),\n\ttid(_tid)\n{}\n\nt_event_failure::t_event_failure(t_failure f, const string &_branch, const t_method &_cseq_method) :\n\t\tfailure(f),\n\t\ttid_populated(false),\n\t\tbranch(_branch),\n\t\tcseq_method(_cseq_method)\n{}\n\nt_event_type t_event_failure::get_type(void) const {\n\treturn EV_FAILURE;\n}\n\nt_failure t_event_failure::get_failure(void) const {\n\treturn failure;\n}\n\nunsigned short t_event_failure::get_tid(void) const {\n\treturn tid;\n}\n\nstring t_event_failure::get_branch(void) const {\n\treturn branch;\n}\n\nt_method t_event_failure::get_cseq_method(void) const {\n\treturn cseq_method;\n}\n\nbool t_event_failure::is_tid_populated(void) const {\n\treturn tid_populated;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_start_timer\n///////////////////////////////////////////////////////////\nt_event_start_timer::t_event_start_timer(t_timer *t) :\n\tt_event()\n{\n\ttimer = t->copy();\n}\n\nt_event_type t_event_start_timer::get_type(void) const {\n\treturn EV_START_TIMER;\n}\n\nt_timer *t_event_start_timer::get_timer(void) const {\n\treturn timer;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_stop_timer\n///////////////////////////////////////////////////////////\nt_event_stop_timer::t_event_stop_timer(unsigned short id) :\n\tt_event()\n{\n\ttimer_id = id;\n}\n\nt_event_type t_event_stop_timer::get_type(void) const {\n\treturn EV_STOP_TIMER;\n}\n\nunsigned short t_event_stop_timer::get_timer_id(void) const {\n\treturn timer_id;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_abort_trans\n///////////////////////////////////////////////////////////\nt_event_abort_trans::t_event_abort_trans(unsigned short _tid) :\n\tt_event()\n{\n\ttid = _tid;\n}\n\nt_event_type t_event_abort_trans::get_type(void) const {\n\treturn EV_ABORT_TRANS;\n}\n\nunsigned short t_event_abort_trans::get_tid(void) const {\n\treturn tid;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_stun_request\n///////////////////////////////////////////////////////////\n\nt_event_stun_request::t_event_stun_request(t_user *u,\n\t\tStunMessage *m, t_stun_event_type ev_type,\n\t\tunsigned short _tuid, unsigned short _tid) : t_event()\n{\n\tmsg = new StunMessage(*m);\n\tMEMMAN_NEW(msg);\n\tstun_event_type = ev_type;\n\ttuid = _tuid;\n\ttid = _tid;\n\tdst_addr = 0;\n\tdst_port = 0;\n\tuser_config = u->copy();\n}\n\nt_event_stun_request::~t_event_stun_request() {\n\tMEMMAN_DELETE(msg);\n\tdelete msg;\n\tMEMMAN_DELETE(user_config);\n\tdelete user_config;\n}\n\nt_event_type t_event_stun_request::get_type(void) const {\n\treturn EV_STUN_REQUEST;\n}\n\t\nStunMessage *t_event_stun_request::get_msg(void) const {\n\treturn msg;\n}\n\nunsigned short t_event_stun_request::get_tuid(void) const {\n\treturn tuid;\n}\nunsigned short t_event_stun_request::get_tid(void) const {\n\treturn tid;\n}\n\nt_stun_event_type t_event_stun_request::get_stun_event_type(void) const {\n\treturn stun_event_type;\n}\n\nt_user *t_event_stun_request::get_user_config(void) const {\n\treturn user_config;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_stun_response\n///////////////////////////////////////////////////////////\n\nt_event_stun_response::t_event_stun_response(StunMessage *m, unsigned short _tuid,\n\t\tunsigned short _tid) : t_event()\n{\n\tmsg = new StunMessage(*m);\n\tMEMMAN_NEW(msg);\n\ttuid = _tuid;\n\ttid = _tid;\n}\n\nt_event_stun_response::~t_event_stun_response() {\n\tMEMMAN_DELETE(msg);\n\tdelete(msg);\n}\n\nt_event_type t_event_stun_response::get_type(void) const {\n\treturn EV_STUN_RESPONSE;\n}\n\nStunMessage *t_event_stun_response::get_msg(void) const {\n\treturn msg;\n}\n\nunsigned short t_event_stun_response::get_tuid(void) const {\n\treturn tuid;\n}\n\nunsigned short t_event_stun_response::get_tid(void) const {\n\treturn tid;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_nat_keepalive\n///////////////////////////////////////////////////////////\nt_event_type t_event_nat_keepalive::get_type(void) const {\n\treturn EV_NAT_KEEPALIVE;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_icmp\n///////////////////////////////////////////////////////////\nt_event_icmp::t_event_icmp(const t_icmp_msg &m) : icmp(m) {}\n\nt_event_type t_event_icmp::get_type(void) const {\n\treturn EV_ICMP;\n}\n\nt_icmp_msg t_event_icmp::get_icmp(void) const {\n\treturn icmp;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_ui\n///////////////////////////////////////////////////////////\nt_event_ui::t_event_ui(t_ui_event_type _type) : \n\tt_event(),\n\ttype(_type) \n{}\n\nt_event_type t_event_ui::get_type(void) const {\n\treturn EV_UI;\n}\n\nvoid t_event_ui::set_line(int _line) {\n\tline = _line;\n}\n\nvoid t_event_ui::set_codec(t_audio_codec _codec) {\n\tcodec = _codec;\n}\n\nvoid t_event_ui::set_dtmf_event(t_dtmf_ev _dtmf_event) {\n\tdtmf_event = _dtmf_event;\n}\n\nvoid t_event_ui::set_encrypted(bool on) {\n\tencrypted = on;\n}\n\nvoid t_event_ui::set_cipher_mode(const string &_cipher_mode) {\n\tcipher_mode = _cipher_mode;\n}\n\nvoid t_event_ui::set_zrtp_sas(const string &sas) {\n\tzrtp_sas = sas;\n}\n\nvoid t_event_ui::set_display_msg(const string &_msg, t_msg_priority &_msg_priority) {\n\tmsg = _msg;\n\tmsg_priority = _msg_priority;\n}\n\nvoid t_event_ui::exec(t_userintf *user_intf) {\n\tswitch (type) {\n\tcase TYPE_UI_CB_DISPLAY_MSG:\n\t\tui->cb_display_msg(msg, msg_priority);\n\t\tbreak;\n\tcase TYPE_UI_CB_DTMF_DETECTED:\n\t\tui->cb_dtmf_detected(line, dtmf_event);\n\t\tbreak;\n\tcase TYPE_UI_CB_SEND_DTMF:\n\t\tui->cb_send_dtmf(line, dtmf_event);\n\t\tbreak;\n\tcase TYPE_UI_CB_RECV_CODEC_CHANGED:\n\t\tui->cb_recv_codec_changed(line, codec);\n\t\tbreak;\n\tcase TYPE_UI_CB_LINE_STATE_CHANGED:\n\t\tui->cb_line_state_changed();\n\t\tbreak;\n\tcase TYPE_UI_CB_LINE_ENCRYPTED:\n\t\tui->cb_line_encrypted(line, encrypted, cipher_mode);\n\t\tbreak;\n\tcase TYPE_UI_CB_SHOW_ZRTP_SAS:\n\t\tui->cb_show_zrtp_sas(line, zrtp_sas);\n\t\tbreak;\n\tcase TYPE_UI_CB_ZRTP_CONFIRM_GO_CLEAR:\n\t\tui->cb_zrtp_confirm_go_clear(line);\n\t\tbreak;\n\tcase TYPE_UI_CB_QUIT:\n\t\tui->cmd_quit();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_async_response\n///////////////////////////////////////////////////////////\n\nt_event_async_response::t_event_async_response(t_response_type type) :\n\tt_event(),\n\tresponse_type(type)\n{}\n\nt_event_type t_event_async_response::get_type(void) const {\n\treturn EV_ASYNC_RESPONSE;\n}\n\nvoid t_event_async_response::set_bool_response(bool b) {\n\tbool_response = b;\n}\n\nt_event_async_response::t_response_type t_event_async_response::get_response_type(void) const {\n\treturn response_type;\n}\n\nbool t_event_async_response::get_bool_response(void) const {\n\treturn bool_response;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_broken_connection\n///////////////////////////////////////////////////////////\n\nt_event_broken_connection::t_event_broken_connection(const t_url &url) :\n\tt_event(),\n\tuser_uri_(url)\n{}\n\nt_event_type t_event_broken_connection::get_type(void) const {\n\treturn EV_BROKEN_CONNECTION;\n}\n\nt_url t_event_broken_connection::get_user_uri(void) const {\n\treturn user_uri_;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_tcp_ping\n///////////////////////////////////////////////////////////\n\nt_event_tcp_ping::t_event_tcp_ping(const t_url &url, IPaddr dst_addr, unsigned short dst_port) :\n\tt_event(),\n\tuser_uri_(url),\n\tdst_addr_(dst_addr),\n\tdst_port_(dst_port)\n{}\n\nt_event_type t_event_tcp_ping::get_type(void) const {\n\treturn EV_TCP_PING;\n}\n\nt_url t_event_tcp_ping::get_user_uri(void) const {\n\treturn user_uri_;\n}\n\nIPaddr t_event_tcp_ping::get_dst_addr(void) const {\n\treturn dst_addr_;\n}\n\nunsigned short t_event_tcp_ping::get_dst_port(void) const {\n\treturn dst_port_;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_fncall\n///////////////////////////////////////////////////////////\n\nt_event_fncall::t_event_fncall(std::function<void()> fn)\n\t: m_fn(fn) {\n\n}\nvoid t_event_fncall::invoke() {\n\tm_fn();\n}\n\nt_event_type t_event_fncall::get_type(void) const {\n\treturn EV_FN_CALL;\n}\n\n///////////////////////////////////////////////////////////\n// class t_event_queue\n///////////////////////////////////////////////////////////\n\nt_event_queue::t_event_queue() : sema_evq(0), sema_caught_interrupt(0) {}\n\nt_event_queue::~t_event_queue() {\n\tlog_file->write_header(\"t_event_queue::~t_event_queue\", LOG_NORMAL, LOG_INFO);\n\tlog_file->write_raw(\"Clean up event queue.\\n\");\n\n\twhile (!ev_queue.empty())\n\t{\n\t\tt_event *e = ev_queue.front();\n\t\tev_queue.pop();\n\t\tlog_file->write_raw(\"\\nDeleting unprocessed event: \\n\");\n\t\tlog_file->write_raw(\"Type: \");\n\t\tlog_file->write_raw(event_type2str(e->get_type()));\n\t\tlog_file->write_raw(\", Pointer: \");\n\t\tlog_file->write_raw(ptr2str(e));\n\t\tlog_file->write_endl();\n\t\tMEMMAN_DELETE(e);\n\t\tdelete e;\n\t}\n\n\tlog_file->write_footer();\n}\n\nvoid t_event_queue::push(t_event *e) {\n\tmutex_evq.lock();\n\tev_queue.push(e);\n\tmutex_evq.unlock();\n\tsema_evq.up();\n}\n\nvoid t_event_queue::push_quit(void) {\n\tt_event_quit *event = new t_event_quit();\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_fncall(std::function<void()> fn) {\n\tt_event_fncall* event = new t_event_fncall(fn);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_network(t_sip_message *m, const t_ip_port &ip_port) {\n\tt_event_network\t*event = new t_event_network(m);\n\tMEMMAN_NEW(event);\n\tevent->dst_addr = ip_port.ipaddr;\n\tevent->dst_port = ip_port.port;\n\tevent->transport = ip_port.transport;\n\tpush(event);\n}\n\nvoid t_event_queue::push_user(t_user *user_config, t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid)\n{\n\tt_event_user *event = new t_event_user(user_config, m, tuid, tid);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_user(t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid)\n{\n\tpush_user(NULL, m, tuid, tid);\n}\n\nvoid t_event_queue::push_user_cancel(t_user *user_config, t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid, unsigned short target_tid)\n{\n\tt_event_user *event = new t_event_user(user_config, m, tuid, tid, target_tid);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_user_cancel(t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid, unsigned short target_tid)\n{\n\tpush_user_cancel(NULL, m, tuid, tid, target_tid);\n}\n\nvoid t_event_queue::push_timeout(t_timer *t) {\n\tt_event_timeout *event = new t_event_timeout(t);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_failure(t_failure f, unsigned short tid) {\n\tt_event_failure *event = new t_event_failure(f, tid);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_failure(t_failure f, const string &branch, const t_method &cseq_method) {\n\tt_event_failure *event = new t_event_failure(f, branch, cseq_method);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_start_timer(t_timer *t) {\n\tt_event_start_timer *event = new t_event_start_timer(t);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_stop_timer(unsigned short timer_id) {\n\tt_event_stop_timer *event = new t_event_stop_timer(timer_id);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_abort_trans(unsigned short tid) {\n\tt_event_abort_trans *event = new t_event_abort_trans(tid);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_stun_request(t_user *user_config, \n\t\tStunMessage *m, t_stun_event_type ev_type,\n\t\tunsigned short tuid, unsigned short tid,\n\t\tIPaddr ipaddr, unsigned short port, unsigned short src_port)\n{\n\tt_event_stun_request *event = new t_event_stun_request(user_config, \n\t\tm, ev_type, tuid, tid);\n\tMEMMAN_NEW(event);\n\tevent->dst_addr = ipaddr;\n\tevent->dst_port = port;\n\tevent->src_port = src_port;\n\n\tpush(event);\n}\n\nvoid t_event_queue::push_stun_response(StunMessage *m,\n\t\tunsigned short tuid, unsigned short tid)\n{\n\tt_event_stun_response *event = new t_event_stun_response(m, tuid, tid);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_nat_keepalive(IPaddr ipaddr, unsigned short port) {\n\tt_event_nat_keepalive *event = new t_event_nat_keepalive();\n\tMEMMAN_NEW(event);\n\tevent->dst_addr = ipaddr;\n\tevent->dst_port = port;\n\n\tpush(event);\n}\n\nvoid t_event_queue::push_icmp(const t_icmp_msg &m) {\n\tt_event_icmp *event = new t_event_icmp(m);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_refer_permission_response(bool permission) {\n\tt_event_async_response *event = new t_event_async_response(\n\t\t\tt_event_async_response::RESP_REFER_PERMISSION);\n\tMEMMAN_NEW(event);\n\tevent->set_bool_response(permission);\n\tpush(event);\n}\n\nvoid t_event_queue::push_broken_connection(const t_url &user_uri) {\n\tt_event_broken_connection *event = new t_event_broken_connection(user_uri);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nvoid t_event_queue::push_tcp_ping(const t_url &user_uri, IPaddr dst_addr, unsigned short dst_port) \n{\n\tt_event_tcp_ping *event = new t_event_tcp_ping(user_uri, dst_addr, dst_port);\n\tMEMMAN_NEW(event);\n\tpush(event);\n}\n\nt_event *t_event_queue::pop(void) {\n\tt_event *e;\n\tbool interrupt;\n\n\tdo {\n\t\tinterrupt = false;\n\t\tsema_evq.down();\n\t\tmutex_evq.lock();\n\n\t\tif (sema_caught_interrupt.try_down()) {\n\t\t\t// This pop is non-interruptable, so ignore the interrupt\n\t\t\tinterrupt = true;\n\t\t} else {\n\t\t\te = ev_queue.front();\n\t\t\tev_queue.pop();\n\t\t}\n\n\t\tmutex_evq.unlock();\n\t} while (interrupt);\n\n\treturn e;\n}\n\nt_event *t_event_queue::pop(bool &interrupted) {\n\tt_event *e;\n\n\tsema_evq.down();\n\tmutex_evq.lock();\n\n\tif (sema_caught_interrupt.try_down()) {\n\t\tinterrupted = true;\n\t\te = NULL;\n\t} else {\n\t\tinterrupted = false;\n\t\te = ev_queue.front();\n\t\tev_queue.pop();\n\t}\n\n\tmutex_evq.unlock();\n\n\treturn e;\n}\n\nvoid t_event_queue::interrupt(void) {\n\tsema_caught_interrupt.up();\n\tsema_evq.up();\n}\n"
  },
  {
    "path": "src/events.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Threads communicate by passing events via event queues.\n */\n\n#ifndef _EVENTS_H\n#define _EVENTS_H\n\n#include <queue>\n#include \"protocol.h\"\n#include \"timekeeper.h\"\n#include \"stun/stun.h\"\n#include \"audio/audio_codecs.h\"\n#include \"audio/rtp_telephone_event.h\"\n#include \"parser/sip_message.h\"\n#include \"sockets/ipaddr.h\"\n#include \"sockets/socket.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include \"threads/sema.h\"\n\nusing namespace std;\n\n// Forward declarations\nclass t_userintf;\n\n/** Different types of events. */\nenum t_event_type {\n\tEV_QUIT,\t\t/**< Generic quit event */\n\tEV_NETWORK,\t\t/**< Network event, eg. SIP message from/to network */\n\tEV_USER,\t\t/**< User event, eg. SIP message from/to user  */\n\tEV_TIMEOUT,\t\t/**< Timer expiry */\n\tEV_FAILURE,\t\t/**< Failure, eg. transport failure */\n\tEV_START_TIMER,\t\t/**< Start timer */\n\tEV_STOP_TIMER,\t\t/**< Stop timer */\n\tEV_ABORT_TRANS,\t\t/**< Abort transaction */\n\tEV_STUN_REQUEST,\t/**< Outgoing STUN request */\n\tEV_STUN_RESPONSE,\t/**< Received STUN response */\n\tEV_NAT_KEEPALIVE,\t/**< Send a NAT keep alive packet */\n\tEV_ICMP,\t\t/**< ICMP error */\n\tEV_UI,\t\t\t/**< User interface event */\n\tEV_ASYNC_RESPONSE,\t/**< Response on an asynchronous question */\n\tEV_BROKEN_CONNECTION,\t/**< Persitent connection to SIP proxy broken */\n\tEV_TCP_PING,\t\t/**< Send a TCP ping (double CRLF) */\n\tEV_FN_CALL,\t\t/**< Queued function call */\n};\n\n/** Abstract parent class for all events */\nclass t_event {\npublic:\n\tvirtual ~t_event() {}\n\t\n\t/** Get the type of this event. */\n\tvirtual t_event_type get_type(void) const = 0;\n};\n\n\n/** \n * Generic quit event.\n * The quit event instructs a thread to exit gracefully.\n */\nclass t_event_quit : public t_event {\npublic:\n\tvirtual ~t_event_quit();\n\tvirtual t_event_type get_type(void) const;\n};\n\n\n/** \n * Network events.\n * A network event is a SIP message going from the transaction manager\n * to the network or v.v.\n */\nclass t_event_network : public t_event {\nprivate:\n\t/** The SIP message. */\n\tt_sip_message\t*msg;\n\npublic:\n\tIPaddr\tsrc_addr; /**< Source IP address of the SIP message (host order). */\n\tunsigned short\tsrc_port; /**< Source port of the SIP message (host order). */\n\tIPaddr\tdst_addr; /**< Destination IP address of the SIP message (host order). */\n\tunsigned short\tdst_port; /**< Destination port of the SIP message (host order). */\n\tstring\t\ttransport; /**< Transport protocol */\n\n\t/**\n\t * Constructor.\n\t * The event will keep a copy of the SIP message.\n\t * @param m [in] The SIP message.\n\t */\n\tt_event_network(t_sip_message *m);\n\t\n\t~t_event_network();\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/** \n\t * Get the SIP message.\n\t * @return Pointer to the SIP message inside this event.\n\t */\n\tt_sip_message *get_msg(void) const;\n};\n\n\n/**\n * User events.\n * A user event is a SIP message going from the user to the\n * transaction manager or v.v.\n */\nclass t_event_user : public t_event {\nprivate:\n\tt_sip_message\t*msg;\t/**< The SIP message. */\n\tunsigned short\ttuid;\t/**< Transaction user id. */\n\tunsigned short\ttid;\t/**< Transaction id. */\n\n\t/**\n\t * Transaction id that is the target of the CANCEL message.\n\t * Only set if tid is a CANCEL transaction and the event\n\t * is sent towards the user.\n\t */\n\tunsigned short\ttid_cancel_target;\n\t\n\t/** User profile of the user sending/receiving the SIP message. */\n\tt_user\t\t*user_config;\n\npublic:\n\t/** Constructor.\n\t * @param u [in] User profile.\n\t * @param m [in] SIP message\n\t * @param _tuid [in] Transaction user id associated with this message.\n\t * @param _tid [in] Transaction id of the transaction for this message.\n\t */\n\tt_event_user(t_user *u, t_sip_message *m, unsigned short _tuid,\n\t\tunsigned short _tid);\n\t\t\n\t/** Constructor for CANCEL request towards the user.\n\t * @param u [in] User profile.\n\t * @param m [in] SIP message.\n\t * @param _tuid [in] Transaction user id associated with this message.\n\t * @param _tid [in] Transaction id of the transaction for this message.\n\t * @param _tid_cancel_target [in] Id of the target transaction of a CANCEL request.\n\t */\n\tt_event_user(t_user *u, t_sip_message *m, unsigned short _tuid,\n\t\tunsigned short _tid, unsigned short _tid_cancel_target);\n\t\t\n\t~t_event_user();\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/** \n\t * Get the SIP message.\n\t * @return Pointer to the SIP message inside this event.\n\t */\n\tt_sip_message *get_msg(void) const;\n\t\n\t/** Get transaction user id. */\n\tunsigned short get_tuid(void) const;\n\t\n\t/** Get transaction id. */\n\tunsigned short get_tid(void) const;\n\t\n\t/** Get the CANCEL target transaction id. */\n\tunsigned short get_tid_cancel_target(void) const;\n\t\n\t/**\n\t * Get the user profile.\n\t * @return Pointer to the user profile inside the event.\n\t */\n\tt_user *get_user_config(void) const;\n};\n\n\n/**\n * Time out events.\n * Expiration of a timer is signalled by a time out event.\n */\nclass t_event_timeout : public t_event {\nprivate:\n\t/**\n\t * The epxired timer.\n\t * @note Timer pointer will be deleted upon destruction of the object.\n\t */\n\tt_timer\t\t*timer;\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param t [in] The expired timer.\n\t * @note The event will keep a copy of the timer.\n\t */\n\tt_event_timeout(t_timer *t);\n\t\n\t~t_event_timeout();\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get the timer from the event.\n\t * @return The timer.\n\t */\n\tt_timer *get_timer(void) const;\n};\n\n/**\n * Failure events.\n */\nclass t_event_failure : public t_event {\nprivate:\n\tt_failure\tfailure;\t/**< Type of failure. */\n\t\n\t/**\n\t * Indicates if the tid value is populated. If the tid value is not\n\t * populated, then the branch and cseq_method are populated.\n\t */\n\tbool\t\ttid_populated;\n\t\n\tunsigned short\ttid;\t\t/**< Id of transaction that failed. */\n\t\n\tstring\t\tbranch;\t\t/**< Branch parameter of SIP message that failed. */\n\tt_method\tcseq_method;\t/**< CSeq method of SIP message that failed. */\npublic:\n\t/**\n\t * Constructor.\n\t * @param f [in] Type of failure.\n\t * @param _tid [in] Transaction id.\n\t */\n\tt_event_failure(t_failure f, unsigned short _tid);\n\t\n\t/** Constructor */\n\tt_event_failure(t_failure f, const string &_branch, const t_method &_cseq_method);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get the type of failure.\n\t * @return Type of failure.\n\t */\n\tt_failure get_failure(void) const;\n\t\n\t/**\n\t * Get the transaction id.\n\t * @return Transaction id.\n\t */\n\tunsigned short get_tid(void) const;\n\t\n\t/** Get branch parameter. */\n\tstring get_branch(void) const;\n\t\n\t/** Get CSeq method. */\n\tt_method get_cseq_method(void) const;\n\t\n\t/** Check if tid is populated. */\n\tbool is_tid_populated(void) const;\n};\n\n\n/**\n * Start timer event.\n * A start timer event instructs the time keeper to start a timer.\n */\nclass t_event_start_timer : public t_event {\nprivate:\n\tt_timer\t\t*timer;\t\t/**< The timer to start. */\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param t [in] The timer to start.\n\t */\n\tt_event_start_timer(t_timer *t);\n\t\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get the timer.\n\t * @return Timer.\n\t */\n\tt_timer *get_timer(void) const;\n};\n\n\n/**\n * Stop timer event\n * A stop timer event instructs the time keeper to stop a timer.\n */\nclass t_event_stop_timer : public t_event {\nprivate:\n\t/** Id of the timer to stop. */\n\tunsigned short\ttimer_id;\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param id [in] Id of the timer to stop.\n\t */\n\tt_event_stop_timer(unsigned short id);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get the timer id.\n\t * @return Timer id.\n\t */\n\tunsigned short get_timer_id(void) const;\n};\n\n\n/**\n * Abort transaction event.\n * With an abort transaction event, the requester asks the transaction\n * manager to abort a pending transaction.\n */\nclass t_event_abort_trans : public t_event {\nprivate:\n\tunsigned short\ttid; /**< Id of the transaction to abort. */\npublic:\n\t/**\n\t * Constructor.\n\t * @param _tid [in] Transaction id.\n\t */\n\tt_event_abort_trans(unsigned short _tid);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get transaction id.\n\t * @return Transaction id.\n\t */\n\tunsigned short get_tid(void) const;\n};\n\n\n/** STUN event types. */\nenum t_stun_event_type {\n\tTYPE_STUN_SIP,\t\t/**< Request to open a port for SIP. */\n\tTYPE_STUN_MEDIA,\t/**< Request to open a port for media. */\n};\t\n\n/**\n * STUN request event.\n */\nclass t_event_stun_request : public t_event {\nprivate:\n\tStunMessage\t\t*msg;\t\t/**< STUN request to send. */\n\tunsigned short\t\ttuid;\t\t/**< Transaction user id. */\n\tunsigned short\t\ttid;\t\t/**< Transaction id. */\n\tt_stun_event_type\tstun_event_type; /**< Type of STUN event. */\n\tt_user\t\t\t*user_config;\t/**< User profile associated with this request. */\n\npublic:\n\tIPaddr\tdst_addr;\t/**< Destination address of request (host order). */\n\tunsigned short\tdst_port;\t/**< Destination port of request (host order). */\n\tunsigned short\tsrc_port;\t/**< Source port for media event type (host order). */\n\n\t/** Constructor. */\n\tt_event_stun_request(t_user *u, StunMessage *m, t_stun_event_type ev_type,\n\t\tunsigned short _tuid, unsigned short _tid);\n\t\t\n\t~t_event_stun_request();\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/** Get STUN message. */\n\tStunMessage *get_msg(void) const;\n\t\n\t/** Get transaction user id. */\n\tunsigned short get_tuid(void) const;\n\t\n\t/** Get transaction id. */\n\tunsigned short get_tid(void) const;\n\t\n\t/** Get STUN event type. */\n\tt_stun_event_type get_stun_event_type(void) const;\n\t\n\t/** Get user profile. */\n\tt_user *get_user_config(void) const;\n};\n\n\n/**\n * STUN response event.\n */\nclass t_event_stun_response : public t_event {\nprivate:\n\tStunMessage\t*msg;\t\t/**< STUN request to send. */\n\tunsigned short\ttuid;\t\t/**< Transaction user id. */\n\tunsigned short\ttid;\t\t/**< Transaction id. */\n\npublic:\n\t/** Constructor. */\n\tt_event_stun_response(StunMessage *m, unsigned short _tuid,\n\t\tunsigned short _tid);\n\t\t\n\t~t_event_stun_response();\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/** Get STUN message. */\n\tStunMessage *get_msg(void) const;\n\t\n\t/** Get transaction user id. */\n\tunsigned short get_tuid(void) const;\n\t\n\t/** Get transaction id. */\n\tunsigned short get_tid(void) const;\n};\n\n\n/**\n * NAT keep alive event.\n * Request to send a NAT keep alive message.\n */\nclass t_event_nat_keepalive : public t_event {\npublic:\n\tIPaddr\tdst_addr;\t/**< Destination address for keepalive (host order) */\n\tunsigned short\tdst_port;\t/**< Destination port (host order) */\n\t\n\tt_event_type get_type(void) const;\n};\n\n\n/**\n * ICMP event.\n * This event signals the reception of an ICMP error.\n */\nclass t_event_icmp : public t_event {\nprivate:\n\tt_icmp_msg\ticmp;\t/**< The received ICMP message. */\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param m [in] ICMP message.\n\t */\n\tt_event_icmp(const t_icmp_msg &m);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get the ICMP message.\n\t * @return ICMP message.\n\t */\n\tt_icmp_msg get_icmp(void) const;\n};\n\n\n/** User interface callback types. */\nenum t_ui_event_type {\n\tTYPE_UI_CB_DISPLAY_MSG,\t\t\t/**< Display a message */\n\tTYPE_UI_CB_DTMF_DETECTED,\t\t/**< DTMF tone detected */\n\tTYPE_UI_CB_SEND_DTMF,\t\t\t/**< Sending DTMF */\n\tTYPE_UI_CB_RECV_CODEC_CHANGED,\t\t/**< Codec changed */\n\tTYPE_UI_CB_LINE_STATE_CHANGED,\t\t/**< Line state changed */\n\tTYPE_UI_CB_LINE_ENCRYPTED,\t\t/**< Line is now encrypted */\n\tTYPE_UI_CB_SHOW_ZRTP_SAS,\t\t/**< Show the ZRTP SAS */\n\tTYPE_UI_CB_ZRTP_CONFIRM_GO_CLEAR,\t/**< ZRTP Confirm go-clear */\n\tTYPE_UI_CB_QUIT\t\t\t\t/**< Quit the user interface */\n};\n\n/** Display message priorities. */\nenum t_msg_priority {\n\tMSG_NO_PRIO,\n\tMSG_INFO,\n\tMSG_WARNING,\n\tMSG_CRITICAL\n};\n\n/**\n * User interface event.\n * Send a user interface callback to the user interface.\n * Most callbacks are called directly as a function call.\n * Sometimes an asynchronous callback is needed. That's where\n * this event is used for.\n */\nclass t_event_ui : public t_event {\nprivate:\n\tt_ui_event_type\ttype;\t/**< User interface callback type. */\n\t\n\t/** @name Parameters for call back functions */\n\t//@{\n\tint\t\tline;\t\t/**< Line number. */\n\tt_audio_codec\tcodec;\t\t/**< Audio codec. */\n\tt_dtmf_ev\tdtmf_event;\t/**< DTMF event. */\n\tbool\t\tencrypted;\t/**< Encryption indication. */\n\tstring\t\tcipher_mode;\t/**< Cipher mode (algorithm name). */\n\tstring\t\tzrtp_sas;\t/**< ZRTP SAS/ */\n\tt_msg_priority\tmsg_priority;\t/**< Priority of a display message. */\n\tstring\t\tmsg;\t\t/**> Message to display. */\n\t//@}\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param _type [in] Type of callback.\n\t */\n\tt_event_ui(t_ui_event_type _type);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/** @name Set parameters for call back functions */\n\t//@{\n\tvoid set_line(int _line);\n\tvoid set_codec(t_audio_codec _codec);\n\tvoid set_dtmf_event(t_dtmf_ev _dtmf_event);\n\tvoid set_encrypted(bool on);\n\tvoid set_cipher_mode(const string &_cipher_mode);\n\tvoid set_zrtp_sas(const string &sas);\n\tvoid set_display_msg(const string &_msg, t_msg_priority &_msg_priority);\n\t//@}\n\t\n\t/**\n\t * Call the callback function.\n\t * @param user_intf [in] The user interface that receives the callback.\n\t */\n\tvoid exec(t_userintf *user_intf);\n};\n\n\n/**\n * Asynchronous response event.\n * A user interface can open an asynchronous message box to request\n * information from the user. Via this event the user interface signals\n * the response from the user.\n */\nclass t_event_async_response : public t_event {\npublic:\n\t/** Response type */\n\tenum t_response_type {\n\t\tRESP_REFER_PERMISSION\t/**< Response on permission to refer question */\n\t};\n\t\nprivate:\n\tt_response_type\t\tresponse_type;\t/**< Response type. */\n\tbool\t\t\tbool_response;\t/**< Boolean response. */\n\t\npublic:\n\t/**\n\t * Constructor.\n\t * @param type [in] The response type.\n\t */\n\tt_event_async_response(t_response_type type);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Set the boolean response.\n\t * @param b [in] The response.\n\t */\n\tvoid set_bool_response(bool b);\n\t\n\t/**\n\t * Get response type.\n\t * @return Response type.\n\t */\n\tt_response_type get_response_type(void) const;\n\t\n\t/**\n\t * Get boolean response.\n\t * @return The response.\n\t */\n\tbool get_bool_response(void) const;\n};\n\n/**\n * Broken connection event.\n * A persistent connection to a SIP proxy is broken. With this event\n * the transport layer signals the transaction layer that a connection\n * is broken.\n */\nclass t_event_broken_connection : public t_event {\nprivate:\n\t/** The user URI (AoR) that the connection was associated with. */\n\tt_url\t\tuser_uri_;\n\t\npublic:\n\t/** Constructor */\n\tt_event_broken_connection(const t_url &url);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/**\n\t * Get the user URI.\n\t * @return The user URI.\n\t */\n\tt_url get_user_uri(void) const;\n};\n\n/**\n * TCP ping event.\n * Send a TCP ping (double CRLF).\n */\nclass t_event_tcp_ping : public t_event {\nprivate:\n\t/** The user URI (AoR) for which the ping must be sent. */\n\tt_url\t\tuser_uri_;\n\t\n\tIPaddr\tdst_addr_;\t/**< Destination address for ping (host order) */\n\tunsigned short\tdst_port_;\t/**< Destination port (host order) */\n\t\npublic:\n\t/** Constructor */\n\tt_event_tcp_ping(const t_url &url, IPaddr dst_addr, unsigned short dst_port);\n\t\n\tt_event_type get_type(void) const;\n\t\n\t/** @name Getters */\n\t//@{\n\tt_url get_user_uri(void) const;\n\tIPaddr get_dst_addr(void) const;\n\tunsigned short get_dst_port(void) const;\n\t//@}\n};\n\nclass t_event_fncall : public t_event {\npublic:\n\tt_event_fncall(std::function<void()> fn);\n\tvoid invoke();\n\tvirtual t_event_type get_type(void) const override;\nprivate:\n\tstd::function<void()> m_fn;\n};\n\n/**\n * Event queue.\n * An event queue is the communication pipe between multiple\n * threads. Multiple threads write events into the queue and\n * one thread reads the events from the queue and processes them\n * Access to the queue is protected by a mutex. A semaphore is\n * used to synchronize the reader with the writers of the queue.\n */\nclass t_event_queue {\nprivate:\n\tqueue<t_event *>\tev_queue;\t/**< Queue of events. */\n\tt_mutex\t\t\tmutex_evq;\t/**< Mutex to protect access to the queue. */\n\tt_semaphore\t\tsema_evq;\t/**< Semephore counting the number of events. */\n\n\t/**\n\t * Semaphore to signal an interrupt.\n\t * Will be posted when the interrupt method is called.\n\t */\n\tt_semaphore\t\tsema_caught_interrupt;\n\npublic:\n\t/** Constructor. */\n\tt_event_queue();\n\t\n\t~t_event_queue();\n\n\t/**\n\t * Push an event into the queue.\n\t * @param e [in] Event\n\t */\n\tvoid push(t_event *e);\n\t\n\t/** Push a quit event into the queue. */\n\tvoid push_quit(void);\n\n\t/** Push a queued function call */\n\tvoid push_fncall(std::function<void()> fn);\n\n\t/**\n\t * Create a network event and push it into the queue.\n\t * @param m [in] SIP message.\n\t * @param ipaddr [in] Destination address of the message (host order).\n\t * @param port [in] Port of the message (host order).\n\t */\n\tvoid push_network(t_sip_message *m, const t_ip_port &ip_port);\n\n\t/**\n\t * Create a user event and push it into the queue.\n\t * The user event must be associated with a user profile.\n\t * @param user_config [in] The user profile.\n\t * @param m [in] SIP message.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid push_user(t_user *user_config, t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid);\n\t\t\n\t/**\n\t * Create a user event and push it into the queue.\n\t * The user event must be unrelated to a particular user profile.\n\t * @param m [in] SIP message.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid push_user(t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid);\n\n\t/**\n\t * Create a cancel event for a user.\n\t * @param user_config [in] The user profile.\n\t * @param m [in] SIP message.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid push_user_cancel(t_user *user_config, t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid, unsigned short target_tid);\n\t\t\n\t/**\n\t * Create a cancel event for a user.\n\t * @param m [in] SIP message.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid push_user_cancel(t_sip_message *m, unsigned short tuid,\n\t\tunsigned short tid, unsigned short target_tid);\n\n\t/**\n\t * Create a timeout event and push it into the queue.\n\t * @param t [in] The timer that expired.\n\t */\n\tvoid push_timeout(t_timer *t);\n\n\t/**\n\t * Create failure event and push it into the queue.\n\t * @param f [in] Type of failure.\n\t * @param tid [in] Transaction id of failed transaction.\n\t */\n\tvoid push_failure(t_failure f, unsigned short tid);\n\t\n\t/**\n\t * Create failure event and push it into the queue.\n\t * @param f [in] Type of failure.\n\t * @param branch [in] Branch parameter of failed transaction.\n\t * @param cseq_method [in] CSeq method of failed transaction.\n\t */\n\tvoid push_failure(t_failure f, const string &branch, const t_method &cseq_method);\n\n\t/**\n\t * Create a start timer event.\n\t * @param t [in] Timer to start.\n\t */\n\tvoid push_start_timer(t_timer *t);\n\n\t/**\n\t * Create a stop timer event.\n\t * @param timer_id [in] Timer id of timer to stop.\n\t */\n\tvoid push_stop_timer(unsigned short timer_id);\n\n\t/**\n\t * Create an abort transaction event.\n\t * @param tid [in] Transaction id of transaction to abort.\n\t */\n\tvoid push_abort_trans(unsigned short tid);\n\t\n\t/**\n\t * Create a STUN request event.\n\t * @param user_config [in] The user profile associated with the request.\n\t * @param m [in] STUN request.\n\t * @param ev_type [in] Type of STUN event.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @param ipaddr [in] Destination address (host order)\n\t * @param port [in] Destination port (host order)\n\t * @param src_port [in] Source port of media. This must only be passed for\n\t *   a media STUN event.\n\t */\n\tvoid push_stun_request(t_user *user_config, StunMessage *m, t_stun_event_type ev_type,\n\t\tunsigned short tuid, unsigned short tid,\n\t\tIPaddr ipaddr, unsigned short port, unsigned short src_port = 0);\n\t\t\n\t/**\n\t * Create a STUN response event.\n\t * @param m [in] STUN response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid push_stun_response(StunMessage *m,\n\t\tunsigned short tuid, unsigned short tid);\n\t\t\n\t/**\n\t * Create a NAT keepalive event.\n\t * @param ipaddr [in] Destination address (host order)\n\t * @param port [in] Destination port (host order)\n\t */\n\tvoid push_nat_keepalive(IPaddr ipaddr, unsigned short port);\n\t\n\t/**\n\t * Create ICMP event.\n\t * @param m [in] ICMP message.\n\t */\n\tvoid push_icmp(const t_icmp_msg &m);\n\t\n\t/**\n\t * Create a REFER pemission response event.\n\t * @param permission [in] Permission allowed?.\n\t */\n\tvoid push_refer_permission_response(bool permission);\n\t\n\t/**\n\t * Create a broken connection event.\n\t * @param user_uri [in] The user URI (AoR) associated with the connection.\n\t */\n\tvoid push_broken_connection(const t_url &user_uri);\n\t\n\t/**\n\t * Create a TCP ping event.\n\t * @param user_uri [in] The user URI (AoR) for which the TCP ping must be sent.\n\t * @param dst_addr [in] The destination IPv4 address for the ping.\n\t * @param dst_port [in] The destination TCP port for the ping.\n\t */\n\tvoid push_tcp_ping(const t_url &user_uri, IPaddr dst_addr, unsigned short dst_port);\n\n\t/**\n\t * Pop an event from the queue. \n\t * If the queue is empty then the thread will be blocked until an \n\t * event arrives.\n\t * @return The popped event.\n\t */\n\tt_event *pop(void);\n\n\t/**\n\t * Pop an event from the queue.\n\t * Same method as above, but this one can be interrupted by\n\t * calling the method interrupt.\n\t * @param interrupted [out] When the pop operation is interrupted this\n\t * parameter is set to true. Otherwise it is false.\n\t * @return NULL, when interrupted.\n\t * @return The popped event, otherwise.\n\t */\n\tt_event *pop(bool &interrupted);\n\n\t/**\n\t * Send an interrupt. \n\t * This will cause the interruptable pop to return.\n\t * A non-interruptable pop will ignore the interrupt.\n\t * If pop is currently not suspending the thread execution then the\n\t * next call to pop will catch the interrupt.\n\t */\n\tvoid interrupt(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/exceptions.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Exceptions.\n */\n \n#ifndef _EXCEPTIONS_H\n#define _EXCEPTIONS_H\n\n#include <exception>\n\n/** Exception tupe. */\nenum t_exception {\n\tX_DIALOG_ALREADY_ESTABLISHED,\t/**< Dialog is already established. */\n\tX_WRONG_STATE\t\t\t/**< State machine is in wrong state. */\n};\n\nclass empty_list_exception : public std::exception {\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/CMakeLists.txt",
    "content": "project(twinkle-gui)\n\n# Suppress deprecation warnings from Qt, as they often would require breaking\n# backwards compatibility.\nadd_definitions(-DQT_NO_DEPRECATED_WARNINGS)\n\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\n\nset(twinkle_ui_SRC\n\taddresscardform.ui\n\tauthenticationform.ui\n\tbuddyform.ui\n\tderegisterform.ui\n\tdiamondcardprofileform.ui\n\tdtmfform.ui\n\tgetaddressform.ui\n\tgetprofilenameform.ui\n\thistoryform.ui\n\tinviteform.ui\n\tlogviewform.ui\n\tmessageform.ui\n\tmphoneform.ui\n\tnumberconversionform.ui\n\tredirectform.ui\n\tselectnicform.ui\n\tselectprofileform.ui\n\tselectuserform.ui\n\tsendfileform.ui\n\tsrvredirectform.ui\n\tsyssettingsform.ui\n\ttermcapform.ui\n\ttransferform.ui\n\tuserprofileform.ui\n\twizardform.ui\n)\n\nset (twinkle_lang_SRC\n\tlang/twinkle_cs.ts\n\tlang/twinkle_de.ts\n\tlang/twinkle_fr.ts\n\tlang/twinkle_nl.ts\n\tlang/twinkle_ru.ts\n\tlang/twinkle_sk.ts\n\tlang/twinkle_sv.ts\n)\n\nqt5_wrap_ui(\n\ttwinkle_UIS\n\t${twinkle_ui_SRC}\n)\n\nqt5_add_resources(twinkle_QRC icons.qrc qml/qml.qrc)\n\nqt5_add_translation(twinkle_LANG\n\t${twinkle_lang_SRC}\n)\n\nset(qt_LIBS Qt5::Widgets Qt5::Quick)\nif (WITH_DBUS)\n\tlist(APPEND qt_LIBS Qt5::DBus)\nendif (WITH_DBUS)\n\nset(CMAKE_AUTOMOC ON)\n\nset(TWINKLE_GUI-SRCS\n\tmphoneform.cpp\n\tinviteform.cpp\n\tgetaddressform.cpp\n\tredirectform.cpp\n\ttermcapform.cpp\n\tmessageform.cpp\n\tsrvredirectform.cpp\n\tuserprofileform.cpp\n\ttransferform.cpp\n\tsyssettingsform.cpp\n\thistoryform.cpp\n\tselectuserform.cpp\n\tselectprofileform.cpp\n\tbuddyform.cpp\n\tdiamondcardprofileform.cpp\n\taddresscardform.cpp\n\tauthenticationform.cpp\n\tselectnicform.cpp\n\tsendfileform.cpp\n\twizardform.cpp\n\n\taddress_finder.cpp\n\taddresstablemodel.cpp\n\tbuddylistview.cpp\n\tderegisterform.cpp\n\tdtmfform.cpp\n\tgetprofilenameform.cpp\n\tgui.cpp\n\tlogviewform.cpp\n\tmain.cpp\n\tmessageformview.cpp\n\tnumberconversionform.cpp\n\ttwinkleapplication.cpp\n\tyesnodialog.cpp\n\tosd.cpp\n\tincoming_call_popup.cpp\n\tidlesession_manager.cpp\n\n\t${twinkle_OBJS}\n\t${twinkle_UIS}\n\t${twinkle_QRC}\n\t${twinkle_LANG}\n)\nif (WITH_DBUS)\n\tlist(APPEND TWINKLE_GUI-SRCS\n\t\tidlesession_inhibitor.cpp)\nendif (WITH_DBUS)\nif (WITH_AKONADI)\n\tlist(APPEND qt_LIBS\n\t\tKF5::AkonadiCore\n\t\tKF5::AkonadiWidgets\n\t\tKF5::Contacts)\n\tlist(APPEND TWINKLE_GUI-SRCS\n\t\takonadiaddressbook.cpp\n\t\tkcontactstablemodel.cpp)\nendif (WITH_AKONADI)\n\nadd_executable(twinkle ${TWINKLE_GUI-SRCS})\ntarget_link_libraries(twinkle ${twinkle_LIBS} ${qt_LIBS})\n\ninstall(TARGETS twinkle DESTINATION bin)\ninstall(PROGRAMS twinkle-uri-handler DESTINATION bin)\ninstall(FILES ${twinkle_LANG} DESTINATION share/twinkle/lang)\n"
  },
  {
    "path": "src/gui/address_finder.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"address_finder.h\"\n#include \"gui.h\"\n#include \"log.h\"\n\n#ifdef HAVE_AKONADI\n#include <KContacts/Addressee>\n#include <KContacts/PhoneNumber>\n#endif\n\nt_address_finder *t_address_finder::instance = NULL;\nt_mutex t_address_finder::mtx_instance;\n\nt_address_finder::t_address_finder() {\n#ifdef HAVE_AKONADI\n\t// Load Akonadi address book asynchronously.\n\t// For the first call it may happen that an\n\t// address cannot be found as loading is still in progress.\n\t// This is an inconvenience, but will not harm the call.\n\tabook = AkonadiAddressBook::self();\n\tconnect(abook, \n\t\tSIGNAL(addressBookChanged()),\n\t\tthis, SLOT(invalidate_cache()));\n\t\n\tlog_file->write_report(\"Preload Akonadi contacts.\", \"t_address_finder::t_address_finder\");\n#endif\n}\n\nvoid t_address_finder::find_address(t_user *user_config, const t_url &u) \n{\n\tif (u == last_url) return;\n\t\n\tlast_url = u;\n\tlast_name.clear();\n\tlast_photo = QImage();\n\t\n#ifdef HAVE_AKONADI\n\tfor (const KContacts::Addressee& contact : abook->get_contacts())\n\t{\n\t\t// Normalize url using number conversion rules\n\t\tt_url u_normalized(u);\n\t\tu_normalized.apply_conversion_rules(user_config);\n\t\t\n\t\tfor (const KContacts::PhoneNumber& phone_number : contact.phoneNumbers())\n\t\t{\n\t\t\tQString phone = phone_number.number();\n\t\t\tstring full_address = ui->expand_destination(\n\t\t\t\t\tuser_config, phone.toStdString(), u_normalized.get_scheme());\n\t\t\t\n\t\t\tt_url url_phone(full_address);\n\t\t\tif (!url_phone.is_valid()) continue;\n\t\t\t\n\t\t\tif (u_normalized.user_host_match(url_phone,\n\t\t\t\tuser_config->get_remove_special_phone_symbols(),\n\t\t\t\tuser_config->get_special_phone_symbols()))\n\t\t\t{\n\t\t\t\tlast_name = contact.realName().toStdString();\n\t\t\t\tlast_photo = contact.photo().data();\n\t\t\t\tlast_photo.detach(); // avoid sharing of QImage with KContacts::Addressee\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n\nvoid t_address_finder::preload(void) {\n\t// The address book is preloaded on creation of the\n\t// singleton instance.\n\t(void)t_address_finder::get_instance();\n}\n\nt_address_finder *t_address_finder::get_instance(void) {\n\tmtx_instance.lock();\n\tif (!instance) {\n\t\tinstance = new t_address_finder();\n\t\t// No MEMMAN audit as this instance will only be\n\t\t// cleaned up by process termination.\n\t}\n\tmtx_instance.unlock();\n\t\n\treturn instance;\n}\n\nstring t_address_finder::find_name(t_user *user_config, const t_url &u) {\n\tmtx_finder.lock();\n\tfind_address(user_config, u);\n\tstring name = last_name;\n\tmtx_finder.unlock();\n\treturn name;\n}\n\nQImage t_address_finder:: find_photo(t_user *user_config, const t_url &u) {\n\tmtx_finder.lock();\n\tfind_address(user_config, u);\n\tQImage photo = last_photo;\n\tmtx_finder.unlock();\n\treturn photo;\n}\n\t\nvoid t_address_finder::invalidate_cache(void) {\n\tmtx_finder.lock();\n\tlast_url.set_url(\"\");\n\tmtx_finder.unlock();\n\tlog_file->write_report(\"Address finder cache invalidated.\",\n\t\t\t       \" t_address_finder::invalidate_cache\",\n\t\t\t       LOG_NORMAL, LOG_DEBUG);\n}\n"
  },
  {
    "path": "src/gui/address_finder.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _ADDRESS_FINDER_H\n#define _ADDRESS_FINDER_H\n\n#include \"twinkle_config.h\"\n\n#include <string>\n#include \"user.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include \"qobject.h\"\n#include \"qimage.h\"\n\n#ifdef HAVE_AKONADI\n#include \"akonadiaddressbook.h\"\n#endif\n\nusing namespace std;\n\nclass t_address_finder : public QObject {\nprivate:\n\tQ_OBJECT\n\tstatic t_address_finder *instance;\n\tstatic t_mutex mtx_instance;\n\t\n\tt_mutex\tmtx_finder;\n#ifdef HAVE_AKONADI\n\tAkonadiAddressBook *abook;\n#endif\n\t\n\t// Cached data for the last looked up URL.\n\tt_url\tlast_url;\n\tstring\tlast_name;\n\tQImage\tlast_photo;\n\t\n\tt_address_finder();\n\t\n\t// Find the address based on a URL and put the found\n\t// data in the cache.\n\tvoid find_address(t_user *user_config, const t_url &u);\n\t\npublic:\n\t// Preload Akonadi address book\n\tstatic void preload(void);\n\t\n\tstatic t_address_finder *get_instance(void);\n\t\n\t// Find a name given a URL\n\tstring find_name(t_user *user_config, const t_url &u);\n\t\n\t// Find a photo give a URL\n\tQImage find_photo(t_user *user_config, const t_url &u);\n\t\npublic slots:\n\tvoid invalidate_cache(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/addresscardform.cpp",
    "content": "/*\n   Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    \n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    \n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    \n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"gui.h\"\n#include \"addresscardform.h\"\n\n/*\n *  Constructs a AddressCardForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nAddressCardForm::AddressCardForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nAddressCardForm::~AddressCardForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid AddressCardForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nint AddressCardForm::exec(t_address_card &card) {\n\tfirstNameLineEdit->setText(card.name_first.c_str());\n\tinfixNameLineEdit->setText(card.name_infix.c_str());\n\tlastNameLineEdit->setText(card.name_last.c_str());\n\tphoneLineEdit->setText(card.sip_address.c_str());\n\tremarkLineEdit->setText(card.remark.c_str());\n\t\n\tint retval = QDialog::exec();\n\t\n\tif (retval == QDialog::Accepted) {\n        card.name_first = firstNameLineEdit->text().trimmed().toStdString();\n        card.name_infix = infixNameLineEdit->text().trimmed().toStdString();\n        card.name_last = lastNameLineEdit->text().trimmed().toStdString();\n        card.sip_address = phoneLineEdit->text().trimmed().toStdString();\n        card.remark = remarkLineEdit->text().trimmed().toStdString();\n\t}\n\t\n\treturn retval;\n}\n\nvoid AddressCardForm::validate()\n{\n    QString firstName = firstNameLineEdit->text().trimmed();\n    QString infixName = infixNameLineEdit->text().trimmed();\n    QString lastName = lastNameLineEdit->text().trimmed();\n    QString phone = phoneLineEdit->text().trimmed();\n\t\n\tif (firstName.isEmpty() && infixName.isEmpty() && lastName.isEmpty()) {\n\t\t((t_gui *)ui)->cb_show_msg(this,  \n            tr(\"You must fill in a name.\").toStdString(), MSG_CRITICAL);\n\t\tfirstNameLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\tif (phone.isEmpty()) {\n\t\t((t_gui *)ui)->cb_show_msg(this,  \n            tr(\"You must fill in a phone number or SIP address.\").toStdString(), MSG_CRITICAL);\n\t\tphoneLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\taccept();\n}\n"
  },
  {
    "path": "src/gui/addresscardform.h",
    "content": "#ifndef ADDRESSCARDFORM_H\n#define ADDRESSCARDFORM_H\n#include \"address_book.h\"\n#include \"ui_addresscardform.h\"\n\nclass AddressCardForm : public QDialog, public Ui::AddressCardForm\n{\n\tQ_OBJECT\n\npublic:\n    AddressCardForm(QWidget* parent = 0);\n\t~AddressCardForm();\n\n\tvirtual int exec( t_address_card & card );\n\npublic slots:\n\tvirtual void validate();\n\nprotected slots:\n\tvirtual void languageChange();\n\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/addresscardform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>AddressCardForm</class>\n  <widget class=\"QDialog\" name=\"AddressCardForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>604</width>\n        <height>209</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Address Card</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QGridLayout\">\n          <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"remarkTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Remark:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>remarkLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"infixNameLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Infix name of contact.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"firstNameLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>First name of contact.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"firstNameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;First name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>firstNameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"4\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"remarkLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>You may place any remark about the contact here.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"phoneTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Phone:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>phoneLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"infixNameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Infix name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>infixNameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"phoneLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Phone number or SIP address of contact.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"lastNameLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Last name of contact.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"lastNameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Last name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>lastNameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>31</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>261</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+C</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>firstNameLineEdit</tabstop>\n    <tabstop>infixNameLineEdit</tabstop>\n    <tabstop>lastNameLineEdit</tabstop>\n    <tabstop>phoneLineEdit</tabstop>\n    <tabstop>remarkLineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">address_book.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>AddressCardForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>AddressCardForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/addresstablemodel.cpp",
    "content": "/*\n\tCopyright (C) 2015 Lubos Dolezel <lubos@dolezel.info>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"addresstablemodel.h\"\n#include <QtDebug>\n#include <algorithm>\n\nAddressTableModel::AddressTableModel(QObject *parent, const list<t_address_card>& data)\n\t: QAbstractTableModel(parent)\n{\n\tm_data = QList<t_address_card>::fromStdList(data);\n}\n\nint AddressTableModel::rowCount(const QModelIndex &parent) const\n{\n\treturn m_data.size();\n}\n\nint AddressTableModel::columnCount(const QModelIndex &parent) const\n{\n\treturn 3;\n}\n\nQVariant AddressTableModel::data(const QModelIndex &index, int role) const\n{\n\tif (role != Qt::DisplayRole)\n\t\treturn QVariant();\n\n\tconst int row = index.row();\n\tswitch (index.column())\n\t{\n\tcase COL_ADDR_NAME:\n\t\treturn QString::fromStdString(m_data[row].get_display_name());\n\tcase COL_ADDR_PHONE:\n\t\treturn QString::fromStdString(m_data[row].sip_address);\n\tcase COL_ADDR_REMARK:\n\t\treturn QString::fromStdString(m_data[row].remark);\n\tdefault:\n\t\treturn QVariant();\n\t}\n}\n\nQVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n\tif (role != Qt::DisplayRole)\n\t\treturn QVariant();\n\n\tswitch (section)\n\t{\n\tcase COL_ADDR_NAME:\n\t\treturn tr(\"Name\");\n\tcase COL_ADDR_PHONE:\n\t\treturn tr(\"Phone\");\n\tcase COL_ADDR_REMARK:\n\t\treturn tr(\"Remark\");\n\tdefault:\n\t\treturn QVariant();\n\t}\n}\n\nvoid AddressTableModel::appendAddress(const t_address_card& card)\n{\n\tqDebug() << \"Appending contact\" << QString::fromStdString(card.get_display_name());\n\n\tbeginInsertRows(QModelIndex(), m_data.size(), m_data.size());\n\tm_data << card;\n\tendInsertRows();\n}\n\nvoid AddressTableModel::removeAddress(int index)\n{\n\tbeginRemoveRows(QModelIndex(), index, index);\n\n\tm_data.removeAt(index);\n\n\tendRemoveRows();\n}\n\nvoid AddressTableModel::modifyAddress(int index, const t_address_card& card)\n{\n\tm_data[index] = card;\n\temit dataChanged(createIndex(index, 0), createIndex(index, 2));\n}\n\nvoid AddressTableModel::sort(int column, Qt::SortOrder order)\n{\n\tstd::sort(m_data.begin(), m_data.end(), [=](const t_address_card& a1, const t_address_card& a2) -> bool {\n\t\tbool retval = false;\n\n\t\tswitch (column)\n\t\t{\n\t\tcase COL_ADDR_NAME:\n            if (order == Qt::DescendingOrder)\n                retval = QString::fromStdString(a1.get_display_name()) > QString::fromStdString(a2.get_display_name());\n            else\n                retval = QString::fromStdString(a1.get_display_name()) < QString::fromStdString(a2.get_display_name());\n\t\t\tbreak;\n\t\tcase COL_ADDR_PHONE:\n            if (order == Qt::DescendingOrder)\n                retval = a1.sip_address.compare(a2.sip_address) > 0;\n            else\n                retval = a1.sip_address.compare(a2.sip_address) < 0;\n\t\t\tbreak;\n\t\tcase COL_ADDR_REMARK:\n            if (order == Qt::DescendingOrder)\n                retval = QString::fromStdString(a1.remark) > QString::fromStdString(a2.remark);\n            else\n                retval = QString::fromStdString(a1.remark) < QString::fromStdString(a2.remark);\n\t\t\tbreak;\n\t\t}\n\n\t\treturn retval;\n\t});\n\n\temit dataChanged(createIndex(0, 0), createIndex(m_data.size()-1, 2));\n}\n"
  },
  {
    "path": "src/gui/addresstablemodel.h",
    "content": "/*\n\tCopyright (C) 2015 Lubos Dolezel <lubos@dolezel.info>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _ADDRESSTABLEMODEL_H\n#define _ADDRESSTABLEMODEL_H\n\n#include \"address_book.h\"\n#include <QAbstractTableModel>\n#include <QList>\n\n// Columns\n#define COL_ADDR_NAME\t\t0\n#define COL_ADDR_PHONE\t1\n#define COL_ADDR_REMARK\t2\n\nclass AddressTableModel : public QAbstractTableModel\n{\nprotected:\n\tQList<t_address_card>\tm_data;\n\t\npublic:\n\tAddressTableModel(QObject *parent, const list<t_address_card>& data);\n\tvirtual int rowCount(const QModelIndex &parent = QModelIndex()) const;\n\tvirtual int columnCount(const QModelIndex &parent = QModelIndex()) const;\n\tvirtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;\n\tvirtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\tvirtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);\n\n\tvoid appendAddress(const t_address_card& card);\n\tvoid removeAddress(int index);\n\tvoid modifyAddress(int index, const t_address_card& card);\n\n\tt_address_card getAddress(int index) { return m_data[index]; }\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/akonadiaddressbook.cpp",
    "content": "/*\n    Copyright (C) 2018  Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"akonadiaddressbook.h\"\n\n#include <akonadi_version.h>\n#if AKONADI_VERSION >= QT_VERSION_CHECK(5, 18, 41)\n#include <Akonadi/ItemFetchScope>\n#include <Akonadi/RecursiveItemFetchJob>\n#else\n#include <AkonadiCore/ItemFetchScope>\n#include <AkonadiCore/RecursiveItemFetchJob>\n#endif\n\n#include <KContacts/Addressee>\n\n#include \"events.h\"\n#include \"log.h\"\n#include \"userintf.h\"\n\n\nAkonadiAddressBook::AkonadiAddressBook()\n{\n\t// Monitor all collections\n\tmonitor.setCollectionMonitored(Akonadi::Collection::root());\n\tmonitor.setMimeTypeMonitored(KContacts::Addressee::mimeType());\n\tmonitor.itemFetchScope().fetchFullPayload();\n\n\tconnect(&monitor, &Akonadi::Monitor::itemAdded,\n\t\t\tthis, &AkonadiAddressBook::itemAdded);\n\tconnect(&monitor, &Akonadi::Monitor::itemChanged,\n\t\t\tthis, &AkonadiAddressBook::itemChanged);\n\tconnect(&monitor, &Akonadi::Monitor::itemsRemoved,\n\t\t\tthis, &AkonadiAddressBook::itemsRemoved);\n\n\t// Load the list of contacts asynchronously\n\treload();\n}\n\nAkonadiAddressBook *AkonadiAddressBook::self()\n{\n\t// This is thread-safe in C++11: https://stackoverflow.com/a/449823\n\tstatic AkonadiAddressBook instance;\n\n\treturn &instance;\n}\n\n\n// The following two methods were mostly copied from KAddressBook\nvoid AkonadiAddressBook::reload()\n{\n\tAkonadi::RecursiveItemFetchJob *job =\n\t\tnew Akonadi::RecursiveItemFetchJob(\n\t\t\t\tAkonadi::Collection::root(),\n\t\t\t\tQStringList() << KContacts::Addressee::mimeType());\n\tjob->fetchScope().fetchFullPayload();\n\n\tconnect(job, &Akonadi::RecursiveItemFetchJob::result,\n\t\t\tthis, &AkonadiAddressBook::itemFetchJobFinished);\n\n\t// Despite what the documentation claims, this is *not* done\n\t// automatically.  (For most jobs, this is a no-op, so it does not\n\t// matter anyway.  But this is not the case for *this* type of job.)\n\tjob->start();\n}\n\nvoid AkonadiAddressBook::itemFetchJobFinished(KJob *job)\n{\n\t// Unfortunately, RecursiveItemFetchJob does not implement the nicer\n\t// itemsReceived(Item::List), so we have to unwrap the job ourselves.\n\tif (job->error()) {\n\t\tlog_file->write_report(job->errorString().toStdString(),\n\t\t\t\t\"AkonadiAddressBook::reload\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n                ui->cb_show_msg(job->errorString().toStdString(), MSG_WARNING);\n\t\treturn;\n\t}\n\n\tAkonadi::RecursiveItemFetchJob *fetchJob =\n\t\tqobject_cast<Akonadi::RecursiveItemFetchJob*>(job);\n\n\tmtx_abook.lock();\n\n\tcontacts.clear();\n\tfor (const Akonadi::Item &item : fetchJob->items())\n\t\tinsertItem(item);\n\n\tmtx_abook.unlock();\n\n\temit addressBookChanged();\n}\n\nKContacts::AddresseeList AkonadiAddressBook::get_contacts()\n{\n\tmtx_abook.lock();\n\t// This QMap->QList->QVector conversion is probably inefficient, but\n\t// the list of contacts is probably not that large to matter anyway.\n\tKContacts::AddresseeList ret = KContacts::AddresseeList::fromList(contacts.values());\n\tmtx_abook.unlock();\n\treturn ret;\n}\n\n\n// Slots connected to the Monitor signals\n//\n// Note that Monitor will emit at least one signal (often several, for some\n// reason) per item, causing us to emit addressBookChanged(), and thus\n// reloading the model, more often than necessary.  If this ever becomes a\n// problem, we should probably setup a one-shot timer to merge our signals.\n\nvoid AkonadiAddressBook::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)\n{\n\tmtx_abook.lock();\n\tinsertItem(item);\n\tmtx_abook.unlock();\n\n\temit addressBookChanged();\n}\n\nvoid AkonadiAddressBook::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)\n{\n\tmtx_abook.lock();\n\tinsertItem(item);\n\tmtx_abook.unlock();\n\n\temit addressBookChanged();\n}\n\nvoid AkonadiAddressBook::itemsRemoved(const Akonadi::Item::List &items)\n{\n\tmtx_abook.lock();\n\tfor (const Akonadi::Item &item : items)\n\t\tremoveItem(item);\n\tmtx_abook.unlock();\n\n\temit addressBookChanged();\n}\n\n\n// Private methods to insert/remove items (mtx_abook must be locked beforehand)\n\nvoid AkonadiAddressBook::insertItem(const Akonadi::Item &item) {\n\tif (item.isValid() && item.hasPayload<KContacts::Addressee>())\n\t\tcontacts[item.id()] = item.payload<KContacts::Addressee>();\n}\n\nvoid AkonadiAddressBook::removeItem(const Akonadi::Item &item) {\n\tcontacts.remove(item.id());\n}\n"
  },
  {
    "path": "src/gui/akonadiaddressbook.h",
    "content": "/*\n    Copyright (C) 2018  Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _AKONADIADDRESSBOOK_H\n#define _AKONADIADDRESSBOOK_H\n\n#include \"twinkle_config.h\"\n\n#include \"threads/mutex.h\"\n\n#include <QObject>\n#include <QMap>\n\n#include <akonadi_version.h>\n#if AKONADI_VERSION >= QT_VERSION_CHECK(5, 18, 41)\n#include <Akonadi/Collection>\n#include <Akonadi/Item>\n#include <Akonadi/Monitor>\n#else\n#include <AkonadiCore/Collection>\n#include <AkonadiCore/Item>\n#include <AkonadiCore/Monitor>\n#endif\n\n#include <KContacts/Addressee>\n#include <KContacts/AddresseeList>\n\n// Forward declaration -- we cast this pointer to Akonadi::Job anyway\nclass KJob;\n\n\nclass AkonadiAddressBook : public QObject\n{\nprivate:\n\tQ_OBJECT\n\n\tt_mutex\tmtx_abook;\n\n\tQMap<Akonadi::Item::Id, KContacts::Addressee> contacts;\n\n\tAkonadi::Monitor monitor;\n\n\tAkonadiAddressBook();\n\n\tvoid insertItem(const Akonadi::Item &item);\n\tvoid removeItem(const Akonadi::Item &item);\n\npublic:\n\tstatic AkonadiAddressBook *self();\n\n\t// Disable copy constructor and assignment operator\n\tAkonadiAddressBook(AkonadiAddressBook const&) = delete;\n\tvoid operator=(AkonadiAddressBook const&) = delete;\n\n\tvoid reload();\n\n\tKContacts::AddresseeList get_contacts();\n\nprivate slots:\n\tvoid itemFetchJobFinished(KJob *job);\n\n\tvoid itemAdded(const Akonadi::Item &item,\n\t\t\tconst Akonadi::Collection &collection);\n\tvoid itemChanged(const Akonadi::Item &item,\n\t\t\tconst QSet<QByteArray> &partIdentifiers);\n\tvoid itemsRemoved(const Akonadi::Item::List &items);\n\nsignals:\n\tvoid addressBookChanged();\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/authenticationform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"authenticationform.h\"\n/*\n *  Constructs a AuthenticationForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nAuthenticationForm::AuthenticationForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nAuthenticationForm::~AuthenticationForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid AuthenticationForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nint AuthenticationForm::exec(t_user *user_config, const QString &realm, QString &username,\n\t\t\t     QString &password)\n{\n\tint retval;\n\t\n\tprofileValueTextLabel->setText(user_config->get_profile_name().c_str());\n\tuserValueTextLabel->setText(user_config->get_display_uri().c_str());\n\trealmTextLabel->setText(realm);\n\tusernameLineEdit->setText(username);\n\tpasswordLineEdit->setText(password);\n\tif (!username.isEmpty()) passwordLineEdit->setFocus();\n\tretval = QDialog::exec();\n\tusername = usernameLineEdit->text();\n\tpassword = passwordLineEdit->text();\n\t\n\treturn retval;\n}\n"
  },
  {
    "path": "src/gui/authenticationform.h",
    "content": "#ifndef AUTHENTICATIONFORM_H\n#define AUTHENTICATIONFORM_H\n#include \"user.h\"\n#include \"ui_authenticationform.h\"\n\nclass AuthenticationForm : public QDialog, public Ui::AuthenticationForm\n{\n\tQ_OBJECT\n\npublic:\n    AuthenticationForm(QWidget* parent = 0);\n\t~AuthenticationForm();\n\n\tvirtual int exec( t_user * user_config, const QString & realm, QString & username, QString & password );\n\nprotected slots:\n\tvirtual void languageChange();\n\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/authenticationform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>AuthenticationForm</class>\n  <widget class=\"QDialog\" name=\"AuthenticationForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>582</width>\n        <height>198</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Authentication</string>\n    </property>\n    <layout class=\"QGridLayout\">\n      <item row=\"0\" column=\"0\" rowspan=\"5\" colspan=\"1\">\n        <layout class=\"QVBoxLayout\">\n          <item>\n            <widget class=\"QLabel\" name=\"authIconTextLabel\">\n              <property name=\"text\">\n                <string/>\n              </property>\n              <property name=\"pixmap\">\n                <pixmap>:/icons/images/password.png</pixmap>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>20</width>\n                  <height>51</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Vertical</enum>\n              </property>\n            </spacer>\n          </item>\n        </layout>\n      </item>\n      <item row=\"0\" column=\"1\">\n        <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QLabel\" name=\"userValueTextLabel\">\n              <property name=\"text\">\n                <string comment=\"No need to translate\">user</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The user for which authentication is requested.</string>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QLabel\" name=\"profileValueTextLabel\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>7</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n              <property name=\"text\">\n                <string comment=\"No need to translate\">profile</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The user profile of the user for which authentication is requested.</string>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"profileTextLabel\">\n              <property name=\"text\">\n                <string>User profile:</string>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"userTextLabel\">\n              <property name=\"text\">\n                <string>User:</string>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item row=\"2\" column=\"1\">\n        <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"passwordTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Password:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>passwordLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"passwordLineEdit\">\n              <property name=\"minimumSize\">\n                <size>\n                  <width>200</width>\n                  <height>0</height>\n                </size>\n              </property>\n              <property name=\"echoMode\">\n                <enum>QLineEdit::Password</enum>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Your password for authentication.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"usernameLineEdit\">\n              <property name=\"minimumSize\">\n                <size>\n                  <width>200</width>\n                  <height>0</height>\n                </size>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"usernameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;User name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>usernameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item row=\"3\" column=\"1\">\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item row=\"4\" column=\"1\">\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>101</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item row=\"1\" column=\"1\">\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <widget class=\"QLabel\" name=\"authTextLabel\">\n              <property name=\"maximumSize\">\n                <size>\n                  <width>32767</width>\n                  <height>32767</height>\n                </size>\n              </property>\n              <property name=\"text\">\n                <string>Login required for realm:</string>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QLabel\" name=\"realmTextLabel\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>7</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n              <property name=\"text\">\n                <string comment=\"No need to translate\">realm</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The realm for which you need to authenticate.</string>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>usernameLineEdit</tabstop>\n    <tabstop>passwordLineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">user.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>AuthenticationForm</receiver>\n      <slot>accept()</slot>\n    </connection>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>AuthenticationForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/buddyform.cpp",
    "content": "/*\n   Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    \n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    \n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    \n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"gui.h\"\n#include \"sockets/url.h\"\n#include \"buddylistview.h\"\n#include \"audits/memman.h\"\n#include \"buddyform.h\"\n/*\n *  Constructs a BuddyForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nBuddyForm::BuddyForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nBuddyForm::~BuddyForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid BuddyForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid BuddyForm::init()\n{\n\tgetAddressForm = 0;\n}\n\nvoid BuddyForm::destroy()\n{\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n}\n\nvoid BuddyForm::showNew(t_buddy_list &_buddy_list, QTreeWidgetItem *_profileItem)\n{\n\tuser_config = _buddy_list.get_user_profile();\n\tedit_mode = false;\n\tbuddy_list = &_buddy_list;\n\tprofileItem = _profileItem;\n\t\n\tQDialog::show();\n}\n\nvoid BuddyForm::showEdit(t_buddy &buddy) \n{\n\tuser_config = buddy.get_user_profile();\n\tedit_mode = true;\n\tedit_buddy = &buddy;\n\tbuddy_list = edit_buddy->get_buddy_list();\n\t\n\tnameLineEdit->setText(buddy.get_name().c_str());\n\tphoneLineEdit->setText(buddy.get_sip_address().c_str());\n\tsubscribeCheckBox->setChecked(buddy.get_may_subscribe_presence());\n\t\n\tphoneLineEdit->setEnabled(false);\n\tphoneTextLabel->setEnabled(false);\n\taddressToolButton->hide();\n\t\n\tQDialog::show();\n}\n\nvoid BuddyForm::validate()\n{\n    QString name = nameLineEdit->text().trimmed();\n    QString address = phoneLineEdit->text().trimmed();\n\t\n\tif (name.isEmpty()) {\n\t\t((t_gui *)ui)->cb_show_msg(this,  \n            tr(\"You must fill in a name.\").toStdString(), MSG_CRITICAL);\n\t\tnameLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n    string dest = ui->expand_destination(user_config, address.toStdString());\n\tt_url dest_url(dest);\n\tif (!dest_url.is_valid()) {\n\t\t((t_gui *)ui)->cb_show_msg(this,  \n            tr(\"Invalid phone.\").toStdString(), MSG_CRITICAL);\n\t\tphoneLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\tif (edit_mode) {\n\t\t// Edit existing buddy\n\t\tbool must_subscribe = false;\n\t\tbool must_unsubscribe = false;\n\n\t\tif (edit_buddy->get_may_subscribe_presence() != subscribeCheckBox->isChecked()) \n\t\t{\n\t\t\tif (subscribeCheckBox->isChecked()) {\n\t\t\t\tmust_subscribe = true;;\n\t\t\t} else {\n\t\t\t\tmust_unsubscribe = true;\n\t\t\t}\n\t\t}\n\t\t\n        edit_buddy->set_name(nameLineEdit->text().trimmed().toStdString());\n        edit_buddy->set_sip_address(phoneLineEdit->text().trimmed().toStdString());\n\t\tedit_buddy->set_may_subscribe_presence(subscribeCheckBox->isChecked());\n\t\t\n\t\tif (must_subscribe) edit_buddy->subscribe_presence();\n\t\tif (must_unsubscribe) edit_buddy->unsubscribe_presence();\n\t} else {\n\t\t// Add a new buddy\n\t\tt_buddy buddy;\n        buddy.set_name(nameLineEdit->text().trimmed().toStdString());\n        buddy.set_sip_address(phoneLineEdit->text().trimmed().toStdString());\n\t\tbuddy.set_may_subscribe_presence(subscribeCheckBox->isChecked());\n\t\t\n\t\tt_buddy *new_buddy = buddy_list->add_buddy(buddy);\n\t\tnew BuddyListViewItem(profileItem, new_buddy);\n\t\tnew_buddy->subscribe_presence();\n\t}\n\t\n\tstring err_msg;\n\tif (!buddy_list->save(err_msg)) {\n\t\tQString msg = tr(\"Failed to save buddy list: %1\").arg(err_msg.c_str());\n        ((t_gui *)ui)->cb_show_msg(this, msg.toStdString(), MSG_CRITICAL);\n\t}\n\t\n\taccept();\n}\n\nvoid BuddyForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &, const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &, const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid BuddyForm::selectedAddress(const QString &name, const QString &phone)\n{\n\tnameLineEdit->setText(name);\n\tphoneLineEdit->setText(phone);\n}\n"
  },
  {
    "path": "src/gui/buddyform.h",
    "content": "#ifndef BUDDYFORM_H\n#define BUDDYFORM_H\n#include \"getaddressform.h\"\n#include \"presence/buddy.h\"\n#include <QTreeWidgetItem>\n#include \"user.h\"\n#include \"ui_buddyform.h\"\n\nclass BuddyForm : public QDialog, public Ui::BuddyForm\n{\n\tQ_OBJECT\n\npublic:\n    BuddyForm(QWidget* parent = 0);\n\t~BuddyForm();\n\npublic slots:\n    virtual void showNew( t_buddy_list & _buddy_list, QTreeWidgetItem * _profileItem );\n\tvirtual void showEdit( t_buddy & buddy );\n\tvirtual void validate();\n\tvirtual void showAddressBook();\n\tvirtual void selectedAddress( const QString & name, const QString & phone );\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tGetAddressForm *getAddressForm;\n\tt_user *user_config;\n\tbool edit_mode;\n\tt_buddy_list *buddy_list;\n\tt_buddy *edit_buddy;\n    QTreeWidgetItem *profileItem;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/buddyform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>BuddyForm</class>\n  <widget class=\"QDialog\" name=\"BuddyForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>484</width>\n        <height>154</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Buddy</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"2\" rowspan=\"2\" colspan=\"1\">\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>20</width>\n                  <height>47</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Vertical</enum>\n              </property>\n            </spacer>\n          </item>\n          <item row=\"0\" column=\"2\">\n            <widget class=\"QToolButton\" name=\"addressToolButton\">\n              <property name=\"focusPolicy\">\n                <enum>Qt::TabFocus</enum>\n              </property>\n              <property name=\"text\">\n                <string/>\n              </property>\n              <property name=\"icon\">\n                <iconset>:/icons/images/kontact_contacts.png</iconset>\n              </property>\n              <property name=\"toolTip\" stdset=\"0\">\n                <string>Address book</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Select an address from the address book.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"phoneTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Phone:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>phoneLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"nameLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Name of your buddy.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"0\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QCheckBox\" name=\"subscribeCheckBox\">\n              <property name=\"text\">\n                <string>&amp;Show availability</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+S</string>\n              </property>\n              <property name=\"checked\">\n                <bool>true</bool>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"nameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>nameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"phoneLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>SIP address your buddy.</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>131</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+C</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>nameLineEdit</tabstop>\n    <tabstop>phoneLineEdit</tabstop>\n    <tabstop>subscribeCheckBox</tabstop>\n    <tabstop>addressToolButton</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">presence/buddy.h</include>\n    <include location=\"local\">user.h</include>\n    <include location=\"local\">getaddressform.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>BuddyForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>BuddyForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>addressToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>BuddyForm</receiver>\n      <slot>showAddressBook()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/buddylistview.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"buddylistview.h\"\n\n#include \"gui.h\"\n\n#include \"qapplication.h\"\n#include \"qfont.h\"\n#include \"qpixmap.h\"\n#include \"qrect.h\"\n#include \"qsize.h\"\n#include <QTextDocument>\n\nvoid AbstractBLVItem::set_icon(t_presence_state::t_basic_state state) {\n\tswitch (state) {\n\tcase t_presence_state::ST_BASIC_UNKNOWN:\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_unknown.png\"));\n\t\tbreak;\n\tcase t_presence_state::ST_BASIC_CLOSED:\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_offline.png\"));\n\t\tbreak;\n\tcase t_presence_state::ST_BASIC_OPEN:\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_online.png\"));\n\t\tbreak;\n\tcase t_presence_state::ST_BASIC_FAILED:\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_failed.png\"));\n\t\tbreak;\n\tcase t_presence_state::ST_BASIC_REJECTED:\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_rejected.png\"));\n\t\tbreak;\n\tdefault:\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_unknown.png\"));\n\t\tbreak;\n\t}\n}\n\nAbstractBLVItem::AbstractBLVItem(QTreeWidgetItem *parent, const QString &text) :\n        QTreeWidgetItem(parent, QStringList(text))\n{}\n\t\t\nAbstractBLVItem::AbstractBLVItem(QTreeWidget *parent, const QString &text) :\n        QTreeWidgetItem(parent, QStringList(text))\n{}\n\nAbstractBLVItem::~AbstractBLVItem() {}\n\nQString AbstractBLVItem::get_tip(void) {\n\treturn tip;\n}\n\n\nvoid BuddyListViewItem::set_icon(void) {\n\tt_user *user_config = buddy->get_user_profile();\n\tstring url_str = ui->expand_destination(user_config, buddy->get_sip_address());\n\tQString address = QString::fromStdString(ui->format_sip_address(user_config, buddy->get_name(), t_url(url_str)));\n\t\n\ttip = \"<html>\";\n\ttip += address.toHtmlEscaped().replace(' ', \"&nbsp;\");\n\t\n\tif (!buddy->get_may_subscribe_presence()) {\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/buddy.png\"));\n\t} else {\n\t\tQString failure;\n\t\tt_presence_state::t_basic_state basic_state = buddy->\n\t\t\t\t\tget_presence_state()->get_basic_state();\n\t\tAbstractBLVItem::set_icon(basic_state);\n\t\t\n\t\ttip += \"<br>\";\n\t\ttip += \"<b>\";\n\t\ttip += qApp->translate(\"BuddyList\", \"Availability\");\n\t\ttip += \":&nbsp;</b>\";\n\t\t\n\t\tswitch (basic_state) {\n\t\tcase t_presence_state::ST_BASIC_UNKNOWN:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"unknown\");\n\t\t\tbreak;\n\t\tcase t_presence_state::ST_BASIC_CLOSED:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"offline\");\n\t\t\tbreak;\n\t\tcase t_presence_state::ST_BASIC_OPEN:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"online\");\n\t\t\tbreak;\n\t\tcase t_presence_state::ST_BASIC_FAILED:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"request failed\");\n            failure = QString::fromStdString(buddy->get_presence_state()->get_failure_msg());\n\t\t\tif (!failure.isEmpty()) {\n\t\t\t\ttip += QString(\" (%1)\").arg(failure);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase t_presence_state::ST_BASIC_REJECTED:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"request rejected\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"unknown\");\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\ttip += \"</html>\";\n\ttip = tip.replace(' ', \"&nbsp;\");\n}\n\nBuddyListViewItem::BuddyListViewItem(QTreeWidgetItem *parent, t_buddy *_buddy) :\n        AbstractBLVItem(parent, QString::fromStdString(_buddy->get_name())),\n\t\tbuddy(_buddy)\n{\n\tset_icon();\n\tbuddy->attach(this);\n\tQObject::connect(this, SIGNAL(update_signal()), this, SLOT(update_slot()));\n}\n\nBuddyListViewItem::~BuddyListViewItem() {\n\tbuddy->detach(this);\n}\n\nvoid BuddyListViewItem::update_slot(void) {\n\t// This method is called directly from the core, so lock the GUI\n\tui->lock();\n\tset_icon();\n\t\n\tif (buddy->get_name().c_str() != text(0)) {\n        setText(0, QString::fromStdString(buddy->get_name()));\n        QTreeWidgetItem::treeWidget()->sortItems(0, Qt::AscendingOrder);\n\t}\n\tui->unlock();\n}\n\nvoid BuddyListViewItem::update(void) {\n\temit update_signal();\n}\n\nvoid BuddyListViewItem::subject_destroyed(void) {\n\tdelete this;\n}\n\nt_buddy *BuddyListViewItem::get_buddy(void) {\n\treturn buddy;\n}\n\n\nvoid BLViewUserItem::set_icon(void) {\n\tt_presence_state::t_basic_state basic_state;\n\tQString failure;\n    QString profile_name = QString::fromStdString(presence_epa->get_user_profile()->get_profile_name());\n\t\n\ttip = \"<html>\";\n\ttip += profile_name.toHtmlEscaped();\n\ttip += \"<br>\";\n\ttip += \"<b>\";\n\ttip += qApp->translate(\"BuddyList\", \"Availability\");\n\ttip += \":&nbsp;</b>\";\n\t\n\tswitch (presence_epa->get_epa_state()) {\n\tcase t_presence_epa::EPA_UNPUBLISHED:\n\t\ttip += qApp->translate(\"BuddyList\", \"not published\");\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/penguin-small.png\"));\n\t\tbreak;\n\tcase t_presence_epa::EPA_FAILED:\n\t\ttip += qApp->translate(\"BuddyList\", \"failed to publish\");\n\t\tfailure = presence_epa->get_failure_msg().c_str();\n\t\tif (!failure.isEmpty()) {\n\t\t\t\ttip += QString(\" (%1)\").arg(failure);\n\t\t\t}\n        setData(0, Qt::DecorationRole, QPixmap(\":/icons/images/presence_failed.png\"));\n\t\tbreak;\n\tcase t_presence_epa::EPA_PUBLISHED:\n\t\tbasic_state = presence_epa->get_basic_state();\n\t\tAbstractBLVItem::set_icon(basic_state);\n\t\t\n\t\tswitch (presence_epa->get_basic_state()) {\n\t\tcase t_presence_state::ST_BASIC_CLOSED:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"offline\");\n\t\t\tbreak;\n\t\tcase t_presence_state::ST_BASIC_OPEN:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"online\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttip += qApp->translate(\"BuddyList\", \"unknown\");\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\ttip += qApp->translate(\"BuddyList\", \"unknown\");\n\t\tbreak;\n\t}\n\t\n\ttip += \"<p><i>\";\n\ttip += qApp->translate(\"BuddyList\", \"Click right to add a buddy.\");\n\ttip += \"<i></html>\";\n\ttip = tip.replace(' ', \"&nbsp;\");\n}\n\nBLViewUserItem::BLViewUserItem(QTreeWidget *parent, t_presence_epa *_presence_epa) :\n        AbstractBLVItem(parent, QString::fromStdString(_presence_epa->get_user_profile()->get_profile_name())),\n\t\tpresence_epa(_presence_epa)\n{\n\tset_icon();\n\tpresence_epa->attach(this);\n\tQObject::connect(this, SIGNAL(update_signal()), this, SLOT(update_slot()));\n\n    QFont font = this->font(0);\n    font.setBold(true);\n    this->setFont(0, font);\n}\n\nBLViewUserItem::~BLViewUserItem() {\n\tpresence_epa->detach(this);\n}\n\nvoid BLViewUserItem::update_slot(void) {\n\t// This method is called directly from the core, so lock the GUI\n\tui->lock();\n\tset_icon();\n\t\n\tif (presence_epa->get_user_profile()->get_profile_name().c_str() == text(0)) {\n        setText(0, QString::fromStdString(presence_epa->get_user_profile()->get_profile_name()));\n        QTreeWidgetItem::treeWidget()->sortItems(0, Qt::AscendingOrder);\n\t}\n\tui->unlock();\n}\n\nvoid BLViewUserItem::update(void) {\n\temit update_signal();\n}\n\nvoid BLViewUserItem::subject_destroyed(void) {\n\tdelete this;\n}\n\nt_presence_epa *BLViewUserItem::get_presence_epa(void) {\n\treturn presence_epa;\n}\n\n"
  },
  {
    "path": "src/gui/buddylistview.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef BUDDYLISTVIEW_H\n#define BUDDYLISTVIEW_H\n\n#include <QTreeWidgetItem>\n#include \"qpainter.h\"\n#include \"qtooltip.h\"\n#include \"presence/buddy.h\"\n#include \"presence/presence_epa.h\"\n#include \"patterns/observer.h\"\n\nclass AbstractBLVItem : public QTreeWidgetItem {\nprotected:\n\t// Text to show as a tool tip.\n\tQString\t\ttip;\n\t\n\t// Set the presence icon to reflect the presence state\n\tvirtual void set_icon(t_presence_state::t_basic_state state);\n\t\npublic:\n    AbstractBLVItem(QTreeWidgetItem *parent, const QString &text);\n    AbstractBLVItem(QTreeWidget *parent, const QString &text);\n\tvirtual ~AbstractBLVItem();\n\tvirtual QString get_tip(void);\n};\n\n// List view item representing a buddy.\nclass BuddyListViewItem : public QObject, public AbstractBLVItem, public patterns::t_observer {\n\tQ_OBJECT\nprivate:\n\tt_buddy\t\t*buddy;\n\t\n\t// Set the presence icon to reflect the buddy's presence\n\tvoid set_icon(void);\n\t\npublic:\n    BuddyListViewItem(QTreeWidgetItem *parent, t_buddy *_buddy);\n\tvirtual ~BuddyListViewItem();\n\t\n\tvirtual void update(void);\n\tvirtual void subject_destroyed(void);\n\t\n\tt_buddy *get_buddy(void);\n\nsignals:\n\tvoid update_signal();\n\nprivate slots:\n\tvoid update_slot();\n};\n\n// List view item representing a user\nclass BLViewUserItem : public QObject, public AbstractBLVItem, public patterns::t_observer {\n\tQ_OBJECT\nprivate:\n\tt_presence_epa *presence_epa;\n\t\n\tvoid set_icon(void);\n\t\npublic:\n    BLViewUserItem(QTreeWidget *parent, t_presence_epa *_presence_epa);\n\tvirtual ~BLViewUserItem();\n\t\n\tvirtual void update(void);\n\tvirtual void subject_destroyed(void);\n\t\n\tt_presence_epa *get_presence_epa(void);\n\nsignals:\n\tvoid update_signal();\n\nprivate slots:\n\tvoid update_slot();\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/command_args.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _COMMAND_ARGS_H\n#define _COMMAND_ARGS_H\n\n#include \"twinkle_config.h\"\n\n/** Command arguments. */\nstruct t_command_args {\n\t/** SIP URI to be called passed via the --call command line parameter. */\n\tQString\t\t\tcallto_destination;\n\t\n\t/** CLI command passed via the --cmd command line parameter. */\n\tQString\t\t\tcli_command;\n\t\n\t/** Indicates if the --call or --cmd must be performed immediately. */\n\tbool\t\t\tcmd_immediate_mode;\n\t\n\t/** Indicates the profile that should be made active before performing\n\t * --call or --cmd\n\t */\n\tQString\t\t\tcmd_set_profile;\n\t\n\t/** Indicates if the --show option was given. */\n\tbool\t\t\tcmd_show;\n\t\n\t/** Indicates if the --hide option was given. */\n\tbool\t\t\tcmd_hide;\n\t\n\t/** If a port number is passed by the user on the command line, then\n\t * that port number overrides the port from the system settings.\n\t */\n\tunsigned short\t\toverride_sip_port;\n\tunsigned short\t\toverride_rtp_port;\n\t\n\tt_command_args() :\n\t\t\tcmd_immediate_mode(false),\n\t\t\tcmd_show(false),\n\t\t\tcmd_hide(false),\n\t\t\toverride_sip_port(0),\n\t\t\toverride_rtp_port(0)\n\t{}\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/core_strings.h",
    "content": "// This file is generated by translator.py\n// It contains all strings that need translation from the\n// core of Twinkle.\n\n#define _ZAP(s)\n\n// userintf.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Anonymous\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Warning:\"))\n\n// log.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Failed to create log file %1 .\"))\n\n// call_history.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"local user\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"remote user\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"failure\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"unknown\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"in\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"out\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreCallHistory\", \"unknown\"))\n\n// sender.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Excessive number of socket errors.\"))\n\n// sys_settings.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Built with support for:\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Contributions:\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"This software contains the following software from 3rd parties:\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"* G.711/G.726 codecs from Sun Microsystems (public domain)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"* G.722 codec from Steve Underwood (public domain)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"* Parts of the STUN project at http://sourceforge.net/projects/stun\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"* Parts of libsrv at http://libsrv.sourceforge.net/\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"For RTP the following dynamic libraries are linked:\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Translated to english by <your name>\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Directory %1 does not exist.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open file %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open file %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"%1 is not set to your home directory.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Directory %1 (%2) does not exist.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot create directory %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot create directory %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Failed to create file %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Failed to write data to file %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot create %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"%1 is already running.\\nLock file %2 already exists.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot lock %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open file for reading: %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"File system error while reading file %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Syntax error in file %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Failed to backup %1 to %2\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open file for writing: %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"File system error while writing file %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"unknown name (device is busy)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Default device\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot access the ring tone device (%1).\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot access the speaker (%1).\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot access the microphone (%1).\"))\n\n// listener.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Excessive number of socket errors.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot receive incoming TCP connections.\"))\n\n// phone.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Call transfer - %1\"))\n\n// audio_session.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"CoreAudio\", \"Failed to open sound card\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreAudio\", \"Failed to open sound card\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreAudio\", \"Failed to open sound card\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreAudio\", \"Failed to create a UDP socket (RTP) on port %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreAudio\", \"Failed to create audio receiver thread.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"CoreAudio\", \"Failed to create audio transmitter thread.\"))\n\n// audio_device.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Sound card cannot be set to full duplex.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot set buffer size on sound card.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Sound card cannot be set to %1 channels.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Sound card cannot be set to %1 channels.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot set sound card to 16 bits recording.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot set sound card to 16 bits playing.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot set sound card sample rate to %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Opening ALSA driver failed\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open ALSA driver for PCM playback\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open ALSA driver for PCM capture\"))\n\n// msg_session.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Failed to send message.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Failed to send message.\"))\n\n// stun_transaction.cpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot resolve STUN server: %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"You are behind a symmetric NAT.\\nSTUN will not work.\\nConfigure a public IP address in the user profile\\nand create the following static bindings (UDP) in your NAT.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"public IP: %1 --> private IP: %2 (SIP signaling)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"public IP: %1-%2 --> private IP: %3-%4 (RTP/RTCP)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot reach the STUN server: %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"If you are behind a firewall then you need to open the following UDP ports.\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Port %1 (SIP signaling)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Ports %1-%2 (RTP/RTCP)\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"NAT type discovery via STUN failed.\"))\n\n// record_file.hpp\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open file for reading: %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"File system error while reading file %1 .\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"Cannot open file for writing: %1\"))\n_ZAP(QT_TRANSLATE_NOOP(\"TwinkleCore\", \"File system error while writing file %1 .\"))\n\n"
  },
  {
    "path": "src/gui/deregisterform.cpp",
    "content": "#include \"deregisterform.h\"\n\n/*\n *  Constructs a DeregisterForm which is a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nDeregisterForm::DeregisterForm(QWidget* parent)\n\t: QDialog(parent)\n{\n\tsetupUi(this);\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nDeregisterForm::~DeregisterForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n"
  },
  {
    "path": "src/gui/deregisterform.h",
    "content": "#ifndef DEREGISTERFORM_H\n#define DEREGISTERFORM_H\n\n#include <QDialog>\n\n#include \"ui_deregisterform.h\"\n\nclass DeregisterForm : public QDialog, private Ui::DeregisterForm\n{\n    Q_OBJECT\n\npublic:\n    DeregisterForm(QWidget* parent = 0);\n    ~DeregisterForm();\n};\n\n#endif // DEREGISTERFORM_H\n"
  },
  {
    "path": "src/gui/deregisterform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>DeregisterForm</class>\n  <widget class=\"QDialog\" name=\"DeregisterForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>287</width>\n        <height>82</height>\n      </rect>\n    </property>\n    <property name=\"sizePolicy\">\n      <sizepolicy>\n        <hsizetype>0</hsizetype>\n        <vsizetype>0</vsizetype>\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n      </sizepolicy>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Deregister</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <widget class=\"QCheckBox\" name=\"deregAllCheckBox\">\n          <property name=\"text\">\n            <string>deregister all devices</string>\n          </property>\n        </widget>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>111</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <connections>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>DeregisterForm</receiver>\n      <slot>accept()</slot>\n    </connection>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>DeregisterForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/diamondcardprofileform.cpp",
    "content": "//Added by qt3to4:\n#include <QEvent>\n#include <QMouseEvent>\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    \n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    \n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    \n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <QRegularExpression>\n#include <QValidator>\n#include <QRegularExpressionValidator>\n#include \"gui.h\"\n#include \"diamondcard.h\"\n#include \"getprofilenameform.h\"\n#include \"audits/memman.h\"\n#include \"diamondcardprofileform.h\"\n/*\n *  Constructs a DiamondcardProfileForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nDiamondcardProfileForm::DiamondcardProfileForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nDiamondcardProfileForm::~DiamondcardProfileForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid DiamondcardProfileForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid DiamondcardProfileForm::init()\n{\n\tuser_config = NULL;\n\tdestroy_user_config = false;\n\t\n\tQRegularExpression rxNoSpace(\"\\\\S*\");\n\taccountIdLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\tpinCodeLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n}\n\nvoid DiamondcardProfileForm::destroy()\n{\n\tdestroyOldUserConfig();\n}\n\nvoid DiamondcardProfileForm::destroyOldUserConfig()\n{\n\tif (user_config && destroy_user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\n\tuser_config = NULL;\n}\n\n// Show the form\nvoid DiamondcardProfileForm::show(t_user *user)\n{\n\tdestroyOldUserConfig();\n\t\n\tif (user) {\n\t\tuser_config = user;\n\t\tdestroy_user_config = false;\n\t} else {\n\t\tuser_config = new t_user();\n\t\tMEMMAN_NEW(user_config);\n\t\tdestroy_user_config = true;\n\t}\n\tQDialog::show();\n}\n\n// Modal execution\nint DiamondcardProfileForm::exec(t_user *user)\n{\n\tdestroyOldUserConfig();\n\tuser_config = user;\n\tdestroy_user_config = false;\n\treturn QDialog::exec();\n}\n\nvoid DiamondcardProfileForm::validate()\n{\n\tif (accountIdLineEdit->text().isEmpty()) {\n        ((t_gui *)ui)->cb_show_msg(this, tr(\"Fill in your account ID.\").toStdString(),\n\t\t\t\t\t   MSG_CRITICAL);\n\t\taccountIdLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\tif (pinCodeLineEdit->text().isEmpty()) {\n        ((t_gui *)ui)->cb_show_msg(this, tr(\"Fill in your PIN code.\").toStdString(),\n\t\t\t\t\t   MSG_CRITICAL);\n\t\tpinCodeLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\tQString profileName(\"Diamondcard-\");\n\tprofileName.append(accountIdLineEdit->text());\n\tQString filename(profileName);\n\tfilename.append(USER_FILE_EXT);\n\t\n\t// Create a new user config\n    while (!user_config->set_config(filename.toStdString())) {\n\t\t((t_gui *)ui)->cb_show_msg(this, \n            tr(\"A user profile with name %1 already exists.\").arg(profileName).toStdString(),\n\t\t\tMSG_WARNING);\n\t\t\n\t\t// Ask user for a profile name\n        GetProfileNameForm getProfileNameForm(this);\n        getProfileNameForm.setModal(true);\n\t\tif (!getProfileNameForm.execNewName()) return;\n\t\t\n\t\tprofileName = getProfileNameForm.getProfileName();\n\t\tfilename = profileName;\n\t\tfilename.append(USER_FILE_EXT);\n\t}\n\t\n\tdiamondcard_set_user_config(*user_config, \n                    nameLineEdit->text().toStdString(),\n                    accountIdLineEdit->text().toStdString(),\n                    pinCodeLineEdit->text().toStdString());\n\t\n\tstring error_msg;\n\tif (!user_config->write_config(user_config->get_filename(), error_msg)) {\n\t\t// Failed to write config file\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\temit newDiamondcardProfile(user_config->get_filename().c_str());\n\temit success();\n\taccept();\n}\n\nvoid DiamondcardProfileForm::signUpLinkActivated()\n{\n\tstring url = diamondcard_url(DC_ACT_SIGNUP, \"\", \"\");\n\t((t_gui *)ui)->open_url_in_browser(url.c_str());\n}\n"
  },
  {
    "path": "src/gui/diamondcardprofileform.h",
    "content": "#ifndef DIAMONDCARDPROFILEFORM_H\n#define DIAMONDCARDPROFILEFORM_H\n#include <QLabel>\n#include <QLineEdit>\n#include \"user.h\"\n#include \"ui_diamondcardprofileform.h\"\n\nclass DiamondcardProfileForm : public QDialog, public Ui_DiamondcardProfileForm\n{\n\tQ_OBJECT\n\npublic:\n    DiamondcardProfileForm(QWidget* parent = 0);\n\t~DiamondcardProfileForm();\n\n\tvirtual int exec( t_user * user );\n\npublic slots:\n\tvirtual void destroyOldUserConfig();\n\tvirtual void show( t_user * user );\n\tvirtual void validate();\n\tvirtual void signUpLinkActivated();\n\nsignals:\n\tvoid success();\n\tvoid newDiamondcardProfile(const QString&);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tt_user *user_config;\n\tbool destroy_user_config;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/diamondcardprofileform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DiamondcardProfileForm</class>\n <widget class=\"QDialog\" name=\"DiamondcardProfileForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>541</width>\n    <height>433</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Diamondcard User Profile</string>\n  </property>\n  <layout class=\"QVBoxLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"explainTextLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string>&lt;p&gt;With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages. To sign up for a Diamondcard account click on the &quot;sign up&quot; link below. Once you have signed up you receive an account ID and PIN code. Enter the account ID and PIN code below to create a Twinkle user profile for your Diamondcard account.&lt;/p&gt;\n       &lt;p&gt;For call rates see the sign up web page that will be shown to you when you click on the &quot;sign up&quot; link.&lt;/p&gt;</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::RichText</enum>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer>\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>16</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <layout class=\"QGridLayout\">\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"accountIdLineEdit\">\n       <property name=\"whatsThis\">\n        <string>Your Diamondcard account ID.</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"nameLineEdit\">\n       <property name=\"whatsThis\">\n        <string>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"accountIdTextLabel\">\n       <property name=\"text\">\n        <string>&amp;Account ID:</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>accountIdLineEdit</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"pinCodeTextLabel\">\n       <property name=\"text\">\n        <string>&amp;PIN code:</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>pinCodeLineEdit</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"nameTextLabel\">\n       <property name=\"text\">\n        <string>&amp;Your name:</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>nameLineEdit</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"pinCodeLineEdit\">\n       <property name=\"whatsThis\">\n        <string>Your Diamondcard PIN code.</string>\n       </property>\n       <property name=\"echoMode\">\n        <enum>QLineEdit::Password</enum>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer>\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"signUpTextLabel\">\n       <property name=\"text\">\n        <string>&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;#signup&quot;&gt;Sign up for a Diamondcard account&lt;/a&gt;&lt;/p&gt;</string>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignVCenter</set>\n       </property>\n       <property name=\"openExternalLinks\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer>\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>21</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>61</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"okPushButton\">\n       <property name=\"text\">\n        <string>&amp;OK</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+O</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelPushButton\">\n       <property name=\"text\">\n        <string>&amp;Cancel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>nameLineEdit</tabstop>\n  <tabstop>accountIdLineEdit</tabstop>\n  <tabstop>pinCodeLineEdit</tabstop>\n  <tabstop>okPushButton</tabstop>\n  <tabstop>cancelPushButton</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>okPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DiamondcardProfileForm</receiver>\n   <slot>validate()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DiamondcardProfileForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>signUpTextLabel</sender>\n   <signal>linkActivated(QString)</signal>\n   <receiver>DiamondcardProfileForm</receiver>\n   <slot>signUpLinkActivated()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/dtmfform.cpp",
    "content": "#include \"dtmfform.h\"\n#include <QClipboard>\n#include <QTimer>\n\n/*\n *  Constructs a DtmfForm which is a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nDtmfForm::DtmfForm(QWidget* parent)\n\t: QDialog(parent)\n{\n\tsetupUi(this);\n\n\t// Speed 40/40 (i.e. one tone every 80ms)\n\tm_insertTimer.setInterval(80);\n\tm_insertTimer.setSingleShot(false);\n\n\tconnect(&m_insertTimer, SIGNAL(timeout()), this, SLOT(insertNextKey()));\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nDtmfForm::~DtmfForm()\n{\n    // no need to delete child widgets, Qt does it all for us\n}\n\n\nvoid DtmfForm::dtmf1()\n{\n\temit digits(\"1\");\n}\n\nvoid DtmfForm::dtmf2()\n{\n\temit digits(\"2\");\n}\n\nvoid DtmfForm::dtmf3()\n{\n\temit digits(\"3\");\n}\n\nvoid DtmfForm::dtmf4()\n{\n\temit digits(\"4\");\n}\n\nvoid DtmfForm::dtmf5()\n{\n\temit digits(\"5\");\n}\n\nvoid DtmfForm::dtmf6()\n{\n\temit digits(\"6\");\n}\n\nvoid DtmfForm::dtmf7()\n{\n\temit digits(\"7\");\n}\n\nvoid DtmfForm::dtmf8()\n{\n\temit digits(\"8\");\n}\n\nvoid DtmfForm::dtmf9()\n{\n\temit digits(\"9\");\n}\n\nvoid DtmfForm::dtmf0()\n{\n\temit digits(\"0\");\n}\n\nvoid DtmfForm::dtmfStar()\n{\n\temit digits(\"*\");\n}\n\nvoid DtmfForm::dtmfPound()\n{\n\temit digits(\"#\");\n}\n\nvoid DtmfForm::dtmfA()\n{\n\temit digits(\"A\");\n}\n\nvoid DtmfForm::dtmfB()\n{\n\temit digits(\"B\");\n}\n\nvoid DtmfForm::dtmfC()\n{\n\temit digits(\"C\");\n}\n\nvoid DtmfForm::dtmfD()\n{\n\temit digits(\"D\");\n}\n\nvoid DtmfForm::keyPressEvent(QKeyEvent *e)\n{\n\t// DTMF keys\n\tif ((e->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))\n\t\t\t== Qt::NoModifier)\n\t{\n\t\tswitch (e->key()) {\n\t\tcase Qt::Key_1:\n\t\t\tonePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_2:\n\t\tcase Qt::Key_A:\n\t\tcase Qt::Key_B:\n\t\tcase Qt::Key_C:\n\t\t\ttwoPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_3:\n\t\tcase Qt::Key_D:\n\t\tcase Qt::Key_E:\n\t\tcase Qt::Key_F:\n\t\t\tthreePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_4:\n\t\tcase Qt::Key_G:\n\t\tcase Qt::Key_H:\n\t\tcase Qt::Key_I:\n\t\t\tfourPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_5:\n\t\tcase Qt::Key_J:\n\t\tcase Qt::Key_K:\n\t\tcase Qt::Key_L:\n\t\t\tfivePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_6:\n\t\tcase Qt::Key_M:\n\t\tcase Qt::Key_N:\n\t\tcase Qt::Key_O:\n\t\t\tsixPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_7:\n\t\tcase Qt::Key_P:\n\t\tcase Qt::Key_Q:\n\t\tcase Qt::Key_R:\n\t\tcase Qt::Key_S:\n\t\t\tsevenPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_8:\n\t\tcase Qt::Key_T:\n\t\tcase Qt::Key_U:\n\t\tcase Qt::Key_V:\n\t\t\teightPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_9:\n\t\tcase Qt::Key_W:\n\t\tcase Qt::Key_X:\n\t\tcase Qt::Key_Y:\n\t\tcase Qt::Key_Z:\n\t\t\tninePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_0:\n\t\tcase Qt::Key_Space:\n\t\t\tzeroPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_Asterisk:\n\t\t\tstarPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase Qt::Key_NumberSign:\n\t\t\tpoundPushButton->animateClick();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\te->ignore();\n\t\t}\n\t}\n\telse if ((e->modifiers() == Qt::ShiftModifier && e->key() == Qt::Key_Insert)\n\t\t\t || (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_V))\n\t{\n\t\t// Insert from clipboard\n\t\tQClipboard *clipboard = QApplication::clipboard();\n\t\tQString text = clipboard->text();\n\n\t\tif (!text.isEmpty())\n\t\t{\n\t\t\tm_remainingKeys = text;\n\t\t\tinsertNextKey();\n\t\t\tm_insertTimer.start();\n\t\t}\n\t}\n}\n\nvoid DtmfForm::insertNextKey()\n{\n\tQChar key;\n\tbool keyValid;\n\n\tdo\n\t{\n\t\tkeyValid = true;\n\t\tkey = m_remainingKeys[0].toLower();\n\t\tm_remainingKeys = m_remainingKeys.mid(1);\n\n\t\tswitch (key.toLatin1()) {\n\t\tcase '0':\n\t\t\tzeroPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '1':\n\t\t\tonePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '2':\n\t\t\ttwoPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '3':\n\t\t\tthreePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '4':\n\t\t\tfourPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '5':\n\t\t\tfivePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '6' :\n\t\t\tsixPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '7':\n\t\t\tsevenPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '8':\n\t\t\teightPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '9':\n\t\t\tninePushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '#':\n\t\t\tpoundPushButton->animateClick();\n\t\t\tbreak;\n\t\tcase '*':\n\t\t\tstarPushButton->animateClick();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tkeyValid = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\twhile (!keyValid);\n\n\tif (m_remainingKeys.isEmpty())\n\t\tm_insertTimer.stop();\n}\n"
  },
  {
    "path": "src/gui/dtmfform.h",
    "content": "#ifndef DTMFFORM_H\n#define DTMFFORM_H\n\n#include <QDialog>\n#include <QKeyEvent>\n#include <QTimer>\n#include \"ui_dtmfform.h\"\n\nclass DtmfForm : public QDialog, private Ui::DtmfForm\n{\n    Q_OBJECT\n\npublic:\n    explicit DtmfForm(QWidget *parent = 0);\n    ~DtmfForm();\n\npublic slots:\n    void dtmf1();\n    void dtmf2();\n    void dtmf3();\n    void dtmf4();\n    void dtmf5();\n    void dtmf6();\n    void dtmf7();\n    void dtmf8();\n    void dtmf9();\n    void dtmf0();\n    void dtmfStar();\n    void dtmfPound();\n    void dtmfA();\n    void dtmfB();\n    void dtmfC();\n    void dtmfD();\n\n\tvoid insertNextKey();\n\nprotected:\n    void keyPressEvent(QKeyEvent* e);\nsignals:\n    void digits(const QString&);\nprivate:\n\tQTimer m_insertTimer;\n\tQString m_remainingKeys;\n};\n\n#endif // DTMFFORM_H\n"
  },
  {
    "path": "src/gui/dtmfform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DtmfForm</class>\n <widget class=\"QDialog\" name=\"DtmfForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>350</width>\n    <height>302</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - DTMF</string>\n  </property>\n  <layout class=\"QVBoxLayout\">\n   <item>\n    <widget class=\"QGroupBox\" name=\"keypadGroupBox\">\n     <property name=\"title\">\n      <string>Keypad</string>\n     </property>\n     <layout class=\"QGridLayout\">\n      <item row=\"0\" column=\"1\">\n       <widget class=\"QPushButton\" name=\"twoPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"font\">\n         <font>\n          <pointsize>8</pointsize>\n         </font>\n        </property>\n        <property name=\"whatsThis\">\n         <string>2</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">2\nabc</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"2\">\n       <widget class=\"QPushButton\" name=\"threePushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>3</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">3\ndef</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"3\">\n       <widget class=\"QPushButton\" name=\"aPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"palette\">\n         <palette>\n          <active>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </active>\n          <inactive>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </inactive>\n          <disabled>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </disabled>\n         </palette>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Over decadic A. Normally not needed.</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">A</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"QPushButton\" name=\"fourPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>4</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">4\nghi</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"1\">\n       <widget class=\"QPushButton\" name=\"fivePushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>5</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">5\njkl</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"2\">\n       <widget class=\"QPushButton\" name=\"sixPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>6</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">6\nmno</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"3\">\n       <widget class=\"QPushButton\" name=\"bPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"palette\">\n         <palette>\n          <active>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </active>\n          <inactive>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </inactive>\n          <disabled>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </disabled>\n         </palette>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Over decadic B. Normally not needed.</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">B</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"0\">\n       <widget class=\"QPushButton\" name=\"sevenPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>7</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">7\npqrs</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"1\">\n       <widget class=\"QPushButton\" name=\"eightPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>8</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">8\ntuv</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"2\">\n       <widget class=\"QPushButton\" name=\"ninePushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>9</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">9\nwxyz</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"3\">\n       <widget class=\"QPushButton\" name=\"cPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"palette\">\n         <palette>\n          <active>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </active>\n          <inactive>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </inactive>\n          <disabled>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </disabled>\n         </palette>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Over decadic C. Normally not needed.</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">C</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"0\">\n       <widget class=\"QPushButton\" name=\"starPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Star (*)</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">*</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"1\">\n       <widget class=\"QPushButton\" name=\"zeroPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>0</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">0</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"2\">\n       <widget class=\"QPushButton\" name=\"poundPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Pound (#)</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">#</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"3\">\n       <widget class=\"QPushButton\" name=\"dPushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"palette\">\n         <palette>\n          <active>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </active>\n          <inactive>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </inactive>\n          <disabled>\n           <colorrole role=\"Button\">\n            <brush brushstyle=\"SolidPattern\">\n             <color alpha=\"255\">\n              <red>194</red>\n              <green>202</green>\n              <blue>210</blue>\n             </color>\n            </brush>\n           </colorrole>\n          </disabled>\n         </palette>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Over decadic D. Normally not needed.</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">D</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QPushButton\" name=\"onePushButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"whatsThis\">\n         <string>1</string>\n        </property>\n        <property name=\"text\">\n         <string notr=\"true\">1</string>\n        </property>\n        <property name=\"autoDefault\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>291</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"closePushButton\">\n       <property name=\"text\">\n        <string>&amp;Close</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>onePushButton</tabstop>\n  <tabstop>twoPushButton</tabstop>\n  <tabstop>threePushButton</tabstop>\n  <tabstop>aPushButton</tabstop>\n  <tabstop>fourPushButton</tabstop>\n  <tabstop>fivePushButton</tabstop>\n  <tabstop>sixPushButton</tabstop>\n  <tabstop>bPushButton</tabstop>\n  <tabstop>sevenPushButton</tabstop>\n  <tabstop>eightPushButton</tabstop>\n  <tabstop>ninePushButton</tabstop>\n  <tabstop>cPushButton</tabstop>\n  <tabstop>starPushButton</tabstop>\n  <tabstop>zeroPushButton</tabstop>\n  <tabstop>poundPushButton</tabstop>\n  <tabstop>dPushButton</tabstop>\n  <tabstop>closePushButton</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>closePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>onePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf1()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>twoPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf2()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>threePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf3()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>fourPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf4()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>fivePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf5()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>sixPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf6()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>sevenPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf7()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>eightPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf8()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>ninePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf9()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>zeroPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmf0()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>starPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmfStar()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>poundPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmfPound()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>aPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmfA()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>bPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmfB()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmfC()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>dPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>DtmfForm</receiver>\n   <slot>dtmfD()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/getaddressform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"getaddressform.h\"\n#include <QMessageBox>\n#include \"sys_settings.h\"\n#include \"gui.h\"\n#include \"address_book.h\"\n#include \"addresstablemodel.h\"\n#include \"addresscardform.h\"\n#include \"audits/memman.h\"\n#define TAB_AKONADI\t0\n#define TAB_LOCAL\t1\n\n#ifdef HAVE_AKONADI\n#include \"akonadiaddressbook.h\"\n#include \"kcontactstablemodel.h\"\n\n#include <akonadi_version.h>\n#if AKONADI_VERSION >= QT_VERSION_CHECK(5, 18, 41)\n#include <Akonadi/ControlGui>\n#else\n#include <AkonadiWidgets/ControlGui>\n#endif\n\n// Column numbers\n#define AB_COL_NAME\t0\n#define AB_COL_PHONE\t2\n#endif\n\nGetAddressForm::GetAddressForm(QWidget *parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tm_model = new AddressTableModel(this, ab_local->get_address_list());\n\tlocalListView->setModel(m_model);\n#ifdef HAVE_AKONADI\n\tk_model = new KContactsTableModel(this);\n\taddressListView->setModel(k_model);\n#endif\n\n\tinit();\n\n\tlocalListView->sortByColumn(COL_ADDR_NAME, Qt::AscendingOrder);\n\taddressListView->sortByColumn(COL_ADDR_NAME, Qt::AscendingOrder);\n\n\tlocalListView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);\n\taddressListView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);\n}\n\nGetAddressForm::~GetAddressForm()\n{\n\t// destroy();\n}\n\nvoid GetAddressForm::init() \n{\n#ifdef HAVE_AKONADI\n\tloadAddresses();\n\t\n\tconnect(AkonadiAddressBook::self(),\n\t\tSIGNAL(addressBookChanged()),\n\t\tthis, SLOT(loadAddresses()));\n\t\n\tsipOnlyCheckBox->setChecked(sys_config->get_ab_show_sip_only());\n\n\tAkonadi::ControlGui::widgetNeedsAkonadi(tabAkonadi);\n#else\n    addressTabWidget->setTabEnabled(addressTabWidget->indexOf(tabAkonadi), false);\n    addressTabWidget->setCurrentIndex(TAB_LOCAL);\n#endif\n}\n\nvoid GetAddressForm::reload()\n{\n#ifdef HAVE_AKONADI\n\tAkonadiAddressBook::self()->reload();\n#endif\n}\n\nvoid GetAddressForm::show()\n{\n\tQDialog::show();\n\t\n#ifdef HAVE_AKONADI\n\tif (k_model->rowCount() == 0) {\n\t\tif (m_model->rowCount() == 0) {\n\t\t\tQMessageBox::information(this, PRODUCT_NAME, tr(\n\t\t\t\t\"<p>\"\n\t\t\t\t\"You seem not to have any contacts with a phone number \"\n\t\t\t\t\"in <b>Akonadi</b>, KDE's PIM Storage Service. \"\n\t\t\t\t\"Twinkle retrieves all contacts with a phone number from \"\n\t\t\t\t\"Akonadi. To manage your contacts you have to \"\n\t\t\t\t\"use another application such as KAddressBook or Kontact.\"\n\t\t\t\t\"<p>\"\n\t\t\t\t\"As an alternative you may use Twinkle's local address book.\"\n\t\t\t\t\"</p>\"));\n\t\t} else {\n\t\t\taddressTabWidget->setCurrentIndex(TAB_LOCAL);\n\t\t}\n\t}\n#endif\n}\n\nvoid GetAddressForm::loadAddresses()\n{\n#ifdef HAVE_AKONADI\n\tk_model->loadContacts(AkonadiAddressBook::self()->get_contacts(),\n\t\t\t      sys_config->get_ab_show_sip_only());\n\t\n\taddressListView->sortByColumn(addressListView->horizontalHeader()->sortIndicatorSection(), addressListView->horizontalHeader()->sortIndicatorOrder());\n#endif\n}\n\nvoid GetAddressForm::selectAddress()\n{\n    if (addressTabWidget->currentIndex() == TAB_AKONADI) {\n\t\tselectAkonadiAddress();\n\t} else {\n\t\tselectLocalAddress();\n\t}\n}\n\nvoid GetAddressForm::selectAkonadiAddress()\n{\n#ifdef HAVE_AKONADI\n\tQModelIndexList sel = addressListView->selectionModel()->selectedRows();\n\tif (!sel.isEmpty())\n\t{\n\t\tt_address_card card = k_model->getAddress(sel[0].row());\n\t\temit address(QString::fromStdString(card.get_display_name()),\n\t\t\t\tQString::fromStdString(card.sip_address));\n\t\t\n\t\t// Signal display name and url combined.\n\t\tt_display_url du(t_url(card.sip_address), card.get_display_name());\n\t\temit address(du.encode().c_str());\n\t}\n\t\n\taccept();\n#endif\n}\n\nvoid GetAddressForm::selectLocalAddress()\n{\n\tQModelIndexList sel = localListView->selectionModel()->selectedRows();\n\tif (!sel.isEmpty())\n\t{\n\t\tt_address_card card = m_model->getAddress(sel[0].row());\n\t\temit address(QString::fromStdString(card.get_display_name()), QString::fromStdString(card.sip_address));\n\n\t\t// Signal display name and url combined.\n\t\tt_display_url du(t_url(card.sip_address), card.get_display_name());\n\t\temit address(du.encode().c_str());\n\t}\n\t\n\taccept();\n}\n\nvoid GetAddressForm::toggleSipOnly(bool on)\n{\n#ifdef HAVE_AKONADI\n\tstring msg;\n\t\n\tsys_config->set_ab_show_sip_only(on);\n\t\n\t// Ignore write failures. If for some reason the system config\n\t// could not be written, then this settings is lost after exiting Twinkle.\n\t// No need to bother the user at this point.\n\t(void)sys_config->write_config(msg);\n\t\n\tloadAddresses();\n#endif\n}\n\nvoid GetAddressForm::addLocalAddress()\n{\n\tt_address_card card;\n\tAddressCardForm f(this);\n\tif (f.exec(card)) {\n\t\tab_local->add_address(card);\n\n\t\tm_model->appendAddress(card);\n\n\t\tlocalListView->sortByColumn(localListView->horizontalHeader()->sortIndicatorSection(), localListView->horizontalHeader()->sortIndicatorOrder());\n\t\t\n\t\tstring error_msg;\n\t\tif (!ab_local->save(error_msg)) {\n\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t}\n\t}\n}\n\nvoid GetAddressForm::deleteLocalAddress()\n{\n\tQModelIndexList sel = localListView->selectionModel()->selectedRows();\n\tif (sel.isEmpty())\n\t\treturn;\n\n\tt_address_card card = m_model->getAddress(sel[0].row());\n\n\tQString card_name = QString::fromStdString(card.get_display_name());\n\tQString msg = tr(\"Are you sure you want to delete contact '%1' from the local address book?\").arg(card_name);\n\tint button = QMessageBox::warning(this, tr(\"Delete contact\"), msg,\n\t\t\tQMessageBox::Yes | QMessageBox::No);\n\n\tif (button == QMessageBox::Yes) {\n\t\tif (ab_local->del_address(card)) {\n\t\t\tm_model->removeAddress(sel[0].row());\n\n\t\t\tstring error_msg;\n\t\t\tif (!ab_local->save(error_msg)) {\n\t\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid GetAddressForm::editLocalAddress()\n{\n\tQModelIndexList sel = localListView->selectionModel()->selectedRows();\n\tif (sel.isEmpty())\n\t\treturn;\n\t\n\tt_address_card oldCard = m_model->getAddress(sel[0].row());\n\tt_address_card newCard = oldCard;\n\tAddressCardForm f(this);\n\tif (f.exec(newCard)) {\n\t\tif (ab_local->update_address(oldCard, newCard)) {\n\t\t\tm_model->modifyAddress(sel[0].row(), newCard);\n\n\t\t\tlocalListView->sortByColumn(localListView->horizontalHeader()->sortIndicatorSection(), localListView->horizontalHeader()->sortIndicatorOrder());\n\t\t\t\n\t\t\tstring error_msg;\n\t\t\tif (!ab_local->save(error_msg)) {\n\t\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/gui/getaddressform.h",
    "content": "#ifndef GETADDRESSFORM_UI_H\n#define GETADDRESSFORM_UI_H\n\n#include \"twinkle_config.h\"\n\n#include \"ui_getaddressform.h\"\n#include \"user.h\"\n\nclass AddressTableModel;\n#ifdef HAVE_AKONADI\nclass KContactsTableModel;\n#endif\n\nclass GetAddressForm : public QDialog, public Ui::GetAddressForm\n{\nQ_OBJECT\npublic:\n    GetAddressForm(QWidget *parent);\n\t~GetAddressForm();\nprivate:\n\tvoid init();\n\t// void destroy();\npublic slots:\n\tvoid reload();\n\tvoid show();\n\tvoid loadAddresses();\n\tvoid selectAddress();\n\tvoid selectAkonadiAddress();\n\tvoid selectLocalAddress();\n\tvoid toggleSipOnly( bool on );\n\tvoid addLocalAddress();\n\tvoid deleteLocalAddress();\n\tvoid editLocalAddress();\n\nsignals:\n\tvoid address(const QString &, const QString &);\n\tvoid address(const QString &);\nprivate:\n\tAddressTableModel* m_model;\n#ifdef HAVE_AKONADI\n\tKContactsTableModel* k_model;\n#endif\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/getaddressform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GetAddressForm</class>\n <widget class=\"QDialog\" name=\"GetAddressForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>655</width>\n    <height>474</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Select address</string>\n  </property>\n  <layout class=\"QVBoxLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"addressTabWidget\">\n     <property name=\"tabShape\">\n      <enum>QTabWidget::Rounded</enum>\n     </property>\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"tabAkonadi\">\n      <attribute name=\"title\">\n       <string>&amp;Akonadi contacts</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QTableView\" name=\"addressListView\">\n         <property name=\"whatsThis\">\n          <string>This list of addresses is taken from &lt;b&gt;Akonadi&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook or Kontact.</string>\n         </property>\n         <property name=\"selectionMode\">\n          <enum>QAbstractItemView::SingleSelection</enum>\n         </property>\n         <property name=\"selectionBehavior\">\n          <enum>QAbstractItemView::SelectRows</enum>\n         </property>\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n         <property name=\"columnCount\" stdset=\"0\">\n          <number>3</number>\n         </property>\n         <property name=\"allColumnsShowFocus\" stdset=\"0\">\n          <bool>true</bool>\n         </property>\n         <property name=\"showSortIndicator\" stdset=\"0\">\n          <bool>true</bool>\n         </property>\n         <property name=\"itemMargin\" stdset=\"0\">\n          <number>1</number>\n         </property>\n         <attribute name=\"verticalHeaderVisible\">\n          <bool>false</bool>\n         </attribute>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QCheckBox\" name=\"sipOnlyCheckBox\">\n           <property name=\"whatsThis\">\n            <string>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</string>\n           </property>\n           <property name=\"text\">\n            <string>&amp;Show only SIP addresses</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+S</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>201</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QPushButton\" name=\"reloadPushButton\">\n           <property name=\"whatsThis\">\n            <string>Reload the list of contacts from Akonadi.</string>\n           </property>\n           <property name=\"text\">\n            <string>&amp;Reload</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+R</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>491</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"tabLocal\">\n      <attribute name=\"title\">\n       <string>&amp;Local address book</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QTableView\" name=\"localListView\">\n         <property name=\"whatsThis\">\n          <string>Contacts in the local address book of Twinkle.</string>\n         </property>\n         <property name=\"selectionMode\">\n          <enum>QAbstractItemView::SingleSelection</enum>\n         </property>\n         <property name=\"selectionBehavior\">\n          <enum>QAbstractItemView::SelectRows</enum>\n         </property>\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n         <property name=\"columnCount\" stdset=\"0\">\n          <number>3</number>\n         </property>\n         <property name=\"allColumnsShowFocus\" stdset=\"0\">\n          <bool>true</bool>\n         </property>\n         <property name=\"showSortIndicator\" stdset=\"0\">\n          <bool>true</bool>\n         </property>\n         <attribute name=\"verticalHeaderVisible\">\n          <bool>false</bool>\n         </attribute>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QPushButton\" name=\"addPushButton\">\n           <property name=\"whatsThis\">\n            <string>Add a new contact to the local address book.</string>\n           </property>\n           <property name=\"text\">\n            <string>&amp;Add</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+A</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"deletePushButton\">\n           <property name=\"whatsThis\">\n            <string>Delete a contact from the local address book.</string>\n           </property>\n           <property name=\"text\">\n            <string>&amp;Delete</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+D</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"editPushButton\">\n           <property name=\"whatsThis\">\n            <string>Edit a contact from the local address book.</string>\n           </property>\n           <property name=\"text\">\n            <string>&amp;Edit</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+E</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>161</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>378</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"okPushButton\">\n       <property name=\"text\">\n        <string>&amp;OK</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+O</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelPushButton\">\n       <property name=\"text\">\n        <string>&amp;Cancel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>addressListView</tabstop>\n  <tabstop>sipOnlyCheckBox</tabstop>\n  <tabstop>reloadPushButton</tabstop>\n  <tabstop>addressTabWidget</tabstop>\n  <tabstop>localListView</tabstop>\n  <tabstop>addPushButton</tabstop>\n  <tabstop>deletePushButton</tabstop>\n  <tabstop>editPushButton</tabstop>\n  <tabstop>okPushButton</tabstop>\n  <tabstop>cancelPushButton</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">user.h</include>\n </includes>\n <resources/>\n <connections>\n  <connection>\n   <sender>okPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>selectAddress()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>496</x>\n     <y>458</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>583</x>\n     <y>458</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>addressListView</sender>\n   <signal>doubleClicked(QModelIndex)</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>selectAkonadiAddress()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>33</x>\n     <y>61</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>sipOnlyCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>toggleSipOnly(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>34</x>\n     <y>365</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>reloadPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>reload()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>34</x>\n     <y>392</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>addPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>addLocalAddress()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>34</x>\n     <y>393</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>deletePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>deleteLocalAddress()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>121</x>\n     <y>393</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>editPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>editLocalAddress()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>208</x>\n     <y>393</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>localListView</sender>\n   <signal>doubleClicked(QModelIndex)</signal>\n   <receiver>GetAddressForm</receiver>\n   <slot>selectLocalAddress()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>33</x>\n     <y>61</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/getprofilenameform.cpp",
    "content": "#include \"getprofilenameform.h\"\n#include <QDir>\n#include <QMessageBox>\n#include <QRegularExpressionValidator>\n#include \"user.h\"\n#include \"protocol.h\"\n\n/*\n *  Constructs a GetProfileNameForm which is a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nGetProfileNameForm::GetProfileNameForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nGetProfileNameForm::~GetProfileNameForm()\n{\n    // no need to delete child widgets, Qt does it all for us\n}\n\nvoid GetProfileNameForm::init()\n{\n\t// Letters, digits, underscore, minus, \"@\" and period\n\tQRegularExpression rxFilenameChars(\"[\\\\w\\\\-@\\\\.]*\");\n\n\t// Set validators\n\t// USER\n\tprofileLineEdit->setValidator(new QRegularExpressionValidator(rxFilenameChars, this));\n\n\t// Focus seems to default to OK button, despite tab order\n\tprofileLineEdit->setFocus();\n}\n\nvoid GetProfileNameForm::validate()\n{\n\tQString profileName = profileLineEdit->text();\n\n\tif (profileName.isEmpty()) return;\n\n\t// Check for anything that was let through by the validator\n\tQRegularExpression rxFilename(\"^[\\\\w\\\\-][\\\\w\\\\-@\\\\.]*$\");\n\tif (!rxFilename.match(profileName).hasMatch()) {\n\t\tQMessageBox::critical(this, PRODUCT_NAME,\n\t\t\ttr(\"Invalid profile name: %1\").arg(profileName)\n\t\t\t// While this would be better suited as informativeText,\n\t\t\t// Qt wouldn't take it into account when sizing the box\n\t\t\t// (see http://apocalyptech.com/linux/qt/qmessagebox/)\n\t\t\t+ QString(\"\\n\\n\") + tr(\n\t\t\t\t\"Allowed characters are: letters, digits, '-', '_', '.' and '@'.\\n\"\n\t\t\t\t\"Furthermore, the initial character cannot be '.' or '@'.\"\n\t\t\t));\n\n\t\treturn;\n\t}\n\n\t// Find the .twinkle directory in HOME\n\tQDir d = QDir::home();\n\tif (!d.cd(USER_DIR)) {\n\t\tQMessageBox::critical(this, PRODUCT_NAME,\n\t\t\ttr(\"Cannot find .twinkle directory in your home directory.\"));\n\t\treject();\n\t}\n\n\tQString filename = profileName;\n\tfilename.append(USER_FILE_EXT);\n\tQString fullname = d.filePath(filename);\n\tif (QFile::exists(fullname)) {\n\t\tQMessageBox::warning(this, PRODUCT_NAME,\n\t\t\ttr(\"Profile already exists.\"));\n\t\treturn;\n\t}\n\n\taccept();\n}\n\nQString GetProfileNameForm:: getProfileName()\n{\n\treturn profileLineEdit->text();\n}\n\n// Execute a dialog to get a name for a new profile\nint GetProfileNameForm::execNewName()\n{\n\tprofileTextLabel->setText(tr(\"Enter a name for your profile:\"));\n\treturn exec();\n}\n\n// Execute this dialog to get a new name for an existing profile\nint GetProfileNameForm::execRename(const QString &oldName)\n{\n\tQString s = tr(\"Rename profile '%1' to:\").arg(oldName);\n\tprofileTextLabel->setText(s);\n\treturn exec();\n}\n"
  },
  {
    "path": "src/gui/getprofilenameform.h",
    "content": "#ifndef GETPROFILENAMEFORM_H\n#define GETPROFILENAMEFORM_H\n\n#include <QDialog>\n#include \"ui_getprofilenameform.h\"\n\nclass GetProfileNameForm : public QDialog, private Ui::GetProfileNameForm\n{\n    Q_OBJECT\n\npublic:\n    GetProfileNameForm(QWidget* parent = 0);\n    ~GetProfileNameForm();\n\n    QString getProfileName();\n    int execNewName();\n    int execRename( const QString & oldName );\n\npublic slots:\n    void validate();\n\nprivate:\n    void init();\n};\n\n#endif // GETPROFILENAMEFORM_H\n"
  },
  {
    "path": "src/gui/getprofilenameform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>GetProfileNameForm</class>\n  <widget class=\"QDialog\" name=\"GetProfileNameForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>430</width>\n        <height>127</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Profile name</string>\n    </property>\n    <layout class=\"QGridLayout\">\n      <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"userIconTextLabel\">\n          <property name=\"text\">\n            <string/>\n          </property>\n          <property name=\"pixmap\">\n            <pixmap resource=\"icons.qrc\">:/icons/images/penguin_big.png</pixmap>\n          </property>\n          <property name=\"wordWrap\">\n            <bool>false</bool>\n          </property>\n        </widget>\n      </item>\n      <item row=\"1\" column=\"0\" rowspan=\"1\" colspan=\"2\">\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>81</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item row=\"0\" column=\"1\">\n        <layout class=\"QVBoxLayout\">\n          <item>\n            <widget class=\"QLabel\" name=\"profileTextLabel\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>5</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n              <property name=\"text\">\n                <string>Enter a name for your profile:</string>\n              </property>\n              <property name=\"scaledContents\">\n                <bool>false</bool>\n              </property>\n              <property name=\"alignment\">\n                <set>Qt::AlignVCenter</set>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QLineEdit\" name=\"profileLineEdit\">\n              <property name=\"text\">\n                <string/>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QLabel\" name=\"profileNoteLabel\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>5</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n              <property name=\"text\">\n                <string>(Allowed characters: letters, digits, '-', '_', '.', '@')</string>\n              </property>\n              <property name=\"scaledContents\">\n                <bool>false</bool>\n              </property>\n              <property name=\"alignment\">\n                <set>Qt::AlignVCenter</set>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <tabstops>\n    <tabstop>profileLineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>GetProfileNameForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>GetProfileNameForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/gui.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    \n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    \n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    \n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"twinkle_config.h\"\n\n#include <iostream>\n#include <cstdlib>\n#include <qapplication.h>\n#include <QFrame>\n\n#ifdef HAVE_KDE\n#include <kmimetype.h>\n#include <krun.h>\n#include <kservice.h>\n#include <ktrader.h>\n#endif\n\n// This include is needed to avoid build error when building Twinkle\n// without Qt. One of the other includes below seems to include qdir.h\n// indirectly. But by that time it probably conflicts with a macro causing\n// compilation errors reported in qdir.h Include qdir.h here avoids the\n// conflict.\n#include \"qdir.h\"\n\n#include \"gui.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"user.h\"\n#include \"cmd_socket.h\"\n#include \"audio/rtp_telephone_event.h\"\n#include \"sockets/interfaces.h\"\n#include \"threads/thread.h\"\n#include \"audits/memman.h\"\n#include \"authenticationform.h\"\n#include \"mphoneform.h\"\n#include \"selectnicform.h\"\n#include \"selectprofileform.h\"\n#include \"messageformview.h\"\n#include \"util.h\"\n#include \"address_finder.h\"\n#include \"yesnodialog.h\"\n#include \"command_args.h\"\n#include \"im/msg_session.h\"\n#include \"idlesession_manager.h\"\n\n#include \"qcombobox.h\"\n#include \"qlabel.h\"\n#include \"qlayout.h\"\n#include \"qmessagebox.h\"\n#include \"qpixmap.h\"\n#include <QProcess>\n#include \"qpushbutton.h\"\n#include \"qsize.h\"\n#include \"qsizepolicy.h\"\n#include \"qstring.h\"\n#include \"qtextcodec.h\"\n#include \"qtooltip.h\"\n#include <QSettings>\n\nextern string user_host;\nextern pthread_t thread_id_main;\n\n// External command arguments\nextern t_command_args g_cmd_args;\n\nextern QSettings* g_gui_state;\n\nQString str2html(const QString &s)\n{\n\tQString result(s);\n\t\n\tresult.replace('&', \"&amp;\");\n\tresult.replace('<', \"&lt;\");\n\tresult.replace('>', \"&gt;\");\n\t\n\treturn result;\n}\n\nvoid setDisabledIcon(QAction *action, const QString &icon) {\n    QIcon i = action->icon();\n    i.addPixmap(QPixmap(icon), QIcon::Disabled);\n    action->setIcon(i);\n}\n\nvoid setDisabledIcon(QToolButton *toolButton, const QString &icon) {\n    QIcon i = toolButton->icon();\n    i.addPixmap(QPixmap(icon), QIcon::Disabled);\n    toolButton->setIcon(i);\n}\n\n/////////////////////////////////////////////////\n// PRIVATE\n/////////////////////////////////////////////////\n\nvoid t_gui::setLineFields(int line) {\n\tif (line == 0) {\n\t\tfromLabel = mainWindow->from1Label;\n\t\ttoLabel = mainWindow->to1Label;\n\t\tsubjectLabel = mainWindow->subject1Label;\n\t\tcodecLabel = mainWindow->codec1TextLabel;\n\t\tphotoLabel = mainWindow->photo1Label;\n\t} else {\n\t\tfromLabel = mainWindow->from2Label;\n\t\ttoLabel = mainWindow->to2Label;\n\t\tsubjectLabel = mainWindow->subject2Label;\n\t\tcodecLabel = mainWindow->codec2TextLabel;\n\t\tphotoLabel = mainWindow->photo2Label;\n\t}\n}\n\nvoid t_gui::clearLineFields(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tsetLineFields(line);\n\tfromLabel->clear();\n    fromLabel->setToolTip(QString());\n\ttoLabel->clear();\n    toLabel->setToolTip(QString());\n\tsubjectLabel->clear();\n    subjectLabel->setToolTip(QString());\n\tcodecLabel->clear();\n\tphotoLabel->clear();\n\tphotoLabel->hide();\n}\n\nvoid t_gui::displayTo(const QString &s) {\n\ttoLabel->setText(s);\n\ttoLabel->setCursorPosition(0);\n    toLabel->setToolTip(s);\n}\n\nvoid t_gui::displayFrom(const QString &s) {\n\tfromLabel->setText(s);\n\tfromLabel->setCursorPosition(0);\n    fromLabel->setToolTip(s);\n}\n\nvoid t_gui::displaySubject(const QString &s) {\n\tsubjectLabel->setText(s);\n\tsubjectLabel->setCursorPosition(0);\n    subjectLabel->setToolTip(s);\n}\n\nvoid t_gui::displayCodecInfo(int line) {\n\tif (line > NUM_USER_LINES) return;\n\t\n\tsetLineFields(line);\n\tcodecLabel->clear();\n\t\n\tt_call_info call_info = phone->get_call_info(line);\n\t\n\tif (call_info.send_codec == CODEC_NULL && call_info.recv_codec == CODEC_NULL) {\n\t\treturn;\n\t}\n\t\n\tif (call_info.send_codec == CODEC_NULL) {\n\t\tcodecLabel->setText(format_codec(call_info.recv_codec).c_str());\n\t\treturn;\n\t}\n\t\n\tif (call_info.recv_codec == CODEC_NULL) {\n\t\tcodecLabel->setText(format_codec(call_info.send_codec).c_str());\n\t\treturn;\n\t}\n\t\n\tif (call_info.send_codec == call_info.recv_codec) {\n\t\tcodecLabel->setText(format_codec(call_info.send_codec).c_str());\n\t\treturn;\n\t}\n\t\n\tQString s = format_codec(call_info.send_codec).c_str();\n\ts.append('/').append(format_codec(call_info.recv_codec).c_str());\n\tcodecLabel->setText(s);\n}\n\nvoid t_gui::displayPhoto(const QImage &photo) {\n\tif (mainWindow->getViewCompactLineStatus()) {\n\t\t// In compact line status mode, no photo can be shown\n\t\treturn;\n\t}\n\t\n\tif (photo.isNull()) {\n\t\tphotoLabel->hide();\n\t} else {\n\t\tQPixmap pm;\n        pm.convertFromImage(photo.scaled(\n            QSize(photoLabel->width(), photoLabel->height()), Qt::KeepAspectRatio, Qt::SmoothTransformation));\n\t\tphotoLabel->setPixmap(pm);\n\t\tphotoLabel->show();\n\t}\n}\n\n/////////////////////////////////////////////////\n// PROTECTED\n/////////////////////////////////////////////////\nbool t_gui::do_invite(const string &destination, const string &display, \n\t\t\tconst string &subject, bool immediate, bool anonymous)\n{\n\tQMetaObject::invokeMethod(this, \"gui_do_invite\",\n\t\t\t\t  Q_ARG(QString, QString::fromStdString(destination)),\n\t\t\t\t  Q_ARG(QString, QString::fromStdString(display)),\n\t\t\t\t  Q_ARG(QString, QString::fromStdString(subject)),\n\t\t\t\t  Q_ARG(bool, immediate),\n\t\t\t\t  Q_ARG(bool, anonymous));\n\n\treturn true;\n}\n\nvoid t_gui::do_redial(void) {\n\tQMetaObject::invokeMethod(this, \"gui_do_redial\");\n}\n\nvoid t_gui::do_answer(void) {\n\tQMetaObject::invokeMethod(this, \"gui_do_answer\");\n}\n\nvoid t_gui::do_answerbye(void) {\n\tQMetaObject::invokeMethod(this, \"gui_do_answerbye\");\n}\n\nvoid t_gui::do_reject(void) {\n\n\tQMetaObject::invokeMethod(this, \"gui_do_reject\");\n}\n\nvoid t_gui::do_redirect(bool show_status, bool type_present, t_cf_type cf_type, \n\t\tbool action_present, bool enable, int num_redirections,\n\t\tconst list<string> &dest_strlist, bool immediate)\n{\n\tif (show_status) {\n\t\t// Show status not supported in GUI\n\t\treturn;\n\t}\n\n\tQMetaObject::invokeMethod(this, \"gui_do_redirect\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_ARG(bool, type_present),\n\t\t\t\t  Q_ARG(t_cf_type, cf_type),\n\t\t\t\t  Q_ARG(bool, action_present),\n\t\t\t\t  Q_ARG(bool, enable),\n\t\t\t\t  Q_ARG(int, num_redirections),\n\t\t\t\t  Q_ARG(std::list<std::string>, dest_strlist),\n\t\t\t\t  Q_ARG(bool, immediate));\n\t\n}\n\nvoid t_gui::do_dnd(bool show_status, bool toggle, bool enable) {\n\tif (show_status) {\n\t\t// Show status not supported in GUI\n\t\treturn;\n\t}\n\t\n\tQMetaObject::invokeMethod(this, \"gui_do_dnd\",\n\t\t\t\t  Q_ARG(bool, toggle), Q_ARG(bool, enable));\n}\n\nvoid t_gui::do_auto_answer(bool show_status, bool toggle, bool enable) {\n\tif (show_status) {\n\t\t// Show status not supported in GUI\n\t\treturn;\n\t}\n\t\n\tQMetaObject::invokeMethod(this, \"gui_do_auto_answer\",\n\t\t\t\t  Q_ARG(bool, toggle), Q_ARG(bool, enable));\n}\n\nvoid t_gui::do_bye(void) {\n\n\tQMetaObject::invokeMethod(this, \"gui_do_bye\");\n}\n\nvoid t_gui::do_hold(bool toggle) {\n\n\tQMetaObject::invokeMethod(this, \"gui_do_hold\",\n\t\t\t\t  Q_ARG(bool, toggle));\n}\n\nvoid t_gui::do_retrieve(void) {\n\n\tQMetaObject::invokeMethod(this, \"gui_do_retrieve\");\n}\n\nbool t_gui::do_refer(const string &destination, t_transfer_type transfer_type, bool immediate) {\n\n\tQMetaObject::invokeMethod(this, \"gui_do_refer\",\n\t\t\t\t  Q_ARG(QString, QString::fromStdString(destination)),\n\t\t\t\t  Q_ARG(t_transfer_type, transfer_type),\n\t\t\t\t  Q_ARG(bool, immediate));\n\t\n\treturn true;\n}\n\nvoid t_gui::do_conference(void) {\n\tQMetaObject::invokeMethod(this, \"gui_do_conference\");\n}\n\nvoid t_gui::do_mute(bool show_status, bool toggle, bool enable) {\n\tif (show_status) {\n\t\t// Show status not supported in GUI\n\t\treturn;\n\t}\n\t\n\tQMetaObject::invokeMethod(this, \"gui_do_mute\",\n\t\t\t\t  Q_ARG(bool, toggle), Q_ARG(bool, enable));\n}\n\nvoid t_gui::do_dtmf(const string &digits) {\n\n\tQMetaObject::invokeMethod(this, \"gui_do_dtmf\",\n\t\t\t\t  Q_ARG(QString, QString::fromStdString(digits)));\n}\n\nvoid t_gui::do_register(bool reg_all_profiles) {\n\tlist<t_user *> l;\n\t\n\tif (reg_all_profiles) {\n\t\tl = phone->ref_users();\n\t} else {\n\t\tQString profile;\n\t\tt_user *user;\n\n\t\tQMetaObject::invokeMethod(this, \"gui_get_current_profile\",\n\t\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t\t  Q_RETURN_ARG(QString, profile));\n\n\t\tuser = phone->ref_user_profile(profile.toStdString());\n\t\tl.push_back(user);\n\t}\n\t\n\tmainWindow->do_phoneRegister(l);\n}\n\nvoid t_gui::do_deregister(bool dereg_all_profiles, bool dereg_all_devices) {\n\tlist<t_user *> l;\n\t\n\tif (dereg_all_profiles) {\n\t\tl = phone->ref_users();\n\t} else {\n\t\tQString profile;\n\t\tt_user *user;\n\n\t\tQMetaObject::invokeMethod(this, \"gui_get_current_profile\",\n\t\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t\t  Q_RETURN_ARG(QString, profile));\n\n\t\tuser = phone->ref_user_profile(profile.toStdString());\n\t\tl.push_back(user);\n\t}\n\t\n\tif (dereg_all_devices) {\n\t\tmainWindow->do_phoneDeregisterAll(l);\n\t} else {\n\t\tmainWindow->do_phoneDeregister(l);\n\t}\n}\n\nvoid t_gui::do_fetch_registrations(void) {\n\tQMetaObject::invokeMethod(mainWindow, \"phoneShowRegistrations\");\n}\n\nbool t_gui::do_options(bool dest_set, const string &destination, bool immediate) {\n\n\t// In-dialog OPTIONS request\n\tint line = phone->get_active_line();\n\tif (phone->get_line_substate(line) == LSSUB_ESTABLISHED) {\n\t\t((t_gui *)ui)->action_options();\n\t\treturn true;\n\t}\n\t\n\tif (immediate) {\n\t\tQString profile;\n\t\tt_user *user;\n\n\t\tQMetaObject::invokeMethod(this, \"gui_get_current_profile\",\n\t\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t\t  Q_RETURN_ARG(QString, profile));\n\n\t\tuser = phone->ref_user_profile(profile.toStdString());\n\t\tt_url dst_url(expand_destination(user, destination));\n\t\t\n\t\tif (dst_url.is_valid()) {\n\t\t\tmainWindow->do_phoneTermCap(user, dst_url);\n\t\t}\n\t} else {\n\t\tQMetaObject::invokeMethod(mainWindow, \"phoneTermCap\",\n\t\t\t\t\t  Q_ARG(QString,\n\t\t\t\t\t  QString::fromStdString(destination)));\n\t}\n\t\n\treturn true;\n}\n\nvoid t_gui::do_line(int line) {\n\t// Cannot get current line number via CLI interface on GUI.\n\t// So return in this case.\n\tif (line == 0) return;\n\t\n\tif (line == -1) {\n\t\tint current = phone->get_active_line();\n\t\tint other = 1 - current;\n\t\tline = other + 1;\n\t}\n\n\tphone->pub_activate_line(line - 1);\n}\n\nvoid t_gui::do_user(const string &profile_name) {\n\tQMetaObject::invokeMethod(this, \"gui_do_user\",\n\t\t\t\t  Q_ARG(QString, QString::fromStdString(profile_name)));\n}\n\nQString t_gui::gui_get_current_profile()\n{\n\treturn mainWindow->userComboBox->currentText();\n}\n\nbool t_gui::do_message(const string &destination, const string &display,\n\t\t       const im::t_msg &msg)\n{\n\tt_user *user;\n\tQString profile;\n\n\tQMetaObject::invokeMethod(this, \"gui_get_current_profile\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_RETURN_ARG(QString, profile));\n\n\tuser = phone->ref_user_profile(profile.toStdString());\n\t\n\tt_url dest_url(expand_destination(user, destination));\n\t\n\tif (dest_url.is_valid()) {\n\t\tphone->pub_send_message(user, dest_url, display, msg);\n\t}\n\t\n\treturn true;\n}\n\nvoid t_gui::do_presence(t_presence_state::t_basic_state basic_state) {\n\tQString profile;\n\tt_user *user;\n\n\tQMetaObject::invokeMethod(this, \"gui_get_current_profile\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_RETURN_ARG(QString, profile));\n\n\tuser = phone->ref_user_profile(profile.toStdString());\n\t\n\tphone->pub_publish_presence(user, basic_state);\n}\n\nvoid t_gui::do_zrtp(t_zrtp_cmd zrtp_cmd) {\n\tlock();\n\t\n\tswitch (zrtp_cmd) {\n\tcase ZRTP_ENCRYPT:\n\t\tQMetaObject::invokeMethod(mainWindow, \"phoneEnableZrtp\",\n\t\t\t\t\t  Q_ARG(bool, true));\n\t\tbreak;\n\tcase ZRTP_GO_CLEAR:\n\t\tQMetaObject::invokeMethod(mainWindow, \"phoneEnableZrtp\",\n\t\t\t\t\t  Q_ARG(bool, false));\n\t\tbreak;\n\tcase ZRTP_CONFIRM_SAS:\n\t\tQMetaObject::invokeMethod(mainWindow, \"phoneConfirmZrtpSas\");\n\t\tbreak;\n\tcase ZRTP_RESET_SAS:\n\t\tQMetaObject::invokeMethod(mainWindow, \"phoneResetZrtpSasConfirmation\");\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\tunlock();\n}\n\n\n\nvoid t_gui::do_quit(void) {\n\tQMetaObject::invokeMethod(mainWindow, \"fileExit\");\n}\n\nvoid t_gui::do_help(const list<t_command_arg> &al) {\n\t// Nothing to do in GUI mode\n\treturn;\n}\n\nvoid t_gui::gui_do_invite(const QString &destination, const QString &display,\n\t\tconst QString &subject, bool immediate,\n\t\tbool anonymous)\n{\n\tif (mainWindow->callInvite->isEnabled()) {\n\t\tif (immediate) {\n\t\t\tt_user *user = phone->ref_user_profile(\n\t\t\t\tmainWindow->userComboBox->currentText().toStdString());\n\t\t\tt_url dst_url(expand_destination(user, destination.toStdString()));\n\t\t\tif (dst_url.is_valid()) {\n\t\t\t\tmainWindow->do_phoneInvite(user,\n\t\t\t\t\t\tdisplay, dst_url, subject,\n\t\t\t\t\t\tanonymous);\n\t\t\t}\n\t\t} else {\n\t\t\tt_url dest_url(destination.toStdString());\n\t\t\tt_display_url du(dest_url, display.toStdString());\n\t\t\tmainWindow->phoneInvite(du.encode().c_str(), subject,\n\t\t\t\t\t\tanonymous);\n\t\t}\n\t}\n}\n\nvoid t_gui::gui_do_redial(void)\n{\n\tif (mainWindow->callRedial->isEnabled()) {\n\t\tmainWindow->phoneRedial();\n\t}\n}\n\nvoid t_gui::gui_do_answer(void)\n{\n\tif (mainWindow->callAnswer->isEnabled()) {\n\t\tmainWindow->phoneAnswer();\n\t}\n}\n\nvoid t_gui::gui_do_answerbye(void)\n{\n\tif (mainWindow->callAnswer->isEnabled()) {\n\t\tmainWindow->phoneAnswer();\n\t} else if (mainWindow->callBye->isEnabled()) {\n\t\tmainWindow->phoneBye();\n\t}\n}\n\nvoid t_gui::gui_do_reject(void)\n{\n\tif (mainWindow->callReject->isEnabled()) {\n\t\tmainWindow->phoneReject();\n\t}\n}\n\nvoid t_gui::gui_do_redirect(bool type_present, t_cf_type cf_type,\n\tbool action_present, bool enable, int num_redirections,\n\tconst list<string> &dest_strlist, bool immediate)\n{\n\tt_user *user = phone->ref_user_profile(mainWindow->\n\t\t\t\tuserComboBox->currentText().toStdString());\n\n\tlist<t_display_url> dest_list;\n\tfor (list<string>::const_iterator i = dest_strlist.begin();\n\t\t i != dest_strlist.end(); i++)\n\t{\n\t\tt_display_url du;\n\t\tdu.url = expand_destination(user, *i);\n\t\tdu.display.clear();\n\t\tif (!du.is_valid()) return;\n\t\tdest_list.push_back(du);\n\t}\n\n\t// Enable/disable permanent redirections\n\tif (type_present) {\n\n\t\tif (enable) {\n\t\t\tphone->ref_service(user)->enable_cf(cf_type, dest_list);\n\t\t} else {\n\t\t\tphone->ref_service(user)->disable_cf(cf_type);\n\t\t}\n\t\tmainWindow->updateServicesStatus();\n\n\n\t\treturn;\n\t} else {\n\t\tif (action_present) {\n\t\t\tif (!enable) {\n\n\t\t\t\tphone->ref_service(user)->disable_cf(CF_ALWAYS);\n\t\t\t\tphone->ref_service(user)->disable_cf(CF_BUSY);\n\t\t\t\tphone->ref_service(user)->disable_cf(CF_NOANSWER);\n\t\t\t\tmainWindow->updateServicesStatus();\n\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (mainWindow->callRedirect->isEnabled()) {\n\t\tif (immediate) {\n\t\t\tmainWindow->do_phoneRedirect(dest_list);\n\t\t} else {\n\t\t\tmainWindow->phoneRedirect(dest_strlist);\n\t\t}\n\t}\n}\n\nvoid t_gui::gui_do_dnd(bool toggle, bool enable)\n{\n\tif (phone->ref_users().size() == 1) {\n\t\tif (toggle) {\n\t\t\tenable = !mainWindow->serviceDnd->isChecked();\n\t\t}\n\t\tmainWindow->srvDnd(enable);\n\t\tmainWindow->serviceDnd->setChecked(enable);\n\t} else {\n\t\tt_user *user = phone->ref_user_profile(mainWindow->\n\t\t\t\tuserComboBox->currentText().toStdString());\n\t\tlist<t_user *> l;\n\t\tl.push_back(user);\n\t\tif (toggle) {\n\t\t\tenable = !phone->ref_service(user)->is_dnd_active();\n\t\t}\n\n\t\tif (enable) {\n\t\t\tmainWindow->do_srvDnd_enable(l);\n\t\t} else {\n\t\t\tmainWindow->do_srvDnd_disable(l);\n\t\t}\n\t}\n}\n\nvoid t_gui::gui_do_auto_answer(bool toggle, bool enable)\n{\n\tif (phone->ref_users().size() == 1) {\n\t\tif (toggle) {\n\t\t\tenable = !mainWindow->serviceAutoAnswer->isChecked();\n\t\t}\n\t\tmainWindow->srvAutoAnswer(enable);\n\t\tmainWindow->serviceAutoAnswer->setChecked(enable);\n\t} else {\n\t\tt_user *user = phone->ref_user_profile(mainWindow->\n\t\t\t\tuserComboBox->currentText().toStdString());\n\t\tlist<t_user *> l;\n\t\tl.push_back(user);\n\t\tif (toggle) {\n\t\t\tenable = !phone->ref_service(user)->\n\t\t\t\t is_auto_answer_active();\n\t\t}\n\n\t\tif (enable) {\n\t\t\tmainWindow->do_srvAutoAnswer_enable(l);\n\t\t} else {\n\t\t\tmainWindow->do_srvAutoAnswer_disable(l);\n\t\t}\n\t}\n}\n\nvoid t_gui::gui_do_bye(void)\n{\n\tif (mainWindow->callBye->isEnabled()) {\n\t\tmainWindow->phoneBye();\n\t}\n}\n\nvoid t_gui::gui_do_hold(bool toggle)\n{\n\tif (mainWindow->callHold->isEnabled()) {\n\t\tif (toggle && mainWindow->callHold->isChecked())\n\t\t\tmainWindow->phoneHold(false);\n\t\telse\n\t\t\tmainWindow->phoneHold(true);\n\t}\n}\n\nvoid t_gui::gui_do_retrieve(void)\n{\n\tif (mainWindow->callHold->isEnabled()) {\n\t\tif (mainWindow->callHold->isChecked())\n\t\t\tmainWindow->phoneHold(false);\n\t}\n}\n\nvoid t_gui::gui_do_refer(const QString &destination, t_transfer_type transfer_type,\n\tbool immediate)\n{\n\tif (mainWindow->callTransfer->isEnabled() &&\n\t\t!mainWindow->callTransfer->isChecked())\n\t{\n\t\tif (immediate) {\n\t\t\tt_display_url du;\n\t\t\tt_user *user = phone->ref_user_profile(mainWindow->\n\t\t\t\tuserComboBox->currentText().toStdString());\n\t\t\tdu.url = expand_destination(user, destination.toStdString());\n\n\t\t\tif (du.is_valid() || transfer_type == TRANSFER_OTHER_LINE) {\n\t\t\t\tmainWindow->do_phoneTransfer(du, transfer_type);\n\t\t\t}\n\t\t} else {\n\t\t\tmainWindow->phoneTransfer(destination.toStdString(), transfer_type);\n\t\t}\n\t} else if(mainWindow->callTransfer->isEnabled() &&\n\t\t  mainWindow->callTransfer->isChecked())\n\t{\n\t\tif (transfer_type != TRANSFER_CONSULT) {\n\t\t\tmainWindow->do_phoneTransferLine();\n\t\t}\n\t}\n}\n\nvoid t_gui::gui_do_conference(void)\n{\n\tif (mainWindow->callConference->isEnabled()) {\n\t\tmainWindow->phoneConference();\n\t}\n}\n\nvoid t_gui::gui_do_mute(bool toggle, bool enable)\n{\n\tif (mainWindow->callMute->isEnabled()) {\n\t\tif (toggle) enable = !phone->is_line_muted(phone->get_active_line());\n\t\tmainWindow->phoneMute(enable);\n\t}\n}\n\nvoid t_gui::gui_do_dtmf(const QString &digits)\n{\n\tif (mainWindow->callDTMF->isEnabled()) {\n\t\tmainWindow->sendDTMF(digits);\n\t}\n}\n\nvoid t_gui::gui_do_user(const QString &profile_name)\n{\n\tfor (int i = 0; i < mainWindow->userComboBox->count(); i++) {\n\t\tif (mainWindow->userComboBox->itemText(i) == profile_name)\n\t\t{\n\t\t\tmainWindow->userComboBox->setCurrentIndex(i);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid t_gui::gui_cmd_call(const string &destination, bool immediate) {\n\tstring subject;\n\tstring dst_no_headers;\n\tt_display_url du;\n\n\tt_user *user = phone->ref_user_profile(\n            mainWindow->userComboBox->currentText().toStdString());\n\texpand_destination(user, destination, du, subject, dst_no_headers);\n\tif (!du.is_valid()) return;\n\n\tif (immediate) {\n\t\tmainWindow->do_phoneInvite(user, du.display.c_str(), du.url,\n\t\t\t\t\t   subject.c_str(), false);\n\t} else {\n\t\tmainWindow->phoneInvite(dst_no_headers.c_str(), subject.c_str(), false);\n\t}\n}\n\nvoid t_gui::gui_cmd_show(void) {\n\tif (mainWindow->isMinimized()) {\n\t\tmainWindow->setWindowState((mainWindow->windowState() & ~Qt::WindowMinimized) |\n\t\t\t\t\t   Qt::WindowActive);\n\t\tmainWindow->raise();\n\t} else {\n\t\tmainWindow->show();\n\t\tmainWindow->raise();\n        mainWindow->activateWindow();\n\t}\n}\n\nvoid t_gui::gui_cmd_hide(void) {\n\tif (sys_config->get_gui_use_systray()) {\n\t\tmainWindow->hide();\n\t} else {\n\t\tmainWindow->setWindowState(mainWindow->windowState() | Qt::WindowMinimized);\n\t}\n}\n\n/////////////////////////////////////////////////\n// PUBLIC\n/////////////////////////////////////////////////\n\nt_gui::t_gui(t_phone *_phone) : t_userintf(_phone), timerUpdateMessageSessions(NULL)\n{\n\tuse_stdout = false;\n\tlastFileBrowsePath = DIR_HOME;\n\n    qRegisterMetaType<t_user*>(\"t_user*\");\n    qRegisterMetaType<t_register_type>(\"t_register_type\");\n\tqRegisterMetaType<t_transfer_type>(\"t_transfer_type\");\n\tqRegisterMetaType<t_cf_type>(\"t_cf_type\");\n\tqRegisterMetaType<string>(\"string\");\n\tqRegisterMetaType<std::list<std::string>>(\"std::list<std::string>\");\n\n\tm_idle_session_manager = new IdleSessionManager(this);\n\tconnect(this, &t_gui::update_state,\n\t\t\tthis, &t_gui::updateIdleSessionState,\n\t\t\tQt::QueuedConnection);\n\t\n    mainWindow = new MphoneForm;\n#ifdef HAVE_KDE\n\tsys_tray_popup = NULL;\n#endif\n\t\n\tfor (int i = 0; i < NUM_USER_LINES; i++) {\n\t\tQObject::connect(&autoShowTimer[i], SIGNAL(timeout()),\n\t\t\tmainWindow, SLOT(show()));\n\t}\n\t\n\tMEMMAN_NEW(mainWindow);\n\n\tconnect(this, SIGNAL(update_reg_status()), mainWindow, SLOT(updateRegStatus()), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(update_mwi()), mainWindow, SLOT(updateMwi()), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(update_state()), mainWindow, SLOT(updateState()), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(mw_display(const QString&)), mainWindow, SLOT(display(const QString&)), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(mw_display_header()), mainWindow, SLOT(displayHeader()), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(mw_update_log(bool)), mainWindow, SLOT(updateLog(bool)), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(mw_update_call_history()), mainWindow, SLOT(updateCallHistory()), Qt::QueuedConnection);\n\tconnect(this, SIGNAL(mw_update_missed_call_status(int)), mainWindow, SLOT(updateMissedCallStatus(int)), Qt::QueuedConnection);\n}\n\nt_gui::~t_gui() {\n\tdestroyAllMessageSessions();\n\t\n\tMEMMAN_DELETE(mainWindow);\n\tdelete mainWindow;\n}\n\nvoid t_gui::run(void) {\n\t// Start asynchronous event processor\n\tthr_process_events = new t_thread(process_events_main, NULL);\n\tMEMMAN_NEW(thr_process_events);\n\t\n\tupdateInhibitIdleSession();\n\n\tQString s;\n\tlist<t_user *> user_list = phone->ref_users();\n\t\n\t// The Qt event loop is not running yet. Explicitly take the Qt lock\n\t// to avoid race conditions with other threads that may call GUI call\n\t// backs.\n\t// NOTE: the t_gui::lock() method cannot be used as this method\n\t// will not lock from the main thread and we are running in the\n\t// main thread (a bit of a kludge).\n    //qApp->lock();\n\t\n\t// Set configuration file name in titlebar\n\ts = PRODUCT_NAME;\n    mainWindow->setWindowTitle(s);\n\t\n\t// Set user combo box\n\tmainWindow->updateUserComboBox();\n\t\n\t// Display product information\n\ts = PRODUCT_NAME;\n\ts.append(' ').append(PRODUCT_VERSION).append(\", \");\n\ts.append(sys_config->get_product_date().c_str());\n\tmainWindow->display(s);\n\ts = \"Copyright (C) 2005-2015  \";\n\ts.append(PRODUCT_AUTHOR);\n\tmainWindow->display(s);\n\t\n\t// Restore user interface state from previous session\n\trestore_state();\n\t\n\t// Initialize phone functions\n\trun_on_event_queue([=]() {\n\t\tphone->init();\n\t});\n\t\n\t// Set controls in correct status\n\tmainWindow->updateState();\n\tmainWindow->updateRegStatus();\n\tmainWindow->updateMwi();\n\tmainWindow->updateServicesStatus();\n\tmainWindow->updateMissedCallStatus(0);\n\tmainWindow->updateMenuStatus();\n\t\n\t// Clear line field info fields\n\tclearLineFields(0);\n\tclearLineFields(1);\n\t\n\t// Populate buddy list\n\tmainWindow->populateBuddyList();\n\t\n\t// Set width of window to width of tool bar\n\t// int widthToolBar = mainWindow->callToolbar->width();\n\t// QSize sizeMainWin = mainWindow->size();\n\t// sizeMainWin.setWidth(widthToolBar);\n\t// mainWindow->resize(sizeMainWin);\n\t\n\t// Start QApplication/KApplication\n\tif (qApp->isSessionRestored() && \n        sys_config->get_ui_session_id() == qApp->sessionId().toStdString())\n\t{\n\t\t// Restore previous session\n\t\trestore_session_state();\n\t} else {\n\t\tif ((sys_config->get_start_hidden() && !g_cmd_args.cmd_show) ||\n\t\t    g_cmd_args.cmd_hide)\n\t\t{\n\t\t\tmainWindow->hide();\n\t\t} else {\n\t\t\tmainWindow->show();\n\t\t}\n\t}\n\t\n\t// Activate a profile if the --set-profile option was given on the command\n\t// line.\n\tif (!g_cmd_args.cmd_set_profile.isEmpty()) {\n\t\tcmdsocket::cmd_cli(string(\"user \") + \n                   g_cmd_args.cmd_set_profile.toStdString(), true);\n\t}\n\t\n\t// Execute the call command if a callto destination was specified on the\n\t// command line\n\tif (!g_cmd_args.callto_destination.isEmpty()) {\n        cmdsocket::cmd_call(g_cmd_args.callto_destination.toStdString(),\n\t\t\t g_cmd_args.cmd_immediate_mode);\n\t}\n\t\n\t// Execute a CLI command if one was given on the command line\n\tif (!g_cmd_args.cli_command.isEmpty()) {\n        cmdsocket::cmd_cli(g_cmd_args.cli_command.toStdString(),\n\t\t\tg_cmd_args.cmd_immediate_mode);\n\t}\n\t\n    //qApp->unlock();\n\t\n\t// Start Qt application\n\tqApp->exec();\n\t\n\t// Terminate phone functions\n\tphone->terminate();\n\t\n\t// Make user interface state persistent\n\tsave_state();\n}\n\nvoid t_gui::save_state(void) {\n\tlock();\n\t\n\tsys_config->set_last_used_profile(\n            mainWindow->userComboBox->currentText().toStdString());\n\n\tlist<string> history;\n\tfor (int i = 0; i < mainWindow->callComboBox->count(); i++) {\n        history.push_back(mainWindow->callComboBox->itemText(i).toStdString());\n\t}\n\tsys_config->set_dial_history(history);\n\t\n\tsys_config->set_show_display(mainWindow->getViewDisplay());\n\tsys_config->set_compact_line_status(mainWindow->getViewCompactLineStatus());\n\tsys_config->set_show_buddy_list(mainWindow->getViewBuddyList());\n\t\n\tt_userintf::save_state();\n\t\n\tunlock();\n}\n\nvoid t_gui::restore_state(void) {\n\tlock();\n\t\n\t// The last used profile is selected when the userComboBox is\n\t// filled by MphoneForm::updateUserComboBox\n\t\n\tmainWindow->callComboBox->clear();\n\tlist<string> dial_history = sys_config->get_dial_history();\n\tfor (list<string>::reverse_iterator i = dial_history.rbegin(); i != dial_history.rend(); i++)\n\t{\n\t\tmainWindow->addToCallComboBox(i->c_str());\n\t}\n\t\n\tmainWindow->showDisplay(sys_config->get_show_display());\n\tmainWindow->showCompactLineStatus(sys_config->get_compact_line_status());\n\tmainWindow->showBuddyList(sys_config->get_show_buddy_list());\n\t\n\tt_userintf::restore_state();\n\t\n\tunlock();\n}\n\nvoid t_gui::save_session_state(void) {\n\tlock();\n\t\n\tt_win_geometry geometry(mainWindow->x(), mainWindow->y(),\n\t\t\t\tmainWindow->width(), mainWindow->height());\n\tsys_config->set_ui_session_main_geometry(geometry);\n\tsys_config->set_ui_session_main_state(mainWindow->windowState());\n\tsys_config->set_ui_session_main_hidden(mainWindow->isHidden());\n\t\n\tunlock();\n}\n\nvoid t_gui::restore_session_state(void) {\n\tlock();\n\t\n\tt_win_geometry geometry = sys_config->get_ui_session_main_geometry();\n\tmainWindow->setGeometry(geometry.x, geometry.y, geometry.width, geometry.height);\n\tmainWindow->setWindowState(Qt::WindowStates(sys_config->get_ui_session_main_state()));\n\tmainWindow->setHidden(sys_config->get_ui_session_main_hidden());\n\t\n\tunlock();\n}\n\nvoid t_gui::lock(void) {\n\t// To synchronize actions on the Qt widget the application lock\n\t// is used. The main thread running the Qt event loop takes the\n\t// application lock itself already. So take the lock if this is not the\n\t// main thread.\n\t// If the Qt event loop has not been started yet, then the lock\n\t// should also be taken from the main thread.\n    t_userintf::lock();\n    //if (!t_thread::is_self(thread_id_main)) {\n    //\tqApp->lock();\n    //}\n}\n\nvoid t_gui::unlock(void) {\n\tt_userintf::lock();\n    //if (!t_thread::is_self(thread_id_main)) {\n    //\tqApp->unlock();\n    //}\n}\n\nstring t_gui::select_network_intf(void) {\n\tstring ip;\n\tlist<t_interface> *l = get_interfaces();\n\t// The socket routines are not under control of MEMMAN so report\n\t// the allocation here.\n\tMEMMAN_NEW(l);\n\tif (l->size() == 0) {\n\t\tcb_show_msg(qApp->translate(\"GUI\",\n\t\t\t    \"Cannot find a network interface. Twinkle will use \"\n\t\t\t    \"127.0.0.1 as the local IP address. When you connect to \"\n\t\t\t    \"the network you have to restart Twinkle to use the correct \"\n                \"IP address.\").toStdString(),\n\t\t\t    MSG_WARNING);\n\t\t\n\t\tMEMMAN_DELETE(l);\n\t\tdelete l;\n\t\treturn \"127.0.0.1\";\n\t}\n\t\n\tif (l->size() == 1) {\n\t\t// There is only 1 interface\n\t\tip = l->front().get_ip_addr();\n\t} else {\n\t\t// There are multiple interfaces\n        SelectNicForm *sf = new SelectNicForm(NULL);\n        sf->setModal(true);\n\t\tMEMMAN_NEW(sf);\n\t\tQString item;\n\t\tfor (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {\n\t\t\titem = i->name.c_str();\n\t\t\titem.append(':').append(i->get_ip_addr().c_str());\n\n            new QListWidgetItem(QPixmap(\":/icons/images/kcmpci16.png\"), item, sf->nicListBox);\n\t\t}\n\t\t\n\t\tsf->nicListBox->setCurrentItem(0);\n\t\tsf->exec();\n\t\t\n        int selection = sf->nicListBox->currentRow();\n\t\tint num = 0;\n\t\tfor (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {\n\t\t\tif (num == selection) {\n\t\t\t\tip = i->get_ip_addr();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnum++;\n\t\t}\n\t\t\n\t\tMEMMAN_DELETE(sf);\n\t\tdelete sf;\t\t\n\t}\n\t\n\tMEMMAN_DELETE(l);\n\tdelete l;\n\treturn ip;\n}\n\nbool t_gui::select_user_config(list<string> &config_files) {\n    SelectProfileForm f(nullptr);\n    f.setModal(true);\n\t\n\tif (f.execForm()) {\n\t\tconfig_files = f.selectedProfiles;\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\n// GUI call back functions\n\nvoid t_gui::cb_incoming_call(t_user *user_config, int line, const t_request *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\tsetLineFields(line);\n\t\n\t// Incoming call for to-header\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: incoming call for %2\").arg(line + 1).arg(\n\t\t\tformat_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri).c_str());\n\temit mw_display(s);\n\t\n\t// Is this a transferred call?\n\tQString referredByParty;\n\tif (r->hdr_referred_by.is_populated()) {\n\t\treferredByParty = format_sip_address(user_config, \n\t\t\t\tr->hdr_referred_by.display, r->hdr_referred_by.uri).c_str();\n\t\ts = \"Call transferred by \";\n\t\ts = qApp->translate(\"GUI\", \"Call transferred by %1\").arg(referredByParty);\n\t\temit mw_display(s);\n\t}\n\t\n\t// From\n\tQString fromParty = format_sip_address(user_config,\n\t\t\t\tr->hdr_from.get_display_presentation(), r->hdr_from.uri).c_str();\n\ts = fromParty;\n\tQString organization(\"\");\n\tif (r->hdr_organization.is_populated()) {\n\t\torganization = r->hdr_organization.name.c_str();\n\t\ts.append(\", \").append(organization);\n\t}\n\tdisplayFrom(s);\n\t\n\t// Display photo\n\tQImage fromPhoto;\n\t\n\tif (sys_config->get_ab_lookup_photo()) {\n\t\tt_address_finder *af = t_address_finder::get_instance();\n\t\tfromPhoto = af->find_photo(user_config, r->hdr_from.uri);\n\t}\n\t\n\tdisplayPhoto(fromPhoto);\n\t\n\t// To\n\ts = \"\";\n\ts.append(format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri).c_str());\n\tdisplayTo(s);\n\t\n\t// Subject\n\tQString subject(\"\");\n\tif (r->hdr_subject.is_populated()) {\n\t\tsubject = r->hdr_subject.subject.c_str();\n\t}\n\tdisplaySubject(subject);\n\t\n\tcb_notify_call(line, fromParty, organization, fromPhoto, subject, referredByParty);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_call_cancelled(int line, const std::string &reason) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: far end cancelled call.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\tif (!reason.empty()) {\n\t\ts = reason.c_str();\n\t\temit mw_display(s);\n\t}\n\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_far_end_hung_up(int line, const std::string &reason) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: far end released call.\").arg(line + 1);\n\temit mw_display(s);\n\n\tif (!reason.empty()) {\n\t\ts = reason.c_str();\n\t\temit mw_display(s);\n\t}\n\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_answer_timeout(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\t\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_sdp_answer_not_supported(int line, const string &reason) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: SDP answer from far end not supported.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = reason.c_str();\n\temit mw_display(s);\n\t\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_sdp_answer_missing(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: SDP answer from far end missing.\").arg(line + 1);\n\temit mw_display(s);\n\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_unsupported_content_type(int line, const t_sip_message *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: Unsupported content type in answer from far end.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = r->hdr_content_type.media.type.c_str();\n\ts.append(\"/\").append(r->hdr_content_type.media.subtype.c_str());\n\temit mw_display(s);\n\t\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_ack_timeout(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: no ACK received, call will be terminated.\").arg(line + 1);\n\temit mw_display(s);\n\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_100rel_timeout(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: no PRACK received, call will be terminated.\").arg(line + 1);\n\temit mw_display(s);\n\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_session_expired(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\n\tlock();\n\tQString s;\n\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: session has expired, call will be terminated.\").arg(line + 1);\n\temit mw_display(s);\n\n\tcb_stop_call_notification(line);\n\n\tunlock();\n}\n\nvoid t_gui::cb_prack_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: PRACK failed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = QString().setNum(r->code);\n\ts.append(' ').append(r->reason.c_str());\n\temit mw_display(s);\n\n\tcb_stop_call_notification(line);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_provisional_resp_invite(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\temit update_state();\n\tunlock();\n}\n\nvoid t_gui::cb_cancel_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: failed to cancel call.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = QString().setNum(r->code);\n\ts.append(' ').append(r->reason.c_str());\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_call_answered(t_user *user_config, int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\tsetLineFields(line);\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: far end answered call.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\t// Put far-end party in line to-field\n\ts = \"\";\n\ts.append(format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri).c_str());\n\tif (r->hdr_organization.is_populated()) {\n\t\ts.append(\", \").append(r->hdr_organization.name.c_str());\n\t}\n\tdisplayTo(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_call_failed(t_user *user_config, int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call failed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = QString().setNum(r->code);\n\ts.append(' ').append(r->reason.c_str());\n\temit mw_display(s);\n\t\n\t// Warnings\n\tif (r->hdr_warning.is_populated()) {\n\t\tlist<string> l = format_warnings(r->hdr_warning);\n\t\tfor (list<string>::iterator i = l.begin(); i != l.end(); i++) {\n\t\t\temit mw_display(i->c_str());\n\t\t}\n\t}\n\t\n\t// Redirect response\n\tif (r->get_class() == R_3XX && r->hdr_contact.is_populated()) {\n\t\tlist<t_contact_param> l = r->hdr_contact.contact_list;\n\t\tl.sort();\n\t\temit mw_display(qApp->translate(\"GUI\",\n\t\t\t\t\"The call can be redirected to:\"));\n\t\tfor (list<t_contact_param>::iterator i = l.begin();\n\t\ti != l.end(); i++)\n\t\t{\n\t\t\ts = format_sip_address(user_config,\n\t\t\t\t\ti->display, i->uri).c_str();\n\t\t\temit mw_display(s);\n\t\t}\n\t}\n\t\n\t// Unsupported extensions\n\tif (r->code == R_420_BAD_EXTENSION) {\n\t\temit mw_display(r->hdr_unsupported.encode().c_str());\n\t}\n\n\tunlock();\n}\n\nvoid t_gui::cb_stun_failed_call_ended(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call failed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_call_ended(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call released.\").arg(line + 1);\n\temit mw_display(s);\n\n\tunlock();\n}\n\nvoid t_gui::cb_call_established(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call established.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_options_response(const t_response *r) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Response on terminal capability request: %1 %2\")\n\t    .arg(r->code).arg(r->reason.c_str());\n\temit mw_display(s);\n\t\n\tif (r->code == R_408_REQUEST_TIMEOUT) {\n\t\t// The request timed out, so no capabilities are known.\n\t\tunlock();\n\t\treturn;\n\t}\n\t\n\ts = qApp->translate(\"GUI\", \"Terminal capabilities of %1\").arg(r->hdr_to.uri.encode().c_str());\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"Accepted body types:\").append(\" \");\n\tif (r->hdr_accept.is_populated()) {\n\t\ts.append(r->hdr_accept.get_value().c_str());\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"unknown\"));\n\t}\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"Accepted encodings:\").append(\" \");\n\tif (r->hdr_accept_encoding.is_populated()) {\n\t\ts.append(r->hdr_accept_encoding.get_value().c_str());\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"unknown\"));\n\t}\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"Accepted languages:\").append(\" \");\n\tif (r->hdr_accept_language.is_populated()) {\n\t\ts.append(r->hdr_accept_language.get_value().c_str());\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"unknown\"));\n\t}\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"Allowed requests:\").append(\" \");\n\tif (r->hdr_allow.is_populated()) {\n\t\ts.append(r->hdr_allow.get_value().c_str());\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"unknown\"));\n\t}\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"Supported extensions:\").append(\" \");\n\tif (r->hdr_supported.is_populated()) {\n\t\tif (r->hdr_supported.features.empty()) {\n\t\t\ts.append(qApp->translate(\"GUI\", \"none\"));\n\t\t} else {\n\t\t\ts.append(r->hdr_supported.get_value().c_str());\n\t\t}\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"unknown\"));\n\t}\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"End point type:\").append(\" \");\n\tif (r->hdr_server.is_populated()) {\n\t\ts.append(r->hdr_server.get_value().c_str());\n\t} else if (r->hdr_user_agent.is_populated()) {\n\t\t// Some end-points put a User-Agent header in the response\n\t\t// instead of a Server header.\n\t\ts.append(r->hdr_user_agent.get_value().c_str());\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"unknown\"));\n\t}\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_reinvite_success(int line, const t_response *r) {\n\t// Do not bother GUI user.\n\treturn;\n}\n\nvoid t_gui::cb_reinvite_failed(int line, const t_response *r) {\n\t// Do not bother GUI user.\n\treturn;\n}\n\nvoid t_gui::cb_retrieve_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call retrieve failed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = QString().setNum(r->code);\n\ts.append(' ').append(r->reason.c_str());\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\n\nvoid  t_gui::cb_invalid_reg_resp(t_user *user_config, const t_response *r, const string &reason) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"%1, registration failed: %2 %3\")\n\t\t\t.arg(user_config->get_profile_name().c_str())\n\t\t\t.arg(r->code)\n\t\t\t.arg(r->reason.c_str());\n\temit mw_display(s);\n\temit mw_display(reason.c_str());\n\t\n\temit update_reg_status();\n\tunlock();\n}\n\nvoid t_gui::cb_register_success(t_user *user_config, const t_response *r, unsigned long expires,\n\t\t\t\tbool first_success) \n{\n\tlock();\n\tQString s;\n\t\n\tif (first_success) {\n\t\temit mw_display_header();\n\t\ts = qApp->translate(\"GUI\", \"%1, registration succeeded (expires = %2 seconds)\")\n\t\t    .arg(user_config->get_profile_name().c_str())\n\t\t    .arg(expires);\n\t\temit mw_display(s);\n\t}\n\t\n\temit update_reg_status();\n\tunlock();\n}\n\nvoid t_gui::cb_register_failed(t_user *user_config, const t_response *r, bool first_failure) {\n\tlock();\n\tQString s;\n\t\n\tif (first_failure) {\n\t\temit mw_display_header();\n\t\ts = qApp->translate(\"GUI\", \"%1, registration failed: %2 %3\")\n\t\t    .arg(user_config->get_profile_name().c_str())\n\t\t    .arg(r->code)\n\t\t    .arg(r->reason.c_str());\n\t\temit mw_display(s);\n\t}\n\t\n\temit update_reg_status();\n\tunlock();\n}\n\nvoid t_gui::cb_register_stun_failed(t_user *user_config, bool first_failure) {\n\tlock();\n\tQString s;\n\t\n\tif (first_failure) {\n\t\temit mw_display_header();\n\t\ts = qApp->translate(\"GUI\", \"%1, registration failed: STUN failure\")\n\t\t    .arg(user_config->get_profile_name().c_str());\n\t\temit mw_display(s);\n\t}\n\t\n\temit update_reg_status();\n\tunlock();\n}\n\nvoid t_gui::cb_deregister_success(t_user *user_config, const t_response *r) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"%1, de-registration succeeded: %2 %3\")\n\t    .arg(user_config->get_profile_name().c_str())\n\t    .arg(r->code)\n\t    .arg(r->reason.c_str());\n\temit mw_display(s);\n\t\n\temit update_reg_status();\n\tunlock();\n}\n\nvoid t_gui::cb_deregister_failed(t_user *user_config, const t_response *r) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"%1, de-registration failed: %2 %3\")\n\t    .arg(user_config->get_profile_name().c_str())\n\t    .arg(r->code)\n\t    .arg(r->reason.c_str());\n\temit mw_display(s);\n\t\n\temit update_reg_status();\n\tunlock();\n}\n\nvoid t_gui::cb_fetch_reg_failed(t_user *user_config, const t_response *r) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"%1, fetching registrations failed: %2 %3\")\n\t    .arg(user_config->get_profile_name().c_str())\n\t    .arg(r->code)\n\t    .arg(r->reason.c_str());\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_fetch_reg_result(t_user *user_config, const t_response *r) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\t\n\ts = user_config->get_profile_name().c_str();\n\tconst list<t_contact_param> &l = r->hdr_contact.contact_list;\n\tif (l.size() == 0) {\n\t\ts += qApp->translate(\"GUI\", \": you are not registered\");\n\t\temit mw_display(s);\n\t} else {\n\t\ts += qApp->translate(\"GUI\", \": you have the following registrations\");\n\t\temit mw_display(s);\n\t\tfor (list<t_contact_param>::const_iterator i = l.begin();\n\t\ti != l.end(); i++)\n\t\t{\n\t\t\temit mw_display(i->encode().c_str());\n\t\t}\n\t}\n\t\n\tunlock();\n}\n\nvoid t_gui::do_cb_register_inprog(t_user *user_config, t_register_type register_type)\n{\n    QString s;\n\n    switch(register_type) {\n    case REG_REGISTER:\n        // Do not report registration refreshments\n        if (phone->get_is_registered(user_config)) break;\n        mainWindow->statRegLabel->setPixmap(\n                QPixmap(\":/icons/images/gear.png\"));\n        break;\n    case REG_DEREGISTER:\n    case REG_DEREGISTER_ALL:\n        mainWindow->statRegLabel->setPixmap(\n                QPixmap(\":/icons/images/gear.png\"));\n        break;\n    case REG_QUERY:\n        emit mw_display_header();\n        s = user_config->get_profile_name().c_str();\n        s += qApp->translate(\"GUI\", \": fetching registrations...\");\n        emit mw_display(s);\n        break;\n    }\n}\n\nvoid t_gui::cb_register_inprog(t_user *user_config, t_register_type register_type) {\n    QMetaObject::invokeMethod(this, \"do_cb_register_inprog\",\n\t\t\t      Qt::QueuedConnection,\n\t\t\t      Q_ARG(t_user*, user_config),\n\t\t\t      Q_ARG(t_register_type, register_type));\n\n}\n\nvoid t_gui::cb_redirecting_request(t_user *user_config, int line, const t_contact_param &contact) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: redirecting request to\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = format_sip_address(user_config, contact.display, contact.uri).c_str();\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_redirecting_request(t_user *user_config, const t_contact_param &contact) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Redirecting request to: %1\").arg(\n\t\t\tformat_sip_address(user_config, contact.display, contact.uri).c_str());\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_notify_call(int line, const QString &from_party, const QString &organization,\n\t\t\t   const QImage &photo, const QString &subject, QString &referred_by_party)\n{\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\t\n\t// Play ringtone if the call is received on the active line.\n\tif (line == phone->get_active_line() &&\n\t    !phone->is_line_auto_answered(line))\n\t{\n\t\tcb_play_ringtone(line);\n\t}\n\t\n\t// Pop up sys tray balloon\n#ifdef HAVE_KDE\n\tt_twinkle_sys_tray *tray = mainWindow->getSysTray();\n\tif (tray && !sys_tray_popup &&  !phone->is_line_auto_answered(line)) {\n\t\tQString presFromParty(\"\");\n\t\tif (!from_party.isEmpty()) {\n            presFromParty = dotted_truncate(from_party.toStdString(), 50).c_str();\n\t\t}\n\t\tQString presOrganization(\"\");\n\t\tif (!organization.isEmpty()) {\n            presOrganization = dotted_truncate(organization.toStdString(), 50).c_str();\n\t\t}\n\t\tQString presSubject(\"\");\n\t\tif (!subject.isEmpty()) {\n            presSubject = dotted_truncate(subject.toStdString(), 50).c_str();\n\t\t}\n\t\tQString presReferredByParty(\"\");\n\t\tif (!referred_by_party.isEmpty()) {\n\t\t\tpresReferredByParty = qApp->translate(\"GUI\", \"Transferred by: %1\").arg(\n                    dotted_truncate(referred_by_party.toStdString(), 40).c_str());\n\t\t}\n\t\t\n\t\t// Create photo pixmap. If no photo is available, then use\n\t\t// the Twinkle icon.\n\t\tQPixmap pm;\n\t\tQ3Frame::Shape photoFrameShape = Q3Frame::NoFrame;\n\t\tif (photo.isNull()) {\n\t\t\tpm = QPixmap(\":/icons/images/twinkle32.png\");\n\t\t} else {\n\t\t\tpm.convertFromImage(photo);\n\t\t\tphotoFrameShape = QFrame::Box;\n\t\t}\n\t\t\n\t\t// Create the popup view.\n\t\tsys_tray_popup = new KPassivePopup(tray);\n\t\tMEMMAN_NEW(sys_tray_popup);\n\t\tsys_tray_popup->setAutoDelete(false);\n\t\tsys_tray_popup->setTimeout(0);\n\t\tQ3VBox *popup_view = new Q3VBox(sys_tray_popup);\n\t\tQ3HBox *hb = new Q3HBox(popup_view);\n\t\thb->setSpacing(5);\n\t\tQLabel *lblPhoto = new QLabel(hb);\n\t\tlblPhoto->setPixmap(pm);\n\t\tlblPhoto->setFrameShape(photoFrameShape);\n\t\tQ3VBox *vb = new Q3VBox(hb);\n\t\tQString captionText(\"<H2>\");\n\t\tcaptionText += qApp->translate(\"SysTrayPopup\", \"Incoming Call\");\n\t\tcaptionText += \"</H2>\";\n\t\tQLabel *lblCaption = new QLabel(captionText, vb);\n\t\tlblCaption->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\t\tlblCaption->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);\n\t\tQLabel *lblFrom = new QLabel(presFromParty, vb);\n\t\tlblFrom->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\t\tlblFrom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);\n\t\tQLabel *lastLabel = lblFrom;\n\t\tif (!presOrganization.isEmpty()) {\n\t\t\tQLabel *lblOrganization = new QLabel(presOrganization, vb);\n\t\t\tlblOrganization->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\t\t\tlblOrganization->setSizePolicy(QSizePolicy::Expanding, \n\t\t\t\t\t\t       QSizePolicy::Minimum);\n\t\t\tlastLabel = lblOrganization;\n\t\t}\n\t\tif (!presReferredByParty.isEmpty()) {\n\t\t\tQLabel *lblReferredBy = new QLabel(presReferredByParty, vb);\n\t\t\tlblReferredBy->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\t\t\tlblReferredBy->setSizePolicy(QSizePolicy::Expanding, \n\t\t\t\t\t\t       QSizePolicy::Minimum);\n\t\t\tlastLabel = lblReferredBy;\n\t\t}\n\t\tif (!presSubject.isEmpty()) {\n\t\t\tQLabel *lblSubject = new QLabel(presSubject, vb);\n\t\t\tlblSubject->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\t\t\tlblSubject->setSizePolicy(QSizePolicy::Expanding, \n\t\t\t\t\t\t       QSizePolicy::Minimum);\n\t\t\tlastLabel = lblSubject;\n\t\t}\n\t\tlastLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\t\t\n\t\t// Answer and reject buttons\n\t\t\n\t\tQ3HBox *buttonBox = new Q3HBox(vb);\n\t\tQIcon iconAnswer(QPixmap(\":/icons/images/answer.png\"));\n\t\tQPushButton *pbAnswer = new QPushButton(iconAnswer, \n\t\t\tqApp->translate(\"SysTrayPopup\", \"Answer\"), buttonBox);\n\t\tQObject::connect(pbAnswer, SIGNAL(clicked()), \n\t\t\t\t mainWindow, SLOT(phoneAnswerFromSystrayPopup()));\n\t\tQIcon iconReject(QPixmap(\":/icons/images/reject.png\"));\n\t\tQPushButton *pbReject = new QPushButton(iconReject, \n\t\t\tqApp->translate(\"SysTrayPopup\", \"Reject\"), buttonBox);\n\t\tQObject::connect(pbReject, SIGNAL(clicked()), \n\t\t\t\t mainWindow, SLOT(phoneRejectFromSystrayPopup()));\n\t\t\n\t\tsys_tray_popup->setView(popup_view);\n\t\t\n\t\t// Show the popup\n\t\tline_sys_tray_popup = line;\n\t\tsys_tray_popup->show();\n\t\tQObject::connect(sys_tray_popup, SIGNAL(clicked()),\n\t\t\tsys_tray_popup, SLOT(hide()));\n\t}\n#endif\n\t\n\t// Show main window after a few seconds\n\tif (sys_config->get_gui_auto_show_incoming()) {\n\n        autoShowTimer[line].setSingleShot(true);\n        autoShowTimer[line].start(\n            sys_config->get_gui_auto_show_timeout() * 1000);\n\t}\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_stop_call_notification(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tcb_stop_tone(line);\n\tautoShowTimer[line].stop();\n\t\n#ifdef HAVE_KDE\n\tif (sys_tray_popup && line_sys_tray_popup == line) {\n\t\tsys_tray_popup->hide();\n\t\tMEMMAN_DELETE(sys_tray_popup);\n\t\tdelete sys_tray_popup;\n\t\tsys_tray_popup = NULL;\n\t}\n#endif\n\tunlock();\n}\n\nvoid t_gui::cb_dtmf_detected(int line, t_dtmf_ev dtmf_event) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: DTMF detected:\").arg(line + 1).append(\" \");\n\t\n\tif (is_valid_dtmf_ev(dtmf_event)) {\n\t\ts.append(dtmf_ev2char(dtmf_event));\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"invalid DTMF telephone event (%1)\").arg(\n\t\t\t\t(int)dtmf_event));\n\t}\n\t\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_send_dtmf(int line, t_dtmf_ev dtmf_event) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\tif (!is_valid_dtmf_ev(dtmf_event)) return;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: send DTMF %2\").arg(line + 1).arg(\n\t\t\tdtmf_ev2char(dtmf_event));\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_dtmf_not_supported(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tQString s;\n\t\n\tif (throttle_dtmf_not_supported) return;\n\t\n\tlock();\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: far end does not support DTMF telephone events.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\t// Throttle subsequent call backs\n\tthrottle_dtmf_not_supported = true;\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_dtmf_supported(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\temit update_state();\n\tunlock();\n}\n\nvoid t_gui::cb_line_state_changed(void) {\n\tlock();\n\temit update_state();\n\tunlock();\n}\n\nvoid t_gui::cb_send_codec_changed(int line, t_audio_codec codec) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tdisplayCodecInfo(line);\n\tunlock();\n}\n\nvoid t_gui::cb_recv_codec_changed(int line, t_audio_codec codec) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tdisplayCodecInfo(line);\n\tunlock();\n}\n\nvoid t_gui::cb_notify_recvd(int line, const t_request *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: received notification.\").arg(line+1);\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"Event: %1\").arg(r->hdr_event.event_type.c_str());\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"State: %1\").arg(r->hdr_subscription_state.substate.c_str());\n\temit mw_display(s);\n\t\n\tif (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {\n\t\ts = qApp->translate(\"GUI\", \"Reason: %1\").arg(r->hdr_subscription_state.reason.c_str());\n\t\temit mw_display(s);\n\t}\n\t\n\tt_response *sipfrag = (t_response *)((t_sip_body_sipfrag *)r->body)->sipfrag;\n\ts = qApp->translate(\"GUI\", \"Progress: %1 %2\").arg(sipfrag->code).arg(sipfrag->reason.c_str());\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_refer_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call transfer failed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = QString().setNum(r->code);\n\ts.append(' ').append(r->reason.c_str());\n\temit mw_display(s);\n\t\n\t// The refer state has changed, so update the main window.\n\temit update_state();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_refer_result_success(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call successfully transferred.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\t// The refer state has changed, so update the main window.\n\temit update_state();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_refer_result_failed(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call transfer failed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\t// The refer state has changed, so update the main window.\n\temit update_state();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_refer_result_inprog(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: call transfer still in progress.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\ts = qApp->translate(\"GUI\", \"No further notifications will be received.\");\n\temit mw_display(s);\n\t\n\t// The refer state has changed, so update the main window.\n\temit update_state();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_call_referred(t_user *user_config, int line, t_request *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tQString s;\n\t\n\tlock();\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: transferring call to %2\").arg(line +1).arg(\n\t\t\tformat_sip_address(user_config,\n\t\t\tr->hdr_refer_to.display, r->hdr_refer_to.uri).c_str());\n\temit mw_display(s);\n\t\n\tif (r->hdr_referred_by.is_populated()) {\n\t\ts = qApp->translate(\"GUI\", \"Transfer requested by %1\").arg(\n\t\t\t\tformat_sip_address(user_config,\n\t\t\t\t\t    r->hdr_referred_by.display, \n\t\t\t\t\t    r->hdr_referred_by.uri).c_str());\n\t\temit mw_display(s);\n\t}\n\t\n\tsetLineFields(line);\n\ts = format_sip_address(user_config, \n\t\t\t       user_config->get_display(false),  user_config->create_user_uri(false)).c_str();\n\tdisplayFrom(s);\n\tphotoLabel->hide();\n\t\n\ts = format_sip_address(user_config,\n\t\t\t       r->hdr_refer_to.display, r->hdr_refer_to.uri).c_str();\n\tdisplayTo(s);\n\t\n\tsubjectLabel->clear();\n\tcodecLabel->clear();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_retrieve_referrer(t_user *user_config, int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tQString s;\n\t\n\tlock();\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: Call transfer failed. Retrieving original call.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\tsetLineFields(line);\n\tconst t_call_info call_info = phone->get_call_info(line);\n\t\n\ts = format_sip_address(user_config, call_info.get_from_display_presentation(), \n\t\t\t       call_info.from_uri).c_str();\n\tif (!call_info.from_organization.empty()) {\n\t\ts += \", \";\n\t\ts += call_info.from_organization.c_str();\n\t}\n\tdisplayFrom(s);\n\t\n\t// Display photo\n\tQImage fromPhoto;\n\t\n\tif (sys_config->get_ab_lookup_photo()) {\n\t\tt_address_finder *af = t_address_finder::get_instance();\n\t\tfromPhoto = af->find_photo(user_config, call_info.from_uri);\n\t}\n\t\n\tdisplayPhoto(fromPhoto);\t\n\t\n\ts = format_sip_address(user_config, call_info.to_display, call_info.to_uri).c_str();\n\tif (!call_info.to_organization.empty()) {\n\t\ts += \", \";\n\t\ts += call_info.to_organization.c_str();\n\t}\n\tdisplayTo(s);\n\t\n\tdisplaySubject(call_info.subject.c_str());\n\tcodecLabel->clear();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_consultation_call_setup(t_user *user_config, int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tQString s;\n\t\n\tlock();\n\t\n\tsetLineFields(line);\n\tconst t_call_info call_info = phone->get_call_info(line);\n\t\n\ts = format_sip_address(user_config, call_info.get_from_display_presentation(), \n\t\t\t       call_info.from_uri).c_str();\n\tif (!call_info.from_organization.empty()) {\n\t\ts += \", \";\n\t\ts += call_info.from_organization.c_str();\n\t}\n\tdisplayFrom(s);\n\t\n\tphotoLabel->hide();\n\t\n\ts = format_sip_address(user_config, call_info.to_display, call_info.to_uri).c_str();\n\tif (!call_info.to_organization.empty()) {\n\t\ts += \", \";\n\t\ts += call_info.to_organization.c_str();\n\t}\n\tdisplayTo(s);\n\t\n\tdisplaySubject(call_info.subject.c_str());\n\tcodecLabel->clear();\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_stun_failed(t_user *user_config, int err_code, const string &err_reason) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"%1, STUN request failed: %2 %3\")\n\t    .arg(user_config->get_profile_name().c_str())\n\t    .arg(err_code).arg(err_reason.c_str());\n\temit mw_display(s);\n\n\tunlock();\n}\n\nvoid t_gui::cb_stun_failed(t_user *user_config) {\n\tlock();\n\tQString s;\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"%1, STUN request failed.\")\n\t    .arg(user_config->get_profile_name().c_str());\n\temit mw_display(s);\n\n\tunlock();\n}\n\nbool t_gui::cb_ask_user_to_redirect_invite(t_user *user_config, const t_url &destination,\n\t\t\t\t\t   const string &display)\n{\n\tbool retval;\n\tQMetaObject::invokeMethod(this, \"do_cb_ask_user_to_redirect_invite\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_RETURN_ARG(bool, retval),\n\t\t\t\t  Q_ARG(t_user*, user_config),\n\t\t\t\t  Q_ARG(const t_url&, destination),\n\t\t\t\t  Q_ARG(const string&, display));\n\n\treturn retval;\n}\n\nbool t_gui::cb_ask_user_to_redirect_request(t_user *user_config,\n\t\t\t\t\t    const t_url &destination,\n\t\t\t\t\t    const string &display, t_method method)\n{\n\tbool retval;\n\tQMetaObject::invokeMethod(this, \"do_cb_ask_user_to_redirect_request\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_RETURN_ARG(bool, retval),\n\t\t\t\t  Q_ARG(t_user*, user_config),\n\t\t\t\t  Q_ARG(const t_url&, destination),\n\t\t\t\t  Q_ARG(const string&, display),\n\t\t\t\t  Q_ARG(t_method, method));\n\n\treturn retval;\n}\n\nbool t_gui::do_cb_ask_credentials(t_user *user_config, const string &realm, string &username,\n\t\t\t\t   string &password)\n{\n\tQString user(username.c_str());\n\tQString passwd(password.c_str());\n\n    AuthenticationForm *af = new AuthenticationForm(mainWindow);\n    af->setModal(true);\n\tMEMMAN_NEW(af);\n\tif (!af->exec(user_config, QString(realm.c_str()), user, passwd)) {\n\t\tMEMMAN_DELETE(af);\n\t\tdelete af;\n\t\treturn false;\n\t}\n\n    username = user.toStdString();\n    password = passwd.toStdString();\n\tMEMMAN_DELETE(af);\n\tdelete af;\n\n\treturn true;\n}\n\nbool t_gui::cb_ask_credentials(t_user *user_config, const string &realm, string &username,\n\t\t\t       string &password)\n{\n\tbool retval;\n\tQMetaObject::invokeMethod(this, \"do_cb_ask_credentials\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_RETURN_ARG(bool, retval),\n\t\t\t\t  Q_ARG(t_user*, user_config),\n\t\t\t\t  Q_ARG(const string&, realm),\n\t\t\t\t  Q_ARG(string&, username),\n\t\t\t\t  Q_ARG(string&, password));\n\n\treturn retval;\n}\n\nbool t_gui::do_cb_ask_user_to_redirect_invite(t_user *user_config, const t_url &destination,\n\t\tconst string &display)\n{\n\tQString s;\n\tQString title;\n\n\ttitle = PRODUCT_NAME;\n\ttitle.append(\" - \").append(qApp->translate(\"GUI\", \"Redirecting call\"));\n\n\ts = qApp->translate(\"GUI\", \"User profile:\").append(\" <b>\");\n\ts.append(user_config->get_profile_name().c_str());\n\ts.append(\"</b><br>\").append(qApp->translate(\"GUI\", \"User:\")).append(\" <b>\");\n\ts.append(str2html(user_config->get_display_uri().c_str()));\n\ts.append(\"</b><br><br>\");\n\n\ts.append(qApp->translate(\"GUI\", \"Do you allow the call to be redirected to the following destination?\"));\n\ts.append(\"<br><br>\");\n\ts.append(str2html(ui->format_sip_address(user_config, display, destination).c_str()));\n\ts.append(\"<br><br>\");\n\ts.append(qApp->translate(\"GUI\",\n\t\t\"If you don't want to be asked this anymore, then you must change \"\n\t\t\"the settings in the SIP protocol section of the user profile.\"));\n\tint button = QMessageBox::warning(mainWindow, title, s,\n\t\t\tQMessageBox::Yes | QMessageBox::No);\n\tbool permission = (button == QMessageBox::Yes);\n\n\treturn permission;\n}\n\nbool t_gui::do_cb_ask_user_to_redirect_request(t_user *user_config, const t_url &destination,\n\t\tconst string &display, t_method method)\n{\n\tQString s;\n\tQString title;\n\n\ttitle = PRODUCT_NAME;\n\ttitle.append(\" - \").append(qApp->translate(\"GUI\", \"Redirecting request\"));\n\n\ts = qApp->translate(\"GUI\", \"User profile:\").append(\" <b>\");\n\ts.append(user_config->get_profile_name().c_str());\n\ts.append(\"</b><br>\").append(qApp->translate(\"GUI\", \"User:\")).append(\" <b>\");\n\ts.append(str2html(user_config->get_display_uri().c_str()));\n\ts.append(\"</b><br><br>\");\n\n\ts.append(qApp->translate(\"GUI\",\n\t\t\"Do you allow the %1 request to be redirected to the following destination?\").arg(\n\t\tmethod2str(method).c_str()));\n\ts.append(\"<br><br>\");\n\ts.append(str2html(ui->format_sip_address(user_config, display, destination).c_str()));\n\ts.append(\"<br><br>\");\n\ts.append(qApp->translate(\"GUI\",\n\t\t\"If you don't want to be asked this anymore, then you must change \"\n\t\t\"the settings in the SIP protocol section of the user profile.\"));\n\tint button = QMessageBox::warning(mainWindow, title, s,\n\t\t\tQMessageBox::Yes | QMessageBox::No);\n\tbool permission = (button == QMessageBox::Yes);\n\n\treturn permission;\n}\n\nvoid t_gui::do_cb_ask_user_to_refer(t_user *user_config, const string &refer_to_uri_str,\n\t\t\t\t const string &refer_to_display,\n\t\t\t\t const string &referred_by_uri_str,\n\t\t\t\t const string &referred_by_display)\n{\n\tt_url refer_to_uri(refer_to_uri_str);\n\tt_url referred_by_uri(referred_by_uri_str);\n\n\tQString s;\n\tQString title;\n\t\n\ttitle = PRODUCT_NAME;\n\ttitle.append(\" - \").append(qApp->translate(\"GUI\", \"Transferring call\"));\n\t\n\ts = qApp->translate(\"GUI\", \"User profile:\").append(\" <b>\");\n\ts.append(user_config->get_profile_name().c_str());\n\ts.append(\"</b><br>\").append(qApp->translate(\"GUI\", \"User:\")).append(\" <b>\");\n\ts.append(str2html(user_config->get_display_uri().c_str()));\n\ts.append(\"</b><br><br>\");\n\t\n\tif (referred_by_uri.is_valid()) {\n\t\ts.append(qApp->translate(\"GUI\",\"Request to transfer call received from:\"));\n\t\ts.append(\"<br>\");\n\t\ts.append(str2html(format_sip_address(user_config, referred_by_display,\n\t\t\t\t\t    referred_by_uri).c_str()));\n\t\ts.append(\"<br>\");\n\t} else {\n\t\ts.append(qApp->translate(\"GUI\", \"Request to transfer call received.\"));\n\t\ts.append(\"<br>\");\n\t}\n\ts.append(\"<br>\");\n\t\n\ts.append(qApp->translate(\"GUI\",\n\t\t\"Do you allow the call to be transferred to the following destination?\"));\n\ts.append(\"<br><br>\");\n\ts.append(str2html(ui->format_sip_address(user_config, refer_to_display, \n\t\t\t\t\trefer_to_uri).c_str()));\n\ts.append(\"<br><br>\");\n\ts.append(qApp->translate(\"GUI\",\n\t\t\"If you don't want to be asked this anymore, then you must change \"\n\t\t\"the settings in the SIP protocol section of the user profile.\"));\n\t\n\tReferPermissionDialog *dialog = new ReferPermissionDialog(mainWindow, title, s);\n\t// Do not report to MEMMAN as Qt will auto destruct this dialog on close.\n\tdialog->show();\n}\n\nvoid t_gui::cb_ask_user_to_refer(t_user *user_config, const t_url &refer_to_uri,\n\t\t\t\t const string &refer_to_display,\n\t\t\t\t const t_url &referred_by_uri,\n\t\t\t\t const string &referred_by_display)\n{\n\tQMetaObject::invokeMethod(this, \"do_cb_ask_user_to_refer\",\n\t\t\t\t  Q_ARG(t_user*, user_config),\n\t\t\t\t  Q_ARG(const string&, refer_to_uri.encode()),\n\t\t\t\t  Q_ARG(const string&, refer_to_display),\n\t\t\t\t  Q_ARG(const string&, referred_by_uri.encode()),\n\t\t\t\t  Q_ARG(const string&, referred_by_display));\n}\n\nvoid t_gui::cb_show_msg(const string &msg, t_msg_priority prio) {\n\tcb_show_msg(NULL, msg, prio);\n}\n\nvoid t_gui::cb_show_msg(QWidget *parent, const string &msg, t_msg_priority prio) {\n\tlock();\n\t\n\tswitch (prio) {\n\tcase MSG_INFO:\n\t\tQMessageBox::information(parent, PRODUCT_NAME, msg.c_str());\n\t\tbreak;\n\tcase MSG_WARNING:\n\t\tQMessageBox::warning(parent, PRODUCT_NAME, msg.c_str());\n\t\tbreak;\n\tcase MSG_CRITICAL:\n\tdefault:\n\t\tQMessageBox::critical(parent, PRODUCT_NAME, msg.c_str());\n\t\tbreak;\n\t}\n\t\n\tunlock();\n}\n\nbool t_gui::cb_ask_msg(const string &msg, t_msg_priority prio) {\n\treturn cb_ask_msg(NULL, msg, prio);\n}\n\nbool t_gui::cb_ask_msg(QWidget *parent, const string &msg, t_msg_priority prio) {\n\tlock();\n\t\n\tint button = QMessageBox::No;\n\tswitch (prio) {\n\tcase MSG_INFO:\n\t\tbutton = QMessageBox::information(parent, PRODUCT_NAME, msg.c_str(),\n\t\t\tQMessageBox::Yes | QMessageBox::No,\n\t\t\tQMessageBox::No /* default */);\n\t\tbreak;\n\tcase MSG_WARNING:\n\t\tbutton = QMessageBox::warning(parent, PRODUCT_NAME, msg.c_str(),\n\t\t\tQMessageBox::Yes | QMessageBox::No,\n\t\t\tQMessageBox::No /* default */);\n\t\tbreak;\n\tcase MSG_CRITICAL:\n\tdefault:\n\t\tbutton = QMessageBox::critical(parent, PRODUCT_NAME, msg.c_str(),\n\t\t\tQMessageBox::Yes | QMessageBox::No,\n\t\t\tQMessageBox::No /* default */);\n\t\tbreak;\n\t}\n\t\n\tunlock();\n\t\n\treturn (button == QMessageBox::Yes);\n}\n\nvoid t_gui::cb_display_msg(const string &msg, t_msg_priority prio) {\n\t// If this thread may not lock the UI, then push the display message on\n\t// the UI event queue. The message will be display asynchronously.\n\tif (is_prohibited_thread()) {\n\t\tcb_async_display_msg(msg, prio);\n\t\treturn;\n\t}\n\t\n\tQString s;\n\t\n\tlock();\n\t\n\tswitch (prio) {\n\tcase MSG_NO_PRIO:\n\t\tbreak;\n\tcase MSG_INFO:\n\t\ts = qApp->translate(\"GUI\", \"Info:\");\n\t\tbreak;\n\tcase MSG_WARNING:\n\t\ts = qApp->translate(\"GUI\", \"Warning:\");\n\t\tbreak;\n\tcase MSG_CRITICAL:\n\tdefault:\n\t\ts = qApp->translate(\"GUI\", \"Critical:\");\n\t\tbreak;\n\t}\t\n\t\n\tif (prio == MSG_NO_PRIO) {\n\t\ts = msg.c_str();\n\t} else {\n\t\ts.append(\" \").append(msg.c_str());\n\t}\n\temit mw_display_header();\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_log_updated(bool log_zapped) {\n\temit mw_update_log(log_zapped);\n}\n\nvoid t_gui::cb_call_history_updated(void) {\n\temit mw_update_call_history();\n}\n\nvoid  t_gui::cb_missed_call(int num_missed_calls) {\n\temit mw_update_missed_call_status(num_missed_calls);\n}\n\nvoid t_gui::cb_nat_discovery_progress_start(int num_steps) {\n\tnatDiscoveryProgressDialog = new QProgressDialog(\n\t\t\tqApp->translate(\"GUI\", \"Firewall / NAT discovery...\"), \n\t\t\tqApp->translate(\"GUI\", \"Abort\"), \n\t\t\t0, num_steps, mainWindow);\n\tMEMMAN_NEW(natDiscoveryProgressDialog);\n    natDiscoveryProgressDialog->setWindowTitle(PRODUCT_NAME);\n\tnatDiscoveryProgressDialog->setMinimumDuration(200);\n}\n\nvoid t_gui::cb_nat_discovery_progress_step(int step) {\n\tnatDiscoveryProgressDialog->setValue(step);\n\tqApp->processEvents();\n}\n\nvoid t_gui::cb_nat_discovery_finished(void) {\n\tMEMMAN_DELETE(natDiscoveryProgressDialog);\n\tdelete natDiscoveryProgressDialog;\n}\n\nbool t_gui::cb_nat_discovery_cancelled(void) {\n\treturn natDiscoveryProgressDialog->wasCanceled();\n}\n\nvoid t_gui::cb_line_encrypted(int line, bool encrypted, const string &cipher_mode) {\n\t// Nothing todo in GUI\n\t// Encryption state is shown by the line state updata methods on\n\t// MphoneForm\n}\n\nvoid t_gui::cb_show_zrtp_sas(int line, const string &sas) {\n\t\tif (line >= NUM_USER_LINES) return;\n\t\n\tlock();\n\tQString s;\n\t\n\tsetLineFields(line);\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1\").arg(line + 1);\n\ts.append(\": SAS = \").append(sas.c_str());\n\temit mw_display(s);\n\ts = qApp->translate(\"GUI\", \"Click the padlock to confirm a correct SAS.\");\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_zrtp_confirm_go_clear(int line) {\n\tt_user *user_config = phone->get_line_user(line);\n\tif (!user_config) return;\n\t\n\tQString msg(qApp->translate(\"GUI\", \"The remote user on line %1 disabled the encryption.\")\n\t\t\t.arg(line + 1));\n\tif (user_config->get_zrtp_goclear_warning()) {\n        cb_show_msg(msg.toStdString(), MSG_WARNING);\n\t} else {\n        cb_display_msg(msg.toStdString(), MSG_WARNING);\n\t}\n\t\n\taction_zrtp_go_clear_ok(line);\n}\n\nvoid t_gui::cb_zrtp_sas_confirmed(int line) {\n\tlock();\n\tQString s;\n\t\n\tsetLineFields(line);\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: SAS confirmed.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_zrtp_sas_confirmation_reset(int line) {\n\tlock();\n\tQString s;\n\t\n\tsetLineFields(line);\n\t\n\temit mw_display_header();\n\ts = qApp->translate(\"GUI\", \"Line %1: SAS confirmation reset.\").arg(line + 1);\n\temit mw_display(s);\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_update_mwi(void) {\n\tlock();\n\temit update_mwi();\n\tunlock();\n}\n\nvoid t_gui::cb_mwi_subscribe_failed(t_user *user_config, t_response *r, bool first_failure) {\n\tlock();\n\tQString s;\n\t\n\tif (first_failure) {\n\t\temit mw_display_header();\n\t\ts = qApp->translate(\"GUI\", \"%1, voice mail status failure.\")\n\t\t    .arg(user_config->get_profile_name().c_str());\n\t\temit mw_display(s);\n\t}\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_mwi_terminated(t_user *user_config, const string &reason) {\n\tlock();\n\tQString s;\n\t\n\tif (reason == \"EV_REASON_REJECTED\") {\n\t\ts = qApp->translate(\"GUI\", \"%1, voice mail status rejected.\");\n\t} else if (reason == \"EV_REASON_NORESOURCE\") {\n\t\ts = qApp->translate(\"GUI\", \"%1, voice mailbox does not exist.\");\n\t} else {\n\t\ts = qApp->translate(\"GUI\", \"%1, voice mail status terminated.\");\n\t}\n\n\temit mw_display_header();\n\temit mw_display(s.arg(user_config->get_profile_name().c_str()));\n\n\tunlock();\n}\n\nbool t_gui::cb_message_request(t_user *user_config, t_request *r) {\t\n\tbool retval;\n\tQMetaObject::invokeMethod(this, \"do_cb_message_request\",\n\t\t\t\t  Qt::BlockingQueuedConnection,\n\t\t\t\t  Q_RETURN_ARG(bool, retval),\n\t\t\t\t  Q_ARG(t_user*, user_config),\n\t\t\t\t  Q_ARG(t_request*, r));\n\n\treturn retval;\n}\n\nbool t_gui::do_cb_message_request(t_user *user_config, t_request *r) {\n\tstring text;\n\tim::t_text_format text_format = im::TXT_PLAIN;\n\tbool attachment = false;\n\tbool failed_to_save_attachment = false;\n\tstring attachment_error_msg;\n\tstring attachment_saved_name;\n\tstring attachment_save_as_name;\n\t\n\tif (!r->body) {\n\t\tlog_file->write_report(\"Missing body.\", \"t_gui::cb_message_request\",\n\t\t\t\t       LOG_NORMAL, LOG_DEBUG);\n\t\treturn true;\n\t}\n\t\n\t// Determine if message should be shown inline or as an attachment\n\tif ((r->hdr_content_disp.is_populated() && r->hdr_content_disp.type == \"attachment\") ||\n\t    (r->body->get_type() == BODY_OPAQUE) ||\n\t    (r->hdr_content_length.is_populated() && r->hdr_content_length.length > im::MAX_INLINE_TEXT_LEN) ||\n\t    (r->body->encode().size() > im::MAX_INLINE_TEXT_LEN))\n\t{\n\t\tattachment = true;\n\t\t\n\t\tstring suggested_file_extension = mime2file_extension(r->hdr_content_type.media);\n\t\t\n\t\tif (!sys_config->save_sip_body(*r, suggested_file_extension,\n\t\t\t\t\t       attachment_saved_name, \n\t\t\t\t\t       attachment_save_as_name, \n\t\t\t\t\t       attachment_error_msg)) \n\t\t{\n\t\t\tfailed_to_save_attachment = true;\n\t\t}\n\t} else if (r->body->get_type() == BODY_PLAIN_TEXT) {\n\t\tt_sip_body_plain_text *sb = dynamic_cast<t_sip_body_plain_text *>(r->body);\n\t\ttext = sb->text;\n\t\ttext_format = im::TXT_PLAIN;\n\t} else if (r->body->get_type() == BODY_HTML_TEXT) {\n\t\tt_sip_body_html_text *sb = dynamic_cast<t_sip_body_html_text *>(r->body);\n\t\ttext = sb->text;\n\t\ttext_format = im::TXT_HTML;\n\t} else {\n\t\tlog_file->write_header(\"t_gui::cb_message_request\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tlog_file->write_raw(\"Unsupported content type: \");\n\t\tlog_file->write_raw(r->body->get_type());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn true;\n\t}\n\t\n\tif (!text.empty()) {\n\t\tstring charset = r->hdr_content_type.media.charset;\n\t\tif (!charset.empty() && cmp_nocase(charset, \"utf-8\") != 0) {\n\t\t\t// Try to decode the text\n\t\t\tQTextCodec *c = QTextCodec::codecForName(charset.c_str());\n\t\t\tif (c) {\n                text = c->toUnicode(text.c_str()).toStdString();\n\t\t\t} else {\n\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\t\"t_gui::cb_message_request\",\n\t\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tlog_file->write_raw(\"Cannot decode charset: \");\n\t\t\t\tlog_file->write_raw(charset);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tlock();\n\t\n\t// Find an existing session\n\tim::t_msg_session *session = getMessageSession(user_config, r->hdr_from.uri,\n\t\t\t\tr->hdr_from.get_display_presentation());\n\tif (!session) {\n\t\t// There is no session yet.\n\t\tif (messageSessions.size() >= user_config->get_im_max_sessions()) {\n\t\t\tlog_file->write_report(\n\t\t\t\t\"Maximum number of message sessions reached. Reject message\",\n\t\t\t\t\"t_gui::cb_message_request\");\n\t\t\tunlock();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Create a new session.\n\t\tsession = new im::t_msg_session(user_config, t_display_url(r->hdr_from.uri, \n\t\t\t\tr->hdr_from.get_display_presentation()));\n\t\tMEMMAN_NEW(session);\n\t\taddMessageSession(session);\n\t\tMessageFormView *view = new MessageFormView(NULL, session);\n\t\tMEMMAN_NEW(view);\n\t\tview->show();\n\t\tview->raise();\n\t}\n\t\n\t// Construct message\n\tim::t_msg msg;\n\t\n\tmsg.direction = im::MSG_DIR_IN;\n\t\n\tif (r->hdr_subject.is_populated()) {\n\t\tmsg.subject = r->hdr_subject.subject;\n\t}\n\t\n\tif (!text.empty()) {\n\t\tmsg.message = text;\n\t\tmsg.format = text_format;\n\t}\n\t\n\tif (attachment && !failed_to_save_attachment) {\n\t\tmsg.has_attachment = true;\n\t\tmsg.attachment_filename = attachment_saved_name;\n\t\tmsg.attachment_save_as_name = attachment_save_as_name;\n\t\tmsg.attachment_media = r->hdr_content_type.media;\n\t}\n\t\n\tsession->recv_msg(msg);\n\t\n\t// Set error message if attachment could not be saved\n\tif (failed_to_save_attachment) {\n\t\tQString s = qApp->translate(\"GUI\", \"Failed to save message attachment: %1\").arg(attachment_error_msg.c_str());\n\t\tsession->set_error(s.toStdString());\n\t}\n\t\n\tunlock();\n\treturn true;\n}\n\nvoid t_gui::cb_message_response(t_user *user_config, t_response *r, t_request *req) {\n\tlock();\n\t\n\t// Find session associated with the response\n\tim::t_msg_session *session = getMessageSession(user_config, r->hdr_to.uri,\n\t\t\t\tr->hdr_to.display);\n\t\n\tif (session) {\n\t\tif (!r->is_success()) {\n\t\t\tstring s = int2str(r->code);\n\t\t\ts += ' ';\n\t\t\ts += r->reason;\n\t\t\tsession->set_error(s);\n\t\t} else {\n\t\t\tif (r->code == R_202_ACCEPTED) {\n\t\t\t\tstring s;\n\t\t\t\tif (r->reason == REASON_202) {\n                    s = qApp->translate(\"GUI\", \"Accepted by network\").toStdString();\n\t\t\t\t} else { \n\t\t\t\t\ts = r->reason;\n\t\t\t\t}\n\t\t\t\tsession->set_delivery_notification(s);\n\t\t\t}\n\t\t}\n\t\t\n\t\tsession->set_msg_in_flight(false);\n\t}\n\t// If there is no session anymore, then discard the response\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_im_iscomposing_request(t_user *user_config, t_request *r,\n\t\t\tim::t_composing_state state, time_t refresh)\n{\n\tlock();\n\t\n\t// Find an existing session\n\tim::t_msg_session *session = getMessageSession(user_config, r->hdr_from.uri,\n\t\t\t\tr->hdr_from.get_display_presentation());\n\tif (!session) {\n\t\t// A composing indication does not initiate a new message session.\n\t\tlog_file->write_report(\n\t\t\t\"Received composing indication for unknown session.\",\n\t\t\t\"t_gui::cb_im_iscomposing_request\");\n\t} else {\n\t\tsession->set_remote_composing_state(state, refresh);\n\t}\n\t\n\tunlock();\n}\n\nvoid t_gui::cb_im_iscomposing_not_supported(t_user *user_config, t_response *r) {\n\tlock();\n\t\n\t// Find an existing session\n\tim::t_msg_session *session = getMessageSession(user_config, r->hdr_to.uri,\n\t\t\t\tr->hdr_to.display);\n\t\n\tif (session) session->set_send_composing_state(false);\n\t\n\tunlock();\n}\n\nvoid t_gui::cmd_call(const string &destination, bool immediate) {\n\tQMetaObject::invokeMethod(this, \"gui_cmd_call\",\n\t\t\t\t  Q_ARG(const string&, destination),\n\t\t\t\t  Q_ARG(bool, immediate));\n}\n\nvoid t_gui::cmd_quit(void) {\n\tlock();\n\tmainWindow->fileExit();\n\tunlock();\n}\n\nvoid t_gui::cmd_show(void) {\n\tQMetaObject::invokeMethod(this, \"gui_cmd_show\");\n}\n\nvoid t_gui::cmd_hide(void) {\n\tQMetaObject::invokeMethod(this, \"gui_cmd_hide\");\n}\n\nstring t_gui::get_name_from_abook(t_user *user_config, const t_url &u) {\n\tstring name;\n\t\n\tlock();\n\t// Search local address book first\n\tname = t_userintf::get_name_from_abook(user_config, u);\n\t\n\t// Search Akonadi address book\n\tif (name.empty()) {\n\t\tt_address_finder *af = t_address_finder::get_instance();\n\t\tname = af->find_name(user_config, u);\n\t}\n\tunlock();\n\t\n\treturn name;\n}\n\n// User invoked actions on the phone object\n\nvoid t_gui::action_register(list<t_user *> user_list) {\n\trun_on_event_queue([=]() {\n\t\tfor (list<t_user *>::const_iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\t\tphone->pub_registration(*i, REG_REGISTER,\n\t\t\t\tDUR_REGISTRATION(*i));\n\t\t}\n\t});\n}\n\nvoid t_gui::action_deregister(list<t_user *> user_list, bool dereg_all) {\n\trun_on_event_queue([=]() {\n\t\tfor (list<t_user *>::const_iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\t\tif (dereg_all) {\n\t\t\t\tphone->pub_registration(*i, REG_DEREGISTER_ALL);\n\t\t\t} else {\n\t\t\t\tphone->pub_registration(*i, REG_DEREGISTER);\n\t\t\t}\n\t\t}\n\t});\n}\n\nvoid t_gui::action_show_registrations(list<t_user *> user_list) {\n\trun_on_event_queue([=]() {\n\t\tfor (list<t_user *>::const_iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\t\tphone->pub_registration(*i, REG_QUERY);\n\t\t}\n\t});\n}\n\nvoid t_gui::action_invite(t_user *user_config, const t_url &destination,\n\t\t\t  const string &display, const string &subject, bool anonymous) \n{\n\tQString s;\n\t\n\t// Call can only be made if line is idle\n\tint line = phone->get_active_line();\n\tif (phone->get_line_state(line) == LS_BUSY) return;\n\t\n\tt_url vm_url(expand_destination(user_config, user_config->get_mwi_vm_address()));\n\tif (destination != vm_url) {\n\t\t// Store call info for redial\n\t\tlast_called_url = destination;\n\t\tlast_called_display = display;\n\t\tlast_called_subject = subject;\n\t\tlast_called_profile = user_config->get_profile_name();\n\t\tlast_called_hide_user = anonymous;\n\t}\n\t\n\tsetLineFields(line);\n\t\n\t// Set party and subject line fields\n\ts = \"\";\n\ts.append(format_sip_address(user_config, display, destination).c_str());\n\tdisplayTo(s);\n\t\n\ts = \"\";\n\ts.append(format_sip_address(user_config, user_config->get_display(false), \n\t\t\t\t    user_config->create_user_uri(false)).c_str());\n\tdisplayFrom(s);\n\t\n\tdisplaySubject(subject.c_str());\n\t\n\trun_on_event_queue([=]() {\n\t\tphone->pub_invite(user_config, destination, display, subject.c_str(), anonymous);\n\t});\n}\n\nvoid t_gui::action_answer(void) {\n\tcb_stop_call_notification(phone->get_active_line());\n\tphone->pub_answer();\n}\n\nvoid t_gui::action_bye(void) {\n\tphone->pub_end_call();\n}\n\nvoid t_gui::action_reject(void) {\n\tQString s;\n\t\n\tcb_stop_call_notification(phone->get_active_line());\n\tphone->pub_reject();\n\t\n\tint line = phone->get_active_line();\n\tmainWindow->displayHeader();\n\ts = qApp->translate(\"GUI\", \"Line %1: call rejected.\").arg(line + 1);\n\tmainWindow->display(s);\n}\n\nvoid t_gui::action_reject(unsigned short line) {\n\tQString s;\n\t\n\tcb_stop_call_notification(line);\n\tphone->pub_reject(line);\n\t\n\tmainWindow->displayHeader();\n\ts = qApp->translate(\"GUI\", \"Line %1: call rejected.\").arg(line + 1);\n\tmainWindow->display(s);\n}\n\nvoid t_gui::action_redirect(const list<t_display_url> &contacts) {\n\tQString s;\n\t\n\tcb_stop_call_notification(phone->get_active_line());\n\tphone->pub_redirect(contacts, 302);\n\t\n\tint line = phone->get_active_line();\n\tmainWindow->displayHeader();\n\ts = qApp->translate(\"GUI\", \"Line %1: call redirected.\").arg(line + 1);\n\tmainWindow->display(s);\n}\n\nvoid t_gui::action_refer(const t_url &destination, const string &display) {\n\tphone->pub_refer(destination, display);\n}\n\nvoid t_gui::action_refer(unsigned short line_from, unsigned short line_to) {\n\tphone->pub_refer(line_from, line_to);\n}\n\nvoid t_gui::action_setup_consultation_call(const t_url &destination, const string &display) {\n\tphone->pub_setup_consultation_call(destination, display);\n}\n\nvoid t_gui::action_hold(void) {\n\tphone->pub_hold();\n}\n\nvoid t_gui::action_retrieve(void) {\n\tphone->pub_retrieve();\n}\n\nvoid t_gui::action_conference(void) {\n\tif (!phone->join_3way(0, 1)) {\n\t\tmainWindow->display(qApp->translate(\"GUI\", \"Failed to start conference.\"));\n\t}\n}\n\nvoid t_gui::action_mute(bool on) {\n\tphone->mute(on);\n}\n\nvoid t_gui::action_options(void) {\n\tphone->pub_options();\n}\n\nvoid t_gui::action_options(t_user *user_config, const t_url &contact) {\n\trun_on_event_queue([=]() {\n\t\tphone->pub_options(user_config, contact);\n\t});\n}\n\nvoid t_gui::action_dtmf(const string &digits) {\n\tconst t_call_info call_info = phone->get_call_info(phone->get_active_line());\n\tthrottle_dtmf_not_supported = false;\n\t\n\tif (!call_info.dtmf_supported) return;\n\t\n\tfor (string::const_iterator i = digits.begin(); i != digits.end(); i++) {\n\t\tif (is_valid_dtmf_sym(*i)) {\n\t\t\tphone->pub_send_dtmf(*i, call_info.dtmf_inband, call_info.dtmf_info);\n\t\t}\n\t}\n}\n\nvoid t_gui::action_activate_line(unsigned short line) {\n\tphone->pub_activate_line(line);\n}\n\nbool t_gui::action_seize(void) {\n\treturn phone->pub_seize();\n}\n\nvoid t_gui::action_unseize(void) {\n\tphone->pub_unseize();\n}\n\nvoid t_gui::action_confirm_zrtp_sas(int line) {\n\tphone->pub_confirm_zrtp_sas(line);\n}\n\nvoid t_gui::action_confirm_zrtp_sas() {\n\tphone->pub_confirm_zrtp_sas();\n}\n\nvoid t_gui::action_reset_zrtp_sas_confirmation(int line) {\n\tphone->pub_reset_zrtp_sas_confirmation(line);\n}\n\nvoid t_gui::action_reset_zrtp_sas_confirmation() {\n\tphone->pub_reset_zrtp_sas_confirmation();\n}\n\nvoid  t_gui::action_enable_zrtp(void) {\n\tphone->pub_enable_zrtp();\n}\n\nvoid  t_gui::action_zrtp_request_go_clear(void) {\n\tphone->pub_zrtp_request_go_clear();\n}\n\nvoid  t_gui::action_zrtp_go_clear_ok(unsigned short line) {\n\tphone->pub_zrtp_go_clear_ok(line);\n}\n\nvoid t_gui::srv_dnd(list<t_user *> user_list, bool on) {\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tif (on) {\n\t\t\tphone->ref_service(*i)->enable_dnd();\n\t\t} else {\n\t\t\tphone->ref_service(*i)->disable_dnd();\n\t\t}\n\t}\n}\n\nvoid t_gui::srv_enable_cf(t_user *user_config,\n\t\tt_cf_type cf_type, const list<t_display_url> &cf_dest) \n{\n\tphone->ref_service(user_config)->enable_cf(cf_type, cf_dest);\n}\n\nvoid t_gui::srv_disable_cf(t_user *user_config, t_cf_type cf_type) {\n\tphone->ref_service(user_config)->disable_cf(cf_type);\n}\n\nvoid t_gui::srv_auto_answer(list<t_user *> user_list, bool on) {\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tphone->ref_service(*i)->enable_auto_answer(on);\n\t}\n}\n\nvoid t_gui::fill_user_combo(QComboBox *cb) {\n\tcb->clear();\n\t\n\tlist<t_user *> user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\t// OLD code: used display uri\n\t\t// cb->insertItem((*i)->get_display_uri().c_str());\n        cb->addItem((*i)->get_profile_name().c_str());\n\t}\n\t\n    cb->setCurrentIndex(0);\n}\n\nQString t_gui::get_last_file_browse_path(void) const {\n\treturn lastFileBrowsePath;\n}\n\nvoid t_gui::set_last_file_browse_path(QString path) {\n\tlock();\n\tlastFileBrowsePath = path;\n\tunlock();\n}\n\n#ifdef HAVE_KDE\nunsigned short t_gui::get_line_sys_tray_popup(void) const {\n\treturn line_sys_tray_popup;\n}\n#endif\n\nim::t_msg_session *t_gui::getMessageSession(t_user *user_config, \n\t\t\tconst t_url &remote_url, const string &display) const \n{\n\tfor (list<im::t_msg_session *>::const_iterator it = messageSessions.begin();\n\tit != messageSessions.end(); ++it)\n\t{\n\t\tif ((*it)->match(user_config, remote_url)) {\n\t\t\t(*it)->set_display_if_empty(display);\n\t\t\treturn *it;\n\t\t}\n\t}\n\t\n\treturn NULL;\n}\n\t\nvoid t_gui::addMessageSession(im::t_msg_session *s) {\n\tmessageSessions.push_back(s);\n\t\n\tif (!timerUpdateMessageSessions) {\n\t\ttimerUpdateMessageSessions = new QTimer();\n\t\tMEMMAN_NEW(timerUpdateMessageSessions);\n\t\t\n\t\tconnect(timerUpdateMessageSessions, SIGNAL(timeout()),\n\t\t\t\t this, SLOT(updateTimersMessageSessions()));\n\t\ttimerUpdateMessageSessions->start(1000);\n\t}\n}\n\nvoid t_gui::removeMessageSession(im::t_msg_session *s) {\n\tmessageSessions.remove(s);\n\t\n\tif (messageSessions.empty()) {\n\t\ttimerUpdateMessageSessions->stop();\n\t\t\n\t\tMEMMAN_DELETE(timerUpdateMessageSessions);\n\t\tdelete timerUpdateMessageSessions;\n\t\ttimerUpdateMessageSessions = NULL;\n\t}\n}\n\nvoid t_gui::destroyAllMessageSessions(void) {\n\tif (timerUpdateMessageSessions) {\n\t\tMEMMAN_DELETE(timerUpdateMessageSessions);\n\t\tdelete timerUpdateMessageSessions;\n\t\ttimerUpdateMessageSessions = NULL;\n\t}\n\t\n\tfor (list<im::t_msg_session *>::iterator it = messageSessions.begin();\n\tit != messageSessions.end(); ++it)\n\t{\n\t\tMEMMAN_DELETE(*it);\n\t\tdelete *it;\n\t}\n\t\n\tmessageSessions.clear();\n}\n\nvoid t_gui::updateTimersMessageSessions() {\n\tfor (list<im::t_msg_session *>::iterator it = messageSessions.begin();\n\tit != messageSessions.end(); ++it)\n\t{\n\t\t(*it)->dec_local_composing_timeout();\n\t\t(*it)->dec_remote_composing_timeout();\n\t}\n}\n\nvoid t_gui::updateInhibitIdleSession() {\n\tm_idle_session_manager->setEnabled(sys_config->get_inhibit_idle_session());\n}\n\nvoid t_gui::updateIdleSessionState() {\n\tbool busy = false;\n\tfor (int i = 0; i < NUM_USER_LINES; i++) {\n\t\tif (phone->get_line_state(i) == LS_BUSY)\n\t\t\tbusy = true;\n\t}\n\tm_idle_session_manager->setActivityState(busy);\n}\n\nstring t_gui::mime2file_extension(t_media media) {\n\tstring extension;\n\t\n#ifdef HAVE_KDE\n\t// If KDE is available then use KDE to retrieve the proper file\n\t// extension so we nicely integrate with the desktop settings.\n\tstring mime = media.type + \"/\" + media.subtype;\n\tKMimeType::Ptr pMime = KMimeType::mimeType(mime.c_str());\n\tconst QStringList &patterns = pMime->patterns();\n\t\n\tif (!patterns.empty()) {\n        extension = patterns.front().toStdString();\n\t} else {\n\t\tlog_file->write_header(\"t_gui::mime2file_extension\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Cannot find file extension for mime type \");\n\t\tlog_file->write_raw(mime);\n\t\tlog_file->write_footer();\n\t}\n#endif\n\treturn extension;\n}\n\nvoid t_gui::open_url_in_browser(const QString &url) {\n\tstring sys_browser = sys_config->get_gui_browser_cmd();\n#ifdef HAVE_KDE\n\tif (sys_browser.empty())\n\t{\n\t\tKTrader::OfferList offers = KTrader::self()->query(\"text/html\", \"Type == 'Application'\");\n\t\tif (!offers.empty()) {\n\t\t\tKService::Ptr ptr = offers.first();\n\t\t\tKURL::List lst;\n\t\t\tlst.append(url);\n\t\t\tKRun::run(*ptr, lst);\n\t\t\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\tbool process_started = false;\n\t\n\tQStringList browsers;\n\t\n\tif (sys_browser.empty()) {\n\t\tbrowsers << \"xdg-open\" << \"firefox\" << \"mozilla\" << \"netscape\" << \"opera\";\n\t\tbrowsers << \"galeon\" << \"epiphany\" << \"konqueror\";\n\t} else {\n\t\tbrowsers << sys_browser.c_str();\n\t}\n\t\n\tfor (QStringList::Iterator it = browsers.begin(); it != browsers.end(); ++it)\n\t{\n\t\tprocess_started = QProcess::startDetached(*it, QStringList(url));\n\t\tif (process_started) break;\n\t}\n\t\n\tif (!process_started) {\n\t\tQString msg = qApp->translate(\"GUI\", \"Cannot open web browser: %1\").arg(url);\n\t\tmsg += \"\\n\\n\";\n\t\tmsg += qApp->translate(\"GUI\", \"Configure your web browser in the system settings.\");\n        cb_show_msg(msg.toStdString(), MSG_CRITICAL);\n\t}\n}\n"
  },
  {
    "path": "src/gui/gui.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _GUI_H\n#define _GUI_H\n\n#include \"twinkle_config.h\"\n\n#include \"phone.h\"\n#include \"userintf.h\"\n#include \"im/msg_session.h\"\n\n#include \"messageform.h\"\n\n#include \"qaction.h\"\n#include \"qcombobox.h\"\n#include \"qlabel.h\"\n#include \"qlineedit.h\"\n#include <QProgressDialog>\n#include \"qtimer.h\"\n#include \"qtoolbutton.h\"\n#include \"qwidget.h\"\n\n#ifdef HAVE_KDE\n#include <kpassivepopup.h>\n#endif\n\nusing namespace std;\n\n// Forward declaration\nclass MphoneForm;\nclass IdleSessionManager;\n\n// Length of redial list in combo boxes\n#define SIZE_REDIAL_LIST 10\n\n// Selection purpose for select user form\nenum t_select_purpose {\n\tSELECT_REGISTER,\n\tSELECT_DEREGISTER,\n\tSELECT_DEREGISTER_ALL,\n\tSELECT_DND,\n\tSELECT_AUTO_ANSWER\n};\n\nQString str2html(const QString &s);\n\nvoid setDisabledIcon(QAction *action, const QString &icon);\nvoid setDisabledIcon(QToolButton *toolButton, const QString &icon);\n\nclass t_gui : public QObject, public t_userintf {\n\tQ_OBJECT\nprivate:\n\tMphoneForm\t*mainWindow;\n\n\tIdleSessionManager *m_idle_session_manager;\n\t\n\t// List of active instant messaging session.\n\tlist<im::t_msg_session *> messageSessions;\n\t\n\t// Timer to schedule updating of message sessions every second.\n\tQTimer *timerUpdateMessageSessions;\n\t\n\t// Progress dialog for FW/NAT discovery progress bar\n\tQProgressDialog\t*natDiscoveryProgressDialog;\n\t\n\t// Pointers to line information fields to display information\n\tQLineEdit\t*fromLabel;\n\tQLineEdit\t*toLabel;\n\tQLineEdit\t*subjectLabel;\n\tQLabel\t\t*codecLabel;\n\tQLabel\t\t*photoLabel;\n\t\n\t// Timers to auto show main window on incoming call\n\tQTimer\t\tautoShowTimer[NUM_USER_LINES];\n\t\n#ifdef HAVE_KDE\n\t// Popup window on system tray for incoming call notification\n\tKPassivePopup\t*sys_tray_popup;\n\tunsigned short\tline_sys_tray_popup; // lineno for popup\n#endif\n\t\n\t// Last dir path browsed by the user with a file dialog\n\tQString\t\tlastFileBrowsePath;\n\t\n\t// Set the line information field pointers to the fields for 'line'\n\tvoid setLineFields(int line);\n\t\n\t// Set text inf from, to and subject fields\n\tvoid displayTo(const QString &s);\n\tvoid displayFrom(const QString &s);\n\tvoid displaySubject(const QString &s);\n\t\n\t// Display the codecs in use for the line\n\tvoid displayCodecInfo(int line);\n\t\n\t// Display a photo\n\tvoid displayPhoto(const QImage &photo);\n\t\nprotected:\n\t// The do_* methods perform the commands parsed by the exec_* methods.\n\tvirtual bool do_invite(const string &destination, const string &display,\n\t\t\t       const string &subject, bool immediate,\n\t\t\t       bool anonymous);\n\tvirtual void do_redial(void);\n\tvirtual void do_answer(void);\n\tvirtual void do_answerbye(void);\n\tvirtual void do_reject(void);\n\tvirtual void do_redirect(bool show_status, bool type_present,\n\t\t\t\t t_cf_type cf_type, bool action_present,\n\t\t\t\t bool enable, int num_redirections,\n\t\t\t\t const list<string> &dest_strlist,\n\t\t\t\t bool immediate);\n\tvirtual void do_dnd(bool show_status, bool toggle, bool enable);\n\tvirtual void do_auto_answer(bool show_status, bool toggle, bool enable);\n\tvirtual void do_bye(void);\n\tvirtual void do_hold(bool toggle);\n\tvirtual void do_retrieve(void);\n\tvirtual bool do_refer(const string &destination,\n\t\t\t      t_transfer_type transfer_type, bool immediate);\n\tvirtual void do_conference(void);\n\tvirtual void do_mute(bool show_status, bool toggle, bool enable);\n\tvirtual void do_dtmf(const string &digits);\n\tvirtual void do_register(bool reg_all_profiles);\n\tvirtual void do_deregister(bool dereg_all_profiles,\n\t\t\t\t   bool dereg_all_devices);\n\tvirtual void do_fetch_registrations(void);\n\tvirtual bool do_options(bool dest_set, const string &destination,\n\t\t\t\tbool immediate);\n\tvirtual void do_line(int line);\n\tvirtual void do_user(const string &profile_name);\n\tvirtual void do_zrtp(t_zrtp_cmd zrtp_cmd);\n\tvirtual bool do_message(const string &destination,\n\t\t\t\tconst string &display, const im::t_msg &msg);\n\tvirtual void do_presence(t_presence_state::t_basic_state basic_state);\n\tvirtual void do_quit(void);\n\tvirtual void do_help(const list<t_command_arg> &al);\nprivate slots:\n\tvoid gui_do_invite(const QString &destination, const QString &display,\n\t\t\t   const QString &subject, bool immediate,\n\t\t\t   bool anonymous);\n\tvoid gui_do_redial(void);\n\tvoid gui_do_answer(void);\n\tvoid gui_do_answerbye(void);\n\tvoid gui_do_reject(void);\n\tvoid gui_do_redirect(bool type_present, t_cf_type cf_type,\n\t\t\t     bool action_present, bool enable,\n\t\t\t     int num_redirections,\n\t\t\t     const std::list<std::string> &dest_strlist,\n\t\t\t     bool immediate);\n\tvoid gui_do_dnd(bool toggle, bool enable);\n\tvoid gui_do_auto_answer(bool toggle, bool enable);\n\tvoid gui_do_bye(void);\n\tvoid gui_do_hold(bool toggle);\n\tvoid gui_do_retrieve(void);\n\tvoid gui_do_refer(const QString &destination,\n\t\t\t  t_transfer_type transfer_type, bool immediate);\n\tvoid gui_do_conference(void);\n\tvoid gui_do_mute(bool toggle, bool enable);\n\tvoid gui_do_dtmf(const QString &digits);\n\tvoid gui_do_user(const QString &profile_name);\n\tQString gui_get_current_profile();\n\n\tvoid gui_cmd_call(const string &destination, bool immediate);\n\tvoid gui_cmd_show(void);\n\tvoid gui_cmd_hide(void);\npublic:\n\tt_gui(t_phone *_phone);\n\tvirtual ~t_gui();\n\t\n\t// Start the GUI\n\tvoid run(void);\n\t\n\t// Save user interface state to system settings\n\tvoid save_state(void);\n\t\n\t// Restore user interface state from system settings\n\tvoid restore_state(void);\n\t\n\t/** Save state to restore a UI session. */\n\tvoid save_session_state(void);\n\t\n\t/** Restore UI session state. */\n\tvoid restore_session_state(void);\n\t\n\t// Lock the user interface to synchornize output\n\tvoid lock(void);\n\tvoid unlock(void);\n\t\n\t// Select network interface to use.\n\tstring select_network_intf(void);\n\t\n\t// Select a user configuration file. Returns false if selection failed.\n\tbool select_user_config(list<string> &config_files);\n\t\n\t// Clear the contents of the line information fields. After clearing\n\t// the field pointers point to the fields for 'line'\n\tvoid clearLineFields(int line);\n\t\n\t// Call back functions\n\tvoid cb_incoming_call(t_user *user_config, int line, const t_request *r);\n\tvoid cb_call_cancelled(int line, const std::string &reason);\n\tvoid cb_far_end_hung_up(int line, const std::string &reason);\n\tvoid cb_answer_timeout(int line);\n\tvoid cb_sdp_answer_not_supported(int line, const string &reason);\n\tvoid cb_sdp_answer_missing(int line);\n\tvoid cb_unsupported_content_type(int line, const t_sip_message *r);\n\tvoid cb_ack_timeout(int line);\n\tvoid cb_100rel_timeout(int line);\n\tvoid cb_session_expired(int line);\n\tvoid cb_prack_failed(int line, const t_response *r);\n\tvoid cb_provisional_resp_invite(int line, const t_response *r);\n\tvoid cb_cancel_failed(int line, const t_response *r);\n\tvoid cb_call_answered(t_user *user_config, int line, const t_response *r);\n\tvoid cb_call_failed(t_user *user_config, int line, const t_response *r);\n\tvoid cb_stun_failed_call_ended(int line);\n\tvoid cb_call_ended(int line);\n\tvoid cb_call_established(int line);\n\tvoid cb_options_response(const t_response *r);\n\tvoid cb_reinvite_success(int line, const t_response *r);\n\tvoid cb_reinvite_failed(int line, const t_response *r);\n\tvoid cb_retrieve_failed(int line, const t_response *r);\n\tvoid cb_invalid_reg_resp(t_user *user_config, const t_response *r, const string &reason);\n\tvoid cb_register_success(t_user *user_config, const t_response *r, unsigned long expires,\n\t\t\t\t bool first_success);\n\tvoid cb_register_failed(t_user *user_config, const t_response *r, bool first_failure);\n\tvoid cb_register_stun_failed(t_user *user_config, bool first_failure);\n\tvoid cb_deregister_success(t_user *user_config, const t_response *r);\n\tvoid cb_deregister_failed(t_user *user_config, const t_response *r);\n\tvoid cb_fetch_reg_failed(t_user *user_config, const t_response *r);\n\tvoid cb_fetch_reg_result(t_user *user_config, const t_response *r);\n\tvoid cb_register_inprog(t_user *user_config, t_register_type register_type);\n\tvoid cb_redirecting_request(t_user *user_config, int line, const t_contact_param &contact);\n\tvoid cb_redirecting_request(t_user *user_config, const t_contact_param &contact);\n\tvoid cb_notify_call(int line, const QString &from_party, const QString &organization,\n\t\t\t   const QImage &photo, const QString &subject, QString &referred_by_party);\n\tvoid cb_stop_call_notification(int line);\n\tvoid cb_dtmf_detected(int line, t_dtmf_ev dtmf_event);\n\tvoid cb_send_dtmf(int line, t_dtmf_ev dtmf_event);\n\tvoid cb_dtmf_not_supported(int line);\n\tvoid cb_dtmf_supported(int line);\n\tvoid cb_line_state_changed(void);\n\tvoid cb_send_codec_changed(int line, t_audio_codec codec);\n\tvoid cb_recv_codec_changed(int line, t_audio_codec codec);\n\tvoid cb_notify_recvd(int line, const t_request *r);\n\tvoid cb_refer_failed(int line, const t_response *r);\n\tvoid cb_refer_result_success(int line);\n\tvoid cb_refer_result_failed(int line);\n\tvoid cb_refer_result_inprog(int line);\n\t\n\t// A call is being referred by the far end. r must be the REFER request.\n\tvoid cb_call_referred(t_user *user_config, int line, t_request *r);\n\n\t// The reference failed. Call to referrer is retrieved.\n\tvoid cb_retrieve_referrer(t_user *user_config, int line);\n\t\n\t// A consulation call for a call transfer is being setup.\n\tvoid cb_consultation_call_setup(t_user *user_config, int line);\n\t\n\t// STUN errors\n\tvoid cb_stun_failed(t_user *user_config, int err_code, const string &err_reason);\n\tvoid cb_stun_failed(t_user *user_config);\n\t\n\t// Interactive call back functions\n\tbool cb_ask_user_to_redirect_invite(t_user *user_config, const t_url &destination,\n\t\t\tconst string &display);\n\tbool cb_ask_user_to_redirect_request(t_user *user_config, const t_url &destination,\n\t\t\tconst string &display, t_method method);\n\tbool cb_ask_credentials(t_user *user_config, const string &realm, string &username,\n\t\t\tstring &password);\n\t\n\t// Ask questions asynchronously.\n\tvoid cb_ask_user_to_refer(t_user *user_config, const t_url &refer_to_uri,\n\t\t\tconst string &refer_to_display,\n\t\t\tconst t_url &referred_by_uri,\n\t\t\tconst string &referred_by_display);\n\t\n\t// Show an error message to the user. Depending on the interface mode\n\t// the user has to acknowledge the error before processing continues.\n\tvoid cb_show_msg(const string &msg, t_msg_priority prio = MSG_INFO);\n\tvoid cb_show_msg(QWidget *parent, const string &msg, t_msg_priority prio = MSG_INFO);\n\t\n\t// Ask a yes/no question to the user.\n\t// Returns true for yes and false for no.\n\tbool cb_ask_msg(const string &msg, t_msg_priority prio = MSG_INFO);\n\tbool cb_ask_msg(QWidget *parent, const string &msg, t_msg_priority prio = MSG_INFO);\n\t\n\t// Display an error message.\n\tvoid cb_display_msg(const string &msg, t_msg_priority prio = MSG_INFO);\n\t\n\t// Log file has been updated\n\tvoid cb_log_updated(bool log_zapped = false);\n\t\n\t// Call history has been updated\n\tvoid cb_call_history_updated(void);\n\tvoid cb_missed_call(int num_missed_calls);\n\t\n\t// Show firewall/NAT discovery progress\n\tvoid cb_nat_discovery_progress_start(int num_steps);\n\tvoid cb_nat_discovery_progress_step(int step);\n\tvoid cb_nat_discovery_finished(void);\n\tbool cb_nat_discovery_cancelled(void);\n\t\n\t// ZRTP\n\tvoid cb_line_encrypted(int line, bool encrypted, const string &cipher_mode = \"\");\n\tvoid cb_show_zrtp_sas(int line, const string &sas);\n\tvoid cb_zrtp_confirm_go_clear(int line);\n\tvoid cb_zrtp_sas_confirmed(int line);\n\tvoid cb_zrtp_sas_confirmation_reset(int line);\n\t\n\t// MWI\n\tvoid cb_update_mwi(void);\n\tvoid cb_mwi_subscribe_failed(t_user *user_config, t_response *r, bool first_failure);\n\tvoid cb_mwi_terminated(t_user *user_config, const string &reason);\n\t\n\t// Instant messaging\n\tbool cb_message_request(t_user *user_config, t_request *r);\n\tvoid cb_message_response(t_user *user_config, t_response *r, t_request *req);\n\tvoid cb_im_iscomposing_request(t_user *user_config, t_request *r,\n\t\t\tim::t_composing_state state, time_t refresh);\n\tvoid cb_im_iscomposing_not_supported(t_user *user_config, t_response *r);\n\t\n\t// Execute external commands\n\tvoid cmd_call(const string &destination, bool immediate);\n\tvoid cmd_quit(void);\n\tvoid cmd_show(void);\n\tvoid cmd_hide(void);\n\t\n\t// Lookup a URL in the address book\n\tstring get_name_from_abook(t_user *user_config, const t_url &u);\n\t\n\t// Actions\n\tvoid action_register(list<t_user *> user_list);\n\tvoid action_deregister(list<t_user *> user_list, bool dereg_all);\n\tvoid action_show_registrations(list<t_user *> user_list);\n\tvoid action_invite(t_user *user_config, \n\t\t\t   const t_url &destination, const string &display, \n\t\t\t   const string &subject, bool anonymous);\n\tvoid action_answer(void);\n\tvoid action_bye(void);\n\tvoid action_reject(void);\n\tvoid action_reject(unsigned short line);\n\tvoid action_redirect(const list<t_display_url> &contacts);\n\tvoid action_refer(const t_url &destination, const string &display);\n\tvoid action_refer(unsigned short line_from, unsigned short line_to);\n\tvoid action_setup_consultation_call(const t_url &destination, const string &display);\n\tvoid action_hold(void);\n\tvoid action_retrieve(void);\n\tvoid action_conference(void);\n\tvoid action_mute(bool on);\n\tvoid action_options(void);\n\tvoid action_options(t_user *user_config, const t_url &contact);\n\tvoid action_dtmf(const string &digits);\n\tvoid action_activate_line(unsigned short line);\n\tbool action_seize(void);\n\tvoid action_unseize(void);\n\tvoid action_confirm_zrtp_sas(int line);\n\tvoid action_confirm_zrtp_sas();\n\tvoid action_reset_zrtp_sas_confirmation(int line);\n\tvoid action_reset_zrtp_sas_confirmation();\n\tvoid action_enable_zrtp(void);\n\tvoid action_zrtp_request_go_clear(void);\n\tvoid action_zrtp_go_clear_ok(unsigned short line);\n\t\n\t// Service (de)activation\n\tvoid srv_dnd(list<t_user *> user_list, bool on);\n\tvoid srv_enable_cf(t_user *user_config,\n\t\tt_cf_type cf_type, const list<t_display_url> &cf_dest);\n\tvoid srv_disable_cf(t_user *user_config, t_cf_type cf_type);\n\tvoid srv_auto_answer(list<t_user *> user_list, bool on);\n\t\n\t// Fill a combo box with user names (display, uri) of active users\n\tvoid fill_user_combo(QComboBox *cb);\n\t\n\t// Get/set last dir path for a file dialog browse session\n\tQString get_last_file_browse_path(void) const;\n\tvoid set_last_file_browse_path(QString path);\n\t\n#ifdef HAVE_KDE\n\t// Get the line associated with the sys tray popup\n\tunsigned short get_line_sys_tray_popup(void) const;\n#endif\n\t\n\t// Get the message session for a dialog between the user\n\t// and the remote url. If the display name was not known\n\t// to the session yet, it is set to the passed display.\n\t// Returns NULL if no form exists.\n\tim::t_msg_session *getMessageSession(t_user *user_config,\n\t\t\t\t    const t_url &remote_url,\n\t\t\t\t    const string &display) const;\n\t\n\tvoid addMessageSession(im::t_msg_session *s);\n\tvoid removeMessageSession(im::t_msg_session *s);\n\tvoid destroyAllMessageSessions(void);\n\t\n\t/**\n\t  * Convert a mime type to a file extension.\n\t  * @param media [in] The mime type.\n\t  * @return file extension as glob expression.\n\t  */\n\tstring mime2file_extension(t_media media);\n\t\n\t/** \n             * Open a URL in an external web browser.\n\t  * @param url [in] URL to open.\n             */\n\tvoid open_url_in_browser(const QString &url);\n\nsignals:\n\tvoid update_reg_status();\n\tvoid update_mwi();\n\tvoid update_state();\n\tvoid mw_display(const QString& s);\n\tvoid mw_display_header();\n\tvoid mw_update_log(bool log_zapped);\n\tvoid mw_update_call_history();\n\tvoid mw_update_missed_call_status(int num_missed_calls);\n\t\npublic slots:\n\t// Apply the current \"inhibit_idle_session\" setting\n\tvoid updateInhibitIdleSession();\n\nprivate slots:\n\t/** \n            * Update timers associated with message sessions. This\n\t * function should be called every second.\n\t */\n\tvoid updateTimersMessageSessions();\n\n\t// Update the current idle/busy state\n\tvoid updateIdleSessionState();\n\n\tbool do_cb_ask_user_to_redirect_invite(t_user *user_config, const t_url &destination,\n\t\t\tconst string &display);\n\tbool do_cb_ask_user_to_redirect_request(t_user *user_config, const t_url &destination,\n\t\t\tconst string &display, t_method method);\n\tbool do_cb_ask_credentials(t_user *user_config, const string &realm, string &username,\n\t\t\tstring &password);\n\tvoid do_cb_ask_user_to_refer(t_user *user_config, const string &refer_to_uri_str,\n\t\t\tconst string &refer_to_display,\n\t\t\tconst string &referred_by_uri_str,\n\t\t\tconst string &referred_by_display);\n\tbool do_cb_message_request(t_user *user_config, t_request *r);\n    void do_cb_register_inprog(t_user *user_config, t_register_type register_type);\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/historyform.cpp",
    "content": "//Added by qt3to4:\n#include <QCloseEvent>\n#include <QPixmap>\n#include <QMenu>\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"call_history.h\"\n#include \"util.h\"\n#include \"gui.h\"\n#include \"qicon.h\"\n#include \"audits/memman.h\"\n#include \"historyform.h\"\n#include <QDateTime>\n\n#define HISTCOL_TIMESTAMP \t0\n#define HISTCOL_DIRECTION\t1\n#define HISTCOL_FROMTO\t\t2\n#define HISTCOL_SUBJECT\t3\n#define HISTCOL_STATUS\t\t4\n\n/*\n *  Constructs a HistoryForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nHistoryForm::HistoryForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nHistoryForm::~HistoryForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid HistoryForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid HistoryForm::init()\n{\n\n    m_model = new QStandardItemModel(historyListView);\n    historyListView->setModel(m_model);\n    m_model->setColumnCount(5);\n\n    m_model->setHorizontalHeaderLabels(QStringList() << tr(\"Time\") << tr(\"In/Out\") << tr(\"From/To\") << tr(\"Subject\") << tr(\"Status\"));\n    historyListView->horizontalHeader()->setSortIndicator(HISTCOL_TIMESTAMP, Qt::DescendingOrder);\n\n    historyListView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);\n\n    connect(historyListView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), SLOT(showCallDetails(QModelIndex)));\n\t\n\tinCheckBox->setChecked(true);\n\toutCheckBox->setChecked(true);\n\tsuccessCheckBox->setChecked(true);\n\tmissedCheckBox->setChecked(true);\n\tprofileCheckBox->setChecked(true);\n\t\n\ttimeLastViewed = phone->get_startup_time();\n\t\n\tQIcon inviteIcon(QPixmap(\":/icons/images/invite.png\"));\n\tQIcon deleteIcon(QPixmap(\":/icons/images/editdelete.png\"));\n    histPopupMenu = new QMenu(this);\n\t\n    itemCall = histPopupMenu->addAction(inviteIcon, tr(\"Call...\"), this, SLOT(call()));\n    histPopupMenu->addAction(deleteIcon, tr(\"Delete\"), this, SLOT(deleteEntry()));\n\n    m_pixmapIn = QPixmap(\":/icons/images/1leftarrow-yellow.png\");\n    m_pixmapOut = QPixmap(\":/icons/images/1rightarrow.png\");\n\n    m_pixmapOk = QPixmap(\":/icons/images/ok.png\");\n    m_pixmapCancel = QPixmap(\":/icons/images/cancel.png\");\n}\n\nvoid HistoryForm::destroy()\n{\n}\n\nvoid HistoryForm::loadHistory()\n{\n\t// Create list of all active profile names\n\tQStringList profile_name_list;\n\tlist<t_user *>user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tprofile_name_list.append((*i)->get_profile_name().c_str());\n\t}\n\t\n\t// Fill the history table\n\tunsigned long numberOfCalls = 0;\n\tunsigned long totalCallDuration = 0;\n\tunsigned long totalConversationDuration = 0;\n\n\tm_model->setRowCount(0);\n\n    std::list<t_call_record> history;\n\n    call_history->get_history(history);\n    m_history = QList<t_call_record>::fromStdList(history);\n\n    for (int x = 0; x < m_history.size(); x++) {\n        const t_call_record* cr = &m_history[x];\n\n        if (cr->direction == t_call_record::DIR_IN && !inCheckBox->isChecked()) {\n\t\t\tcontinue;\n\t\t}\n        if (cr->direction == t_call_record::DIR_OUT && !outCheckBox->isChecked()) {\n\t\t\tcontinue;\n\t\t}\n        if (cr->invite_resp_code < 300 && !successCheckBox->isChecked()) {\n\t\t\tcontinue;\n\t\t}\n        if (cr->invite_resp_code >= 300 && !missedCheckBox->isChecked()) {\n\t\t\tcontinue;\n\t\t}\n        if (!profile_name_list.contains(cr->user_profile.c_str()) &&\n\t\t    profileCheckBox->isChecked())\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tnumberOfCalls++;\n\t\t\n\t\t// Calculate total duration\n        totalCallDuration += cr->time_end - cr->time_start;\n        if (cr->time_answer != 0) {\n            totalConversationDuration += cr->time_end - cr->time_answer;\n\t\t}\n\t\t\n        t_user *user_config = phone->ref_user_profile(cr->user_profile);\n\t\t\n\t\t// If the user profile is not active, then use the\n\t\t// first user profile for formatting\t\n\t\tif (!user_config) {\n\t\t\tuser_config = phone->ref_users().front();\n\t\t}\n\t\t\n        m_model->setRowCount(numberOfCalls);\n\n        for (int j = 0; j < 5; j++)\n        {\n            QModelIndex index = m_model->index(m_model->rowCount()-1, j);\n\n            m_model->setData(index, QVariant(x), Qt::UserRole);\n            switch (j)\n            {\n                case HISTCOL_TIMESTAMP:\n                {\n                    m_model->setData(index, QDateTime::fromTime_t(cr->time_start));\n                    break;\n                }\n                case HISTCOL_DIRECTION:\n                {\n                    m_model->setData(index, QString::fromStdString(cr->get_direction()));\n\n                    m_model->setData(index, (cr->direction == t_call_record::DIR_IN ?\n                                    m_pixmapIn : m_pixmapOut), Qt::DecorationRole);\n\n                    break;\n                }\n                case HISTCOL_FROMTO:\n                {\n                    std::string address;\n\n                    address = (cr->direction == t_call_record::DIR_IN ?\n                         ui->format_sip_address(user_config,\n                          cr->from_display, cr->from_uri) :\n                         ui->format_sip_address(user_config,\n                          cr->to_display, cr->to_uri));\n\n                    m_model->setData(index, QString::fromStdString(address));\n\n                    m_model->setData(index, (cr->invite_resp_code < 300 ?\n                                    m_pixmapOk : m_pixmapCancel), Qt::DecorationRole);\n                    break;\n                }\n                case HISTCOL_SUBJECT:\n                {\n                    m_model->setData(index, QString::fromStdString(cr->subject));\n                    break;\n                }\n                case HISTCOL_STATUS:\n                {\n                    m_model->setData(index, QString::fromStdString(cr->invite_resp_reason));\n                    break;\n                }\n            }\n        }\n\t}\n\t\n\tnumberCallsValueTextLabel->setText(QString().setNum(numberOfCalls));\n\t\n\t// Total call duration formatting\n\tQString durationText = duration2str(totalCallDuration).c_str();\n\tdurationText += \" (\";\n\tdurationText += tr(\"conversation\");\n\tdurationText += \": \";\n\tdurationText += duration2str(totalConversationDuration).c_str();\n\tdurationText += \")\";\n\ttotalDurationValueTextLabel->setText(durationText);\n\t\n\t// Sort entries using currently selected sort column and order.\n\thistoryListView->sortByColumn(historyListView->horizontalHeader()->sortIndicatorSection(), historyListView->horizontalHeader()->sortIndicatorOrder());\n\t// Make the first entry the selected entry.\n\tif (numberOfCalls) historyListView->selectRow(0);\n}\n\n// Update history when triggered by a call back function on the user\n// interface.\nvoid HistoryForm::update()\n{\n\t// There is no need to update the history when the window is\n\t// hidden.\n    if (isVisible()) loadHistory();\n}\n\nvoid HistoryForm::show()\n{\n    if (isVisible()) {\n\t\traise();\n        activateWindow();\n\t\treturn;\n\t}\n\t\n\tloadHistory();\n\tQDialog::show();\n\traise();\n}\n\nvoid HistoryForm::closeEvent( QCloseEvent *e )\n{\n\tstruct timeval t;\n\t\n\tgettimeofday(&t, NULL);\n\ttimeLastViewed = t.tv_sec;\n\t\n\t// If Twinkle is terminated while the history window is\n\t// shown, then the call_history object is destroyed, before this\n\t// window is closed.\n\tif (call_history) {\n\t\tcall_history->clear_num_missed_calls();\n\t}\n\tQDialog::closeEvent(e);\n}\n\nvoid HistoryForm::showCallDetails(const QModelIndex &index)\n{\n\tcdrTextEdit->clear();\n\n\tif (!index.isValid()) return;\n\t\n    int x = m_model->data(index, Qt::UserRole).toInt();\n    const t_call_record& cr = m_history[x];\n\t\n\tt_user *user_config = phone->ref_user_profile(cr.user_profile);\n\t// If the user profile is not active, then use the\n\t// first user profile for formatting\t\n\tif (!user_config) {\n\t\tuser_config = phone->ref_users().front();\n\t}\n\t\n\tQString s = \"<table>\";\n\t\n\t// Left column: header names\n\ts += \"<tr><td><b>\";\n\ts += tr(\"Call start:\") + \"<br>\";\n\ts += tr(\"Call answer:\") + \"<br>\";\n\ts += tr(\"Call end:\") + \"<br>\";\n\ts += tr(\"Call duration:\") + \"<br>\";\n\ts += tr(\"Direction:\") + \"<br>\";\n\ts += tr(\"From:\") + \"<br>\";\n\ts += tr(\"To:\") + \"<br>\";\n\tif (cr.reply_to_uri.is_valid()) s += tr(\"Reply to:\") + \"<br>\";\n\tif (cr.referred_by_uri.is_valid()) s += tr(\"Referred by:\") + \"<br>\";\n\ts += tr(\"Subject:\") + \"<br>\";\n\ts += tr(\"Released by:\") + \"<br>\";\n\ts += tr(\"Status:\") + \"<br>\";\n\tif (!cr.far_end_device.empty()) s += tr(\"Far end device:\") + \"<br>\";\n\ts += tr(\"User profile:\");\n\ts += \"</b></td>\";\n\t\n\t// Right column: values\n\ts += \"<td>\";\n\ts += time2str(cr.time_start, \"%d %b %Y %H:%M:%S\").c_str();\n\ts += \"<br>\";\n\tif (cr.time_answer != 0) {\n\t\ts += time2str(cr.time_answer,  \"%d %b %Y %H:%M:%S\").c_str();\n\t}\n\ts += \"<br>\";\n\ts += time2str(cr.time_end, \"%d %b %Y %H:%M:%S\").c_str();\n\ts += \"<br>\";\n\t\n\ts += duration2str((unsigned long)(cr.time_end - cr.time_start)).c_str();\n\tif (cr.time_answer != 0) {\n\t\ts += \" (\";\n\t\ts += tr(\"conversation\");\n\t\ts += \": \";\n\t\ts += duration2str((unsigned long)(cr.time_end - cr.time_answer)).c_str();\n\t\ts += \")\";\n\t}\n\ts += \"<br>\";\n\t\n\ts += cr.get_direction().c_str();\n\ts += \"<br>\";\n\ts += str2html(ui->format_sip_address(user_config, cr.from_display, cr.from_uri).c_str());\n\tif (cr.from_organization != \"\") {\n\t\ts += \", \";\n\t\ts += str2html(cr.from_organization.c_str());\n\t}\n\ts += \"<br>\";\n\ts +=  str2html(ui->format_sip_address(user_config, cr.to_display, cr.to_uri).c_str());\n\tif (cr.to_organization != \"\") {\n\t\ts += \", \";\n\t\ts +=  str2html(cr.to_organization.c_str());\n\t}\n\ts += \"<br>\";\n\tif (cr.reply_to_uri.is_valid()) {\n\t\ts +=  str2html(ui->format_sip_address(user_config,\n\t\t\t\t\tcr.reply_to_display, cr.reply_to_uri).c_str());\n\t\ts += \"<br>\";\n\t}\n\tif (cr.referred_by_uri.is_valid()) {\n\t\ts +=  str2html(ui->format_sip_address(user_config,\n\t\t\t\tcr.referred_by_display, cr.referred_by_uri).c_str());\n\t\ts += \"<br>\";\n\t}\n\ts +=  str2html(cr.subject.c_str());\n\ts += \"<br>\";\n\ts += cr.get_rel_cause().c_str();\n\ts += \"<br>\";\n\ts += int2str(cr.invite_resp_code).c_str();\n\ts += ' ';\n\ts +=  str2html(cr.invite_resp_reason.c_str());\n\ts += \"<br>\";\n\tif (!cr.far_end_device.empty()) {\n\t\ts += str2html(cr.far_end_device.c_str());\n\t\ts += \"<br>\";\n\t}\n\ts +=  str2html(cr.user_profile.c_str());\n\ts += \"</td></tr>\";\n\t\n\ts += \"</table>\";\n\t\n\tcdrTextEdit->setText(s);\n}\n\nvoid HistoryForm::popupMenu(QPoint pos)\n{\n    if (!historyListView->selectionModel()->hasSelection())\n        return;\n\n    QModelIndex index = historyListView->selectionModel()->currentIndex();\n    int x = m_model->data(index, Qt::UserRole).toInt();\n\t\n    const t_call_record& cr = m_history[x];\n\t\n\t// An anonymous caller cannot be called\n\tbool canCall = !(cr.direction == t_call_record::DIR_IN &&\n\t\t\t    cr.from_uri.encode() == ANONYMOUS_URI);\n\t\n    itemCall->setEnabled(canCall);\n\thistPopupMenu->popup(pos);\n}\n\nvoid HistoryForm::call(QModelIndex index)\n{\n    int i = m_model->data(index, Qt::UserRole).toInt();\n    const t_call_record& cr = m_history[i];\n\t\n\tt_user *user_config = phone->ref_user_profile(cr.user_profile);\n\t// If the user profile is not active, then use the first profile\n\tif (!user_config) {\n\t\tuser_config = phone->ref_users().front();\n\t}\n\t\n\t// Determine subject\n\tQString subject;\n\tif (cr.direction == t_call_record::DIR_IN) {\n\t\tif (!cr.subject.empty()) {\n            if (cr.subject.substr(0, tr(\"Re:\").length()) != tr(\"Re:\").toStdString()) {\n\t\t\t\tsubject = tr(\"Re:\").append(\" \");\n\t\t\t\tsubject += cr.subject.c_str();\n\t\t\t} else {\n\t\t\t\tsubject = cr.subject.c_str();\n\t\t\t}\n\t\t}\n\t} else {\n\t\tsubject = cr.subject.c_str();\n\t}\n\t\n\t// Send call signal\n\tif (cr.direction == t_call_record::DIR_IN && cr.reply_to_uri.is_valid()) {\n\t\t// Call to the Reply-To contact\n\t\temit call(user_config,\n\t\t\tui->format_sip_address(user_config, \n\t\t\t\tcr.reply_to_display, cr.reply_to_uri).c_str(), \n\t\t\tsubject, false);\n\t} else {\n\t\t// For incoming calls, call to the From contact\n\t\t// For outgoing calls, call to the To contact\n\t\tbool hide_user = false;\n\t\tif (cr.direction == t_call_record::DIR_OUT && \n\t\t    cr.from_uri.encode() == ANONYMOUS_URI)\n\t\t{\n\t\t\thide_user = true;\n\t\t}\n        emit call(user_config, m_model->data(m_model->index(index.row(), HISTCOL_FROMTO)).toString(), subject, hide_user);\n\t}\n}\n\nvoid HistoryForm::call(void)\n{\n    QModelIndex index = historyListView->selectionModel()->currentIndex();\n    if (index.isValid()) call(index);\n}\n\nvoid HistoryForm::deleteEntry(void)\n{\n    QModelIndex index = historyListView->selectionModel()->currentIndex();\n    int i = m_model->data(index, Qt::UserRole).toInt();\n    m_model->removeRow(index.row());\n\t\n    call_history->delete_call_record(m_history[i].get_id());\n}\n\nvoid HistoryForm::clearHistory()\n{\n\tcall_history->clear();\n    m_model->setRowCount(0);\n}\n"
  },
  {
    "path": "src/gui/historyform.h",
    "content": "#ifndef HISTORYFORM_H\n#define HISTORYFORM_H\n#include \"phone.h\"\n#include <QMenu>\n#include <QStandardItemModel>\n#include <QPixmap>\n#include \"user.h\"\n#include \"ui_historyform.h\"\n\nclass HistoryForm : public QDialog, public Ui::HistoryForm\n{\n\tQ_OBJECT\n\npublic:\n    HistoryForm(QWidget* parent = 0);\n\t~HistoryForm();\n\npublic slots:\n\tvirtual void loadHistory();\n\tvirtual void update();\n\tvirtual void show();\n\tvirtual void closeEvent( QCloseEvent * e );\n    virtual void popupMenu( QPoint pos );\n    virtual void call( QModelIndex index );\n\tvirtual void call( void );\n\tvirtual void deleteEntry( void );\n\tvirtual void clearHistory();\n\nsignals:\n\tvoid call(t_user *, const QString &, const QString &, bool);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\ttime_t timeLastViewed;\n    QMenu *histPopupMenu;\n    QStandardItemModel *m_model;\n    QAction* itemCall;\n    QPixmap m_pixmapIn, m_pixmapOut;\n    QPixmap m_pixmapOk, m_pixmapCancel;\n    QList<t_call_record> m_history;\n\n\tvoid init();\n\tvoid destroy();\n\nprivate slots:\n\tvoid showCallDetails(const QModelIndex &);\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/historyform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>HistoryForm</class>\n <widget class=\"QDialog\" name=\"HistoryForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>864</width>\n    <height>639</height>\n   </rect>\n  </property>\n  <property name=\"contextMenuPolicy\">\n   <enum>Qt::CustomContextMenu</enum>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Call History</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QTableView\" name=\"historyListView\">\n     <property name=\"selectionMode\">\n      <enum>QAbstractItemView::SingleSelection</enum>\n     </property>\n     <property name=\"selectionBehavior\">\n      <enum>QAbstractItemView::SelectRows</enum>\n     </property>\n     <property name=\"editTriggers\">\n      <enum>QAbstractItemView::NoEditTriggers</enum>\n     </property>\n     <property name=\"sortingEnabled\">\n      <bool>true</bool>\n     </property>\n     <attribute name=\"horizontalHeaderHighlightSections\">\n      <bool>false</bool>\n     </attribute>\n     <attribute name=\"verticalHeaderVisible\">\n      <bool>false</bool>\n     </attribute>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <widget class=\"QGroupBox\" name=\"cdrGroupBox\">\n       <property name=\"title\">\n        <string>Call details</string>\n       </property>\n       <layout class=\"QGridLayout\">\n        <item row=\"0\" column=\"0\">\n         <widget class=\"QTextEdit\" name=\"cdrTextEdit\">\n          <property name=\"whatsThis\">\n           <string>Details of the selected call record.</string>\n          </property>\n          <property name=\"autoFormatting\">\n           <set>QTextEdit::AutoAll</set>\n          </property>\n          <property name=\"readOnly\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"viewGroupBox\">\n       <property name=\"title\">\n        <string>View</string>\n       </property>\n       <layout class=\"QVBoxLayout\">\n        <item>\n         <widget class=\"QCheckBox\" name=\"inCheckBox\">\n          <property name=\"whatsThis\">\n           <string>Check this option to show incoming calls.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Incoming calls</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+I</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QCheckBox\" name=\"outCheckBox\">\n          <property name=\"whatsThis\">\n           <string>Check this option to show outgoing calls.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Outgoing calls</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+O</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QCheckBox\" name=\"successCheckBox\">\n          <property name=\"whatsThis\">\n           <string>Check this option to show answered calls.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Answered calls</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+A</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QCheckBox\" name=\"missedCheckBox\">\n          <property name=\"whatsThis\">\n           <string>Check this option to show missed calls.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Missed calls</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+M</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QCheckBox\" name=\"profileCheckBox\">\n          <property name=\"whatsThis\">\n           <string>Check this option to show only calls associated with this user profile.</string>\n          </property>\n          <property name=\"text\">\n           <string>Current &amp;user profiles only</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+U</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"3\" column=\"0\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"clearPushButton\">\n       <property name=\"whatsThis\">\n        <string>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</string>\n       </property>\n       <property name=\"text\">\n        <string>C&amp;lear</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+L</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>540</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"closePushButton\">\n       <property name=\"whatsThis\">\n        <string>Close this window.</string>\n       </property>\n       <property name=\"text\">\n        <string>Clo&amp;se</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+S</string>\n       </property>\n       <property name=\"default\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"callPushButton\">\n       <property name=\"whatsThis\">\n        <string>Call selected address.</string>\n       </property>\n       <property name=\"text\">\n        <string>&amp;Call</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"numberlCallsTtextLabel\">\n       <property name=\"text\">\n        <string>Number of calls:</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"numberCallsValueTextLabel\">\n       <property name=\"text\">\n        <string>###</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"totalDurationTextLabel\">\n       <property name=\"text\">\n        <string>Total call duration:</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"totalDurationValueTextLabel\">\n       <property name=\"text\">\n        <string>###</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>460</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>historyListView</tabstop>\n  <tabstop>cdrTextEdit</tabstop>\n  <tabstop>inCheckBox</tabstop>\n  <tabstop>outCheckBox</tabstop>\n  <tabstop>successCheckBox</tabstop>\n  <tabstop>missedCheckBox</tabstop>\n  <tabstop>profileCheckBox</tabstop>\n  <tabstop>clearPushButton</tabstop>\n  <tabstop>closePushButton</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">user.h</include>\n  <include location=\"local\">phone.h</include>\n </includes>\n <resources/>\n <connections>\n  <connection>\n   <sender>closePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>close()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>713</x>\n     <y>631</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>inCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>loadHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>689</x>\n     <y>356</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>missedCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>loadHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>689</x>\n     <y>494</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>outCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>loadHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>689</x>\n     <y>402</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>profileCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>loadHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>689</x>\n     <y>540</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>successCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>loadHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>689</x>\n     <y>448</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>clearPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>clearHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>27</x>\n     <y>631</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>call()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>799</x>\n     <y>631</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>historyListView</sender>\n   <signal>doubleClicked(QModelIndex)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>call(QModelIndex)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>827</x>\n     <y>91</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>862</x>\n     <y>300</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>historyListView</sender>\n   <signal>customContextMenuRequested(QPoint)</signal>\n   <receiver>HistoryForm</receiver>\n   <slot>popupMenu(QPoint)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>819</x>\n     <y>45</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>858</x>\n     <y>290</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n <slots>\n  <slot>call(QModelIndex)</slot>\n  <slot>popupMenu(QPoint)</slot>\n </slots>\n</ui>\n"
  },
  {
    "path": "src/gui/icons.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/icons\">\n        <file>images/answer-disabled.png</file>\n        <file>images/answer.png</file>\n        <file>images/attach.png</file>\n        <file>images/auto_answer-disabled.png</file>\n        <file>images/auto_answer.png</file>\n        <file>images/buddy.png</file>\n        <file>images/bye-disabled.png</file>\n        <file>images/bye.png</file>\n        <file>images/cancel-disabled.png</file>\n        <file>images/cancel.png</file>\n        <file>images/cf-disabled.png</file>\n        <file>images/cf.png</file>\n        <file>images/clock.png</file>\n        <file>images/conf-disabled.png</file>\n        <file>images/conference-disabled.png</file>\n        <file>images/conference.png</file>\n        <file>images/conf.png</file>\n        <file>images/consult-xfer.png</file>\n        <file>images/contexthelp.png</file>\n        <file>images/dtmf-a.png</file>\n        <file>images/dtmf-b.png</file>\n        <file>images/dtmf-c.png</file>\n        <file>images/dtmf-disabled.png</file>\n        <file>images/dtmf-d.png</file>\n        <file>images/dtmf.png</file>\n        <file>images/dtmf-pound.png</file>\n        <file>images/dtmf-star.png</file>\n        <file>images/dtmf-0.png</file>\n        <file>images/dtmf-1.png</file>\n        <file>images/dtmf-2.png</file>\n        <file>images/dtmf-3.png</file>\n        <file>images/dtmf-4.png</file>\n        <file>images/dtmf-5.png</file>\n        <file>images/dtmf-6.png</file>\n        <file>images/dtmf-7.png</file>\n        <file>images/dtmf-8.png</file>\n        <file>images/dtmf-9.png</file>\n        <file>images/editcopy</file>\n        <file>images/editcut</file>\n        <file>images/editdelete.png</file>\n        <file>images/editpaste</file>\n        <file>images/edit.png</file>\n        <file>images/edit16.png</file>\n        <file>images/encrypted-disabled.png</file>\n        <file>images/encrypted.png</file>\n        <file>images/encrypted_verified.png</file>\n        <file>images/encrypted32.png</file>\n        <file>images/exit.png</file>\n        <file>images/favorites.png</file>\n        <file>images/filenew</file>\n        <file>images/fileopen-disabled.png</file>\n        <file>images/fileopen.png</file>\n        <file>images/filesave</file>\n        <file>images/gear.png</file>\n        <file>images/hold-disabled.png</file>\n        <file>images/hold.png</file>\n        <file>images/invite-disabled.png</file>\n        <file>images/invite.png</file>\n        <file>images/kcmpci.png</file>\n        <file>images/kcmpci16.png</file>\n        <file>images/kmix.png</file>\n        <file>images/knotify.png</file>\n        <file>images/kontact_contacts-disabled.png</file>\n        <file>images/kontact_contacts.png</file>\n        <file>images/kontact_contacts32.png</file>\n        <file>images/log.png</file>\n        <file>images/log_small.png</file>\n        <file>images/message.png</file>\n        <file>images/message32.png</file>\n        <file>images/mime_application.png</file>\n        <file>images/mime_audio.png</file>\n        <file>images/mime_image.png</file>\n        <file>images/mime_text.png</file>\n        <file>images/mime_video.png</file>\n        <file>images/missed-disabled.png</file>\n        <file>images/missed.png</file>\n        <file>images/mute-disabled.png</file>\n        <file>images/mute.png</file>\n        <file>images/mwi_failure16.png</file>\n        <file>images/mwi_new16.png</file>\n        <file>images/mwi_none.png</file>\n        <file>images/mwi_none16_dis.png</file>\n        <file>images/mwi_none16.png</file>\n        <file>images/network.png</file>\n        <file>images/no-indication.png</file>\n        <file>images/ok.png</file>\n        <file>images/package_network.png</file>\n        <file>images/package_system.png</file>\n        <file>images/password.png</file>\n        <file>images/penguin_big.png</file>\n        <file>images/penguin.png</file>\n        <file>images/penguin-small.png</file>\n        <file>images/presence_failed.png</file>\n        <file>images/presence_offline.png</file>\n        <file>images/presence_online.png</file>\n        <file>images/presence.png</file>\n        <file>images/presence_rejected.png</file>\n        <file>images/presence_unknown.png</file>\n        <file>images/print</file>\n        <file>images/qt-logo.png</file>\n        <file>images/redial-disabled.png</file>\n        <file>images/redial.png</file>\n        <file>images/redirect-disabled.png</file>\n        <file>images/redirect.png</file>\n        <file>images/redo</file>\n        <file>images/reg_failed-disabled.png</file>\n        <file>images/reg_failed.png</file>\n        <file>images/reg-query.png</file>\n        <file>images/reject-disabled.png</file>\n        <file>images/reject.png</file>\n        <file>images/save_as.png</file>\n        <file>images/searchfind</file>\n        <file>images/settings.png</file>\n        <file>images/stat_conference.png</file>\n        <file>images/stat_established_nomedia.png</file>\n        <file>images/stat_established.png</file>\n        <file>images/stat_mute.png</file>\n        <file>images/stat_outgoing.png</file>\n        <file>images/stat_ringing.png</file>\n        <file>images/sys_auto_ans_dis.png</file>\n        <file>images/sys_auto_ans.png</file>\n        <file>images/sys_busy_estab_dis.png</file>\n        <file>images/sys_busy_estab.png</file>\n        <file>images/sys_busy_trans_dis.png</file>\n        <file>images/sys_busy_trans.png</file>\n        <file>images/sys_dnd_dis.png</file>\n        <file>images/sys_dnd.png</file>\n        <file>images/sys_encrypted_dis.png</file>\n        <file>images/sys_encrypted.png</file>\n        <file>images/sys_encrypted_verified_dis.png</file>\n        <file>images/sys_encrypted_verified.png</file>\n        <file>images/sys_hold_dis.png</file>\n        <file>images/sys_hold.png</file>\n        <file>images/sys_idle_dis.png</file>\n        <file>images/sys_idle.png</file>\n        <file>images/sys_missed_dis.png</file>\n        <file>images/sys_missed.png</file>\n        <file>images/sys_mute_dis.png</file>\n        <file>images/sys_mute.png</file>\n        <file>images/sys_mwi_dis.png</file>\n        <file>images/sys_mwi.png</file>\n        <file>images/sys_redir_dis.png</file>\n        <file>images/sys_redir.png</file>\n        <file>images/sys_services_dis.png</file>\n        <file>images/sys_services.png</file>\n        <file>images/telephone-hook.png</file>\n        <file>images/transfer-disabled.png</file>\n        <file>images/transfer.png</file>\n        <file>images/twinkle16-disabled.png</file>\n        <file>images/twinkle16.png</file>\n        <file>images/twinkle24.png</file>\n        <file>images/twinkle32.png</file>\n        <file>images/twinkle48.png</file>\n        <file>images/undo</file>\n        <file>images/yast_babelfish.png</file>\n        <file>images/yast_PhoneTTOffhook.png</file>\n        <file>images/1downarrow.png</file>\n        <file>images/1leftarrow.png</file>\n        <file>images/1leftarrow-yellow.png</file>\n        <file>images/1rightarrow.png</file>\n        <file>images/1uparrow.png</file>\n        <file>images/osd_hangup.png</file>\n        <file>images/osd_mic_off.png</file>\n        <file>images/osd_mic_on.png</file>\n        <file>images/popup_incoming_answer.png</file>\n        <file>images/popup_incoming_reject.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/gui/idlesession_inhibitor.cpp",
    "content": "/*\n   (This file was initially copied from qBittorrent.)\n\n   Copyright (C) 2019  Vladimir Golovnev <glassez@yandex.ru>\n                       Frédéric Brière <fbriere@fbriere.net>\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2 of the License, or\n   (at your option) any later version.\n\n   This program is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"idlesession_inhibitor.h\"\n\n#include \"log.h\"\n#include \"userintf.h\"\n\n#include <QDBusConnection>\n#include <QDBusConnectionInterface>\n#include <QDBusMessage>\n#include <QDBusPendingCall>\n#include <QDBusPendingCallWatcher>\n#include <QDBusPendingReply>\n\n// There are various standards for session/power management out there.\n// Fortunately, most of them are either obsolete or redundant; the following\n// two should cover most, if not all, cases.\n\n// freedesktop.org Power Management Specification  (KDE, Xfce)\n#define FDO_SERVICE          \"org.freedesktop.PowerManagement\"\n#define FDO_PATH             \"/org/freedesktop/PowerManagement/Inhibit\"\n#define FDO_INTERFACE        \"org.freedesktop.PowerManagement.Inhibit\"\n#define FDO_METHOD_INHIBIT   \"Inhibit\"\n#define FDO_METHOD_UNINHIBIT \"UnInhibit\"\n\n// GNOME Session  (GNOME, MATE)\n#define GSM_SERVICE          \"org.gnome.SessionManager\"\n#define GSM_PATH             \"/org/gnome/SessionManager\"\n#define GSM_INTERFACE        \"org.gnome.SessionManager\"\n#define GSM_METHOD_INHIBIT   \"Inhibit\"\n#define GSM_METHOD_UNINHIBIT \"Uninhibit\"\n// Additional arguments required by GNOME's Inhibit()\n// - toplevel_xid: The toplevel X window identifier\n//                 (No idea what this does; everyone just uses 0)\n#define GSM_ARG_TOPLEVEL_XID 0u\n// - flags: Flags that spefify what should be inhibited\n//          (8: Inhibit the session being marked as idle)\n#define GSM_ARG_INHIBIT_FLAG 8u\n\n// Common arguments to Inhibit()\n#define INHIBIT_REASON       \"Call in progress\"\n// (Included for PRODUCT_NAME)\n#include \"protocol.h\"\n\n// How long (in ms) to wait for a reply to our D-Bus (un)inhibit requests\n#define DBUS_CALL_TIMEOUT    1000\n\n\nIdleSessionInhibitor::IdleSessionInhibitor(QObject *parent)\n\t: QObject(parent)\n{\n\tif (!QDBusConnection::sessionBus().isConnected()) {\n\t\tissueWarning(tr(\"D-Bus: Could not connect to session bus\"));\n\t\tm_state = error;\n\t\treturn;\n\t}\n\n\tauto interface = QDBusConnection::sessionBus().interface();\n\tif (interface->isServiceRegistered(FDO_SERVICE)) {\n\t\tm_use_gsm = false;\n\t} else if (interface->isServiceRegistered(GSM_SERVICE)) {\n\t\tm_use_gsm = true;\n\t} else {\n\t\tissueWarning(tr(\"D-Bus: No supported session/power management service found\"));\n\t\tm_state = error;\n\t\treturn;\n\t}\n\n\tm_state = idle;\n\tm_intended_state = idle;\n\tm_cookie = 0;\n}\n\nvoid IdleSessionInhibitor::sendRequest(bool inhibit)\n{\n\tQDBusMessage call;\n\tif (!m_use_gsm)\n\t\tcall = QDBusMessage::createMethodCall(\n\t\t\t\tFDO_SERVICE,\n\t\t\t\tFDO_PATH,\n\t\t\t\tFDO_INTERFACE,\n\t\t\t\tinhibit ? FDO_METHOD_INHIBIT : FDO_METHOD_UNINHIBIT);\n\telse\n\t\tcall = QDBusMessage::createMethodCall(\n\t\t\t\tGSM_SERVICE,\n\t\t\t\tGSM_PATH,\n\t\t\t\tGSM_INTERFACE,\n\t\t\t\tinhibit ? GSM_METHOD_INHIBIT : GSM_METHOD_UNINHIBIT);\n\n\tQList<QVariant> args;\n\tif (inhibit) {\n\t\targs << PRODUCT_NAME;\n\t\tif (m_use_gsm)\n\t\t\targs << GSM_ARG_TOPLEVEL_XID;\n\t\targs << INHIBIT_REASON;\n\t\tif (m_use_gsm)\n\t\t\targs << GSM_ARG_INHIBIT_FLAG;\n\t} else {\n\t\targs << m_cookie;\n\t}\n\tcall.setArguments(args);\n\n\tQDBusPendingCall pcall = QDBusConnection::sessionBus().asyncCall(call, DBUS_CALL_TIMEOUT);\n\tQDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);\n\tconnect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),\n\t\t\tthis, SLOT(onAsyncReply(QDBusPendingCallWatcher*)));\n}\n\nvoid IdleSessionInhibitor::requestBusy()\n{\n\tm_intended_state = busy;\n\tif (m_state != idle)\n\t\treturn;\n\n\tm_state = request_busy;\n\n\tsendRequest(true);\n}\n\nvoid IdleSessionInhibitor::requestIdle()\n{\n\tm_intended_state = idle;\n\tif (m_state != busy)\n\t\treturn;\n\n\tm_state = request_idle;\n\n\tsendRequest(false);\n}\n\nvoid IdleSessionInhibitor::onAsyncReply(QDBusPendingCallWatcher *call)\n{\n\tswitch (m_state) {\n\tcase request_idle: {\n\t\t// Reply to \"Uninhibit\" has no return value\n\t\tQDBusPendingReply<> reply = *call;\n\n\t\tif (reply.isError()) {\n\t\t\tissueWarning(tr(\"D-Bus: Reply: Error: %1\").arg(reply.error().message()));\n\t\t\tm_state = error;\n\t\t} else {\n\t\t\tm_state = idle;\n\t\t\t// Process any pending requestBusy() call\n\t\t\tif (m_intended_state == busy)\n\t\t\t\trequestBusy();\n\t\t}\n\t\tbreak;\n\t}\n\tcase request_busy: {\n\t\t// Reply to \"Inhibit\" has a cookie as return value\n\t\tQDBusPendingReply<unsigned int> reply = *call;\n\n\t\tif (reply.isError()) {\n\t\t\tissueWarning(tr(\"D-Bus: Reply: Error: %1\").arg(reply.error().message()));\n\t\t\tm_state = error;\n\t\t} else {\n\t\t\tm_state = busy;\n\t\t\tm_cookie = reply.value();\n\t\t\t// Process any pending requestIdle() call\n\t\t\tif (m_intended_state == idle)\n\t\t\t\trequestIdle();\n\t\t}\n\t\tbreak;\n\t}\n\tdefault:\n\t\tissueWarning(tr(\"D-Bus: Unexpected reply in state %1\").arg(m_state));\n\t\tm_state = error;\n\t}\n\n\tcall->deleteLater();\n}\n\nvoid IdleSessionInhibitor::issueWarning(const QString &msg) const\n{\n\tlog_file->write_report(msg.toStdString(), \"IdleSessionInhibitor\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\tui->cb_display_msg(msg.toStdString(), MSG_WARNING);\n}\n"
  },
  {
    "path": "src/gui/idlesession_inhibitor.h",
    "content": "/*\n   (This file was initially copied from qBittorrent.)\n\n   Copyright (C) 2019  Vladimir Golovnev <glassez@yandex.ru>\n                       Frédéric Brière <fbriere@fbriere.net>\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2 of the License, or\n   (at your option) any later version.\n\n   This program is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef IDLESESSION_INHIBITOR_H\n#define IDLESESSION_INHIBITOR_H\n\n#include <QObject>\n\n// Forward declaration\nQT_BEGIN_NAMESPACE\nclass QDBusPendingCallWatcher;\nQT_END_NAMESPACE\n\n// Object tasked with inhibiting idle sessions via D-Bus\nclass IdleSessionInhibitor : public QObject\n{\n\tQ_OBJECT\n\npublic:\n\tIdleSessionInhibitor(QObject *parent = 0);\n\n\tvoid requestBusy();\n\tvoid requestIdle();\n\nprivate slots:\n\t// Handle the reply to our (un)inhibit request\n\tvoid onAsyncReply(QDBusPendingCallWatcher *call);\n\nprivate:\n\t// Internal state of the inhibitor\n\tenum _state\n\t{\n\t\terror,\n\t\tbusy,\n\t\tidle,\n\t\trequest_busy,\n\t\trequest_idle\n\t};\n\t// Current state\n\tenum _state m_state;\n\t// Most recent idle/busy setting, possibly pending while we're waiting\n\t// for a reply to our previous D-Bus call\n\tenum _state m_intended_state;\n\n\t// Cookie returned by Inhibit(), to be passed to Uninhibit()\n\tunsigned int m_cookie;\n\n\t// Whether or not we are dealing with GNOME Session\n\tbool m_use_gsm;\n\n\t// Send an (un)inhibit request via D-Bus\n\tvoid sendRequest(bool inhibit);\n\n\t// Display and log a warning\n\tvoid issueWarning(const QString &msg) const;\n};\n\n#endif // IDLESESSION_INHIBITOR_H\n"
  },
  {
    "path": "src/gui/idlesession_manager.cpp",
    "content": "/*\n   (This file was initially copied from qBittorrent.)\n\n   Copyright (C) 2019  Vladimir Golovnev <glassez@yandex.ru>\n                       Frédéric Brière <fbriere@fbriere.net>\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2 of the License, or\n   (at your option) any later version.\n\n   This program is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"twinkle_config.h\"\n\n#include \"idlesession_manager.h\"\n#ifdef HAVE_DBUS\n#include \"idlesession_inhibitor.h\"\n#endif\n\n#include \"log.h\"\n\n#include <assert.h>\n\nIdleSessionManager::IdleSessionManager(QObject *parent)\n\t: QObject(parent)\n{\n\t// Creation of the inhibitor will only take place in setEnabled(),\n\t// to prevent issueing a warning if no management service is found\n\t// when the option has not been enabled in the first place.\n\n\tm_busy = false;\n\tm_inhibitor = nullptr;\n}\n\nvoid IdleSessionManager::setEnabled(bool enabled)\n{\n\t// Make sure logging has been made available at this point\n\tassert(log_file);\n\n#ifdef HAVE_DBUS\n\t// Create our inhibitor if enabling for the first time\n\tif (enabled && !m_inhibitor)\n\t\tm_inhibitor = new IdleSessionInhibitor(this);\n\n\t// Changing this setting while busy requires special handling\n\tif ((enabled != m_enabled) && m_busy) {\n\t\t// We need to directly call request*() methods, as the current\n\t\t// values of m_enabled and m_busy would screw up set*()\n\t\tif (enabled)\n\t\t\t// Forward the current state to the new inhibitor\n\t\t\tm_inhibitor->requestBusy();\n\t\telse\n\t\t\t// Switch back to idle before going silent\n\t\t\tm_inhibitor->requestIdle();\n\t}\n#endif\n\n\tm_enabled = enabled;\n}\n\nvoid IdleSessionManager::setActivityState(bool busy)\n{\n\tif (busy)\n\t\tsetBusy();\n\telse\n\t\tsetIdle();\n}\n\nvoid IdleSessionManager::setBusy()\n{\n\tif (m_busy)\n\t\treturn;\n\tm_busy = true;\n\n#ifdef HAVE_DBUS\n\tif (m_enabled && m_inhibitor)\n\t\tm_inhibitor->requestBusy();\n#endif\n}\n\nvoid IdleSessionManager::setIdle()\n{\n\tif (!m_busy)\n\t\treturn;\n\tm_busy = false;\n\n#ifdef HAVE_DBUS\n\tif (m_enabled && m_inhibitor)\n\t\tm_inhibitor->requestIdle();\n#endif\n}\n"
  },
  {
    "path": "src/gui/idlesession_manager.h",
    "content": "/*\n   (This file was initially copied from qBittorrent.)\n\n   Copyright (C) 2019  Vladimir Golovnev <glassez@yandex.ru>\n                       Frédéric Brière <fbriere@fbriere.net>\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2 of the License, or\n   (at your option) any later version.\n\n   This program is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef IDLESESSION_MANAGER_H\n#define IDLESESSION_MANAGER_H\n\n#include <QObject>\n\n// Forward declaration\nclass IdleSessionInhibitor;\n\n// Object responsible for keeping track of this session's activity, whether\n// D-Bus is supported or not\nclass IdleSessionManager : public QObject\n{\n\tQ_OBJECT\n\npublic:\n\tIdleSessionManager(QObject *parent = 0);\n\n\t// Enable/disable inhibition of idle session\n\tvoid setEnabled(bool enabled);\n\n\t// Declare the session as busy/idle\n\tvoid setActivityState(bool busy);\n\tvoid setBusy();\n\tvoid setIdle();\n\nprivate:\n\tbool m_enabled;\n\tbool m_busy;\n\n\tIdleSessionInhibitor *m_inhibitor;\n};\n\n#endif // IDLE_SESSION_MANAGER_H\n"
  },
  {
    "path": "src/gui/incoming_call_popup.cpp",
    "content": "#include \"incoming_call_popup.h\"\n#include <QDesktopWidget>\n#include <QApplication>\n#include <QQmlContext>\n#include <QSettings>\n\nextern QSettings* g_gui_state;\n\nIncomingCallPopup::IncomingCallPopup(QObject *parent) : QObject(parent)\n{\n\tm_view = new QQuickView;\n\n\tm_view->setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::ToolTip);\n\n\tm_view->rootContext()->setContextProperty(\"viewerWidget\", m_view);\n\tm_view->setSource(QUrl(\"qrc:/qml/incoming_call.qml\"));\n\n\tif (!m_view->rootObject()) {\n\t\tthrow std::runtime_error(\"Could not load incoming_call.qml\");\n\t}\n\n    // Place into the middle of the screen\n\tpositionWindow();\n\n\tQObject* button;\n\n\tbutton = m_view->rootObject()->findChild<QObject*>(\"buttonAnswer\");\n\tconnect(button, SIGNAL(clicked()), this, SLOT(onAnswerClicked()));\n\n\tbutton = m_view->rootObject()->findChild<QObject*>(\"buttonReject\");\n\tconnect(button, SIGNAL(clicked()), this, SLOT(onRejectClicked()));\n\n\tm_callerText = m_view->rootObject()->findChild<QQuickItem*>(\"callerText\");\n\tconnect(m_view->rootObject(), SIGNAL(moved()), this, SLOT(saveState()));\n}\n\nIncomingCallPopup::~IncomingCallPopup()\n{\n\tdelete m_view;\n}\n\nvoid IncomingCallPopup::positionWindow()\n{\n\tQDesktopWidget* desktop = qApp->desktop();\n\tint x, y;\n\tint defaultX, defaultY;\n\n\tdefaultX = desktop->width()/2 - m_view->width()/2;\n\tdefaultY = desktop->height()/2 - m_view->height()/2;\n\n\tx = g_gui_state->value(\"incoming_popup/x\", defaultX).toInt();\n\ty = g_gui_state->value(\"incoming_popup/y\", defaultY).toInt();\n\n\t// Reset position if off screen\n\tif (x > desktop->width() || x < 0)\n\t\tx = defaultX;\n\tif (y > desktop->height() || y < 0)\n\t\ty = defaultY;\n\n\tm_view->setPosition(x, y);\n}\n\nvoid IncomingCallPopup::saveState()\n{\n\tQPoint pos = m_view->position();\n\tg_gui_state->setValue(\"incoming_popup/x\", pos.x());\n\tg_gui_state->setValue(\"incoming_popup/y\", pos.y());\n}\n\nvoid IncomingCallPopup::move(int x, int y)\n{\n\tm_view->setPosition(QPoint(x, y));\n}\n\nvoid IncomingCallPopup::setCallerName(const QString& name)\n{\n\tQString text = tr(\"%1 calling\").arg(name);\n\tm_callerText->setProperty(\"text\", text);\n}\n\nvoid IncomingCallPopup::onAnswerClicked()\n{\n\temit answerClicked();\n\tm_view->hide();\n}\n\nvoid IncomingCallPopup::onRejectClicked()\n{\n\temit rejectClicked();\n\tm_view->hide();\n}\n\nvoid IncomingCallPopup::show()\n{\n\tm_view->show();\n}\n\nvoid IncomingCallPopup::hide()\n{\n\tm_view->hide();\n}\n\n"
  },
  {
    "path": "src/gui/incoming_call_popup.h",
    "content": "#ifndef T_INCOMING_CALL_POPUP_H\n#define T_INCOMING_CALL_POPUP_H\n\n#include <QQuickItem>\n#include <QQuickView>\n\nclass IncomingCallPopup : public QObject\n{\n\tQ_OBJECT\npublic:\n\texplicit IncomingCallPopup(QObject *parent = 0);\n\tvirtual ~IncomingCallPopup();\n\n\tvoid setCallerName(const QString& name);\n\tvoid show();\n\tvoid hide();\n\tvoid setVisible(bool v) { if (v) show(); else hide(); }\n    void move(int x, int y);\n\nprivate:\n\tvoid positionWindow();\nsignals:\n\tvoid answerClicked();\n\tvoid rejectClicked();\npublic slots:\n\tvoid onAnswerClicked();\n\tvoid onRejectClicked();\n\tvoid saveState();\nprivate:\n\tQQuickView* m_view;\n\tQQuickItem* m_callerText;\n};\n\n#endif // T_INCOMING_CALL_POPUP_H\n"
  },
  {
    "path": "src/gui/inviteform.cpp",
    "content": "#include \"inviteform.h\"\n//Added by qt3to4:\n#include <QCloseEvent>\n#include \"gui.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"sys_settings.h\"\n#include <QRegularExpression>\n#include <QValidator>\n#include <QRegularExpressionValidator>\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nInviteForm::InviteForm(QWidget *parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\tinit();\n}\n\nInviteForm::~InviteForm()\n{\n\tdestroy();\n}\n\nvoid InviteForm::init()\n{\n\tgetAddressForm = 0;\n\t\n\t// Set toolbutton icons for disabled options.\n\tsetDisabledIcon(addressToolButton, \":/icons/images/kontact_contacts-disabled.png\");\n\t\n\t// A QComboBox accepts a new line through copy/paste.\n\tQRegularExpression rxNoNewLine(\"[^\\\\n\\\\r]*\");\n\tinviteComboBox->setValidator(new QRegularExpressionValidator(rxNoNewLine, this));\n}\n\nvoid InviteForm::destroy()\n{\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n}\n\nvoid InviteForm::clear()\n{\n    inviteComboBox->clearEditText();\n\tsubjectLineEdit->clear();\n\thideUserCheckBox->setChecked(false);\n\tinviteComboBox->setFocus();\n}\n\nvoid InviteForm::show(t_user *user_config, const QString &dest, const QString &subject,\n\t\t      bool anonymous)\n{\n\t((t_gui *)ui)->fill_user_combo(fromComboBox);\n\t\n\t// Select from user\n\tif (user_config) {\n\t\tfor (int i = 0; i < fromComboBox->count(); i++) {\n            if (fromComboBox->itemText(i) ==\n\t\t\t    user_config->get_profile_name().c_str())\n\t\t\t{\n                fromComboBox->setCurrentIndex(i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tinviteComboBox->setEditText(dest);\n\tsubjectLineEdit->setText(subject);\n\thideUserCheckBox->setChecked(anonymous);\n\tQDialog::show();\n}\n\nvoid InviteForm::validate()\n{\n\tstring display, dest_str;\n\tt_user *from_user = phone->ref_user_profile(\n                fromComboBox->currentText().toStdString());\n\t\n\tui->expand_destination(from_user, \n                   inviteComboBox->currentText().trimmed().toStdString(),\n\t\t\t       display, dest_str);\n\tt_url dest(dest_str);\n\t\n\tif (dest.is_valid()) {\n\t\taddToInviteComboBox(inviteComboBox->currentText());\n\t\temit raw_destination(inviteComboBox->currentText());\n\t\temit destination(from_user, display.c_str(), dest, subjectLineEdit->text(),\n\t\t\t\t hideUserCheckBox->isChecked());\n\t\taccept();\n\t} else {\n\t\tinviteComboBox->setFocus();\n\t\tinviteComboBox->lineEdit()->selectAll();\n\t}\n}\n\n// Add a destination to the history list of inviteComboBox\nvoid InviteForm::addToInviteComboBox(const QString &destination)\n{\n    inviteComboBox->insertItem(0, destination);\n\tif (inviteComboBox->count() > SIZE_REDIAL_LIST) {\n\t\tinviteComboBox->removeItem(inviteComboBox->count() - 1);\n\t}\n}\n\n\nvoid InviteForm::reject()\n{\n\t// Unseize the line\n\t((t_gui *)ui)->action_unseize();\n\tQDialog::reject();\n}\n\nvoid InviteForm::closeEvent(QCloseEvent *)\n{\n\treject();\n}\n\nvoid InviteForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid InviteForm::selectedAddress(const QString &address)\n{\n\tinviteComboBox->setEditText(address);\n}\n\nvoid InviteForm::warnHideUser(void) {\n\t// Warn only once\n\tif (!sys_config->get_warn_hide_user()) return;\n\t\n\tQString msg = tr(\"Not all SIP providers support identity hiding. Make sure your SIP provider \"\n\t\t\t \"supports it if you really need it.\");\n    ((t_gui *)ui)->cb_show_msg(this, msg.toStdString(), MSG_WARNING);\n\t\n\t// Do not warn again\n\tsys_config->set_warn_hide_user(false);\n}\n"
  },
  {
    "path": "src/gui/inviteform.h",
    "content": "#ifndef INVITEFORM_UI_H\n#define INVITEFORM_UI_H\n#include \"ui_inviteform.h\"\n#include \"sockets/url.h\"\n#include \"getaddressform.h\"\n#include \"user.h\"\n#include \"phone.h\"\n#include <QDialog>\n\nclass t_phone;\nextern t_phone *phone;\n\nclass InviteForm : public QDialog, public Ui::InviteForm\n{\n\tQ_OBJECT\npublic:\n    InviteForm(QWidget *parent);\n\t~InviteForm();\npublic slots:\n\tvoid clear();\n\tvoid show( t_user * user_config, const QString & dest, const QString & subject, bool anonymous );\n\tvoid validate();\n\tvoid addToInviteComboBox( const QString & destination );\n\tvoid reject();\n\tvoid closeEvent( QCloseEvent * );\n\tvoid showAddressBook();\n\tvoid selectedAddress( const QString & address );\n\tvoid warnHideUser( void );\nsignals:\n\tvoid destination(t_user *, const QString &, const t_url &, const QString &, bool);\n\tvoid raw_destination(const QString &);\nprivate:\n\tvoid init();\n\tvoid destroy();\n\n\tGetAddressForm *getAddressForm;\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/inviteform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>InviteForm</class>\n  <widget class=\"QDialog\" name=\"InviteForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>592</width>\n        <height>203</height>\n      </rect>\n    </property>\n    <property name=\"sizePolicy\">\n      <sizepolicy>\n        <hsizetype>5</hsizetype>\n        <vsizetype>5</vsizetype>\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n      </sizepolicy>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Call</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"inviteTextLabel\">\n              <property name=\"text\">\n                <string>&amp;To:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>inviteComboBox</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"2\">\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>20</width>\n                  <height>23</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Vertical</enum>\n              </property>\n            </spacer>\n          </item>\n          <item row=\"2\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"subjectLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Optionally you can provide a subject here. This might be shown to the callee.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"2\">\n            <widget class=\"QToolButton\" name=\"addressToolButton\">\n              <property name=\"focusPolicy\">\n                <enum>Qt::TabFocus</enum>\n              </property>\n              <property name=\"text\">\n                <string/>\n              </property>\n              <property name=\"shortcut\">\n                <string>F10</string>\n              </property>\n              <property name=\"icon\">\n                <iconset>kontact_contacts.png</iconset>\n              </property>\n              <property name=\"toolTip\" stdset=\"0\">\n                <string>Address book</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Select an address from the address book.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"2\">\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>20</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Vertical</enum>\n              </property>\n            </spacer>\n          </item>\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"inviteComboBox\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>7</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n              <property name=\"editable\">\n                <bool>true</bool>\n              </property>\n              <property name=\"maxCount\">\n                <number>10</number>\n              </property>\n              <property name=\"insertPolicy\">\n                <enum>QComboBox::NoInsert</enum>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"fromComboBox\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>7</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The user that will make the call.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"subjectTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Subject:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>subjectLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"fromTextLabel\">\n              <property name=\"text\">\n                <string>&amp;From:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>fromComboBox</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <widget class=\"QCheckBox\" name=\"hideUserCheckBox\">\n              <property name=\"text\">\n                <string>&amp;Hide identity</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+H</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>181</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>20</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>91</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>inviteComboBox</tabstop>\n    <tabstop>subjectLineEdit</tabstop>\n    <tabstop>hideUserCheckBox</tabstop>\n    <tabstop>addressToolButton</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n    <tabstop>fromComboBox</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">qstring.h</include>\n    <include location=\"local\">sockets/url.h</include>\n    <include location=\"local\">ui_getaddressform.h</include>\n    <include location=\"local\">user.h</include>\n    <include location=\"local\">phone.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>InviteForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>InviteForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>addressToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>InviteForm</receiver>\n      <slot>showAddressBook()</slot>\n    </connection>\n    <connection>\n      <sender>hideUserCheckBox</sender>\n      <signal>clicked()</signal>\n      <receiver>InviteForm</receiver>\n      <slot>warnHideUser()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/kcontactstablemodel.cpp",
    "content": "/*\n    Copyright (C) 2018  Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"twinkle_config.h\"\n\n#include \"kcontactstablemodel.h\"\n\n\n// The signature of this constructor makes `data` an optional argument\nKContactsTableModel::KContactsTableModel(QObject *parent, const list<t_address_card>& data)\n\t: AddressTableModel(parent, data)\n{\n}\n\n// Display the appropriate header for the \"Remark\" column\nQVariant KContactsTableModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n\tif (role == Qt::DisplayRole && section == COL_ADDR_REMARK) {\n\t\treturn tr(\"Type\");\n\t} else {\n\t\treturn AddressTableModel::headerData(section, orientation, role);\n\t}\n}\n\n// Replace the current list with a new one\nvoid KContactsTableModel::loadContacts(const KContacts::Addressee::List& data, bool sip_only)\n{\n\tbeginResetModel();\n\tm_data.clear();\n\tfor (const KContacts::Addressee& contact : data)\n\t{\n\t\tfor (const KContacts::PhoneNumber& phone_number : contact.phoneNumbers())\n\t\t{\n\t\t\tif (!sip_only || phone_number.number().startsWith(\"sip:\"))\n\t\t\t\tm_data.append(addressCard(contact, phone_number));\n\t\t}\n\t}\n\tendResetModel();\n}\n\n// Convert a KContacts Addressee+PhoneNumber into a t_address_card\nt_address_card KContactsTableModel::addressCard(const KContacts::Addressee& contact, const KContacts::PhoneNumber& phone_number)\n{\n\tt_address_card address_card;\n\taddress_card.name_last = contact.realName().toStdString();\n\taddress_card.sip_address = phone_number.number().toStdString();\n\taddress_card.remark = phone_number.typeLabel().toStdString();\n\treturn address_card;\n}\n"
  },
  {
    "path": "src/gui/kcontactstablemodel.h",
    "content": "/*\n    Copyright (C) 2018  Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _KCONTACTSTABLEMODEL_H\n#define _KCONTACTSTABLEMODEL_H\n\n#include <KContacts/Addressee>\n#include <KContacts/AddresseeList>\n\n#include \"addresstablemodel.h\"\n\n\n// Variation of AddressTableModel, slightly tweaked for a list of KContacts:\n//\n//  - The list is optional at creation time\n//  - The list can be reloaded\n//  - The \"Remark\" column holds the typeLabel property of the phone number\n\nclass KContactsTableModel : public AddressTableModel\n{\npublic:\n\tKContactsTableModel(QObject *parent, const list<t_address_card>& data = list<t_address_card>());\n\tvirtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\n\tvoid loadContacts(const KContacts::Addressee::List& data, bool sip_only = false);\n\nprivate:\n\tstatic t_address_card addressCard(const KContacts::Addressee& contact, const KContacts::PhoneNumber& phone_number);\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/lang/twinkle_cs.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"cs\" sourcelanguage=\"en\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Adresářový záznam</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation>P&amp;oznámka:</translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation>Prostřední jméno.</translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Křestní jméno nebo jakékoliv jiné jméno. Bude třídícím klíčem.</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>&amp;Křestní jméno:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation>Políčko pro libovolné poznámky.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefon:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation>&amp;Prostřední jméno:</translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Telefonní číslo nebo SIP adresa kontaktu.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Příjmení kontaktu.</translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Příjmení:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Musíte zadat jméno.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>Musíte zadat telefonní číslo nebo SIP adresu.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation>Jméno</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation>Telefon</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation>Poznámka</translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Přihlášení</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation>user</translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>Uživatel, který má být přihlášen.</translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation>profile</translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Profil uživatele, pro kterého je vyžadováno přihlášení.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Uživatelský profil:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Uživatel:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Heslo:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Vaše přihlašovací heslo.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Vaše přihlašovací SIP jméno. Často je identické s vaším uživatelským SIP jménem, ale může se i lišit.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>Uži&amp;vatelské jméno:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation>Přihlášení vyžadováno pro realm:</translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation>realm</translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation>Realm, ke kterému se musíte přihlásit.</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Kontakt</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vybrat adresu z adresáře .</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefon:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Jméno vašeho kontaktu.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>Ukázat dostupno&amp;st</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Vyberte tuto volbu, pokud chcete vidět dostupnost vytvořeného kontaktu. Toto bude fungovat, pouze pokud váš poskytovatel tuto funkčnost nabízí.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Jméno:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>SIP adresa vašeho kontaktu.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Musíte zadat jméno.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Neplatné telefonní číslo.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Nepodařilo se uložit seznam kontaktů: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Dostupnost</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>neznámá</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>offline</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>online</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>požadavek odmítnut</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>není zveřejněna</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>zveřejnění selhalo</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>požadavek selhal</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Pravým kliknutím přidáte kontakt.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>Nepodařilo se získat přístup ke zvukové kartě</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>Nepodařilo se vytvořit UDP socket (RTP) na portu %1</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Nepodařilo se vytvořit vlákno pro nahrávání zvuku.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Nepodařilo se vytvořit vlákno pro přehrávání zvuku.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>lokální uživatel</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>vzdálený uživatel</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>chyba</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>neznámý</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>příchozí</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>odchozí</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Odhlášení</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>odhlásit všechna zařízení</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"obsolete\">Zrušit (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation></translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation>Twinkle - DTMF</translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Numerická klávesnice</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation>2</translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation>3</translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>Funkční klávesa A. Používaná zřídka.</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation>4</translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation>5</translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation>6</translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>Funkční klávesa B. Používaná zřídka.</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation>7</translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation>8</translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation>9</translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>Funkční klávesa C. Používaná zřídka.</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Hvězdička (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation>0</translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Křížek (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>Funkční klávesa D. Používaná zřídka.</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation>1</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Zavřít</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n</context>\n<context>\n    <name>FreeDeskSysTray</name>\n    <message>\n        <source>Show/Hide</source>\n        <translation type=\"obsolete\">Ukázat/Zminimalizovat</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"obsolete\">Ukončit</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Failed to create a UDP socket (SIP) on port %1</source>\n        <translation type=\"obsolete\">Chyba při otevírání UDP socketu (SIP) na portu %1</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Následující uživatelské profily používají stejnou SIP adresu %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Na jeden SIP účet si nemůžete současně aktivovat více profilů.</translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Twinkle nemůže najít žádné aktivní síťové rozhraní a používá nyní 127.0.0.1 jako svoji lokální IP adresu. Pokud se připojíte k nějaké síti později, musíte Twinkle spustit znovu, aby používal správnou IP adresu.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Linka %1: příchozí hovor pro %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Volání přepojeno uživatelem %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation>Linka %1: protistrana přerušila hovor.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation>Linka %1: hovor ukončen protistranou.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Linka %1: SDP odpověď protistrany není podporována.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Linka %1: žádná SDP odpověď protistrany.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation>Linka %1: Typ obsahu v odpovědi protistrany není podporován.</translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Linka %1: žádný ACK od protistrany, volání bude ukončeno.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Linka %1: žádný PRACK od protistrany, volání bude ukončeno.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Linka %1:  PRACK selhalo.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Linka %1:  chyba při pokusu o ukončení hovoru.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Linka %1: protistrana přijala hovor.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Linka %1: volání selhalo.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>Hovor může být přesměrován na:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation>Linka %1: hovor ukončen.</translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Linka %1: spojení navázáno.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Odpověď protistrany na dotaz o výpis schopností: %1 %2</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Schopnosti protistrany %1</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation>Akceptované &quot;body types&quot;:</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>neznámý</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Akceptovaná kódování:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Akceptované jazyky:</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Povolené požadavky:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation>Podporovaná rozšíření:</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation>žádný</translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Typ koncového zařízení:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation>Linka %1: chyba při pokusu o obnovení hovoru.</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, neúspěšné přihlášení: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, registrace úspěšná (platná na %2 sekund)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, registrace selhala: chyba STUN</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, úspěšné odhlášení: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation>%1, chyba při dotazu na registrace: %2 %3</translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: nejste registrován</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: jsou aktivní následující registrace</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: probíhá dotaz na registrace...</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Linka %1: požadavek přesměrováván na </translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Přesměrovat požadavek na: %1</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Linka %1: detekováno DTMF:  </translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation>neplatná událost DTMF (%1)</translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Linka %1: odesílá se DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation>Linka %1: protistrana nepodporuje události DTMF.</translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation>Linka %1: přijata notifikace.</translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Událost: %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Stav: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Důvod: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Průběh: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Linka %1: přepojení hovoru selhalo.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Linka %1: hovor byl přepojen.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Linka %1: přepojení hovoru stále probíhá.</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation>Nebudou přijímány další notifikace.</translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Linka %1: hovor se přepojuje na %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Přepojení vyžádal %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation>Linka %1:  Přepojení hovoru selhalo. Obnovuji původní hovor.</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Přesměrovávám hovor</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Uživatelský profil:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Uživatel:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Souhlasíte, aby hovor byl přesměrován na následující cíl?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation>Pokud nechcete, abyste byl nadále dotazován, musíte změnit nastavení v sekci protokol SIP v uživatelském profilu.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation>Přesměrovávám požadavek</translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation>Má být požadavek %1 přesměrován na následující destinaci?</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Přepojování hovoru</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation>Požadavek na přepojení hovoru přijat od:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Povolit přepojení hovoru k následujícímu cíli?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation>Info:</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Varování:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation>Kritické:</translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Detekce firewallu / NATu...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Přerušit</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Linka %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Pro potvrzení správného SAS hesla klikněte na symbol zámečku.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>Protistrana na lince %1 vypnula zašifrování.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Linka %1: SAS potvrzeno.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation>Linka %1: SAS potvrzení smazáno.</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Linka %1: hovor odmítnut.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Linka %1: hovor přesměrován.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Nepodařilo se zahájit konferenci.</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Ignorovat soubor se zámkem a přesto spustit?</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1, STUN dotaz selhal: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, STUN dotaz selhal.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, chyba stavu hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, odmítnut stav hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, hlasová schránka neexistuje.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, ukončen přenos z hlasové schránky. </translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, deregistrace selhala: %2 %3</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation>Přijat požadavek na přepojení hovoru.</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation>Pokud jsou toto uživatelé pro různé domény, potom aktivujte následující volbu ve vašem uživatelském profilu (Protokol SIP)</translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation>Použít doménové jméno k vytvoření jedinečné kontaktní hlavičky</translation>\n    </message>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>Selhalo vytvoření %1 socketu (SIP) na portu %2</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Akceptováno sítí</translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Selhalo uložení přílohy zprávy: %1</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation>Přepojil: %1</translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation>Nemohu otevřít webový prohlížeč: %1</translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation>Nastavte svůj webový prohlížeč v systémovém nastavení.</translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Výběr adresy</translation>\n    </message>\n    <message>\n        <source>Name</source>\n        <translation type=\"obsolete\">Jméno</translation>\n    </message>\n    <message>\n        <source>Type</source>\n        <translation type=\"obsolete\">Typ</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"obsolete\">Telefon</translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>Zobrazit pouze &amp;SIP adresy</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Pokud je aktivováno, budou zobrazeny pouze kontakty, které obsahují platnou SIP adresu (začínající na &lt;b&gt;sip:&lt;/b&gt;).</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>Aktua&amp;lizovat</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Znovu načíst seznam adres z KAddressbook.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation>&amp;KAddressBook</translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation>Tento seznam kontaktů pochází z &lt;b&gt;KAddressbook&lt;/b&gt;. Kontakty, které neobsahují telefonní číslo nebo SIP adresu zde nejsou uvedeny. K vytvoření nebo úpravě kontaktů použijte program KAddressbook.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>&amp;Místní adresář</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"obsolete\">Poznámka</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Kontakty v lokálním adresáři Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>Přid&amp;at</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Založit v místním adresáři nový kontakt.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>Smaza&amp;t</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Smazat vybraný kontakt z místního adresáře.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Upravit</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Upravit vybraný kontakt v místním adresáři.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation>Zdá se, že &lt;p&gt;&lt;b&gt;KAddressbook&lt;/b&gt; neobsahuje žádné záznamy s telefonními čísly, které by Twinkle mohl načíst. Použijte prosím tento program k úpravě nebo zanesení vašich kontaktů.&lt;/p&gt;\n&lt;p&gt;Druhou možností je používat místní adresář v Twinkle.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation>Smazat kontakt &apos;%1&apos; z místního adresáře?</translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation>Smazat kontakt</translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Název profilu</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Zadejte název profilu:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Název vašeho profilu&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nProfil obsahuje všechna uživatelská nastavení, např. uživatelské jméno SIP, heslo. Každý profil musí být pojmenován.\n&lt;br&gt;&lt;br&gt;\nPokud máte vícero účtů SIP, můžete si vytvořit několik profilů. Při spuštění vám Twinkle zobrazí seznam názvů profilů, ze kterých si můžete vybrat ty, které chcete spustit.\n&lt;br&gt;&lt;br&gt;\nKe snadnému zapamatování si profilu je možné použít k označení profilu uživatelské jméno, např. &lt;b&gt;example@example.com&lt;/b&gt;\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Nelze najít adresář .twinkle ve vašem domovském adresáři.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Profil s tímto názvem již existuje.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Přejmenovat profil %1 na:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - Seznam volání</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Čas</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>Příchozí/Odchozí</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>Protistrana</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Předmět</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Stav</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Podrobnosti hovoru</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Detaily k vybranému hovoru.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Zobrazit</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>&amp;Příchozí hovory</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Zaškrtněte tuto volbu pro zobrazení příchozích hovorů.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>&amp;Odchozí hovory</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Zaškrtněte tuto volbu pro zobrazení odchozích hovorů.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>&amp;Přijaté hovory</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Zaškrtněte tuto volbu pro zobrazení přijatých hovorů.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>&amp;Zmeškané hovory</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Zaškrtněte tuto volbu pro zobrazení zmeškaných hovorů.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>&amp;Pouze aktivní uživatelské profily</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Pokud je aktivováno, budou zobrazeny jen hovory, které byly provedeny pod tímto uživatelským profilem.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Smazat seznam</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Smazat celý protokol volání.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Poznámka:&lt;/b&gt; Tímto dojde ke smazání všech záznamů. Včetně těch, které nejsou zobrazeny dle zvolených parametrů v nastavení.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Zavřít toto okno.</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Volání zahájeno:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Na volání odpovězeno:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Hovor ukončen:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Délka hovoru:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Směr:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Od:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Kam:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Odpovědět na:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Přes:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Předmět:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Ukončil:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Status:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Zařízení protistrany:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Uživatelský profil:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>rozhovor</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Volat...</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Smazat</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Odp:</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>Za&amp;vřít</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>&amp;Volat</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Volat vybranou adresu.</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation>Počet hovorů:</translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation>###</translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation>Celkové trvání hovorů:</translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation>%1 volá</translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Volání</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>K&amp;am:</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Zde můžete volitelně zadat předmět hovoru, který může být zobrazen volanému.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vybrat adresu z adresáře.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa, na kterou chcete zavolat. Toto může být plnohodnotná SIP adresa jako &lt;b&gt;sip:example@example.com&lt;/b&gt; nebo jen uživatel nebo telefonní číslo. Pokud není zadaná úplná adresa, Twinkle ji doplní o doménové jméno aktuálního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Uživatel, který uskuteční hovor.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Předmět:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Od:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>&amp;Skrýt identitu volajícího</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;S touto volbou dáváte najevo vašemu SIP poskytovateli, že nechcete aby byly na protistranu zaslánu informace o vaší identitě. Např. vaše SIP adresa nebo telefonní číslo. Nicméně vaše IP adresa bude protistraně &lt;b&gt;vždy&lt;/b&gt; sdělena.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Upozornění: &lt;/b&gt;Tuto možnost nenabízejí všichni poskytovatelé!&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Ne všichni poskytovatelé SIP umožňují skrytí identity. Pokud funkci skutečně potřebujete, ujistěte se, že ji váš poskytovatel SIP nabízí.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation>Twinkle - Log</translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Obsah aktuálního logu (~/.twinkle/twinkle.log)</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Zavřít</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Vymazat</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Vyčistit okno s logem. Obsah samotného souboru s logem smazán &lt;b&gt;nebude&lt;/b&gt;.</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - textová zpráva</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Komu:</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>Uživatel, který pošle zprávu.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa uživatele, kterému má být zpráva poslána. To může být buď SIP adresa jako &lt;b&gt;sip:example@example.com&lt;/b&gt; nebo jen uživatel nebo telefonní číslo z úplné adresy.  Pokud se neuvede celá adresa, Twinkle doplní adresu doménou z uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výběr adresy z adresáře.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>&amp;Uživatelský profil:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Konverzace</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Sem napište zprávu a poté pro odeslání stiskněte &quot;Odeslat&quot;. </translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Odeslat</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Odeslat zprávu.</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Doručení selhalo</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Potvrzení o doručení</translation>\n    </message>\n    <message>\n        <source>Instant message toolbar</source>\n        <translation type=\"obsolete\">Lišta s instantními zprávami</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Odeslat soubor...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Odeslat soubor</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>obrázek je v náhledu zmenšen</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Otevřít s  %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Otevřít s...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Uložit přílohu jako...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Soubor již existuje. Chcete přepsat tento soubor?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Uložení přílohy selhalo.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 píše zprávu.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation>Velikost</translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>odesílání zprávy</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation>Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>&amp;Volané číslo:</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa, na kterou chcete zavolat. Může to být úplné SIP adresa jako &lt;b&gt;sip:example@example.com&lt;/b&gt; nebo jen uživatel nebo telefonní číslo z úplné adresy. Pokud není zadána celá adresa, Twinkle doplní chybějící část doménovým jménem z vašeho uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Uživatel, který uskuteční volání.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Uživatel:</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Volat</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Volat adresu.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vybrat adresu z adresáře.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation>Indikátor automatického příjmu hovoru.</translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation>Indikátor přesměrování hovoru.</translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation>Indikátor stavu nerušit.</translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation>Indikátor zmeškaných hovorů.</translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation>Stav registrace.</translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation>Stavové hlášky</translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Stav linky</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Linka &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation>Alt+1</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Klikněte pro přepnutí na linku 1.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Od:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Kam:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Předmět:</translation>\n    </message>\n    <message>\n        <source>Visual indication of line state.</source>\n        <translation type=\"obsolete\">Optické zobrazení stavu linky.</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation>idle</translation>\n    </message>\n    <message>\n        <source>Call is on hold</source>\n        <translation type=\"obsolete\">Hovor je podržen</translation>\n    </message>\n    <message>\n        <source>Voice is muted</source>\n        <translation type=\"obsolete\">Hovor je ztišen</translation>\n    </message>\n    <message>\n        <source>Conference call</source>\n        <translation type=\"obsolete\">Konferenční hovor</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Hovor bude přesměrován</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThe padlock indicates that your voice is encrypted during transport over the network.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nBoth ends of an encrypted voice channel receive the same SAS on the first call. If the SAS is different at each end, your voice channel may be compromised by a man-in-the-middle attack (MitM).\n&lt;/p&gt;\n&lt;p&gt;\nIf the SAS is equal at both ends, then you should confirm it by clicking this padlock for stronger security of future calls to the same destination. For subsequent calls to the same destination, you don&apos;t have to confirm the SAS again. The padlock will show a check symbol when the SAS has been confirmed.\n&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p&gt;\nSymbol zámečku se zobrazí pokud je volání přenášeno pomocí šifrování a není možné ho odposlouchávat.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nOběma účastníkům zašifrovaného hovoru bude při prvním kontaktu doručena tzv. SAS značka. Porovnáním této značky při každém dalším volání s toutéž protistranou lze zjistit, v případě že by se kód změnil, že dochází k odposlouchávání hovoru. Šlo by o tzv. &quot;man-in-the-middle attack&quot;.\n&lt;/p&gt;\n&lt;p&gt;\nPokud je SAS shodný na obou stranách, klikněte na ikonku zámečku. Lze se o tom přesvědčit dotazem na tuto značku u volaného. Při každém dalším volání na takto označený kontakt bude jeho identita automaticky ověřena a výsledek bude zobrazen ve formě zatržítka na symbolu zámečku.\n&lt;/p&gt;\n&lt;p&gt;Opětovným kliknutím na ikonku zámečku se zatržítkem dojde ke smazání ověřovací značky SAS a k její nové aktivaci je nutné provést její nové vygenerování.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation>sas</translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation>Krátký ověřovací řetězec</translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation>Audio kodek</translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation>0:00:00</translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Trvání hovoru</translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Linka &amp;2:</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation>Alt+2</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Klikněte pro přepnutí na linku 2.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Soubor</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Upravit</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>&amp;Hovor</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Vybrat linku</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>&amp;Registrace</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>&amp;Služby</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>&amp;Zobrazit</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>&amp;Nápověda</translation>\n    </message>\n    <message>\n        <source>Call Toolbar</source>\n        <translation type=\"obsolete\">Lišta volání</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Ukončit</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>&amp;Ukončit</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>O programu Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>O &amp;programu Twinkle</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Zavolat někomu</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation>F5</translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Přijmout příchozí hovor</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation>F6</translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation>Ukončit hovor</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Odmítnout příchozí hovor</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation>F8</translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Podržet hovor nebo pokračovat v podrženém hovoru</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Přesměrovat příchozí hovor bez přijetí hovoru</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Otevřít numerickou klávesnici pro hlasová menu</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Registrovat se</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>&amp;Registrovat se</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Deregistrovat se</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>&amp;Deregistrovat se</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Deregistrovat toto zařízení</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Zobrazit registrace</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>&amp;Zobrazit registrace</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Parametry protistrany</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Dotaz na parametry protistrany</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Nerušit</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>&amp;Nerušit</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Přesměrování hovoru</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>&amp;Přesměrování hovoru...</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Opakované vytáčení</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>O Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>O &amp;Qt</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Uživatelský profil</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>&amp;Uživatelský profil...</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation>Spojit dva hovory do konference</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Vypnout mikrofon</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Přesměrovat hovor</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Systémová nastavení</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>&amp;Systémová nastavení...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Odhlásit vše</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>&amp;Odhlásit vše</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Odhlásit všechna registrovaná zařízení</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Automaticky přijmout volání</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>&amp;Automaticky přijmout volání</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Log</translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation>&amp;Log...</translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>Seznam volání</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>&amp;Seznam volání...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation>F9</translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Změnit uživatele ...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Změnit uživatele ...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Aktivovat nebo deaktivovat uživatele</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>Co je toto?</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>Co je &amp;toto?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Linka 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Linka 2\n</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>volná</translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>vytáčím</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>pokus o navázání spojení, prosím čekejte</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>příchozí volání</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>navazuji hovor, prosím čekejte</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>navázáno</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>navázáno (čeká se na zvuk)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation>zavěšuji, prosím čekejte</translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>neznámý stav</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Hovor je zašifrován</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>Klikněte pro potvrzení SAS.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation>Klikněte pro smazání ověření SAS.</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Uživatel:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Hovor:</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Stav registrace:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>Registrován</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>Nezdařilo se</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>Neregistrován</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Není přihlášen žádný uživatel.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>Nerušit aktivováno pro:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Přsměrování aktivováno pro:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>Automatické přijetí hovorů aktivováno pro:</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>Nerušit není aktivní.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>Přesměrování volání není aktivováno.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>Automatické přijetí hovorů není aktivní.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>Nemáte žádné zmeškané hovory.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>1 zmeškaný hovor.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>%1 zmeškaných hovorů.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Kliknutím se otevře podrobný seznam hovorů.</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Spouštím uživatelské profily...</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Následující uživatelské profily používají stejné uživatele %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Různé profily musejí mít odlišné uživatele.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation>Byl změněn SIP UDP port. Toto nastavení bude aktivní až při příštím spuštění programu Twinkle.</translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation>Asistované přesměrování</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Skrýt identitu</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Kliknutím se zobrazí registrace.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 nová, 1 stará zpráva</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 nové, %2 staré zprávy</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>1 nová zpráva</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 nových zpráv</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>1 stará zpráva</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 starých zpráv</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Přijatých zpráv</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Žádné zprávy</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Stav hlasové schránky:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Chyba</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Neznámý</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Kliknutím se vstoupí do hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Kliknutím aktivovat / deaktivovat</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Kliknutím aktivovat</translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation>není poskytováno</translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation>Předtím než může být hlasová schránka používána, je nutné nastavit její adresu v uživatelském profilu.</translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>Hlasovou schránku nelze otevřít. Linka je obsazená.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation>Adresa hlasové schránky &quot;%1&quot; je neplatná. Zkontrolujte nastavení ve vašem uživatelském profilu.</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Volat</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <comment>call menu text</comment>\n        <translation type=\"obsolete\">Volat (&amp;Call)...</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Odpovědět</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Odpovědět</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Zavěsit</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Zavěsit (&amp;Bye)</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Odmítnout</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Odmítnout</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Podržet</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Podržet</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Přesměrovat</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Přesměrovat...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">DTMF</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;DTMF...</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Parametry protistrany...</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Opakovat</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Opakovat</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Konference</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Konference</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Ztišit</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Ztišit</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Zprostředkovat</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Zprostředkovat...</translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation>Zobrazení čekajících zpráv.</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Hlasová schránka</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>&amp;Hlasová schránka</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation>Vstoupit do hlasové schránky</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Seznam kontaktů</translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>&amp;Zpráva</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Msg</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>Textová &amp;zpráva...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Textová zpráva</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Volat...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>&amp;Upravit...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Smazat</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>O&amp;ffline</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>&amp;Online</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>&amp;Změnit dostupnost</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Přidat kontakt...</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Selhalo uložení seznamu kontaktů: %1</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation>Je možné vytvořit oddělený seznam kontaktů pro každý uživatelský profil. Dostupnost vašich kontaktů a vlastní dostupnost lze zjistit a využívat jen pokud toto váš poskytovatel podporuje.</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>&amp;Seznam kontaktů</translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation>&amp;Stavové hlášky</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation>Příručka</translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation>&amp;Příručka</translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation>Volat</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation>Přijm&amp;out</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation>Přijmout</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation>&amp;Zavěsit</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation>Zavěsit</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation>&amp;Odmítnout</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Odmítnout</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation>&amp;Podržet</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation>Podržet</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation>&amp;Přesměrovat...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation>Přesměrovat</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation>&amp;DTMF...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation>&amp;Parametry protistrany...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation>&amp;Opakovat</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation>Opakovat</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation>&amp;Konference</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation>Konference</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation>&amp;Ztišit</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation>Ztišit</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation>Přep&amp;ojit...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation>Přepojit</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - konverze tel. čísla</translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Hledaný výraz:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Nahradit:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation>Formátovací řetězec (styl Perlu) s nahrazeným číslem.</translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation>Regulární výraz (styl Perlu) pro nahrazované číslo.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>Hledaný výraz nesmí být prázdný.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>Nahrazený text nesmí být prázdná.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Neplatný regulární výraz.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Přesměrování</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Příchozí hovor přesměrovat na</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Pro přesměrování volání lze zadat max. 3 čísla. Pokud nebude hovor přijat prvním cílem, dojde k pokusu o přesměrování na druhý cíl atd.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3. cíl:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2. cíl:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1. cíl:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výběr adresy z adresáře.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - výběr síťového rozhraní</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Vyberte síťové rozhraní / IP adresu, kterou chcete použít:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>Na vašem počítači je k dispozici více IP adres. Vyberte tu, pod kterou je váš počítač dostupný z internetu nebo  pokud jste připojení na router vaši IP adresu v lokální síti. Tuto adresu bude Twinkle používat uvnitř datových SIP paketů jako adresu odesilatele.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>&amp;Nastavit jako výchozí IP adresu</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Nastavit vybranou IP adresu jako výchozí. Při příštím startu Twinkle bude automaticky zvolena tato adresa.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>Nastavit jako &amp;výchozí rozhraní</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>Nastavit vybrané síťové rozhraní jako výchozí. Při příštím startu Twinkle bude toto rozhraní automaticky zvoleno.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Výchozí nastavení je možné změnit kdykoliv později v systémovém nastavení.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle - výběr uživatelského profilu</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Zvolte uživatelské profily, které mají být aktivovány:</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation type=\"obsolete\">Uživatelský profil</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Označte uživatelský profil, se kterým by měl Twinkle pracovat a stiskněte &quot;Spustit&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;New</source>\n        <translation type=\"obsolete\">&amp;nový</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Pomocí editoru profilu založit nový uživatelský profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>Průvodc&amp;e</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Vytvořit nový uživatelský profil pomocí průvodce.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>U&amp;pravit</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Upravit vybraný uživatelský profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Smazat</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Smazat vybraný uživatelský profil.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>&amp;Přejmenovat</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Přejmenovat označený profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>Nastavit jako &amp;výchozí</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Nastavit vybrané profily jako výchozí. Twinkle je použije automaticky při příštím startu.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Spustit</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Spustit twinkle s označenými uživatelskými profily.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>S&amp;ystémová nastavení</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Upravit systémová nastavení.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Předtím než můžete Twinkle začít používat, musíte založit aspoň jeden uživatelský profil.&lt;br&gt;Klikněte OK pro založení nového profilu.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"obsolete\">&lt;html&gt;Pro vytvoření uživatelského profilu můžete použít editor profilu. Tento vám umožní změnit veškerá nastavení týkající se  SIP protokolu, RTP jakož i dalších parametrů programu.&lt;br&gt;&lt;br&gt;Popřípadě použijte Wizard pro rychlé a jednoduché nastavení základních parametrů ke zvolenému uživatelskému profilu. Wizard se vás dotáže jen na nejzákladnější údaje, které vám váš SIP poskytovatel dá při zaregistrování. Pro některé poskytovatele vám budou dokonce některé z těchto údajů přímo wizardem nabídnuty. I přesto, že profil založíte pomocí wizardu ho budete moci později upravovat pomocí editoru.&lt;br&gt;&lt;br&gt;Nápovědu získáte kdekoliv v Twinkle stiskem klávesové kombinace &quot;Shift + F1&quot;, přes kontextovou nápovědu pomocí stisku pravého tlačítka na myši nebo stiskem na symbol &quot;?&quot; v pravém horním rohu okna.&lt;br&gt;&lt;br&gt;Vyberte si jakým způsobem má být uživatelský profil založen.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;V dalším kroku můžete upravit systémová nastavení. Můžete tak učinit i kdykoliv později.&lt;br&gt;&lt;br&gt;Klikněte na OK pro přístup k systémovým nastavením.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>Nevybrali jste k použití žádný uživatelský profil.\nVyberte prosím aspoň jeden profil.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Opravdu smazat uživatelský profil %1 ?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Smazat profil</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Chyba při mazání profilu.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Chyba při přejmenování profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Výchozí nastavení je možné kdykoliv smazat nebo změnit v systémovém nastavení. &lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Nelze nalézt adresář .twinkle ve vašem domovském adresáři.</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>&amp;Editor profilu</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation>Vytvořit profil</translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation>Ed&amp;itace</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation>Upravit profil</translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation>Profil při spuštění</translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation></translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Výběr uživatele</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>Vybrat &amp;vše</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>O&amp;dznačit vše</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation type=\"obsolete\">Uživatel</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Přihlásit se</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Vybrat uživatele k přihlášení.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Odhlásit se</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Vybrat uživatele k odhlášení.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Odhlásit všechna zařízení</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Vybrat uživatele, u kterého mají být odhlášena všechna zařízení.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Nerušit</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Vybrat uživatele, pro který má být aktivován režim &quot;Nerušit&quot;.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Automaticky přijmout volání</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Vybrat uživatelský profil, pro který má být aktivován režim &quot;Automaticky přijmout volání&quot;.</translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Odeslat soubor</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Výběr souboru k odeslání.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Soubor:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Předmět:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Soubor neexistuje.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Odeslat soubor...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation>Twinkle - Přesměrování hovoru</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Uživatel:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation>Existují 3 způsoby přesměrování volání:&lt;p&gt;\n&lt;b&gt;Nepodmíněné:&lt;/b&gt; přesměrovat všechny hovory\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Obsazeno:&lt;/b&gt; přesměrovat hovor, pokud jsou obě linky obsazené\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Nepřijímá:&lt;/b&gt; přesměrovat volání po uplynutí čekací prodlevy\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation>N&amp;epodmíněné</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation>&amp;Přesměrovat všechna volání</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt-R</translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation>Aktivovat službu přesměrovat všechna volání.</translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation>Přesměrovat na</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Mohou být zadány až 3 cíle pro přesměrování volání. Pokud nebude hovor přebrán prvním cílem, bude použit druhý, popřípadě třetí.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3. cíl:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2. cíl:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1. cíl:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výběr adresy z adresáře.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Obsazeno</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation>&amp;Přesměrovat volání, pokud jsou všechny linky obsazené</translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation>Aktivovat přesměrovaní, pokud je linka nedostupná.</translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>&amp;Neodpovídá</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation>&amp;Přesměrovat volání, pokud hovor nepřijímám</translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation>Aktivovat službu &quot;Přesměrovat v nepřítomnosti&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Uložit změny.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Neukládat provedené změny a zavřít okno.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>Neplatná cílová adresa.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Systémová nastavení</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Obecné</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Audio</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Vyzváněcí tóny</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Síť</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Log</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Vybrat skupinu vlastností, u které chcete změnit nastavení.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Zvuková karta</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Vyberte zvukovou kartu pro přehrávání vyzváněcího tónu příchozího volání.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Vyberte zvukovou kartu, ke které máte připojen mikrofon.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Vyberte zvukovou kartu pro sluchátka nebo reproduktor.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Sluchátka/reproduktor:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Vyzváněcí tón:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Jiné zařízení:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Mikrofon:</translation>\n    </message>\n    <message>\n        <source>When using ALSA, it is not recommended to use the default device for the microphone as it gives poor sound quality.</source>\n        <translation type=\"obsolete\">Při použití ALSA rozhraní není doporučeno mít nastaveno pro mikrofon &quot;standardní zařízení&quot;. Může to být příčinou špatné kvality zvuku.</translation>\n    </message>\n    <message>\n        <source>Reduce &amp;noise from the microphone</source>\n        <translation type=\"obsolete\">Speciální potlačení &amp;rušení z mikrofonu</translation>\n    </message>\n    <message>\n        <source>Recordings from the microphone can contain noise. This could be annoying to the person on the other side of your call. This option removes soft noise coming from the microphone.\nThe noise reduction algorithm is very simplistic. Sound is captured as 16 bits signed linear PCM samples. All samples between -50 and 50 are truncated to 0.</source>\n        <translation type=\"obsolete\">Zvuk z mikrofonu může obsahovat rušení. Tato volba se ho snaží odstranit. Zavedena byla po zkušenosti s vadnými A/D převodníky u některých Provider Gateways.\nAlgoritmus je velmi jednoduchý. Zvuk je navzorkován jako 16 bitový PCM vzorek a poté jsou všechny vzorky s hodnotou mezi -50 až 50 nastaveny na 0.</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Pokročilé nastavení</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation>Velikost &amp;fragmentů OSS:</translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation>Nastavení přehrávací periody ALSA ovlivňuje zpoždění zvuku na zvukové kartě. Při problémech s vypadáváním nebo přeskakováním zvuku zkuste jinou hodnotu.</translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation>Velikost ALSA &amp;periody přehrávání:</translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation>Velikost &amp;ALSA periody nahrávání:</translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation>Velikost fragmentu OSS ovlivňuje zpoždění zvuku na zvukové kartě. Při problémech s vypadáváním nebo přeskakováním zvuku zkuste jinou hodnotu.</translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation>Velikost periody nahrávání ALSA ovlivňuje zpoždění zvuku. Pokud si protistrana stěžuje na časté výpadky zvuku, zkuste použít jinou hodnotu.</translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>&amp;Maximální velikost logu:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation>Maximální velikost souboru s logem v MB. Při dosažení této velikosti je twinkle.log přejmenováno na twinkle.old. Uchovává se jen jeden starý soubor s logem.</translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation>MB</translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation>Zapsat &amp;hlášky pro ladění</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>ALt+H</translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation>Aktivuje zápis &quot;debug&quot; výstupů.</translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>Zapsat &amp;SIP hlášky</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation>Aktivuje zápis SIP stavů.</translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>Zapsat S&amp;TUN hlášky</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation>Aktivuje zápis STUN stavů.</translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation>Zapsat hlášky ohl&amp;edně paměti</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation>Aktivuje zápis protokolů o správě paměti.</translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Systémová lišta</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Při spuštění vytvořit &amp;ikonu v systémové liště</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation>Povolí ikonu Twinkle v systémové liště. Ta se vytváří při spuštění Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation>Skrýt v systémové liště při zavření &amp;hlavního okna</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation>Povolte, pokud chcete, aby se Twinkle při zavření hlavního okna skryl v systémové liště.</translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Start programu</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, this IP address will be automatically selected. This is only useful when your computer has multiple and static IP addresses.</source>\n        <translation type=\"obsolete\">Zde uvedená IP adresa bude automaticky vybrána při příštím startu programu. To má smysl jen pokud má tento počítač vícero síťových připojení a jen jedno je s přístupem do internetu.</translation>\n    </message>\n    <message>\n        <source>Default &amp;IP address:</source>\n        <translation type=\"obsolete\">Standardní &amp;IP adresa:</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, the IP address of this network interface be automatically selected. This is only useful when your computer has multiple network devices.</source>\n        <translation type=\"obsolete\">Pokud má tento počítač vícero síťových připojení, je zde možné uvést, které má být zvoleno. Při příštím startu programu již na toto nebude dotazováno.</translation>\n    </message>\n    <message>\n        <source>Default &amp;network interface:</source>\n        <translation type=\"obsolete\">Standardní síťové &amp;rozhraní:</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation>Spustit zminimalizované do &amp;systémové lišty</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation>Při příštím startu se Twinkle ihned ukryje v systémové liště. Pro nejlepší výsledky také zvolte výchozí profil.</translation>\n    </message>\n    <message>\n        <source>Default user profiles</source>\n        <translation type=\"obsolete\">Standardní uživatelský profil</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation>Pokud vždy používáte ty samé profily, pak je zde můžete označit jako výchozí. Při příštím spuštění Twinkle nebudete na výběr profilů dotazováni. Automaticky budou spuštěny výchozí profily.</translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Služby</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>Čekající hovor&amp;y</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation>S funkcí čekajících hovorů jsou příchozí hovory přijímány, jen pokud je využita jen jedna linka. Pokud tuto funkci zakážete, pak budou odmítány hovory, i když je používána jen jediná linka.</translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation>Zavěsit o&amp;bě linky při ukončování konferenčního hovoru.</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation>Pokud je aktivováno budou při &quot;zavěšení&quot; zavěšeny obě linky. Jinak dojde jen k ukončení volání na aktivní lince a je možné pokračovat v hovoru na druhé lince.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation>&amp;Maximální počet záznamů v seznamu volání:</translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation>Délka seznamu volání bude omezena na zde zadaný počet záznamů. Starší záznamy budou automaticky odstraněny.</translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation>Při hovoru zobrazit &amp;automaticky hlavní okno</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation>Pokud je hlavní okno programu skryto, bude při příchozím hovoru po zadaném počtu sekund automaticky zobrazeno.</translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation>Čas v sekundách, po kterém bude hlavní okno programu zobrazeno.</translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>sekund</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving SIP messages.</source>\n        <translation type=\"obsolete\">UDP Port pro SIP Protokoll. Standardně je to 5060. Nicméně váš VoIP provider může vyžadovat jiný port.</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>&amp;RTP port:</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation>První port přes který běží datový přenos hovoru. Současně vedený hovor na druhé lince používá port o 2 čísla vyšší. Zprostředkování hovoru potom další 2 porty. Např. 1. linka: 8000(+8001), 2. linka: 8002(+8003), Zprostředkování: 8004(+8005). Standardně je to většinou 8000 nebo 5004. Je to však závislé od konkrétního poskytovatele VoIP připojení. Při větším množství SIP telefonů připojených na jedno internetové připojení, potřebuje každý vyhrazenou vlastní skupinu portů! Tedy druhý telefon např. 8006 a výše.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP UDP port:</source>\n        <translation type=\"obsolete\">&amp;SIP UDP port:</translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Vyzváněcí tón</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>Při příchozím volání &amp;přehrávat vyzváněcí tón</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Indikuje, zda má při příchozích hovorech znít vyzvánění.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>&amp;Výchozí vyzváněcí tón</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Spustí výchozí vyzváněcí tón při příchozím volání.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>Individ&amp;uální vyzváněcí tón</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Při příchozím volání přehrávat vlastní vyzváněcí tón.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation>Zadejte název .wav souboru s vlastním vyzváněcím tónem.</translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation>Tón pro signalizaci vyzvánění u volaného</translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation>Přehrát tón pro vyzvánění u vo&amp;laného, pokud telefonní síť žádný tón neposkytuje</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Přehrát tón pro vyzvánění u volaného, pokud telefonní síť žádný takový tón neposkytuje.&lt;/p&gt;\n\n&lt;p&gt;Přítomnost tohoto tónu závisí na vašem poskytovateli.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation>Výchozí tón pro vyzvánění u volan&amp;ého</translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation>Přehrávat výchozí tón vyzvánění u volaného.</translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation>Vla&amp;stní tón vyzvánění u volaného</translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation>Použít vlastní vyzváněcí tón u volaného.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation>Zadat jméno .wav souboru pro váš vlastní tón vyzvánění u volaného.</translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation>Podle čísla &amp;zjistit jméno volajícího</translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation>P&amp;řepisovat jméno volajícího</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation>Volající protistrana může posílat vlastní jméno. Při aktivaci této volby bude zobrazované jméno vzato z vašeho lokálního adresáře.</translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Hledat &amp;fotografii volajícího</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation>Hledat fotografii ve vašem lokálním adresáři a zobrazit při příchozím volání.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Přijmout změny a uložit.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Vrátit zpět všechny změny a zavřít okno.</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default IP address combo</comment>\n        <translation type=\"obsolete\">auto</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default network interface combo</comment>\n        <translation type=\"obsolete\">auto</translation>\n    </message>\n    <message>\n        <source>Either choose a default IP address or a default network interface.</source>\n        <translation type=\"obsolete\">Vybrat buď standardní IP adresu nebo standardní síťové rozhraní.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Vyzváněcí tóny</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Vybrat vyzváněcí tón</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Tón vyzvánění u volaného</translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation>Vybrat tón vyzvánění u volaného</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>Ověřit nasta&amp;vení zvuku před použitím</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation>&lt;p&gt;Twinkle ověřuje zvuková zařízení před použitím, aby se nestalo, že při hovoru nebude fungovat zvuk.\n&lt;p&gt;Pokud je aktivováno, Twinkle při startu zkontroluje zdali je zadané audio zařízení přístupné.&lt;/p&gt;\n&lt;p&gt;Pokud se zdá, že mikrofon nebo reproduktory/sluchátko nejsou v pořádku, bude zobrazeno varovné hlášení a žádné volání nebude dovoleno.&lt;/p&gt;\n&lt;p&gt;Rovněž v případě, že je detekováno příchozí volání a audio zařízení není v pořádku, zobrazí se varování a hovor nebude možné přijmout.\n</translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation>Při příchozím volání se Twinkle bude pokoušet najít k volajícímu v lokálním adresáři odpovídající záznam. Pokud se to podaří, bude jeho jméno zobrazeno.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Vybrat soubor s vyzváněcím tónem.</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Vybrat soubor vyzváněcího tónu u protistrany.</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation>Maximálně povolená velikost příchozí SIP zprávy přes UDP v bajtech (0-65535).</translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>&amp;SIP port:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation>Max. velikost SIP zprávy (&amp;TCP):</translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation>UDP/TCP port použitý pro odesílání a přijímání SIP zpráv.</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation>Max. velikost SIP zprávy (&amp;UDP):</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation>Maximálně povolená velikost příchozí SIP zprávy přes TCP v bajtech (0-4294967295).</translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation>Příkaz pro webový prohlížeč:</translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation>Příkaz pro spuštění webového prohlížeče. Pokud ponecháte toto pole prázdné, Twinkle se pokusí zjistit váš výchozí prohlížeč.</translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation>512</translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation>1024</translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation>Tip: při praskajícím zvuku s PulseAudio nastavte nejvyšší periodu pro přehrávání ALSA.</translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation>Povolit OSD během hovoru</translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Přijmout</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Odmítnout</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation>Příchozí hovor</translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Parametry protistrany</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Od:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Dotázat se na parametry protistrany</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Adresa:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa nebo číslo protistrany jejíž parametry se mají zjistit (OPTION request). Může to být úplná SIP adresa jako &lt;b&gt;sip:example@example.com&lt;/b&gt; nebo jen uživatel nebo telefonní číslo z úplné adresy. Pokud není adresa kompletní, Twinkle doplní adresu o doménu z vašeho profilu.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výběr adresy z adresáře.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Přepojení</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Přepojit hovor na</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Na:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa nebo číslo protistrany, na kterou má být hovor přepojen. Může to být úplná SIP adresa jako &lt;b&gt;sip:example@example.com&lt;/b&gt; nebo jen uživatel nebo telefonní číslo z úplné adresy. Pokud není zadána plná SIP adresa, Twinkle doplní adresu o doménu z uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresář</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výběr adresy z adresáře.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Type of transfer</source>\n        <translation type=\"obsolete\">Typ přesměrování</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>&amp;Slepé přesměrování</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation>Hovor přímo přesměrovat na nového účastníka, aniž by byl nejdřív kontaktován.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation>Asistované p&amp;řepojení</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation>Před přesměrováním hovoru nejprve kontaktovat nového účastníka a volajícího ohlásit.</translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation>Přesměrovat na jinou &amp;linku</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation>Přepojit hovor na aktivní lince s hovorem na druhé lince.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Chyba při vytvoření logového souboru &quot;%1&quot;.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Nelze otevřít soubor %1 ke čtení</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Chyba systému souborů při čtení souboru %1.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Nelze otevřít soubor %1 pro zápis</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Chyba systému souborů při zápisu do %1.</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>Příliš velký počet socketových chyb.</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Vytvořeno s podporou pro:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>Pomocní vývojáři:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Tento program obsahuje softwarové části těchto třetích stran:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation>* GSM kodek od Jutta Degener a Carsten Bormann, University of Berlin</translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation>* G.711/G.726 kodeky od Sun Microsystems (public domain)</translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation>* iLBC implementace RFC 3951 (www.ilbcfreeware.org)</translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation>* Části ze STUN projektu na http://sourceforge.net/projects/stun</translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation>* Části z libsrv na http://libsrv.sourceforge.net/</translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation>Pro RTP jsou linkovány následující dynamické knihovny:</translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Do češtiny přeložil Luboš Doležel</translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Adresář &quot;%1&quot; neexistuje.</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Soubor %1 nelze otevřít.</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>&quot;%1&quot; není vaším domovským adresářem.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Adresář %1 (%2) neexistuje.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Adresář &quot;%1&quot; nelze vytvořit.</translation>\n    </message>\n    <message>\n        <source>Lock file %1 already exist, but cannot be opened.</source>\n        <translation type=\"obsolete\">Zamykací soubor &quot;%1&quot; již existuje, ale nemůže být otevřen.</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>%1 již běží.\nZamykací soubor &quot;%2&quot; již existuje.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>Nelze vytvořit &quot;%1&quot; .</translation>\n    </message>\n    <message>\n        <source>Cannot write to %1 .</source>\n        <translation type=\"obsolete\">Nelze zapisovat do &quot;%1&quot;.</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Syntaktická chyba v souboru &quot;%1&quot; .</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Chyba při zálohování %1 do %2</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>neznámý název (zařízení je zaneprázdněné)</translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Výchozí zařízení</translation>\n    </message>\n    <message>\n        <source>Anonymous</source>\n        <translation>Anonymní</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Varování:</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation>Přesměrování hovoru - %1</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>Zvuková karta nefunguje v režimu full duplex.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>Nelze nastavit velikost bufferu na zvukové kartě.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Zvukové zařízení neze nastavit na %1 kanálů.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation>Audio zařízení nelze nastavit na 16 bitové nahrávání.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation>Audio zařízení nelze nastavit na 16 bitové přehrávání.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation>Nelze nastavit vzorkovací frekvenci audio zařízení na %1</translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation>Chyba při otevírání ovladače ALSA</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation>Nelze otevřít ovladač ALSA pro přehrávání PCM</translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>Nelze vyhledat adresu STUN serveru: %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>Nacházíte se za symetrickým NATem.\nSTUN nebude fungovat.\nJe nutné nastavit v uživatelském profilu veřejnou IP adresu\na na vašem NATu namapovat (UDP) porty.</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>veřejná IP: %1 --&gt; privátní IP: %2 (signalizace SIP)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>veřejná IP: %1 - %2 --&gt; privátní IP: %3 - %4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>Nelze se připojit na STUN server: %1</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Port %1 (signalizace SIP)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>Detekce typu NATu pomocí STUN selhala.</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Pokud se nacházíte za firewallem, je nutné otevřít následující UDP porty.</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Porty %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation>Zvukové zařízení &quot;%1&quot; pro vyzváněcí tón není přístupné.</translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>Zvukové zařízení &quot;%1&quot; pro reproduktory/sluchátka není přístupné.</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>Zvukové zařízení %1 pro mikrofon není přístupné.</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>Nelze otevřít ovladač ALSA pro nahrávání PCM</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Nelze přijímat příchozí TCP spojení.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Selhalo vytvoření souboru %1</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Selhal zápis dat do souboru %1</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Selhalo odeslání zprávy.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation>Nemohu uzamknout %1.</translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Uživatelský profil</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Uživatelský profil:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Vyberte profil, který chcete upravit.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Uživatel</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>SIP server</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP audio</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>Protokol SIP</translation>\n    </message>\n    <message>\n        <source>NAT</source>\n        <translation type=\"obsolete\">NAT (překlad adres)</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Formát adresy</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation>Časovače</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Vyzváněcí tóny</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation>Skripty</translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Zabezpečení</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Vybrat oblast, ve které mají být provedeny změny nastavení.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Akceptovat a uložit změny v nastavení.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Vrátit zpět všechny změny a zavřít okno.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>Účet SIP</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>Uživatelské &amp;jméno*:</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation type=\"vanished\">&amp;Doména*:</translation>\n    </message>\n    <message>\n        <source>Or&amp;ganization:</source>\n        <translation type=\"vanished\">Or&amp;ganizace:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Uživatelské jméno, které vám bylo přiděleno vaším poskytovatelem, je první částí vaší úplné SIP adresy &lt;b&gt;uzivatel&lt;/b&gt;@domain.com.\nMůže se jednat o telefonní číslo.\n&lt;br&gt;&lt;br&gt;\nToto pole je povinné.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Doménová část vaší adresy SIP, uzivatel@&lt;b&gt;domain.com&lt;/b&gt;. Místo skutečné domény zde může být také název hostitele nebo IP adresa vaší &lt;b&gt;SIP proxy&lt;/b&gt;\nPro přímé volání mezi IP adresami se zde uvede název hostitele nebo IP, pod kterým je váš počítač dosažitelný v internetu.\n&lt;br&gt;&lt;br&gt;\nToto pole je povinné.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>Zde je možné uvést jméno vaší organizace. Pokud někomu voláte, může být tento údaj zobrazen protistraně.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Vaše jméno nebo přezdívka. Tato položka je volané protistraně zobrazována jako jméno volajícího.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>Vaše &amp;jméno:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>Přihlašovací údaje SIP</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation>&amp;Realm:</translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation>Přihlašovací &amp;jméno:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Heslo:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation>Hodnota &quot;realm&quot; pro přihlášení. Tento údaj vám musí poskytnout váš poskytovatel SIP. Pokud zůstane pole prázdné, pak se Twinkle pokusí o přihlášení s vaším uživatelským jménem a heslem při libovolném realmu.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Vaše přihlašovací SIP jméno. Je často identické s vaším uživatelským SIP jménem. Může se ale také lišit.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Vaše přihlašovací heslo.</translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation>Registrar (přihlašovací server) </translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation>&amp;Registrar:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation>Název hostitele, doménové jméno nebo IP adresa přihlašovacího serveru. Pokud používáte odchozí proxy, která je stejná jako váš přihlašovací server, pak je možné toto pole ponechat prázdné a vyplnit jen adresu odchozí proxy.</translation>\n    </message>\n    <message>\n        <source>&amp;Expiry:</source>\n        <translation type=\"vanished\">&amp;Platnost:</translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation>Doba platnosti přihlášení v sekundách, kterou si Twinkle vyžádá.</translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>sekund</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Při&amp;hlásit při spuštění</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation>Značí, zda se má Twinkle automaticky přihlásit při spuštění tohoto profilu. Toto byste měli zakázat, pokud chcete přímé volání mezi IP adresami bez SIP proxy.</translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation>Odchozí proxy</translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>Po&amp;užít odchozí-proxy</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation>Značí, zda má Twinkle používat odchozí proxy. Pokud se používá odchozí proxy, pak jsou všechny požadavky SIP posílány na tuto proxy. Bez odchozí proxy se Twinkle pokusí použít název hostitele z adresy SIP a směrovat hovor přímo tam.</translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>Odchozí &amp;proxy:</translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation>Poslat in-&amp;dialog dotazy na proxy</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation>Požadavky SIP se běžně posílají na adresu v kontaktních hlavičkách vyměněných při sestavování hovoru. Pokud je toto pole zaškrtnuté, pak je tato adresa ignorována a všechny žádosti se rovněž zasílají na odchozí proxy.</translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation>Neposílat žá&amp;dosti SIP na proxy, pokud lze cíl spojit lokálně.</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation>Pokud je aktivováno, pokusí se nejprve Twinkle najít k cílové adrese odpovídající IP adresu a poslat SIP dotaz přímo tam. Pokud se nepodaří IP adresu zjistit, je dotaz poslán na proxy. (Upozornění: in-dialog žádosti budou v tomto případě posílány na proxy, jen pokud je aktivována i předchozí volba.)</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Doménové jméno, IP adresa nebo jméno vaší odchozí proxy.</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation>Ko&amp;deky</translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation>Kodeky</translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Dostupné kodeky:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Seznam dostupných, kodeků.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Přesunout kodek ze seznamu dostupných kodeků do seznamu aktivních kodeků.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Deaktivovat kodek.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Aktivní kodeky:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation>Seznam aktivních kodeků. Tyto budou použity při navázání spojení s protistranou. Pořadí zde uvedených kodeků je zároveň pořadím v jakém budou kodeky upřednostňovány.</translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation>Přesunout tento kodek směrem nahoru v seznamu kodeků, tzn. zvýšit jeho prioritu.</translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation>Přesunout tento kodek směrem dolů v seznamu kodeků, tzn. snížit jeho prioritu.</translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation>&amp;G.711/G.726 velikost payloadu:</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation>Upřednostňovaná velikost payloadu pro kodeky G.711 a G.726.</translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation>i&amp;LBC typ payloadu:</translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation>iLBC velikost &amp;payloadu (ms):</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation>Dynamická hodnota typu pro iLBC (96 nebo více).</translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation>Upřednostňovaná velikost payloadu pro iLBC.</translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation>Vylepšení &amp;kvality zvuku</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation>Vylepšení vnímané kvality zvuku je součástí dekodéru, která se snaží snížit (vnímaný) šum vzniklý při procesu kódování/dekódování. Ve většině případů vede tato funkce k většímu objektivnímu odchýlení zvuku od originálu (z hlediska SNR), ale zní přesto lépe (subjektivní vylepšení).</translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation>Typ payload&amp;u pro ultra široké pásmo:</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the audio being encoded is speech or silence/background noise. VAD is always implicitly activated when encoding in VBR, so the option is only useful in non-VBR operation. In this case, Speex detects non-speech periods and encode them with just enough bits to reproduce the background noise. This is called &quot;comfort noise generation&quot; (CNG).</source>\n        <translation type=\"obsolete\">Pokud je aktivováno, testuje systém VAD (Voice Activity Detection), jestli je právě mluveno nebo je v hovoru pauza. Zvuky které nejsou rozpoznány jako hovor jsou nahrazeny jen několika málo bity napodobující šum pozadí. To vede k redukci přenášených dat. \nSystém VAD je vždy aktivován, pokud je nastaveno kódováni s VBR.</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation>Typ payloadu pro široké pásm&amp;o:</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation>Variabilní šířka pásma (VBR) umožní danému kodeku přizpůsobit množství dat potřebných k přenosu hovoru charakteru audio signálu. Zatímco např. některé ostré samohlásky nebo velmi proměnné pasáže potřebují velkou vzorkovací frekvenci a tím velký datový tok, tak měkké souhlásky vystačí s malým datovým tokem. Díky VBR tak lze při dané datové rychlosti docílit lepší kvality zvuku nebo při dané kvalitě hovoru vystačit s nižším datovým tokem. Nevýhodou je, že při zadané kvalitě nelze předpovědět jaký velký datový tok bude ve skutečnosti. A také, že v aplikacích pracujících v reálném čase (jako je právě VoIP) je rozhodující maximální šířka pásma, kterou musí zvládnout komunikační kanál.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation>Dynamická hodnota typu pro speex wide band (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation>Ko&amp;mplexita:</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation>Nesouvislé vysílání je rozšířením funkčnosti VAD/VBR, kdy je možné úplně přestat odesílat data v případě ticha.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation>Dynamická hodnota typu pro speex narrow band (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation>V případě Speexu je možné měnit komplexitu kodéru. Slouží to k zadání hloubky hledání v rozsahu od 1 do 10. Podobný princip je zaveden v kompresních programech gzip a bzip2 s volbou -1 až -9 . Za normálních podmínek je odstup šumu při komplexitě 1 o 1 až 2dB vyšší než při komplexitě 10. Nicméně CPU vytížení je asi 5x vyšší než při komplexitě 1. V praxi se osvědčilo nastavení mezi 2 až 4. Vyšší nastavení jsou vhodná pro přenos tónů DTMF nebo hudebního signálu.</translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation>Typ payloadu pro úz&amp;ké pásmo:</translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation>Typ payloadu pro G.726 &amp;40 kb/s:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation>Dynamická hodnota typu pro G.726 40 kb/s (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation>Dynamická hodnota typu pro G.726 32 kb/s (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation>Typ payloadu pro G.726 &amp;24 kb/s:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation>Dynamická hodnota typu pro G.726 24 kb/s (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation>Typ payloadu pro G.726 &amp;32 kb/s:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation>Dynamická hodnota typu pro G.726 16 kb/s (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation>Typ payloadu pro G.726 &amp;16 kb/s:</translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation>Dynamická hodnota typu pro události DTMF (RFC 2833) (96 nebo vyšší).</translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation>H&amp;lasitost DTMF:</translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation>Hlasitost vysílaných DTMF tónů v dB.</translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation>Doba prodlevy mezi dvěma DTMF tóny.</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation>Trvání &amp;DTMF:</translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation>&amp;Typ payloadu DTMF:</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation>&amp;Prodleva DTMF:</translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation>Doba trvání tónu DTMF.</translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>P&amp;řenos DTMF:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation>Automaticky</translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation>RFC 2833</translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation>Zvukově</translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation>SIP INFO</translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;&lt;h3&gt;RFC 2833&lt;/h3&gt;\nVysílá DTMF tóny jako telefonní události dle RFC 2833.&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Zvukově&lt;/h3&gt;\nVysílá DTMF inband (skutečné tóny, které Twinkle přimíchá do audio signálu).&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Auto&lt;/h3&gt;\nPokud protistrana podporuje RFC 2833, jsou použity DTMF tóny dle RFC 2833 standardu, jinak jako inband.&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;SIP INFO&lt;/h3&gt;\nVysílá DTMF out-of-band přes požadavek SIP INFO.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Obecné</translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation>Přesměrování</translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation>Povolit přesměrov&amp;ání</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation>Značí, zda má Twinkle přesměrovat požadavek při odpovědi 3XX.</translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation>Dotázat se uživatele před &amp;přesměrováním</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation>Pokud je aktivováno, dotáže se Twinkle při přijetí požadavku 3XX uživatele, zda-li má být odchozí volání přesměrováno na nový cíl.</translation>\n    </message>\n    <message>\n        <source>Max re&amp;directions:</source>\n        <translation type=\"vanished\">Max. počet &amp;přesměrování:</translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation>Počet přesměrování odchozího volání, po kterém Twinkle ukončí pokusy navázat spojení. Zabraňuje zacyklení při přesměrování.</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Nastavení protokolu SIP</translation>\n    </message>\n    <message>\n        <source>Call &amp;Hold variant:</source>\n        <translation type=\"vanished\">Způsob přidržení &amp;hovoru:</translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation>Značí, zda-li bude k podržení hovoru použito RFC 2543 (nastavení IP adresy v SDP na 0.0.0.0) nebo RFC 3264 (použít směrové atributy v SDP).</translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation>Povolit chybějící kontaktní hlavičku v 200 OK při REG&amp;ISTER</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation>Odpověď 200 OK na požadavek REGISTER musí obsahovat hlavičku Contact. Někteří poskytovatelé buď hlavičku Contact neposílají, nebo je chybná. Tato možnost povolí takové odchýlení od specifikací.</translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation>Hlavička &amp;Max-Forwards je vyžadována</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation>Podle RFC3261 je hlavička Max-Forwards povinná. Často však není posílána. Pokud je aktivováno, pak Twinkle odmítne SIP požadavky, které hlavičku Max-Forwards neobsahují.</translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation>Vložit do kontaktní hlavičky dobu platnosti při&amp;hlášení</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation>Doba vypršení platnosti přihlášení v požadavku REGISTER může být přenášena jak v hlavičce Contact, tak i v hlavičce Expires. Pokud je aktivováno, posílá ji Twinkle v hlavičce Contact, jinak v hlavičce Expires.</translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation>Použít &amp;kompaktní názvy hlaviček</translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation>Pokud je aktivováno, bude pro názvy hlaviček použita krátká forma, pokud existuje.</translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation>Povolit změny v SDP při navázání volání</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;SIP UAS může odesílat SDP v 1XX odpovědi na zvuk zkraje hovoru, např. vyzváněcí tón. Pokud bude volání přijato, měl by SIP UAS dle RFC 3261 poslat to samé SDP v odpovědi 200 OK. Po přijetí SDP by měly být všechny následující odpovědi SDP být zahozeny.&lt;/p&gt;\n&lt;p&gt;Pokud je povoleno, že se SDP během navázání hovoru může změnit, Twinkle nebude v následujících odpovědích ignorovat SDP, nýbrž změní požadovaným způsobem vlastnosti proudu RTP. Změněné SDP musí mít v &quot;o=&quot; řádku nové číslo verze.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Pokud je aktivováno, vytvoří Twinkle jednoznačnou hodnotu kontaktní hlavičky pomocí kombinace uživatelského SIP jména a doménového jména:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nJe tím umožněno vytvoření vícero uživatelských profilů se stejným uživatelským jménem, ale rozdílnou doménou, které pak mají odlišné kontaktní adresy a proto tyto profily lze použít současně.\n&lt;/p&gt;\n&lt;p&gt;\nNěkteré proxy nemusí s takovýmito kontaktními hlavičkami umět zacházet. Pokud je tato volba deaktivována, posílá Twinkle kontaktní hlavičku v následujícím formátu:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nTento formát je používán většinou SIP telefonů.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation>Via, Route a Record-Route poslat jako &amp;seznam</translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation>Via-, Route- a Record-Route-Header mohou posílány zakódované jako seznam čárkou oddělených hodnot nebo jako jednotlivé hodnoty, každá ve své hlavičce.</translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation>Rozšíření SIP</translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>deaktivováno</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>podporováno</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>vyžadováno</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>upřednostňováno</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation>Definuje způsob podpory rozšíření 100rel(PRACK):&lt;br&gt;&lt;br&gt;\n&lt;b&gt;deaktivováno&lt;/b&gt;: rozšíření 100rel není podporováno\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;povoleno&lt;/b&gt;: 100rel je podporováno (je přidáno do odchozího INVITE jako podporovaná hlavička). Protistrana si potom může vyžádat PRACK na 1xx odpověď.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;vyžadováno&lt;/b&gt;: 100rel je vyžadováno. Požadavek je vložen do hlavičky require v odchozím INVITE. Pokud je v příchozím INVITE značeno, že je 100rel podporováno, pak bude Twinkle vyžadovat PRACK v odpovědi 1xx Pokud protistrana 100rel nepodporuje, nedojde k navázání spojení.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;upřednostňováno&lt;/b&gt;: Podobné jako &quot;vyžadováno&quot;, akorát že v případě, že hovor selže, jelikož protistrana nepodporuje 100rel (odpověď 420), pak se Twinkle pokusí hovor znovu navázat bez vyžadování 100rel.</translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation>Přesměrování volání (REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call &amp;transfer (incoming REFER)</source>\n        <translation type=\"obsolete\">Povolit protistraně přesměrování &amp;volání (příchozí REFER)</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation>Pokud je aktivováno, Twinkle následuje požadavek protistrany (REFER) přesměrovat volání na jinou adresu.</translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation>Dotázat se uživatele na &amp;povolení přesměrování</translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation>Pokud je aktivováno, dotazuje se Twinkle při příchozím (REFER) požadavku na povolení přesměrování.</translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation>Podržet ho&amp;vor, zatím co se spojuje cíl přepojení</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation>Pokud je aktivováno, Twinkle při příchozím požadavku REFER na přesměrování podrží stávající hovor.</translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation>Podržet hovor před odes&amp;láním REFER</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation>Značí, zda má Twinkle přidržet hovor při jeho přesměrování.</translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation>Automaticky obnovovat registraci &amp;pro REFER signál, dokud není přesměrování dokončeno</translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation>Během přesměrování hovoru posílá zprostředkovatel přesměrování zprávy NOTIFY o postupu přesměrování na toho, jehož volání je přesměrováváno. Ovšem jen po krátkou dobu. Tu určí ten, kdo je přesměrováván. Pokud je tato volba aktivována, posílá zprostředkovatel (Twinkle) automaticky SUBCRIBEs tak, aby došlo k prodloužení tohoto času, dokud není proces přesměrování ukončen.</translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>Překonání NATu</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation>Překonání &amp;NATu není nutné</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation>Zvolte tuto volbu, pokud se mezi Twinkle a vaší SIP proxy nenachází žádný NAT nebo pokud váš poskytovatel umí sám NAT překonat.</translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation>Ve zprávách SIP použít &amp;pevně nastavenou veřejnou IP adresu</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation>Pokud je aktivováno, Twinkle použije uvnitř zpráv SIP (v hlavičce SIP a těle SDP) veřejnou IP adresu, namísto IP adresy vašeho síťového rozhraní.&lt;br&gt;&lt;br&gt;\nPokud si tuto volbu vyberete, musíte rovněž na vašem NAT zařízení nasměrovat odpovídající RTP porty na váš počítač.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN</source>\n        <translation type=\"obsolete\">Použít &amp;STUN</translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation>Vyberte tuto volbu, pokud váš poskytovatel SIP nabízí STUN server k přemostění vašeho NATu.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation type=\"vanished\">Adresa S&amp;TUN serveru:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Doménové jméno, IP adresa nebo jméno STUN serveru.</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>Veřejná IP &amp;adresa:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation>Veřejná adresa vašeho NATu.</translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Telefonní čísla</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation>U telefonních čísel zobrazit jen uživatelskou část &amp;URI</translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation>Pokud URI značí telefonní číslo, zobrazí se jen uživatelská část adresy. Např. pokud přijde volání od sip:12345@voipprovider.com, ukáže Twinkle jako adresu jen &quot;12345&quot;. URI je považováno za telefonní číslo, pokud obsahuje parametr &quot;user=phone&quot; nebo pokud je aktivní následující volba a uživatelská část adresy je numerická.</translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation>&amp;URI s numerickou uživatelskou částí je telefonní číslo</translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation>Pokud je aktivováno, považuje Twinkle každou SIP adresu za telefonní číslo, pokud má v uživatelské části pouze číslice, *, #, + a zvláštní znaky. V odchozím SIP zprávách označí Twinkle takové adresy parametrem &quot;user=phone&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation>&amp;Odstranit z telefonního čísla zvláštní znaky</translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation>Aby byla telefonní čísla snázeji čitelná, bývají často zadána s použitím zvláštních znaků jako např. &quot;(&quot;, &quot;)&quot;, &quot; &quot;(prázdný znak), &quot;-&quot;. Při vytáčení, obzvláště nějaké SIP adresy, nesmí být tyto znaky vysílány. Aby bylo možné zjednodušit vytáčení pomocí copy/paste, Twinkle může tyto znaky při vytáčení automaticky odstranit.</translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation>&amp;Zvláštní znaky:</translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation>Seznam všech zvláštních znaků, které má Twinkle z vytáčených čísel odstranit.</translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation>Převod čísel</translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation>Vyhledávaný výraz</translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Nahradit</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telphone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation type=\"vanished\">&lt;p&gt;\nČasto není formát telefonních čísel, které jsou očekávány od VoIP poskytovatele, shodný s formátem čísel uložených v adresáři. Např. u čísel začínajících na &quot;+&quot; a národním kódem země očekává váš poskytovatel namísto &quot;00&quot; znak &quot;+&quot;. Nebo jste-li napojeni na místní SIP síť a je nutné předtočit nejdříve číslo k přístupu ven.\nZde je možné za použití vyhledávacích a zaměňovacích vzorů (podle způsobu regulárních výrazů a la Perl) nastavit obecně platné pravidla pro konverzi telefonních čísel.\n&lt;/p&gt;\n&lt;p&gt;\nPři každém vytáčení se Twinkle pokusí najít pro čísla z uživatelské části SIP adresy odpovídají výraz v seznamu hledaných vzorů. S prvním nalezeným vyhovujícím výrazem je provedena úprava originálního čísla, přičemž pozice v &quot;(&quot; &quot;)&quot; v hledaném výrazu (např. &quot;([0-9]*)&quot; pro &quot;jakkoliv mnoho čísel&quot;) je nahrazena znaky v odpovídajících proměnných. Např. &quot;$1&quot; pro první pozici. Viz též `man 7 regex` nebo konqueror:&quot;#regex&quot;. Pokud není nalezen žádný odpovídající hledaný vzor, zůstane číslo nezměněno.\n&lt;/p&gt;\n&lt;p&gt;\nPravidla budou rovněž použita na čísla v příchozích voláních. Podle nastavených pravidel budou tato přetransformována do žádaného formátu.\n&lt;/p&gt;\n&lt;h3&gt;1. příklad&lt;/h3&gt;\n&lt;p&gt;\nNapř. váš národní kód je &quot;420&quot; pro českou republiku a ve vašem adresáři máte také mnoho vnitrostátních čísel uložených v mezinárodním formátu. Např.. +420 577 2345678. Avšak VoIP poskytovatel očekává pro vnitrostátní hovor 0577 2345678. Chcete tedy nahradit &apos;+420&apos; za &apos;0&apos; a zároveň pro zahraniční hovory nahradit &apos;+&apos; za &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nK tomu jsou potřebné následující pravidla uvedená v tomto pořadí:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHledaný výraz = \\+420([0-9]*) , Náhrada = 0$1&lt;br&gt;\nHledaný výraz = \\+([0-9]*) , Náhrada = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;2. příklad&lt;/h3&gt;\n&lt;p&gt;\nNacházíte se na telefonní ústředně a všem číslům s 0 jako první číslicí, má být předřazeno číslo 9. \n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHledaný výraz = 0[0-9]* , Náhrada = 9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n( $&amp; je speciální proměnná, do které je uloženo celé originální číslo)&lt;br&gt;\nPoznámka: Toto pravidlo nelze nastavit jednoduše jako třetí pravidlo ke dvou pravidlům z předcházejícího příkladu. Bude totiž použito vždy jen to první, které vyhledávání vyhoví. Namísto toho by muselo být změněno nahrazování v pravidlech 1 a 2 - &quot;57$1&quot; a &quot;577$1&quot;</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation>Posunout vybrané pravidlo konverze na vyšší pozici.</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation>Posunout vybrané pravidlo konverze na nižší pozici.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Nové</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation>Vytvořit nové konverzní pravidlo.</translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>&amp;Odstranit</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation>Smazat vybrané konverzní pravidlo.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>U&amp;pravit</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation>Upravit vybrané konverzní pravidlo.</translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation>Pro ověření funkčnosti vytvořeného konverzního pravidla sem napište zkušební telefonní číslo a stiskněte Test.</translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Test</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation>Otestovat, jak bude číslo převedeno konverzními pravidly.</translation>\n    </message>\n    <message>\n        <source>for STUN</source>\n        <translation type=\"obsolete\">pro STUN</translation>\n    </message>\n    <message>\n        <source>Keep alive timer for the STUN protocol. If you have enabled STUN, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"obsolete\">Časový signál pro STUN protokol. Pokud je STUN aktivován, bude Twinkle posílat pravidelně v zadaném časovém odstupu datové STUN-keep-alive pakety. Cílem je, aby nedošlo na routeru ke smazání příslušnosti mezi externí a interní IP adresou z adresní NAT tabulky a docházelo k pravidelnému prodlužování platnosti záznamu. Tato hodnota je proto závislá od konkrétního NATu a neměla by být příliš velká.</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation>Tento časovač je spuštěn při příchozím hovoru. Pokud nebude volání do konce vypršení časové prodlevy přijato, pak Twinkle hovor odmítne zprávou &quot;480 User Not Responding&quot;.</translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation>&amp;Keep alive NATu:</translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>&amp;Nedostupný po:</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation>Tón zv&amp;onění u volaného:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Zadejte zde jméno .wav souboru pro indikaci zvonění u volaného pro tohoto uživatele.&lt;/p&gt;\n\n&lt;p&gt;Tento tón nahrazuje tón zvonění u volaného ze systémového nastavení.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Zadejte zde jméno .wav souboru pro vyzváněcí tón v tomto uživatelském profilu.&lt;/p&gt;\n\n&lt;p&gt;Toto nastavení nahrazuje vyzváněcí tón ze systémového nastavení.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Vyzváněcí tón:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript bude spuštěn, pokud je nějaký hovor ukončen z vaší strany.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všech SIP hlaviček odesílaných SIP BYE požadavků budou předány tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. \n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. \n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; obsahuje request-URI metody BYE. \n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript bude spuštěn, pokud nebude příchozí hovor přijat. Tzn. ukončí se vyzvánění  aniž by byl &quot;zvednuto&quot;.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všech SIP hlaviček odesílaných SIP failure odpovědí budou předány tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. \n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; obsahuje stavový kód odesílané SIP failure odpovědi.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;, tedy chybovou příčinu pomocí obyčejného textu.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript je spuštěn, pokud  je hovor ukončen protistranou.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všech SIP hlaviček příchozích SIP BYE požadavků budou předány tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; obsahuje request-URI signálu BYE.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript bude spuštěn, pokud bude protistranou volání přijato.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všech SIP hlaviček příchozích &quot;200 OK&quot; hlášek budou předány tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript bude spuštěn, pokud bude příchozí hovor přijat.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všech SIP hlaviček odchozích &quot;200 OK&quot; odpovědí budou předány tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation>Lokální &amp;ukončení hovoru:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript bude spuštěn, pokud odchozí volání nebude moci býti realizováno. Např kvůli timeout, DND atd.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsah všech SIP hlaviček přijatých SIP failure odpovědí bude předán tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; obsahuje stavový kód odeslané SIP failure odpovědi.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;, tedy chybovou hlášku ve formě jednoduchého textu.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript bude spuštěn, pokud bude zahájeno nějaké volání.\n&lt;/p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všech SIP hlaviček odeslaných SIP INVITE požadavků budou předány tomuto skriptu pomocí následujících systémových proměnných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; obsahuje request-URI signálu INVITE.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje jméno aktivního uživatelského profilu.</translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation>Volání &amp;přijato protistranou:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation>Příchozí volání bylo &amp;neúspěšné:</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>&amp;Příchozí volání:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation>Ukončení &amp;hovoru protistranou: </translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation>Příchozí volání &amp;přijato:</translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>Odchozí &amp;volání:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation>Od&amp;chozí volání bylo neúspěšné:</translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>Povolit &amp;šifrování ZRTP/SRTP</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unecrypted.</source>\n        <translation type=\"vanished\">Pokud je aktivováno, pokusí se Twinkle při všech odchozích a příchozích hovorech zašifrovat zvuková data. Aby byl hovor opravdu zašifrován musí i protistrana podporovat šifrování ZRTP/SRTP. Jinak zůstane hovor nešifrovaný.</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>Nastavení ZRTP</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation>&amp;Zašifrovat, jen pokud protistrana potvrdí podporu ZRTP v SDP</translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation>Protistrana podporující ZRTP může toto oznámit již na začátku navázání rozhovoru. Pokud je aktivována tato volba, pokusí se Twinkle v takových případech použít zašifrovaný přenos hovoru.</translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation>ZRTP podporu ohlašovat &amp;v SDP</translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation>Pokud je aktivováno, hlásí Twinkle protistraně při navázání hovoru pomocí SDP, že podporuje ZRTP.</translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation>&amp;Upozornit, pokud protistrana přepne na nešifrovaný přenos hovoru</translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation>Protistrana může během zašifrovaného hovoru vyslat příkaz ZRTP-go-clear a tím šifrování zrušit. Pokud je tato volba aktivována, Twinkle na tento bezpečnostní problém okamžitě upozorní.</translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation>Dynamický typ payloadu %1 je použit vícekrát.</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Je nutné zadat uživatelské jméno pro váš SIP účet.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>K vašemu SIP účtu musíte vyplnit doménové jméno)\nPro přímé volání mezi IP adresami je to doménové jméno nebo veřejná IP adresa vašeho počítače. </translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Chybné uživatelské jméno.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Chybný název domény.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation>Chybné jméno registrátora.</translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation>Chybné jméno odchozí proxy.</translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation>Chybí veřejná IP adresa.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Chybný údaj STUN serveru.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Vyzváněcí tóny</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Výběr vyzváněcího tónu</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Tón pro signalizaci vyzvánění u protistrany</translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Všechny soubory</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation>Vyberte skript ke spuštění při příchozím volání</translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation>Výběr skriptu ke spuštění po &quot;přijetí příchozího volání&quot;</translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation>Vyberte skript ke spuštění po selhání příchozího volání</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation>Vyberte skript ke spuštění při odchozím volání</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation>Vyberte skript ke spuštění při přijetí volání protistranou</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation>Vyberte skript ke spuštění při selhání odchozího volání</translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation>Vyberte skript ke spuštění při vlastním ukončení hovoru</translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation>Vyberte skript ke spuštění při ukončení hovoru protistranou</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Hlasová schránka</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation>U příchozích hovorů dávat přednost kodekům dle pre&amp;ferencí protistrany</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation>Pokud je aktivováno, Twinkle upřednostní při příchozím volání povolené kodeky protistrany (SDP offer). Konkrétně bude použit první kodek, který je protistranou nabízen a rovněž se nachází v seznamu lokálních kodeků.\nPokud je deaktivováno, použije Twinkle první kodek ve vlastním seznamu, který je rovněž podporován protistranou.</translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation>U odchozích hovorů dávat přednost kodekům dle pre&amp;ferencí protistrany</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation>Pokud je aktivováno bude Twinkle při odchozím volání řídit seznamem upřednostňovaných kodeků u protistrany (SDP answer). Konkrétně bude použit první kodek na seznamu upřednostňovaných kodeků protistrany, který je rovněž podporován v aktuálním lokálním nastavení Twinkle.\nPokud je deaktivováno, použije Twinkle první kodek z vlastního seznamu, který je rovněž podporován protistranou. Tzn. je uveden v SDP-Answer seznamu.</translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation>Datové pořadí (codeword &amp;packing order):</translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation>Existují dvě metody balení datových paketů G.726 do RTP paketu. Standardně je to RFC 3551. Někteří SIP poskytovatelé používají ovšem ATM AAL2. Pokud je přenos zvuku při použití kodeku G.726 zarušen, je možné zde zkusit jiné nastavení.</translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Rozšíření Replaces</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extenstion is supported.</source>\n        <translation type=\"vanished\">Indikuje, zda je rozšíření Replaces podporováno.</translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation>Asistovaně přepojovat na AoR (Address of Record)</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endoint.</source>\n        <translation type=\"vanished\">Asistované přepojení by mělo používat Contact-URI jako cílovou adresu pro sdělení nového spojení přesměrovávané protistraně. Tato adresa nemusí být ovšem globálně platná. Alternativně se může použít AoR (Address of Record). Nevýhodou je, že při více koncových zařízeních není AoR jednoznačné, zatímco URI kontaktu směřuje na jediné zařízení.</translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Soukromí</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Nastavení ochrany soukromí</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation>&amp;Posílat hlavičku P-Preferred-Identity při skrývání identity uživatele</translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Pokud je vybráno a je aktivována volba &quot;skrýt odesilatele&quot;, bude spolu s údajem odesilatele odeslána při požadavku INVITE hlavička P-Preferred-Identity.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nZde můžete upravit, jak Twinkle obslouží příchozí hovory. Twinkle může při příchozích hovorech spustit skript. V závislosti na výstupu skriptu Twinkle přijme, odmítne nebo přesměruje hovor. Skript může ovlivnit také vyzváněcí tón. Může se jednat o libovolný spustitelný program.\n&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Poznámka:&lt;/b&gt; Twinkle přestane po dobu běhu skriptu pracovat. Doporučujeme, aby skript neběžel déle než 200 ms. Pokud potřebujete více času, můžete po odeslání parametrů poslat &lt;b&gt;end&lt;/b&gt; a pokračovat v běhu. Twinkle bude sám po přijetí parametru &lt;b&gt;end&lt;/b&gt; pokračovat v běhu.\n&lt;h3&gt;Vrácené hodnoty&lt;/h3&gt; -   print po STDOUT (např. `echo &quot;action=dnd&quot;`), jedna hodnota na řádek: &lt;br&gt;\n&lt;tt&gt;action=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;&lt;/tt&gt;\n&lt;blockquote&gt;\n&lt;i&gt;continue&lt;/i&gt; - pokračovat v normálním zpracování volání (výchozí)&lt;br&gt;\n&lt;i&gt;reject&lt;/i&gt; - odmítnout volání&lt;br&gt;\n&lt;i&gt;dnd&lt;/i&gt; - odmítnout volání s poznámkou &quot;nerušit&quot;&lt;br&gt;\n&lt;i&gt;redirect&lt;/i&gt; - přesměrovat volání na &lt;tt&gt;contact&lt;/tt&gt;&lt;br&gt;\n&lt;i&gt;autoanswer&lt;/i&gt; - volání automaticky přijmout&lt;br&gt;\n&lt;/blockquote&gt;\n&lt;br&gt;\n&lt;tt&gt;reason=&amp;lt;string&amp;gt;   &lt;/tt&gt;pro dnd a reject (zobrazení u protistrany)&lt;br&gt;\n&lt;tt&gt;contact=&amp;lt;přesměrovací adresa&amp;gt; &lt;/tt&gt;pro přesměrování&lt;br&gt;\n&lt;tt&gt;caller_name=&amp;lt;nové zobrazované jméno volajícího&amp;gt;   &lt;/tt&gt;nahrazuje pro zobrazení eventuálně již existující jméno z INVITE&lt;br&gt;\n&lt;tt&gt;ringtone=&amp;lt;jméno .wav souboru&amp;gt;   &lt;/tt&gt;vyzváněcí tón speciálně pro toto volání (jen při &lt;i&gt;continue&lt;/i&gt; ;-)&lt;br&gt;\n&lt;tt&gt;display_msg=&amp;lt;libovolná poznámka pro podrobné zobrazení v hlavním okně&amp;gt;&lt;/tt&gt;&lt;br&gt;\n&lt;tt&gt;end   &lt;/tt&gt;Twinkle vyhodnotí všechny vrácené hodnoty, uzavře STDOUT skriptu(!) a pokračuje dále&lt;br&gt;\n&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\n&lt;h3&gt;Systémové proměnné&lt;/h3&gt;\n&lt;p&gt;\nHodnoty všech SIP hlaviček příchozího INVITE budou předány tomuto skriptu. Struktura proměnných: &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; - např. SIP_FROM obsahuje hodnotu &quot;from header&quot;.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. &lt;br&gt;\nSIPREQUEST_METHOD=INVITE. &lt;br&gt;\nSIPREQUEST_URI obsahuje request-URI signálu INVITE.&lt;br&gt;\nTWINKLE_USER_PROFILE obsahuje jméno uživatelského profilu, pro který je příchozí volání určeno.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation>&amp;Adresa hlasové schránky:</translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation>SIP adresa nebo telefonní číslo pro přístup k vaší hlasové schránce.</translation>\n    </message>\n    <message>\n        <source>Unsollicited</source>\n        <translation type=\"vanished\">Nevyžádané</translation>\n    </message>\n    <message>\n        <source>Sollicited</source>\n        <translation type=\"vanished\">Vyžádané</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsollicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsollicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Sollicited&lt;/H3&gt;\n&lt;p&gt;\nSollicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation type=\"vanished\">&lt;H2&gt;Typ indikace čekajících zpráv&lt;/H2&gt;\n&lt;p&gt;\nPokud váš SIP poskytovatel nabízí upozornění na uložené zprávy v hlasové schránce, může vás Twinkle informovat o nových i již vyslechnutých zprávách ve vaší hlasové schránce. Zeptejte se vašeho poskytovatele, jaký typ indikace čekajících zpráv je používán\n&lt;/p&gt;\n&lt;H3&gt;Nevyžádané&lt;/H3&gt;\n&lt;p&gt;\nAsterisk podporuje nevyžádané indikování čekajících zpráv.\n&lt;/p&gt;\n&lt;H3&gt;Vyžádané&lt;/H3&gt;\n&lt;p&gt;\nVyžádaná indikace čekajících zpráv dle RFC 3842.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation>Typ &amp;MWI:</translation>\n    </message>\n    <message>\n        <source>Sollicited MWI</source>\n        <translation type=\"vanished\">Vyžádané MWI</translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation>Doba &amp;platnosti odběru:</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation>Uživatelské jméno &amp;hlasové schránky:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation>Jméno hostitele, doménové jméno nebo IP adresa serveru vaší hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>For sollicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation type=\"vanished\">Dle specifikace MWI se koncové zařízení hlásí na serveru k příjmu zpráv na určitou dobu a před vypršením této doby by se přihlášení mělo znovu obnovit.</translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation>Uživatelské jméno pro přístup k vaší hlasové schránce.</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation>&amp;Server hlasové schránky:</translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation>Přes odchozí &amp;proxy</translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation>Pokud je aktivováno, pak Twinkle zasílá SIP zprávy na server hlasové schránky přes odchozí proxy.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation>Musíte vyplnit uživatelské jméno hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation>Musíte vyplnit server hlasové schránky</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation>Neplatný server hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation>Neplatné uživatelské jméno hlasové schránky.</translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation>Použít doménové &amp;jméno pro vytvoření jednoznačné hlavičky Contact</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Vyberte soubor pro vyzváněcí tón u protistrany.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Vyberte soubor s vyzváněcím tónem.</translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation>Vyberte soubor se skriptem.</translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>%1 převádí na %2</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Textová zpráva</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Přítomnost</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation>&amp;Maximální počet sezení:</translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation>Pokud je již otevřen tento počet sezení s textovými zprávami, nově příchozí sezení budou odmítnuta.</translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Vaše dostupnosti</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>&amp;Zveřejnit dostupnost při spuštění</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Zveřejnit vaši dostupnost při spuštění.</translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation>Přítomnost kontaktů</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation>Interval &amp;odesílání stavu (s):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation>Obnovovací frekvence odesílání vlastní dostupnosti.</translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation>&amp;Interval obnovení příjmu dostupnosti (s):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation>Obnovovací frekvence příjmu dostupnosti kontaktů.</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Přenos/NAT</translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation>Přidat q-hodnotu k registraci</translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation>Hodnota &apos;q&apos; určuje prioritu vašeho zaregistrovaného zařízení. Pokud je mimo Twinkle k VoIP účtu zaregistrováno jiné SIP zařízení, může síť využít těchto hodnot k určení zařízení, které bude přednostně osloveno pro obsloužení hovoru.</translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation>Hodnota &apos;q&apos; je mezi 0.000 and 1.000. Vyšší hodnota znamená vyšší prioritu.</translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>Přenos SIP</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation>UDP</translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation>TCP</translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Režim přenosu pro SIP. V automatickém režimu je velikost zpráv určuje, jaký transportní protokol je použit. Zprávy větší než hranice pro UDP jsou posílány přes TCP. Menší zprávy jsou posílány přes UDP.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>P&amp;řenosový protokol:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation>&amp;Hranice použití UDP:</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Zprávy větší než stanovená hranice jsou odeslány přes TCP. Menší zprávy přes UDP.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN (does not work for incoming TCP)</source>\n        <translation type=\"vanished\">Použít &amp;STUN (nefunguje pro příchozí TCP)</translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation>Tr&amp;valé TCP spojení</translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation>Podržet otevřené TCP spojení vytvořené během registrace tak, aby SIP proxy mohla využít tohoto spojení k vysílání příchozích požadavků. Aplikace odesílá ping pakety tak, aby se zjistilo, zda-li je spojení stále aktivní.</translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation>Při psaní zprávy &amp;odesílat o tomto posílat indikaci.</translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation>Pokud píšete zprávu, pak o tomto Twinkle informuje protistranu. Takto se příjemce může dozvědět, že něco píšete.</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation>Parametry autentizačního managementu pro AKAv1-MD5 autentizaci.</translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation>Operátorová varianta klíče pro autentizaci AKAv1-MD5.</translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation>Před&amp;zpracování</translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation>Předzpracování (vylepšuje kvalitu u příjemce)</translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation>&amp;Automatické řízení hlasitosti</translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation>Z důvodu velkého rozdílu hlasitosti nahrávání v různých nastavení byla zavedena funkce automatického řízení hlasitosti (AGC - Automatic gain control). AGC umožňuje nastavit úroveň signálu na přednastavenou hodnotu. Díky tomu není nutné pokaždé manuálně nastavovat hlasitost mikrofonu. Další výhodou je, že nastavení hlasitosti mikrofonu je většinou na nižší (konzervativní) úrovni, čímž se předchází ořezávání příliš hlasitého zvuku.</translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation>&amp;Úroveň automatického řízení hlasitosti:</translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation>Úroveň automatického řízení hlasitosti představuje procentuální hodnotu maximální hlasitosti mikrofonu. Doporučená hodnota je kolem 25%.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation>Detekce &amp;hlasu</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation>Pokud je aktivní, pak detekce hlasu zjišťuje, zda je na zvukovém vstupu hlas nebo ticho/šum na pozadí.</translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation>&amp;Potlačení šumu</translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation>Potlačení šumu může být použito k snížení okolních rušivých zvuků ve vstupním signálu. Vede to k lepší kvalitě mluveného slova.</translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation>Potlačení &amp;akustické ozvěny</translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation>Pokud je při VoIP komunikaci příchozí zvuk přehráván na hlasitý odposlech v reproduktorech, může se šířit místností a dostávat se zpět do mikrofonu. Pokud je tento signál poslán zpět volajícímu, stává se, že slyší ozvěnu vlastního hlasu. Funkce potlačení akustické ozvěny je navržena k potlačení těchto zvuků před tím, než jsou odeslány volajícímu. Je důležité si uvědomit, že tato funkce je určena pro zlepšení kvality přenosu hlasu na druhé straně hovoru.</translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation>Proměnný &amp;datový tok</translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation>Diskontinuitní &amp;přenos</translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation>&amp;Kvalita:</translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation>Speex je ztrátový kodek, což znamená, že je na úkor kvality možné docílit redukce datového toku. Na rozdíl od jiných hlasových kodeků je možné nastavit kompromis mezi kvalitou a datovým tokem. Kódovací proces u tohoto kodeku je zpravidla řízen nastavením parametru kvality v rozsahu od 0 do 10.</translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation>bajtů</translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation>Použít tel-URI pro &amp;telefonní číslo</translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation>Rozšířit vytáčené telefonní číslo na tel-URI namísto sip-URI.</translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation>Přijímat žádosti o &amp;přepojení hovoru (příchozí REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation>Povolit přepojení hovoru v průběhu asistovaného přepojení</translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation>Pokud děláte asistované přepojení, pak obvykle přepojíte hovor poté, co proběhla konzultace s protistranou. Jakmile povolíte tuto volbu, pak můžete přepojit hovor, zatímco konzultace stále probíhá. Toto je nestandardní implementace, která nemusí správně pracovat se všemi zařízeními SIP.</translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation>Povolit NAT &amp;keep alive</translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation>Posílat UDP pakety pro udržení spojení přes NAT.</translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation>Pokud máte povolený STUN nebo NAT keep alive, pak bude Twinkle zasílat udržovací pakety v tomto intervalu, aby byla udržena mapování na vašem NATu.</translation>\n    </message>\n    <message>\n        <source>Do&amp;main*:</source>\n        <translation>&amp;Doména*:</translation>\n    </message>\n    <message>\n        <source>Organi&amp;zation:</source>\n        <translation>Or&amp;ganizace:</translation>\n    </message>\n    <message>\n        <source>E&amp;xpiry:</source>\n        <translation>&amp;Platnost:</translation>\n    </message>\n    <message>\n        <source>Call Hold &amp;variant:</source>\n        <translation>Způsob přidržení &amp;hovoru:</translation>\n    </message>\n    <message>\n        <source>&amp;Max redirections:</source>\n        <translation>Max. počet &amp;přesměrování:</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extension is supported.</source>\n        <translation>Indikuje, zda je rozšíření Replaces podporováno.</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</source>\n        <translation>Asistované přepojení by mělo používat Contact-URI jako cílovou adresu pro sdělení nového spojení přesměrovávané protistraně. Tato adresa nemusí být ovšem globálně platná. Alternativně se může použít AoR (Address of Record). Nevýhodou je, že při více koncových zařízeních není AoR jednoznačné, zatímco URI kontaktu směřuje na jediné zařízení.</translation>\n    </message>\n    <message>\n        <source>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Pokud je vybráno a je aktivována volba &quot;skrýt odesilatele&quot;, bude spolu s údajem odesilatele odeslána při požadavku INVITE hlavička P-Preferred-Identity.</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Asserted-Identity header when hiding user identity</source>\n        <translation>&amp;Posílat hlavičku P-Preferred-Identity při skrývání identity uživatele</translation>\n    </message>\n    <message>\n        <source>Use STUN (does not wor&amp;k for incoming TCP)</source>\n        <translation>Použít &amp;STUN (nefunguje pro příchozí TCP)</translation>\n    </message>\n    <message>\n        <source>STUN ser&amp;ver:</source>\n        <translation>Adresa S&amp;TUN serveru:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation>&lt;p&gt;\nČasto není formát telefonních čísel, které jsou očekávány od VoIP poskytovatele, shodný s formátem čísel uložených v adresáři. Např. u čísel začínajících na &quot;+&quot; a národním kódem země očekává váš poskytovatel namísto &quot;00&quot; znak &quot;+&quot;. Nebo jste-li napojeni na místní SIP síť a je nutné předtočit nejdříve číslo k přístupu ven.\nZde je možné za použití vyhledávacích a zaměňovacích vzorů (podle způsobu regulárních výrazů a la Perl) nastavit obecně platné pravidla pro konverzi telefonních čísel.\n&lt;/p&gt;\n&lt;p&gt;\nPři každém vytáčení se Twinkle pokusí najít pro čísla z uživatelské části SIP adresy odpovídají výraz v seznamu hledaných vzorů. S prvním nalezeným vyhovujícím výrazem je provedena úprava originálního čísla, přičemž pozice v &quot;(&quot; &quot;)&quot; v hledaném výrazu (např. &quot;([0-9]*)&quot; pro &quot;jakkoliv mnoho čísel&quot;) je nahrazena znaky v odpovídajících proměnných. Např. &quot;$1&quot; pro první pozici. Viz též `man 7 regex` nebo konqueror:&quot;#regex&quot;. Pokud není nalezen žádný odpovídající hledaný vzor, zůstane číslo nezměněno.\n&lt;/p&gt;\n&lt;p&gt;\nPravidla budou rovněž použita na čísla v příchozích voláních. Podle nastavených pravidel budou tato přetransformována do žádaného formátu.\n&lt;/p&gt;\n&lt;h3&gt;1. příklad&lt;/h3&gt;\n&lt;p&gt;\nNapř. váš národní kód je &quot;420&quot; pro českou republiku a ve vašem adresáři máte také mnoho vnitrostátních čísel uložených v mezinárodním formátu. Např.. +420 577 2345678. Avšak VoIP poskytovatel očekává pro vnitrostátní hovor 0577 2345678. Chcete tedy nahradit &apos;+420&apos; za &apos;0&apos; a zároveň pro zahraniční hovory nahradit &apos;+&apos; za &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nK tomu jsou potřebné následující pravidla uvedená v tomto pořadí:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHledaný výraz = \\+420([0-9]*) , Náhrada = 0$1&lt;br&gt;\nHledaný výraz = \\+([0-9]*) , Náhrada = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;2. příklad&lt;/h3&gt;\n&lt;p&gt;\nNacházíte se na telefonní ústředně a všem číslům s 0 jako první číslicí, má být předřazeno číslo 9. \n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHledaný výraz = 0[0-9]* , Náhrada = 9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n( $&amp; je speciální proměnná, do které je uloženo celé originální číslo)&lt;br&gt;\nPoznámka: Toto pravidlo nelze nastavit jednoduše jako třetí pravidlo ke dvou pravidlům z předcházejícího příkladu. Bude totiž použito vždy jen to první, které vyhledávání vyhoví. Namísto toho by muselo být změněno nahrazování v pravidlech 1 a 2 - &quot;57$1&quot; a &quot;577$1&quot;</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</source>\n        <translation>Pokud je aktivováno, pokusí se Twinkle při všech odchozích a příchozích hovorech zašifrovat zvuková data. Aby byl hovor opravdu zašifrován musí i protistrana podporovat šifrování ZRTP/SRTP. Jinak zůstane hovor nešifrovaný.</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation>&lt;H2&gt;Typ indikace čekajících zpráv&lt;/H2&gt;\n&lt;p&gt;\nPokud váš SIP poskytovatel nabízí upozornění na uložené zprávy v hlasové schránce, může vás Twinkle informovat o nových i již vyslechnutých zprávách ve vaší hlasové schránce. Zeptejte se vašeho poskytovatele, jaký typ indikace čekajících zpráv je používán\n&lt;/p&gt;\n&lt;H3&gt;Nevyžádané&lt;/H3&gt;\n&lt;p&gt;\nAsterisk podporuje nevyžádané indikování čekajících zpráv.\n&lt;/p&gt;\n&lt;H3&gt;Vyžádané&lt;/H3&gt;\n&lt;p&gt;\nVyžádaná indikace čekajících zpráv dle RFC 3842.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Unsolicited</source>\n        <translation>Nevyžádané</translation>\n    </message>\n    <message>\n        <source>Solicited</source>\n        <translation>Vyžádané</translation>\n    </message>\n    <message>\n        <source>Solicited MWI</source>\n        <translation>Vyžádané MWI</translation>\n    </message>\n    <message>\n        <source>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation>Dle specifikace MWI se koncové zařízení hlásí na serveru k příjmu zpráv na určitou dobu a před vypršením této doby by se přihlášení mělo znovu obnovit.</translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Průvodce</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Doménové jméno, IP adresa nebo jméno hostitele STUN serveru.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN server:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Uživatelské jméno, které vám bylo přiděleno vaším poskytovatelem SIP. Je také první částí vaší kompletní SIP adresy &lt;b&gt;uzivatel&lt;/b&gt;@domain.com  Může se také jednat o telefonní číslo.\n&lt;br&gt;&lt;br&gt;\nTento údaj je povinný.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Doména*:</translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Vyberte vašeho SIP poskytovatele a uveďte zde vaše plné jméno, vaše uživatelské SIP jméno, popřípadě přihlašovací jméno a heslo.&lt;br&gt;\nPokud váš SIP poskytovatel není v seznamu, vyberte &lt;b&gt;Jiný&lt;/b&gt; a uveďte požadované údaje.</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>&amp;Přihlašovací jméno:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>Vaše &amp;jméno:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Vaše přihlašovací SIP jméno. Často shodné s vaším uživatelským SIP jménem. Nicméně může být i jiné.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Doménová část vaší úplné  SIP adresy uzivatel@&lt;b&gt;domain.com&lt;/b&gt;. Mimo skutečné domény se může jednat také o jméno hostitele nebo IP adresu vaší &lt;b&gt;SIP proxy&lt;/b&gt;. Pro přímé volání mezi IP adresami zde můžete vyplnit jméno hostitele nebo IP adresu vašeho počítače.\n&lt;br&gt;&lt;br&gt;\nTento údaj je povinný.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Vaše plné jméno, např. Jan Novák. Používá se jen pro účely zobrazení. Jakmile uskutečníte hovor, tak toto jméno může být zobrazeno volanému.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>SIP pro&amp;xy:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>Doménové jméno, IP adresa nebo jméno vaší proxy. Pokud se shoduje s doménou, pak je možné toto pole ponechat nevyplněné.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>&amp;SIP VoIP poskytovatel:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Heslo:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>&amp;Uživatelské jméno*:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Vaše přihlašovací heslo.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušit</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Žádné (přímé volání mezi IP adresami)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Jiný</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Průvodce uživatelským profilem: </translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Musíte zadat uživatelské jméno vašeho SIP účtu.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Je nutné zadat doménové jméno vašeho SIP účtu (část vpravo od symbolu &quot;@&quot;).\nV případě přímého volání mezi IP adresami se může jednat o jméno hostitele nebo IP adresu vašeho PC.</translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Neplatná hodnota pro SIP proxy.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Neplatná hodnota pro STUN server.</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Ano</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Ne</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation>Přijmout</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Odmítnout</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/lang/twinkle_de.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"de\" sourcelanguage=\"en\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Adresseintrag</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation>Anme&amp;rkung:</translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation>Mittlerer Name oder Titel.</translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Vorname oder allg. linker Namensbestandteil. Sortierschlüssel!</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>&amp;Vorname:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation>Feld für beliebige Anmerkungen. Eigene Spalte, nach der sortiert werden kann - klicken Sie hierzu einfach auf den Spaltenkopf in der Adressliste.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefon:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation>&amp;Titel:</translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Telefonnummer oder SIP-Adresse des Kontakts.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Nachname oder allg. rechter Namensbestandteil. </translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Nachname:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Sie müssen einen Namen angeben.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>Sie müssen eine Nummer oder SIP-Adresse angeben.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation>Telefon</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation>Anmerkung</translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Authentifizierung</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <translation>Benutzer</translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>Der anzumeldende Benutzer.</translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <translation>Profil</translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Das anzumeldende Benutzerprofil.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Benutzerprofil:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Benutzer:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Passwort:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ihr Passwort für die Authentifizierung.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ihr SIP-Anmeldename. Häufig identisch mit Ihrem SIP-Nutzernamen, dann leerlassen. Falls nicht, wird Ihr Provider dies mitteilen.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>N&amp;utzername:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation>Anmeldung für Realm erforderlich:</translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <translation>Realm</translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation>Der Realm, für den Sie sich anmelden müssen.</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Kumpel</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Name und Rufnummer/SIP-Adresse aus Adressbuch kopieren.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefon:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Name Ihres Kumpels.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>&amp;Erreichbarkeit anzeigen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Wenn aktiviert, erfragt Twinkle den Online-Status (Erreichbarkeit) des Buddy. Diese Funktion muss vom Provider des Buddy und gegebenenfalls auch von Ihrem Provider durch bereitstellen eines &quot;presence agent&quot; im Netz unterstützt werden, um zu funktionieren.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Name:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>SIP-Adresse Ihres Kumpels.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Sie müssen einen Namen angeben.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Unzulässige SIP-Adresse.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Fehler beim Speichern der Kumpelliste: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Erreichbarkeit</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>unbekannt</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>offline</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>online</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>Abfrage nicht angenommen</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>nicht bekanntgegeben</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>Bekanntgeben nicht möglich</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>Abfrage fehlgeschlagen</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Mit Rechtsklick Kumpel hinzufügen.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>Fehler beim Öffnen der Soundkarte</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>Fehler beim Erzeugen des UDP-Socket (RTP) für Port %1</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Fehler beim Erzeugen &quot;audio receiver thread&quot;.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Fehler beim Erzeugen &quot;audio transmitter thread&quot;.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>lokaler Benutzer</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>Gegenstelle</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>Fehler</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>unbekannt</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>kommend</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>gehend</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Abmelden</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>alle Endgeräte abmelden</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Tastatur</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>Funktionstaste A. Selten benötigt.</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>Funktionstaste B. Selten benötigt.</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>Funktionstaste C. Selten benötigt.</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Stern (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Raute (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>Funktionstaste D. Selten benötigt.</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>S&amp;chliessen</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n</context>\n<context>\n    <name>FreeDeskSysTray</name>\n    <message>\n        <source>Show/Hide</source>\n        <translation type=\"obsolete\">Wiederherstellen/Minimieren</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"obsolete\">Beenden</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Failed to create a UDP socket (SIP) on port %1</source>\n        <translation type=\"obsolete\">Fehler beim Erzeugen des UDP socket (SIP) für Port %1</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Die folgenden Benutzerprofile verwenden den gleichen SIP-Benutzernamen &quot;%1&quot;</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Gleichzeitig aktive Benutzerprofile müssen eindeutige SIP-Benutzernamen haben.</translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Twinkle kann kein aktives Netzwerk-Interface finden, und nutzt nun 127.0.0.1 als lokale IP-Adresse. Wenn Sie später eine Netzwerkverbindung herstellen, müssen Sie Twinkle neu starten, damit es die korrekte Netzadresse finden und funktionieren kann.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Leitung %1: eingehender Ruf für %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Ruf weitervermittelt durch %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation>Leitung %1: Gegenstelle hat Ruf abgebrochen.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation>Leitung %1: beendet durch Gegenstelle.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Leitung %1: SDP Antwort der GgSt. nicht unterstützt.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Leitung %1: keine SDP Antwort der Gegenstelle.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation>Leitung %1: Inhaltstyp in Antwort der Ggst nicht unterstützt.</translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Leitung %1: kein ACK von Ggst, Ruf wird beendet.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Leitung %1: kein PRACK von Ggst, Ruf wird beendet.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Leitung %1:  PRACK Fehler.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Leitung %1:  Fehler bei Ruf beenden.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Leitung %1: Ggst hat angenommen.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Leitung %1: Ruf erfolglos.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>Der Ruf kann umgeleitet werden nach:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation>Leitung %1: Anruf beendet.</translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Leitung %1: Verbindung hergestellt.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Antwort GgSt auf Abfrage der Eigenschaften: %1 %2</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Fähigkeiten Gegenstelle %1</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation>Erlaubte &quot;body types&quot;:</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>unbekannt</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Erlaubte &quot;encodings&quot;:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Erlaubte Sprachen:</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Erlaubte &quot;requests&quot;:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation>Unterstützte &quot;extensions&quot;:</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation>keine</translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Endgeräte-Typ:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation>Leitung %1: Fehler bei &quot;Gespräch fortsetzen&quot;.</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, Anmeldung erfolglos: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, Anmeldung erfolgreich (gültig %2 Sek.)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, Anmeldung erfolglos: STUN Fehler</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, Abmeldung erfolgreich: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation>%1, Fehler bei Abfrage Anmeldungen: %2 %3</translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: Sie sind nicht angemeldet</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: folgende Anmeldungen aktiv</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: Abfrage Anmeldungen läuft...</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Leitung %1: leite Anfrage um nach </translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Leite Anfrage um nach: %1</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Leitung %1: DTMF empfangen:  </translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation>Ungültiges DTMF-Ereignis (%1)</translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Leitung %1: sende DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation>Leitung %1: GgSt unterstützt keine DTMF-Ereignisse.</translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation>Leitung %1: Mitteilung empfangen.</translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Ereignis: %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Status: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Ursache: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Fortschritt: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Leitung %1: Rufweitervermittlung erfolglos.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Leitung %1: Ruf wurde weitervermittelt.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Leitung %1: Rufweitervermittlung läuft...</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation>Gegenstelle stoppt Mitteilungsversand.</translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Leitung %1: Rufweitervermittlung an %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Rufweitervermittlung angefordert von %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation>Leitung %1:  Rufweitervermittlung erfolglos. Ursprüngliches Gespräch wird fortgesetzt.</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Ruf wird umgeleitet</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Benutzerprofil:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Benutzer:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Möchten Sie Rufumleitung zu folgendem Ziel gestatten?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation>In den Benutzerprofil-Einstellungen unter &quot;SIP-Protokoll&quot; können Sie festlegen, ob diese Frage angezeigt wird oder nicht.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation>Leite Anfrage um</translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation>Möchten Sie die Umleitung der %1-Anforderung zu folgendem Ziel gestatten?</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Ruf wird weitervermittelt</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation>Weitervermittlung angefordert durch:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Möchten Sie Rufweitervermittlung zu folgendem Ziel gestatten?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation>Info:</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Warnung:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation>Kritisch:</translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Firewall / NAT Analyse...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Abbrechen</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Leitung %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Klicken Sie auf das Vorhängeschloss, um korrektes SAS-Geheimwort zu bestätigen.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>Die Gegenstelle an Leitung %1 hat Verschlüsselung abgeschaltet.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Leitung %1: SAS bestätigt.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation>Leitung %1: SAS Bestätigung gelöscht.</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Leitung %1: Ruf abgelehnt.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Leitung %1: Ruf umgeleitet.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Konferenz konnte nicht geschaltet werden.</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Sperrdatei ignorieren und trotzdem starten?</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1, STUN Anfrage fehlgeschlagen: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, STUN Anfrage fehlgeschlagen.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, Fehler Voice-Mail Status.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, Voice-Mail Status abgelehnt.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, Voice-Mailbox existiert nicht.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, keine weitere Voice-Mail Statusübermittlung. </translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, Abmeldung erfolglos: %2 %3</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation>GgSt fordert Vermittlung an.</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation>Wenn diese Profile unterschiedliche Domains verwenden, bitte in einem unter SIP-Protololl folgende Option aktivieren </translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation>Domain-Name benutzen für eindeutigen Contact-Header</translation>\n    </message>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>Fehler beim Anlegen: %1 socket (SIP) auf port %2</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Akzeptiert durch Netzwerk</translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Fehler beim Speichern des Nachrichtenanhangs: &quot;%1&quot;</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation>Kann Webbrowser nicht öffnen: %1</translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation>Konfigurieren Sie Ihren Webbrowser in den Systemeinstellungen.</translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Auswahl Adresse</translation>\n    </message>\n    <message>\n        <source>Name</source>\n        <translation type=\"obsolete\">Name</translation>\n    </message>\n    <message>\n        <source>Type</source>\n        <translation type=\"obsolete\">Typ</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"obsolete\">Telefon</translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>Nur &amp;SIP-Adressen zeigen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Wenn aktiviert, werden nur Kontakte angezeigt, die eine gültige SIP-Adresse enthalten, also beginnend mit &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>Aktualisie&amp;ren</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Adressliste aus KAddressbook erneut einlesen.&lt;br&gt;\nEin Schliessen und erneutes Öffnen des Fensters führt &lt;i&gt;nicht&lt;/i&gt; zum Neueinlesen.&lt;br&gt;\nÄnderungen im Adressbestand werden erst durch &quot;Aktualisieren&quot; in Twinkle sichtbar.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation>Diese Adressliste stammt aus &lt;b&gt;KAddressbook&lt;/b&gt; (bzw Kontact). Adressen/Kontakte, die keine Telefonnr oder SIP-Adresse enthalten, sind nicht aufgeführt. \nNutzen Sie zum Anlegen und Bearbeiten Ihrer systemweiten Adressinformationen das Programm KAddressbook bzw Kontact.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>&amp;Lokales Adressbuch</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"obsolete\">Anmerkung</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Kontakte des lokalen Twinkle-Adressbuchs.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Neu</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Neuen Kontakt im lokalen Adressbuch anlegen.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Löschen</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Ausgewählten Kontakt aus dem lokalen Adressbuch löschen.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>B&amp;earbeiten</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Ausgewählten Kontakt im lokalen Adressbuch bearbeiten.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;&lt;b&gt;KAddressbook&lt;/b&gt; bzw &lt;b&gt;Kontact&lt;/b&gt; scheint keine Einträge mit Telefonnr zu enthalten, die Twinkle dort auslesen könnte. Bitte nutzen Sie eines dieser Programme, um Ihre Adressdaten zu bearbeiten.&lt;/p&gt;\n&lt;p&gt;Weiterhin steht Ihnen Twinkles lokales Adressbuch unabhängig von o.g. Programmen zur Verfügung.&lt;/p&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Name Benutzerprofil</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Name für das neue Profil:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>Der &lt;b&gt;Name, unter dem das neue Profil angelegt&lt;/b&gt; wird, in dem dann alle zusammengehörenden Daten wie Provider, SIP-Nutzername, Passwort usw gespeichert werden. (entsprechend z.B. einer &quot;Identität&quot; unter KMail)&lt;br&gt;&lt;br&gt;\nDa Sie bei Twinkle mehrere Benutzerprofile anlegen können, z.B. um mehrere SIP-Provider zu nutzen, muss jedes Profil einen Namen erhalten. Unter diesem Namen finden Sie es später in Auswahllisten, Meldungen usw.&lt;br&gt;\nEs bietet sich an, hier Ihre SIP-Adresse als Name zu verwenden, also &lt;b&gt;meinname@meinprovider.de&lt;/b&gt;, aber Sie können letztendlich beliebige Namen wählen.&lt;br&gt;\n&lt;p&gt;\n&lt;b&gt;Bevor Sie hier Ihr erstes SIP-Profil anlegen, sollten Sie sich bei einem SIP-Provider (vertraglich) angemeldet haben&lt;/b&gt; und sich notieren, welche &lt;b&gt;SIP-Zugangsdaten&lt;/b&gt; dieser für Sie zur Verfügung gestellt hat.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Kann den Ordner &quot;.twinkle&quot; in Ihrem home-Ordner (&quot;/home/ihrname/&quot;) nicht finden.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Profil mit diesem Namen existiert bereits.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Profil &quot;%1&quot; umbenennen in:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - Liste Anrufe</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Uhrzeit</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>Ank/Abg</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>Gegenstelle</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Betreff</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Status</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Details</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Details zum ausgewählten Anruf.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Anzeigen</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>E&amp;ingehende Anrufe</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Wenn aktiviert, werden Anrufe angezeigt bei denen Sie von jemand angerufen wurden.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>Ab&amp;gehende Anrufe</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Wenn aktiviert, werden Anrufe angezeigt die Sie selbst getätigt haben.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>Be&amp;antwortete Anrufe</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Wenn aktiviert, werden Anrufe angezeigt die zustande kamen.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>A&amp;nrufversuche</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Wenn aktiviert, werden Anrufe angezeigt die nicht zustande kamen.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>N&amp;ur aktive Benutzerprofile</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Wenn aktiviert, nur Anrufe zeigen, die mit/zu einem der aktuell aktivierten Benutzerprofile getätigt wurden.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Liste löschen</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>Löscht das gesamte Anrufe-Protokoll,&lt;br&gt;\n&lt;b&gt;inklusive&lt;/b&gt; aller evtl gerade über &quot;Anzeigen&quot; &lt;b&gt;ausgeblendeten Einträge.&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Fenster schliessen.</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Anruf Start:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Angenommen:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Anruf Ende:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Anrufdauer:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Richtung:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Von:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>An:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Antwort auf:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Über:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Betreff:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Beendet von:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Status:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Typ Gegenstelle:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Benutzerprofil:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>Gespräch</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Anrufen... (Doppelklick)</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Eintrag löschen</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Aw:</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Markierte Adresse/Nummer anrufen.</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>&amp;Schliessen (Esc)</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>Anrufen (&amp;Enter)</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation>Anzahl der Anrufe:</translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation>###</translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation>Gesamte Anrufdauer:</translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation>%1 ruft an</translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Anrufen</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>An (&amp;Telnr):</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Sie können hier einen Betreff angeben, der ebenso wie Ihr Displayname von der Gegenstelle angezeigt werden kann.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Adresse/Nr aus dem KDE-Adressbuch auswählen.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Der Anschluss, den Sie anrufen möchten.  Dies kann eine vollständige SIP-Adresse sein, wie &lt;b&gt;sip:example@example.com&lt;/b&gt;, oder auch nur eine Telephonnummer bzw. der Benutzername einer SIP-Adresse, dann ergänzt Twinkle sie mit der im Benutzerprofil eingetragenen Domain zur vollständigen SIP-Adresse.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Das Benutzerprofil -und damit der Provider- mit dem der Ruf gestartet wird.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Betreff:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Von:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>Absenderangaben &amp;unterdrücken</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Mit dieser Option weisen Sie Ihren SIP-Provider an, Ihre Absenderangaben (z.B. Telefonnr, SIP-Adresse) nicht an die Gegenstelle weiterzuleiten. Prinzipbedingt wird Ihre IP-Adresse &lt;b&gt;immer&lt;/b&gt; der Gegenstelle mitgeteilt.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Achtung: &lt;/b&gt;Nicht alle Provider unterstützen diese Funktion!&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Nicht alle SIP-Provider unterstützen die Funktion &quot;Absenderangaben unterdrücken&quot;. Bitte vergewissern Sie sich, bevor Sie sich auf diese Funktion verlassen.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Inhalt der aktuellen Logdatei (~/.twinkle/twinkle.log)</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>S&amp;chliessen</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Löschen</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Die Anzeige des Fensters löschen. Die Logdatei selbst wird &lt;b&gt;nicht&lt;/b&gt; gelöscht oder geleert.</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - Instant Message</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;An (Adr):</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>Als Absender verwendetes Benutzerprofil.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Die Adresse/Nummer der Gegenstelle, an die Sie eine Instant Message senden möchten. Wie immer bei Twinkle kann dies eine vollständige Adresse oder ein Username sein. Wenn Sie nur den Usernamen angeben, ergänzt Twinkle die Domain aus dem verwendeten Absender-Benutzerprofil.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Adresse/Nr aus dem KDE-Adressbuch auswählen.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>Ben&amp;utzerprofil:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Dialog</translation>\n    </message>\n    <message>\n        <source>The exchanged messages.</source>\n        <translation type=\"obsolete\">Die gesendeten und empfangenen Nachrichten. Gesendete schwarz, empfangene blau.</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Schreiben Sie hier Ihre Nachricht und klicken Sie &quot;senden&quot; oder drücken Sie &quot;Enter&quot; zum abschicken.</translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Senden</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Nachricht senden.</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Übertragungsfehler</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Übertragungsbestätigung</translation>\n    </message>\n    <message>\n        <source>Instant message toolbar</source>\n        <translation type=\"obsolete\">Instant Message Werkzeugleiste</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Sende Datei...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Sende Datei</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>Bild in Vorschau verkleinert</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Öffnen mit %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Öffnen mit...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Anhang speichern unter...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Datei dieses Namens existiert bereits! Löschen und durch neue Datei ersetzen?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Fehler beim Speichern des Anhangs.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 schreibt gerade eine Nachricht.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation>Größe</translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>Nachricht wird gesendet</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>&amp;Nummer:</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Der Anschluss, den Sie anrufen möchten.  Dies kann eine vollständige SIP-Adresse sein, wie &lt;b&gt;sip:example@example.com&lt;/b&gt;, oder auch nur eine Telephonnummer bzw. der Benutzername einer SIP-Adresse, dann ergänzt Twinkle sie mit der im Benutzerprofil eingetragenen Domain zur vollständigen SIP-Adresse.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Das Benutzerprofil -und damit der Provider- mit dem der Ruf gestartet wird.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Profil:</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Wählen</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Startet den Anruf .</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Rufnummer/SIP-Adresse aus Adressbuch wählen.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation>Anzeige und Einstellen des Dienstes &quot;Automatisch annehmen&quot;.</translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation>Anzeige der Nachrichten auf Voice-Mailboxen; Anklicken zum Abhören der Nachrichten.</translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation>Anzeige und Einstellen des Dienstes &quot;Rufumleitung&quot;.</translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation>Anzeige und Einstellen des Dienstes &quot;Bitte nicht stören&quot;.</translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation>Anzeige &quot;Anrufe in Abwesenheit&quot; und Öffnen der Anruferliste.</translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation>Anzeige und Abruf der Anmeldezustände. Die Ergebnisse des Abrufs werden in der Detailanzeige dargestellt.</translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation>Detailanzeige</translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Leitungsstatus</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Leitung &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation>Alt+1</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Anklicken (oder Alt+1), um auf Leitung 1 zu schalten.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Von:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>An:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Betreff:</translation>\n    </message>\n    <message>\n        <source>Visual indication of line state.</source>\n        <translation type=\"obsolete\">Optische Anzeige des Leitungsstatus.</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call is on hold</source>\n        <translation type=\"obsolete\">Anruf gehalten</translation>\n    </message>\n    <message>\n        <source>Voice is muted</source>\n        <translation type=\"obsolete\">stummgeschaltet</translation>\n    </message>\n    <message>\n        <source>Conference call</source>\n        <translation type=\"obsolete\">Konferenz</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Ruf wird weitervermittelt</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThe padlock indicates that your voice is encrypted during transport over the network.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nBoth ends of an encrypted voice channel receive the same SAS on the first call. If the SAS is different at each end, your voice channel may be compromised by a man-in-the-middle attack (MitM).\n&lt;/p&gt;\n&lt;p&gt;\nIf the SAS is equal at both ends, then you should confirm it by clicking this padlock for stronger security of future calls to the same destination. For subsequent calls to the same destination, you don&apos;t have to confirm the SAS again. The padlock will show a check symbol when the SAS has been confirmed.\n&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p&gt;\nDas Vorhängeschloss erscheint, wenn eine abhörsicher verschüsselte Verbindung zur Übertragung der Sprachdaten aufgebaut werden konnte.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nBeide Teilnehmer eines verschlüsselten Gesprächs bekommen bei der ersten Kontaktaufnahme den SAS angezeigt, einen nicht fälschbaren eindeutigen &quot;Fingerabdruck&quot; der ausgehandelten Verschlüsselung. Durch Vergleich dieses SAS können Sie und Ihr Gesprächspartner sicherstellen, dass Sie tatsächlich &lt;i&gt;direkt&lt;/i&gt; miteinander verbunden sind. Stichwort &quot;man-in-the-middle attack&quot; (MitM).\n&lt;/p&gt;\n&lt;p&gt;\nDa ein Angreifer schlecht mitten im Gespräch die Stimme Ihres Gesprächspartners imitieren kann, reicht es völlig, beim ersten Telefonat den SAS vorzulesen.\nBei Übereinstimmung klicken Sie auf das Vorhängeschloss, und Twinkle merkt sich die (den &quot;Ausweis&quot; der) Gegenstelle als &quot;persönlich bekannt&quot; und lässt sich bei zukünftigen Anrufen von/zu dieser GgSt nicht täuschen (&quot;Ausweiskontrolle&quot;). Das Schloss wird mit einem Häkchen dargestellt, und signalisiert so, dass die GgSt auf ihre Identität überprüft und eindeutig erkannt wurde, und also eine direkte Verbindung besteht.\n&lt;/p&gt;\n&lt;p&gt;Klick auf ein Schloss &lt;i&gt;mit&lt;/i&gt; Häkchen löscht die Vertrauensbeziehung und Sie können/müssen den SAS neu vergleichen&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation>SAS - Geheimwort (Short authentication string)</translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Anrufdauer</translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Leitung &amp;2:\n</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation>Alt+2</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Anklicken (oder Alt+2), um auf Leitung 2 zu schalten.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Datei</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>B&amp;earbeiten</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>&amp;Anruf</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Leitung auswählen</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>A&amp;nmeldung</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>Dien&amp;ste</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>Ans&amp;icht</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>&amp;Hilfe</translation>\n    </message>\n    <message>\n        <source>Call Toolbar</source>\n        <translation type=\"obsolete\">Anruf Werkzeugleiste</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Abmelden und Twinkle beenden</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>B&amp;eenden</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>Über Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>Ü&amp;ber Twinkle</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Anrufen - erweiterte Nummerneingabe, Betreff...</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Anruf entgegennehmen - landläufig &quot;Abheben&quot;</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation>Anruf beenden</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Eingehenden Ruf ablehnen</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Ein Gespräch halten, oder ein gehaltenes fortsetzen</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Eingehenden Ruf umleiten ohne Gesprächsannahme</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Öffnet eine Wähltastatur zur Eingabe von Tastenbefehlen - für Steuerung von zB. Anrufbeantwortern</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Anmelden beim SIP-Sever</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>An&amp;melden</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Abmelden</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>Abmel&amp;den</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Dieses Telefon abmelden</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Anmeldungen bei den Servern abfragen</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>Anmeldungen &amp;zeigen</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Fähigkeiten Gegenstelle</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Abfrage der &quot;terminal capabilities&quot;, der Eigenschaften einer Gegenstelle</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Bitte nicht stören</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>&amp;Bitte nicht stören</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Rufumleitung</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>&amp;Rufumleitung...</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Wahlwiederholung, wählt letzten Ruf erneut</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>Über Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>Über &amp;Qt</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Benutzerprofil bearbeiten</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>Ben&amp;utzerprofil...</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation>Leitung1, 2 und lokal zu einer 3er Konferenz zusammenschalten</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Das Mikrofon für diese Leitung ab- oder wieder anschalten</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Gespräch weitervermitteln</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Systemeinstellungen bearbeiten</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>&amp;Systemeinstellungen...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Abmelden alle Endg</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>&amp;Abmelden alle Endger.</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Abmelden aller Geräte unter dieser Benutzerkennung</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Autom. Annehmen</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>&amp;Autom. Annehmen</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>SystemLog anzeigen</translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>Liste der letzen Anrufe anzeigen</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>Liste aller Anru&amp;fe...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Benutzerprofile ...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Benutzerprofile ...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Benutzerprofile de/aktivieren, bearbeiten usw.</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>&quot;Was ist das?&quot;-Kontexthilfe</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>Was ist &amp;das?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Leitung 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Leitung 2\n</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>frei</translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>wählt</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>versuche Rufaufbau, bitte warten</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>ankommender Ruf</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>Verbindungsaufbau, bitte warten</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>Verbindung hergestellt</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>Verbindung hergestellt (warte auf Daten)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation>trenne Verbindung, bitte warten</translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>unbekannter Status</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Sprachübertragung verschlüsselt</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>SAS bestätigen.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation>SAS Bestätigung löschen.</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Benutzer:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Ruf:</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Anmeldungsstatus:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>angemeldet</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>fehlgeschlagen</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>nicht angemeldet</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Kein Benutzer angemeldet.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>&quot;Bitte nicht stören&quot; aktiviert für:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Rufumleitung aktiviert für:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>&quot;Automatisch annehmen&quot; aktiviert für:</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>&quot;Bitte nicht stören&quot; nicht aktiviert.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>Rufumleitung nicht aktiviert.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>&quot;Automatisch annehmen&quot; nicht aktiviert.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>Keine Anrufe in Abwesenheit.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>1 Anruf in Abwesenheit.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>%1 Anrufe in Abwesenheit.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Anklicken öffnet Anrufliste mit Details .</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Starte Benutzerprofile...</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Die folgenden Benutzerprofile verwenden die gleiche SIP-Adresse %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Sie können nicht für einen SIP-Account gleichzeitig mehrere Profile aktivieren.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation>Der SIP UDP port wurde geändert, dies wird erst beim nächsten Start von Twinkle wirksam.</translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation>Rückfrage</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Absenderangaben unterdrücken</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Anklicken: Anmeldungen abfragen.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 neue, 1 alte Mitteilung</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 neue, %2 alte Mitteilungen</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>1 neue Mitteilung</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 neue Mitteilungen</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>1 alte Mitteilung</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 alte Mitteilungen</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Mitteilungen da</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Keine Mitteilungen</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Voice-Mail Status:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Fehler</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Unbekannt</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Anklicken: Voice-Mail abrufen.</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Anklicken: (de/)aktivieren</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Anklicken: aktivieren</translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation>nicht eingetragen</translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation>Sie müssen die Adresse/Nr Ihres Anrufbeantworters im Profil eintragen, damit dies geht.</translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>Kann Voice-Mail nicht abrufen - Leitung belegt.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation>Die Voice-Mail-Adresse &quot;%1&quot; is ungültig. Bitte korregieren Sie die Einstellungen im Benutzerprofil.</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <comment>toolbar text</comment>\n        <translation>Anruf</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <comment>call menu text</comment>\n        <translation>&amp;Anruf...</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <comment>toolbar text</comment>\n        <translation>Annehmen</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <comment>menu text</comment>\n        <translation>&amp;Annehmen</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <comment>toolbar text</comment>\n        <translation>Auflegen</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <comment>menu text</comment>\n        <translation>Auf&amp;legen</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <comment>toolbar text</comment>\n        <translation>Abweisen</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <comment>menu text</comment>\n        <translation>Ab&amp;weisen</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <comment>toolbar text</comment>\n        <translation>Halten</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <comment>menu text</comment>\n        <translation>&amp;Halten</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <comment>toolbar text</comment>\n        <translation>Umleiten</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <comment>menu text</comment>\n        <translation>Uml&amp;eiten...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <comment>toolbar text</comment>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <comment>menu text</comment>\n        <translation>&amp;DTMF...</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Fähigkeiten Gegenstelle...</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <comment>toolbar text</comment>\n        <translation>Wahlwiederholung</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <comment>menu text</comment>\n        <translation>Wahlwiederholun&amp;g</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <comment>toolbar text</comment>\n        <translation>Konf.</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <comment>menu text</comment>\n        <translation>Konferen&amp;z</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <comment>toolbar text</comment>\n        <translation>Stumm</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <comment>menu text</comment>\n        <translation>Stu&amp;mm\n</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <comment>toolbar text</comment>\n        <translation>Vmtlg</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Vermitte&amp;ln...</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Anrufbeantworter</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>A&amp;nrufbeantworter</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation>Voice-Mailbox abfragen</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Kumpelliste</translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>&amp;Mitteilung</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Msg</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>Instant &amp;Message...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Instant Message senden</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Anrufen...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>B&amp;earbeiten...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Löschen</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>O&amp;ffline</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>&amp;Online</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>Errei&amp;chbarkeit ändern</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Kumpel hinzufügen...</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Fehler beim Speichern der Buddyliste: &quot;%1&quot;</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation>Die Liste Ihrer &lt;b&gt;Benutzerprofile (fett)&lt;/b&gt; und Buddies &lt;i&gt;(deutsch: &quot;Kumpels&quot;. Ihre wichtigen Kontakte)&lt;/i&gt; für die einzelnen Profile.&lt;br&gt;\nÜber das Kontextmenü des einzelnen Benutzerprofils erzeugen Sie neue Buddy-Einträge und stellen Ihren eigenen Online-Status ein.&lt;br&gt;\nDer Online-Status der Buddies wird durch gelbe (=online) und graue (=offline) Icons dargestellt. Details erscheinen, wenn Sie den Cursor über das Icon stellen.&lt;br&gt;\n&lt;br&gt;\nUm Ihren eigenen Online-Status zu veröffentlichen, brauchen Sie die Unterstützung durch einen öffentlichen &quot;presence server&quot; &lt;i&gt;Ihres&lt;/i&gt; Providers.&lt;br&gt;\nUm den Online-Status eines Buddies abzufragen, muss &lt;i&gt;dessen&lt;/i&gt; Provider einen &quot;presence server&quot; im Netz ereichbar halten, und dieser muss Ihre Abfrage gestatten. Es ist daher sinnvoll, Buddies mit einem bestimmten Provider (thomas@&lt;b&gt;DerProvider.de&lt;/b&gt;) unter einem gültigen eigenen Benutzerprofil mit dem selben Provider (ich.selber@&lt;b&gt;DerProvider.de&lt;/b&gt;) anzulegen, da viele &quot;presence server&quot; nur dann die Abfrage gestatten.</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>&amp;Kumpelliste </translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation>&amp;Detailanzeige</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation>Registrieren</translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation>&amp;Registrieren</translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation>Anruf</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation>&amp;Annehmen</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation>Annehmen</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation>Auf&amp;legen</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation>Auflegen</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation>Ab&amp;weisen</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Abweisen</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation>&amp;Halten</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation>Halten</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation>Uml&amp;eiten...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation>Umleiten</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation>&amp;DTMF...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation type=\"unfinished\">&amp;Fähigkeiten Gegenstelle...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation>Wahlwiederholun&amp;g</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation>Wahlwiederholung</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation>Konferen&amp;z</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation>Konf.</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation>Stu&amp;mm\n</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation>Stumm</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation>Vermitte&amp;ln...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation>Vmtlg</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - Rufnummernumwandlung</translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Suchausdruck:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Ersetzung:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation>Formatstring wie in Perl für die Ersetzung der Nummer.</translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation>Regulärer Ausdruck (Perl regex), der die zu ändernde Rufnummer beschreibt. </translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>Suchausdruck darf nicht leer sein.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>Ersetzungswert darf nicht leer sein.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Ungültiger regulärer Ausdruck.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Rufumleitung</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Ankommenden Ruf umleiten nach</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Es können bis zu 3 Ziele für die Rufumleitung angegeben werden. Wird der Ruf vom ersten Ziel nicht angenommen, werden das zweite und dann das dritte verwendet.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3. Ziel:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2. Ziel:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1. Ziel:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Rufnummer/SIP-Adresse aus Adressbuch wählen.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - Netzwerkanschluss auswählen</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Bitte wählen Sie den zu verwendenden Anschluss / IP-Adr.:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>Auf Ihrem Rechner sind mehrere IP-Adressen verfügbar. Bitte wählen Sie diejenige, unter der Ihr Rechner aus dem Internet bzw -wenn Sie einen Router verwenden- in ihrem lokalen Netz erreichbar ist. Diese IP-Adresse verwendet Twinkle dann in den SIP-Datenpaketen als Absenderangabe.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>&amp;IP-Adr. als Standard festlegen</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Die ausgewählte IP-Adresse als default setzen. In Zukunft verwendet Twinkle beim Start automatisch diese Adresse.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>A&amp;nschl. als Standard festlegen</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>Den ausgewählten Netzwerkanschluss als default setzen. In Zukunft verwendet Twinkle beim Start automatisch diesen Anschluss.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Die Defaulteinstellungen für IP bzw. Anschluss lassen sich jederzeit in den Systemeinstellungen löschen oder ändern.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle - Benutzerprofil auswählen</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Wählen Sie die Benutzerprofile, die verwendet werden sollen:</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Benutzerprofil</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Markieren Sie die Benutzerprofile, mit denen Twinkle arbeiten soll und drücken Sie dann &quot;Anwenden&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;New</source>\n        <translation>&amp;Neu</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Anlegen eines neuen Benutzerprofils mit dem Profil-Editor.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>&amp;Assistent</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Anlegen eines neuen Benutzerprofils mit dem Assistenten.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>Änd&amp;ern</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Hervorgehobenes Profil bearbeiten.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Löschen</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Ausgewähltes Profil löschen.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>&amp;Umbenennen</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Hervorgehobenes Profil umbenennen.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>Als &amp;Standard festlegen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Die ausgewählten Profile als default verwenden. In Zukunft verwendet Twinkle beim Start automatisch diese Benutzerprofile.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Ausführen</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Twinkle startet die markierten Benutzerprofile.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>S&amp;ystemeinstellungen</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Systemeinstellungen bearbeiten.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Bevor Sie Twinkle benutzen können, müssen Sie ein Benutzerprofil anlegen.&lt;br&gt;Klicken Sie OK um ein neues Profil anzulegen.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"obsolete\">&lt;html&gt;Sie können zum Erzeugen des Benutzerprofils den Profileditor verwenden. Dieser erlaubt detailierte Einstellungen für SIP-Protokoll, RTP sowie viele weitere Bereiche.&lt;br&gt;&lt;br&gt;Oder Sie nutzen den Wizard, um schnell und einfach die wichtigsten Einstellungen für ein Benutzerprofil vorzunehmen. Der Wizard erfragt von Ihnen nur die absolut notwendigen Daten, die Sie von Ihrem SIP-Provider bei der Anmeldung mitgeteilt bekommen haben sollten. Für einige Provider werden Ihnen sogar viele dieser Daten vorgeschlagen. Wenn Sie den Wizard nutzen, können Sie später immer noch alle Details mit Hilfe des Editors nach Wunsch ändern und ergänzen.&lt;br&gt;&lt;br&gt;Hilfe erhalten Sie überall in Twinkle entweder durch Drücken von &quot;Umschalt + F1&quot;, über das Kontextmenü via rechten Mausknopf, oder durch Anklicken des &quot;?&quot; oben rechts am Fensterrand.&lt;br&gt;&lt;br&gt;Bitte wählen Sie, wie Sie das Benutzerprofil anlegen wollen.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Als nächstes können und sollten Sie die Systemeinstellungen kontrollieren und anpassen. Insbesondere die Einstellung zu Mikrophon und Lautsprecher im Bereich Audio sollte zu der in Ihrem Rechner vorhandenen Hardware passen&lt;br&gt;&lt;br&gt;Klicken Sie OK um in die Systemeinstellungen zu gelangen. Sie können auch später jederzeit alle Systemeinstellungen ändern&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>Sie haben kein Benutzerprofil zur Verwendung ausgewählt. Bitte wählen Sie mindestens ein Profil.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Benutzerprofil %1 wirklich löschen?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Profil löschen</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Fehler beim Löschen des Profils.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Fehler beim Umbenennen des Profils.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Die Defaulteinstellungen lassen sich jederzeit in den Systemeinstellungen löschen oder ändern. &lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Kann den Ordner &quot;.twinkle&quot; in Ihrem home-Ordner (&quot;/home/ihrname/&quot;) nicht finden.</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>&amp;Profil-Editor</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation>Profil erstellen</translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation>Ed&amp;itor</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation>Profil ändern</translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Benutzer auswählen</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>Alle au&amp;swählen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>Alle abwäh&amp;len</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <translation>Zweck</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Benutzer</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Anmelden</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Benutzerprofile zum Anmelden wählen.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Abmelden</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Benutzerprofile zum Abmelden wählen.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Alle Geräte abmelden</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Benutzerprofile zum Abmelden (alle Geräte) wählen.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Bitte nicht stören</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Benutzerprofile wählen, für die der Dienst &quot;Bitte nicht stören&quot; aktiviert werden soll.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Autom. annehmen</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Benutzerprofile wählen, für die der Dienst &quot;Automatisch Annehmen&quot; aktiviert werden soll.</translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Datei senden</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Datei zum Senden auswählen.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Datei:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Betreff:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Datei existiert nicht.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Datei senden...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation>Twinkle - Rufumleitung</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Benutzer:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation>Es gibt 3 Arten von Rufumleitung:&lt;p&gt;\n&lt;b&gt;Immer:&lt;/b&gt; alle Anrufe umleiten\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Besetzt:&lt;/b&gt; Anruf umleiten, wenn beide Leitungen besetzt\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Keine Antwort:&lt;/b&gt; Anruf nach Ablauf der &quot;keine-Antwort&quot;-Zeit umleiten\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation>&amp;Immer</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation>&amp;Alle Anrufe umleiten</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation>Den Dienst &quot;alle Anrufe umleiten&quot; aktivieren.</translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation>Umleiten nach</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Es können bis zu 3 Ziele für die Rufumleitung angegeben werden. Wird der Ruf vom ersten Ziel nicht angenommen, werden das zweite und dann das dritte verwendet.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3. Ziel:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2. Ziel:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1. Ziel:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Rufnummer/SIP-Adresse aus Adressbuch wählen.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Besetzt</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation>&amp;Anrufe umleiten, wenn alle Leitungen besetzt</translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation>Den Dienst &quot;Umleiten, wenn besetzt&quot; aktivieren.</translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>&amp;keine Antwort</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation>&amp;Anrufe umleiten, wenn Benutzer nicht reagiert</translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation>Den Dienst &quot;Umleiten, wenn keine Antwort&quot; aktivieren.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Änderungen speichern.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Ihre Änderungen verwerfen und Fenster schließen.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>Sie haben eine ungültige Zieladresse eingegeben.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Systemeinstellungen</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Allgemein</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Audio</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Klingeltöne</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Netzwerk</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Protokoll</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Bereich wählen, den Sie ändern wollen.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Soundkarte</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Audioanschluss wählen, über den der Klingelton abgespielt werden soll. Darf identisch mit Kopfhörer-Anschluss sein.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Audioanschluss für Mikrofon wählen.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Audioanschluss für Kopfhörer(/Lautsprecher) wählen. Darf identisch mit Anschluss für Klingelton sein.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Kopfhörer:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Klingelton:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Anderes Gerät:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Mikrofon:</translation>\n    </message>\n    <message>\n        <source>When using ALSA, it is not recommended to use the default device for the microphone as it gives poor sound quality.</source>\n        <translation type=\"obsolete\">Wenn Ihr Gesprächspartner schlechte Tonqualität beklagt, versuchen Sie für ALSA ein anderes Gerät statt &quot;default&quot;.</translation>\n    </message>\n    <message>\n        <source>Reduce &amp;noise from the microphone</source>\n        <translation type=\"obsolete\">Spezielle Störgeräusch-U&amp;nterdrückung für manche defekte Gateways</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+N</translation>\n    </message>\n    <message>\n        <source>Recordings from the microphone can contain noise. This could be annoying to the person on the other side of your call. This option removes soft noise coming from the microphone.\n\nThe noise reduction algorithm is very simplistic. Sound is captured as 16 bits signed linear PCM samples. All samples between -50 and 50 are truncated to 0.</source>\n        <translation type=\"obsolete\">Diese Option setzt alle Tonsamples mit  -50 &lt; Messwert &lt; 50 auf 0.\nMichel hat diesen Hack entwickelt, als er mit fehlerhaften A/D-Wandlern in einigen Provider-Gateways konfrontiert war.\nIm Normalfall führt das Aktivieren eher zu einer kaum bemerkbaren Verschlechterung der Tonqualität.</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Erweitert</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation>OSS &amp;Fragmentgröße:</translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation>16</translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation>32</translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation>64</translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation>128</translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation>256</translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation>Die play period size bestimmt  vereinfacht gesagt die Grösse der Pakete, in denen die Soundkarte die Daten geliefert bekommt. Viele kleine Pakete belasten den Prozessor mehr, aber der Verlust eines Pakets stört dann weniger. Bei Problemen mit Aussetzern oder Rattern im Ton sollten Sie hier mit anderen Werten ein wenig experimentieren.</translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation>Die OSS period size bestimmt  vereinfacht gesagt die Grösse der Pakete, in denen die Soundkarte die Daten geliefert bekommt/liefert. Viele kleine Pakete belasten den Prozessor mehr, aber der Verlust eines Pakets stört dann weniger. Bei Problemen mit Aussetzern oder Rattern im Ton sollten Sie hier mit anderen Werten ein wenig experimentieren.</translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation>Die capture period size bestimmt  vereinfacht gesagt die Grösse der Pakete, in denen die Soundkarte die Daten des Mikrofons liefert. Viele kleine Pakete belasten den Prozessor mehr, aber der Verlust eines Pakets stört dann weniger. Bei Klagen der Gegenstelle über Aussetzer oder Rattern im Ton sollten Sie hier mit anderen Werten ein wenig experimentieren.</translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>&amp;Max. Grösse System-Log:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation>Das SystemLog wird nur von Experten zur Fehlersuche benötigt. Dieser Wert legt die maximale Grösse fest, die die Logbuch-Datei  annehmen kann. Bei Erreichen dieser Grösse wird sie von twinkle.log in twinkle.log.old umbenannt, wobei eine schon existierende ältere Datei gleichen Namens gelöscht wird. Das Log wird in eine neue leere Datei twinkle.log fortgesetzt. Im Normalfall reicht hier 1MB völlig aus.</translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation>MB</translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation>&amp;Debug-Meldungen loggen</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation>Aktiviert das Loggen von &quot;debug&quot;-Ausgaben.</translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>&amp;SIP-Meldungen loggen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation>Aktiviert das Loggen von Ausgaben für SIP-Ereignisse.</translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>S&amp;TUN-Meldungen loggen</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation>Aktiviert das Loggen von Ausgaben für STUN-Ereignisse.</translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation>Sp&amp;eicher-Meldungen loggen</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation>Aktiviert das Loggen von RAM-Anforderungs/Freigabe Protokollen.</translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Systemabschnitt</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Bei &amp;Start Icon in Systemabschnitt erzeugen</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation>Mit dieser Option erzeugt Twinkle beim Start ein Twinkle-Sternchen im Systemabschnitt , über das Sie jederzeit auf Twinkle zugreifen können und eingehende Rufe gemeldet bekommen.</translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation>Bei &quot;Fenster sc&amp;hliessen&quot; in Systemabschnitt minimieren</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation>Wenn aktiviert, wird Twinkle durch Schliessen des Hauptfensters nicht beendet, sondern erzeugt das Sternchen im Systemabschnitt. Zum Beenden müssen Sie dann &quot;Beenden&quot; im Menü &quot;Datei&quot; oder im Kontextmenü des Sternchens wählen.</translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Programmstart</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, this IP address will be automatically selected. This is only useful when your computer has multiple and static IP addresses.</source>\n        <translation type=\"obsolete\">Eine hier eingetragene IP-Adresse wird beim Start des Programms automatisch verwendet. Nur sinnvoll, wenn Ihr Rechner mehrere Netzwerkanschlüsse und für den Internetzugang eine unveränderliche IP-Adresse hat.</translation>\n    </message>\n    <message>\n        <source>Default &amp;IP address:</source>\n        <translation type=\"obsolete\">Default &amp;IP-Addresse:</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, the IP address of this network interface be automatically selected. This is only useful when your computer has multiple network devices.</source>\n        <translation type=\"obsolete\">Wenn Ihr Rechner mehrere Netzwerkanschlüsse hat, können Sie hier festlegen, welchen davon Twinkle beim Start verwenden soll. Sie werden dann nicht beim Start nach dem zu verwendenden Anschluss gefragt.</translation>\n    </message>\n    <message>\n        <source>Default &amp;network interface:</source>\n        <translation type=\"obsolete\">Default &amp;Netzwerkanschluss:</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation>Minimiert im Sys&amp;temabschnitt starten</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation>Twinkle öffnet beim Start kein Fenster, sondern startet minimiert im Systemabschnitt - also in Form des Sternchens. Dafür sollten Sie auch ein Default-Benutzerprofil einstellen, sonst erscheint doch ein Auswahlfenster beim Start.</translation>\n    </message>\n    <message>\n        <source>Default user profiles</source>\n        <translation type=\"obsolete\">Default Benutzerprofile</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation>Die hier eingestellten Benutzerprofile werden beim Programmstart automatisch aktiviert. Sie können trotzdem jederzeit über &quot;Datei&quot; &quot;Benutzerprofile...&quot; Profile aktivieren/deaktivieren.</translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Dienste</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>&amp;Anklopfen</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation>Wenn &quot;Anklopfen&quot; aktiviert ist, kann bei einer belegten Leitung über die zweite ein weiterer Anrufer klingeln. Wenn deaktiviert, bekommt er &quot;besetzt&quot;.</translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation>Bei Beenden 3er-Konferenz &amp;beide Leitungen auflegen.</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation>Wenn aktiviert, werden durch &quot;Auflegen&quot; beide Verbindungen getrennt. Sonst trennt &quot;Auflegen&quot; nur die aktive Leitung, das Gespräch auf der anderen Leitung kann weitergeführt werden.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation>&amp;max. Anzahl Einträge in Anrufliste:</translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation>Die Länge der Anrufliste wird auf die hier angegebene Anzahl Einträge begrenzt. Ältere Eintäge werden automatisch entfernt.</translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation>Bei &amp;Anruf Hauptfenster öffnen nach</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation>Wenn das Twinkle-Hauptfenster geschlossen oder minimiert ist, wird es bei eingehendem Ruf nach der angegebenen Sekundenzahl automatisch wiederhergestellt.</translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation>Zeit in Sekunden, nach der das Fenster wiederhergestellt wird.</translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>Sekunden</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving SIP messages.</source>\n        <translation type=\"obsolete\">Der UDP Port, über den das SIP-Protokoll läuft. Standard:5060, ihr Provider kann aber einen anderen Port vorschreiben.</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>&amp;RTP-Port:</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation>Der erste UDP Port, über den das RTP-Protokoll zur Sprachdatenübertragung läuft. Ein zeitgleich geführtes 2. Gespräch nutzt einen um 2 höheren Port. Rufvermittlung weitere 2. Also beispielsweise: 1.Ltg:8000(+8001), 2.Ltg:8002(+8003), Vermitteln:8004(+8005). Standard: abhängig vom Provider meist 8000 oder 5004. Bei mehreren an einem Anschluss betriebenen SIP-fons braucht jedes seinen eigenen Bereich Ports! Also das 2. Twinkle z.B. dann 8006. </translation>\n    </message>\n    <message>\n        <source>&amp;SIP UDP port:</source>\n        <translation type=\"obsolete\">&amp;SIP UDP Port:</translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Klingelton</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>Bei eingeh. Ruf Klingelton s&amp;pielen</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Wenn aktiviert, spielt Twinkle bei eingehenden Anrufen einen Klingelton über den dafür eingestellten Audioanschluss ab.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>&amp;Default Klingelton</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Spielt den Standard-Klingelton, wenn Anruf ankommt.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>individueller &amp;Ton</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Selbst ausgewählten Klingelton abspielen bei eingehendem Anruf.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation>Geben Sie hier den Namen der .wav-Datei für Ihren individuellen Klingelton an.</translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation>Freizeichen</translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation>Freizeichen (Rufton) abspie&amp;len, wenn Tel-netz keinen liefert</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Freizeichen abspielen, wenn die Telefongesellschaft nicht selber eines einspielt. &lt;/p&gt;\n&lt;p&gt;Das Freizeichen ist in D das &quot;tuuut tuuut&quot;&quot;, welches dem Anrufer das Klingeln beim Gerufenen anzeigt. &lt;/p&gt;\n&lt;p&gt;Twinkle spielt dann den eigenen &quot;call back tone&quot; ab, falls nicht eine der beteiligten Vermittlungsstellen selber einen entsprechenden Signalton oder eine Ansage liefert.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation>D&amp;efault Freizeichen</translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation>Den Standard-Signalton für Rückmeldung des Klingelns bein Angerufenen (=Freizeichen) verwenden.</translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation>Individuelle&amp;s Freizeichen</translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation>Individuellen Signalton für Rückmeldung des Klingelns bein Angerufenen (=Freizeichen) verwenden. </translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation>Geben Sie hier den Namen der .wav-Datei für Ihr individuelles Freizeichen an.</translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation>Zu Nummer der Gegenstelle Name ermitte&amp;ln</translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation>Gemeldeten An&amp;rufernamen ersetzen</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation>Die Gegenstelle kann selber einen Namen (displayname) mitschicken. Aktivieren sie diese Option, wenn Sie lieber den aus der Nummer/Adresse ermittelten Eintrag aus Ihrem Adressbuch angezeigt bekommen möchten.</translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Nach &amp;Foto für Gegenstelle suchen</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation>Wenn aufgrund der Nummer/Adresse der Gegenstelle ein Datensatz in Ihrem Adressbuch gefunden wird, zeigt Twinkle ein dort gegebenenfalls hinterlegtes Foto an.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Änderungen übernehmen und speichern.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Fenster schliessen ohne Änderungen zu übernehmen.</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default IP address combo</comment>\n        <translation type=\"obsolete\">auto</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default network interface combo</comment>\n        <translation type=\"obsolete\">auto</translation>\n    </message>\n    <message>\n        <source>Either choose a default IP address or a default network interface.</source>\n        <translation type=\"obsolete\">Sie dürfen nur entweder einen Default-Anschluss oder eine Default-IP-Adresse angeben.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Klingeltöne</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Auswahl Klingelton</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Signaltöne</translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation>Auswahl Freizeichen</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>Audioeinstellungen prüfen &amp;vor Benutzung</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation>&lt;p&gt;Wenn aktiviert, prüft Twinkle die eingestellten Audiodevices, um zu verhindern dass eine Verbindung ohne entsprechende Ton-Ein/Ausgabe aufgebaut wird.&lt;/p&gt;\n&lt;p&gt;Bein Programmstart warnt Twinkle, falls eines der Audiodevices nicht verfügbar ist.&lt;br&gt;\nBei Anrufen werden Mikrofon- und Lautsprecherdevice geprüft.&lt;/p&gt;\n&lt;p&gt;Versuche, einen abgehenden Ruf zu tätigen, werden bei gefundenen Audio-Problemen abgebrochen,&lt;br&gt;\neingehende Rufe werden nicht entgegengenommen. &lt;br&gt;\nStattdessen zeigt Twinkle in beiden Fällen eine Warnung.&lt;/p&gt;\n</translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation>Twinkle versucht, einen zur Nummer/Adresse der Gegenstelle passenden Eintrag im Adressbuch zu finden. Die Details dieses Eintrags werden dann angezeigt.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Dateiauswahl Klingelton.</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Dateiauswahl Freizeichen.</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation>Max. zulässige Größe in Byte (0-65535) für ankommende SIP-Nachrichten über UDP.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>&amp;SIP port:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation>Max. SIP-Nachrichtengröße (&amp;TCP):</translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation>Die Portnummer, über die SIP-Nachrichten sowohl per UDP als auch TCP gesendet und empfangen werden.</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation>Max. SIP-Nachrichtengröße (&amp;UDP):</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation>Max. zulässige Größe in Byte (0-4294967295) für ankommende SIP-Nachrichten über TCP.</translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation>W&amp;ebbrowser-Befehl:</translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation>512</translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation>1024</translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Annehmen</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Abweisen</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation>Eingehender Anruf</translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Fähigkeiten Gegenstelle</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Von:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Fähigkeiten folgender Gegenstelle abfragen</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Adr:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Die Adresse/Nummer der Gegenstelle, deren Fähigkeiten Sie erfragen möchten (OPTION request). Wie immer bei Twinkle kann dies eine vollständige Adresse oder ein Username sein.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Rufnummer/SIP-Adresse aus Adressbuch wählen.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Vermitteln</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Ruf weitervermitteln an</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Adr:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Die Adresse/Nummer der Gegenstelle, an die Sie weitervermitteln möchten. Wie immer bei Twinkle kann dies eine vollständige Adresse oder ein Username sein.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbuch</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Rufnummer/SIP-Adresse aus Adressbuch wählen.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Type of transfer</source>\n        <translation type=\"obsolete\">Art der Vermittlung</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>&amp;Ohne Rücksprache</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation>Das Gespräch wird an den dritten, neuen Teilnehmer umgelenkt, ohne dass Sie vorher mit diesem Rücksprache halten. D.h. wenn der neue Teilnehmer abhebt, ist er sofort mit Ihrem bisherigen Gesprächspartner verbunden. </translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation>Mit &amp;Rücksprache</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation>Sie können mit dem neuen Teilnehmer sprechen und den vermittelten Gesprächspartner ankündigen. Nach Ende dieser Rücksprache wird Ihr bisheriger Gesprächspartner mit der neuen Gegenstelle verbunden.</translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation>Vermitteln an andere &amp;Leitung</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation>Die beiden GgSt an Leitung 1 und 2 zueinander vermitteln. Hierbei ist die GgSt der gerade aktiven Ltg die vermittelte, also den Ruf aufbauende.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Fehler beim Anlegen  der Logdatei &quot;%1&quot;.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Kann Datei &quot;%1&quot; nicht zum Lesen öffnen</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Dateisystem-Fehler beim Lesen aus &quot;%1&quot;.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Kann Datei &quot;%1&quot; nicht zum Schreiben öffnen</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Dateisystem-Fehler beim Schreiben in &quot;%1&quot;.</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>Zu hohe Anzahl von socket-Fehlern.</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Erstellt mit Unterstützung für:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>Beiträge:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Diese Software enthält folgende Teile  Dritter:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Deutsche Übersetzung: ©left 20080810-0830 Reisenweber tech+it-consult&lt;br&gt;\njoerg.twinklephone(AT)gmx.de</translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Ordner &quot;%1&quot; nicht gefunden.</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Datei &quot;%1&quot; nicht zugreifbar. (nicht vorhanden / schreibgeschützt?).</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>&quot;%1&quot; zeigt nicht auf Ihren home-Ordner.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Ordner &quot;%1&quot; (%2) existiert nicht.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Ordner &quot;%1&quot; kann nicht erstellt werden.</translation>\n    </message>\n    <message>\n        <source>Lock file %1 already exist, but cannot be opened.</source>\n        <translation type=\"obsolete\">Sperrdatei &quot;%1&quot; existiert schon, kann aber nicht geöffnet werden.</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>&quot;%1&quot; ist offenbar schon gestartet.\nSperrdatei &quot;%2&quot; existiert schon.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>&quot;%1&quot; kann nicht angelegt werden.</translation>\n    </message>\n    <message>\n        <source>Cannot write to %1 .</source>\n        <translation type=\"obsolete\">Kann in &quot;%1&quot; nicht schreiben.</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Syntaktische Struktur in Datei &quot;%1&quot; fehlerhaft.</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Fehler beim Backup von &quot;%1&quot; nach &quot;%2&quot;</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>Gerät unbekannt oder schon belegt </translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Standard Anschluss</translation>\n    </message>\n    <message>\n        <source>Anonymous</source>\n        <translation>Anonym</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Warnung:</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation>Vermittlung - %1</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>Audiodevice kann nicht auf &quot;voll duplex&quot; eingestellt werden.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>Puffergrösse f. Audiodevice kann nicht eingestellt werden.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Audiodevice kann nicht auf %1 Kanäle eingestellt werden.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation>Audiodevice kann nicht auf 16Bit-Aufnahme eingestellt werden.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation>Audiodevice kann nicht auf 16Bit-Wiedergabe eingestellt werden.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation>Audio Samplerate kann nicht auf %1 eingestellt werden.</translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation>Fehler beim Öffnen des ALSA-Treibers</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation>ALSA-Treiber kann nicht f. PCM-Wiederg. geöffnet werden.</translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>Kann URL d. STUN-Servers nicht auflösen: %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>Sie befinden sich hinter einer &quot;symetric NAT&quot;.\nSTUN kann hier nicht funktionieren.\nSie müssen in Twinkles Benutzerprofil/NAT eine &quot;fest voreingestellte Adresse&quot; einstellen.\nIn Ihrem Router/Firewall/NAT leiten Sie bitte folgende öffentliche Ports auf lokale Ports zum Twinkle-PC weiter:</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>IP öffentl.: %1 --&gt; IP lokal: %2 (SIP Protokoll)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>IP öff.: %1 - %2 --&gt; IP lok.: %3 - %4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>Kann STUN-Server &quot;%1&quot; nicht erreichen.</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Port %1 (SIP Protokoll)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>NAT Analyse mittels STUN fehlgeschlagen.</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Wenn Sie sich hinter einer Firewall befinden, müssen Sie folgende Ports öffnen:</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Ports %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation>&quot;%1&quot;, Audiodevice f. Klingelton nicht zugreifbar.</translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>&quot;%1&quot;, Audiodevice f. Lautsprecher nicht zugreifbar.</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>&quot;%1&quot;, Audiodevice f. Mikrofon nicht zugreifbar.</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>ALSA-Treiber kann nicht für PCM-Aufnahme geöffnet werden</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Kann eingehende TCP-Verbindungen nicht annehmen.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Fehler beim Anlegen  der Datei &quot;%1&quot;</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Fehler beim Schreiben in Datei &quot;%1&quot;</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Fehler beim Senden der Nachricht.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Benutzerprofil</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Benutzerprofil:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Zu bearbeitendes Benutzerprofil wählen.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Benutzer</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>SIP Server</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP Audio</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>SIP-Protokoll</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Adress-Format</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation>Zeitgeber</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Signaltöne</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Sicherheit</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Bereich wählen, den Sie ändern wollen.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Änderungen übernehmen und speichern.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Fenster schliessen ohne Änderungen zu übernehmen.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>SIP-Provider Benutzerdaten</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>N&amp;utzername *:</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Or&amp;ganization:</source>\n        <translation>Or&amp;ganisation:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Der Nutzername, den Sie von Ihrem Provider zugewiesen bekommen haben. Dieser ist der erste Teil ihrer vollständigen SIP-Adresse &lt;b&gt;nutzername&lt;/b&gt;@domain.com .  \nViele Provider bezeichnen diesen -eigentlich falsch- als Telefonnummer.\n&lt;br&gt;&lt;br&gt;\n*DATEN FÜR DIESES FELD SIND ZWINGEND NOTWENDIG.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Die Domain oder IP-Adresse, unter der Sie von Ihrem Provider geführt werden bzw. im Internet erreichbar sind. Dies ist der zweite Teil ihrer vollständigen SIP-Adresse nutzername@&lt;b&gt;domain.com&lt;/b&gt;, bzw. die Domain Ihres SIP-Proxys.\nBei vielen Providern identisch mit der Domain des Providers.\nFür direct-IP-to-IP (siehe Handbuch) ist hier die Adresse (DynDNS oder IP) einzutragen, unter der &lt;b&gt;Ihr Rechner&lt;/b&gt; zu erreichen ist.\n&lt;br&gt;&lt;br&gt;\n*DATEN FÜR DIESES FELD SIND ZWINGEND NOTWENDIG.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>In deutsch etwa Firma. Dieses Feld wird nur als Teil der Absenderangaben zur angerufenen/rufenden Gegenstelle übertragen und dort evtl angezeigt. \nBeliebige Angabe, nicht zwingend erforderlich.\nVermeiden Sie moeglichst Umlaute und Sonderzeichen, manche Gegenstellen haben damit Probleme.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Ihr Absendername oder Pseudonym. Dieses Feld wird nur als Teil der Absenderangaben (display name) zur angerufenen/rufenden Gegernstelle übertragen und dort evtl angezeigt. \nBeliebige Angabe, nicht zwingend erforderlich.\nVermeiden Sie moeglichst Umlaute und Sonderzeichen, manche Gegenstellen haben damit Probleme.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Absender:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>SIP-Anmeldedaten</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation>Anmelde&amp;name:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Passwort:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation>Der &quot;Realm&quot;-Wert (deutsch etwa: Bereich) zur Anmeldung. Wird Ihnen, falls notwendig, gegebenfalls von Ihrem SIP-Provider mitgeteilt. Wenn leer, verwendet Twinkle SIP-Anmeldename und Passwort bei jeder Realm-Anfrage.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ihr SIP-Anmeldename. Häufig identisch mit Ihrem SIP-Nutzernamen, dann leerlassen. Falls nicht, wird Ihr Provider dies mitteilen.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ihr SIP-Anmeldepasswort. Wenn Sie dieses Feld leerlassen, müssen Sie das Passwort bei jeder Anmeldung in den dann erscheinenden Requester eintragen (hilfreich zum anfänglichen Testen!). </translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation>Registrar (Anmelde-Server) </translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation>&amp;Registrar:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation>Die Domain, IP oder Hostname Ihres SIP-Anmelde-Servers. Für die meisten SIP-Provider einfach leer lassen. Wenn unten ein Outbound-Proxy eingetragen ist, wird dieser bei leerem Feld auch hier verwendet. Ohne Outbound-Proxy gilt für beides die Benutzer-SIP-Domain.</translation>\n    </message>\n    <message>\n        <source>&amp;Expiry:</source>\n        <translation>&amp;haltbar:</translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation>Die Gültigkeitsdauer in Sekunden, die Twinkle bei der Anmeldung anfordert. Nach dieser Zeit meldet sich Twinkle automatisch neu an. Unterbleibt dies, bemerkt der Provider nach dieser Zeit, dass Sie offline sind. Auch Änderungen Ihrer IP -z.B. durch Zwangstrennung- werden u.U. erst nach dieser Zeit berücksichtigt. \nWerte kleiner 120 sind nicht zu empfehlen. Standard: 3600 (=1h).</translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>Sekunden</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Bei &amp;Profilstart anmelden</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation>Wenn aktiviert, versucht Twinkle, dieses Benutzerprofil bei seiner Aktivierung automatisch beim Provider (genauer SIP-Anmelde-Server / Registrar) anzumelden.\nFür direct-IP-to-IP gibt es keinen Provider, also dann nicht aktivieren.</translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>Outbound-Proxy ben&amp;utzen</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation>Wenn aktiviert, verwendet Twinkle einen Outbound-Proxy (deutsch etwa: Stellvertreter/Vermittlung für abgehende Rufe), an den alle SIP-Anfragen gesendet werden. Dies kann z.B. ein SIP-Gateway ihres Firmen-LAN sein. \nOhne Outbound-Proxy (Normalfall) versucht Twinkle selbst, die zu rufende Adresse zu einer IP aufzulösen, und sendet die SIP-Anfrage für den Anruf direkt dorthin.</translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>Outbound-&amp;Proxy: </translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation>In-Dialog-Anfragen an Proxy &amp;senden</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation>Wenn aktiviert, SIP-Anfragen &lt;b&gt;immer&lt;/b&gt; an den Outbound-Proxy senden. Twinkle sendet normalerweise SIP-Anfragen während eines laufenden SIP-Dialogs (d.h. während eines Gesprächs) an die Adresse im zu Gesprächsbeginn erhaltenen Contact-Header, also direkt an die Gegenstelle. </translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation>SIP-Anfragen mit lokal auflösbarer A&amp;dresse nicht an Proxy, sondern direkt senden.</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation>Wenn aktiviert, versucht Twinkle zunächst selbst, die Zieladresse zu einer gültigen IP-Adresse aufzulösen und die SIP-Anfrage direkt dorthin zu schicken. Gelingt die Adressauflösung nicht, wird die Anfrage trotzdem an den Proxy geschickt, wie bei nicht aktivierter Option (Beachten Sie: In-Dialog-Anfragen werden in diesem Fall nur an den Proxy gesendet, wenn auch die vorherige Option aktiviert ist)</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Der Domainname, IP-Adresse oder Hostname Ihres Outbound-Proxy.</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Verfügbare Codecs:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Liste der verfügbaren, nicht aktivierten Codecs.\nAbhängig von den Compile-options können manche Codecs nicht verfügbar sein.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Codec aktivieren.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Codec deaktivieren.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Aktive Codecs:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation>Liste der aktiven Codecs. Diese werden beim Gesprächsaufbau der Gegenstelle zur Benutzung angeboten bzw. akzeptiert. Es wird bevorzugt der am weitesten oben in der Liste stehende Codec genutzt, auf den sich die beiden Endgeräte einigen können.</translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation>Codec in der Liste nach oben verschieben, d.h. höheren Vorrang für Benutzung einräumen.</translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation>Codec in der Liste nach unten verschieben, d.h. niedrigeren Vorrang für Benutzung einräumen.</translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation>&amp;G.711/G.726 Nutzdatengrösse:</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation>Die bevorzugte Grösse der Nutzdaten pro RTP-Paket für G.711 and G.726 Codecs.</translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation>i&amp;LBC Nutzdaten-Typ:</translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation>iLBC &amp;Nutzdatengrösse:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation>Die für iLBC verwendete dynamische Nutzdatentypkennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation>Die bevorzugte Grösse der Nutzdaten pro RTP-Paket für iLBC.</translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation>Tonqualität v&amp;erbessern</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation>&quot;Tonqualität verbessern&quot; (engl: perceptual enhancement) ist eine Sammlung von Funktionen des Codecs, die den Ton unter Beachtung der Eigenschaften des menschlichen Hörens so bearbeiten, dass weniger Störgeräusche wahrgenommen werden. Obwohl sich die Übertragung bei Anwendung dieser Funktionen unter messtechnischen Gesichtspunkten (S/N Rauschabstand) verschlechtert und weniger dem Original gleicht, ist letztendlich doch die empfundene Tonqualität besser.</translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation>&amp;Ultra wide band Nutzdaten-Typ:</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the audio being encoded is speech or silence/background noise. VAD is always implicitly activated when encoding in VBR, so the option is only useful in non-VBR operation. In this case, Speex detects non-speech periods and encode them with just enough bits to reproduce the background noise. This is called &quot;comfort noise generation&quot; (CNG).</source>\n        <translation type=\"obsolete\">Wenn aktiviert, prüft VAD (Voice Activity Detection, deutsch etwa: Sprache/Pause-Erkennung), ob gerade gesprochen wird. Nicht als Sprache erkannte Geräusche werden nicht übertragen, sondern es wird stattdessen ein wesentlich weniger Daten-Bandbreite benötigendes &quot;Pausesignal&quot; oder (siehe DTX) gar nichts gesendet. \nVBR (siehe dort) macht VAD unnötig.</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation>&amp;wide band Nutzdaten-Kennung:</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation>Variable Bit-Rate (VBR) erlaubt es dem Codec, die Menge der übertragenen Daten entsprechend der Komplexität des Audiosignals anzupassen. Zischlaute wie &quot;s&quot;, &quot;f&quot; z.B. und besonders Sprechpausen (siehe VAD) können mit wenigen Daten qualitativ gut beschrieben werden, während für Laute mit starken Änderungen im zeitlichen Verlauf (&quot;p&quot;, &quot;k&quot;, &quot;r&quot;...) vergleichsweise hohe Datenmengen nötig sind. Durch VBR kann bei gegebener Datenrate also insgesamt bessere Tonqualität erreicht werden, oder niedrigere Datenraten für gleiche Qualität. Allerdings ist bei Festlegung einer bestimmten einzuhaltenden Qualität nicht mehr vorhersagbar, welche Datenrate dafür ausreichend sein wird. Bei Echtzeitanwendungen wie VoIP ist aber gerade die maximal benötigte und nicht die durchschnittliche Datenrate kritisch.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation>Die für speex wide band verwendete dynamische Nutzdatentyp-Kennung (nicht kleine 96).</translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation>Ko&amp;mplexität:</translation>\n    </message>\n    <message>\n        <source>Alt+X</source>\n        <translation type=\"obsolete\">Alt+X</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation>Discontinuous transmission (deutsch etwa: nicht kontinuierliche Datenübertragung) ist eine Erweiterung der VAD/VBR-Übertragung. Bei gleichbleibenden Audiosignal (insbesondere bei erkannten Sprechpausen) wird statt ständig der gleichen Nutzdaten einfach gar nichts übertragen. Senkt die durchschnittliche Datenrate etwas. Bei Störungen auf dem Übertragungsweg kann diese Option zu den von Mobiltelefonen der Anfangszeit bekannten absurden Tonstörungen (hängenbleiben des Tons, Artefakte) führen.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation>Die für speex narrow band verwendete dynamische Nutzdatentyp-Kennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation>Bei Speex kann die Komplexität (=Genauigkeit) festgelegt werden, mit der der Codec arbeitet. Hierzu wird die Tiefe des Suchvorgangs mit einem Wert von 1 bis 10 gesteuert, ähnlich der -1 bis -9 Option von gzip und bzip2. Im Normalbetrieb ist bei 1 der Rauschabstand 1 bis 2dB schlechter und die CPU-Auslastung nur 10-20% im Vergleich zu 10. In der Praxis bewährt sich für Sprache eine Einstellung von 2 - 4, Inband-DTMF z.B. und andere technische Signale, oder auch Musik, profitieren u.U. von höheren Einstellungen.</translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation>&amp;Narrow band Nutzdatentyp-Kennung:</translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation>G.726 &amp;40 kb/s Nutzdatentyp-Kennung:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation>Die für G.726 40 kb/s verwendete dynamische Nutzdatentypkennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation>Die für G.726 32 kb/s verwendete dynamische Nutzdatentypkennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation>G.726 &amp;24 kb/s Nutzdatentyp-Kennung:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation>Die für G.726 24 kb/s verwendete dynamische Nutzdatentypkennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation>G.726 &amp;32 kb/s Nutzdatentyp-Kennung:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation>Die für G.726 16 kb/s verwendete dynamische Nutzdatentypkennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation>G.726 &amp;16 kb/s Nutzdatentyp-Kennung:</translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation>Die für DTMF (RFC2833) verwendete dynamische Nutzdatentypkennung (nicht kleiner 96).</translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation>DTMF &amp;Lautstärke:</translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation>Die Lautstärke der gesendeten DTMF-Töne in dB, sowohl für reale Töne inband als auch Pegelkennung bei RFC2833. Sollte -10 bis -6 sein.</translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation>Dauer der Pause zwischen 2 DTMF-Tönen. Zu kleine Werte können dazu führen, dass Folgen von gleichen &quot;Ziffern&quot; vom gesteuerten Gerät nicht mehr getrennt und als nur eine erkannt werden. Hohe Werte sind unschädlich, sofern Sie es nicht eilig haben.</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation>DTMF-&amp;Dauer: </translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation>D&amp;TMF Nutzdatentyp-Kennung:</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation>DTMF-&amp;Pause:</translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation>Dauer eines DTMF-Tons in Millisekunden. Bei zu kleinem Wert kann das gesteuerte Gerät die &quot;Ziffer&quot; nicht mehr erkennen. 200 klappt meist auch mit alten Geräten.</translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>DTMF &amp;Methode:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;&lt;h3&gt;RFC 2833&lt;/h3&gt;\nSende DTMF-Töne als RFC 2833 telephone events (Symbole im RTP-Audiodatenstrom).&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Inband&lt;/h3&gt;\nSende DTMF inband (tatsächliche Töne, die Twinkle ins Tonsignal einmischt).&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Auto&lt;/h3&gt;\nWenn die Gegenstelle RFC 2833 unterstützt, dann DTMF-Töne als RFC 2833 telephone events senden, ansonsten inband.&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Out-of-band (SIP INFO)&lt;/h3&gt;\nSende DTMF nur out-of-band via  SIP INFO request.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Allgemein</translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation>Rufweiterleitung abgehende Rufe</translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation>Rufweiterleitung erl&amp;auben</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation>Wenn aktiviert, befolgt Twinkle die Anforderung  (3XX) der gerufenen Gegenstelle, wenn dort Rufumleitung aktiviert wurde.</translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation>Benutzer vor &amp;Weiterleitung fragen</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation>Wenn aktiviert, fragt Twinkle bei Empfang einer 3XX-Anfrage, ob der abgehende Ruf auf ein alternatves Ziel umgeleitet werden darf.</translation>\n    </message>\n    <message>\n        <source>Max re&amp;directions:</source>\n        <translation>Max. Anz. &amp;Umleit.:</translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation>Die Anzahl von Weiterleitungen eines abgehenden Rufes (von A nach B nach C...), nach der Twinkle aufgibt. Verhindert Endlosweiterleitungen im Kreis (A -&gt; B -&gt; A...).</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Protokoll-Optionen</translation>\n    </message>\n    <message>\n        <source>Call &amp;Hold variant:</source>\n        <translation>Gespräch-&amp;halten Variante: </translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation>Auswahl, ob RFC 2543 (set media IP address in SDP to 0.0.0.0) oder RFC 3264 (use direction attributes in SDP) benutzt wird, um ein Gespräch zu halten.</translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation>Erlaube fehlenden Contact header in 200 OK bei REG&amp;ISTER</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation>Eine &quot;200 OK&quot;-Antwort auf ein &quot;REGISTER&quot; muss einen Contact header enthalten. Einige Provider schicken trotzdem keinen oder einen fehlerhaften. Wenn aktiviert, wird Twinkle versuchen, diesen Fehler auszugleichen.</translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation>&amp;Max-Forwards-Header verlangen</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation>Nach RFC3261 ist der Max-Forwards header vorgeschrieben, wird aber oft trotzdem nicht gesendet. Wenn aktiviert, lehnt Twinkle SIP-Anfragen  ohne Max-Forwards header ab.</translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation>Anmeldedaue&amp;r im Contact-Header übertragen</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation>In einer REGISTER-Anfrage kann die Ablaufzeit (expiry, &quot;haltbar:&quot;) der Anmeldung sowohl im Contact-Header als auch im Expires-Header übertragen werden. Wenn aktiviert, sendet Twinkle im Contact-header, sonst im Expires-header.</translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation>&amp;kompakte Headernamen</translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation>Wenn aktiviert, für Headernamen die kurze Form verwenden, soweit eine existiert.</translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation>Erlaube SDP-Änderungen beim Rufaufbau</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Ein SIP UAS kann ein SDP in einer 1XX Antwort für early media, z.B. bei &quot;Freizeichen&quot;, senden. Wenn das Gespräch aufgebaut wird, sollte der SIP UAS das selbe SDP in der &quot;200 OK&quot;-Antwort senden. Nach Empfang eines SDP sollten alle folgenden verworfen werden. Soweit die reine Lehre nach RFC 3261.&lt;/p&gt;\n&lt;p&gt;Wenn erlaubt wird, dass sich SDP wahrend des Gesprächsaufbaus ändert, verwirft Twinkle SDPs in Folgeantworten nicht, sondern ändert die Eigenschaften des RTP-Mediastreams (z.B. codec) entsprechend. Ein geändertes SDP muss eine neue Versionsnummer in der &quot;o=&quot;-Zeile haben.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Wenn aktiviert, erzeugt Twinkle einen eindeutigen contact header Wert durch Kombination des SIP-Nutzernamens und der Domain:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nSo haben 2 Benutzerprofile mit selbem SIP-Nutzernamen aber unterschiedlicher Domain eindeutige contact Adressen und können so gleichzeitig aktiviert werden.\n&lt;/p&gt;\n&lt;p&gt;\nViele Proxies können mit solchen contact header Werten nicht umgehen. Wenn diese Option deaktiviert ist, sendet Twinkle contact header in folgendem Format:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nDieses Format wird von fast allen SIP-Telefonen verwendet.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Nutzen Sie diese Option nur, wenn Sie sie wirklich brauchen! Also wenn Sie mehrere Profile mit gleichem SIP-Benutzernamen haben.&lt;/b&gt;&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation>Via, Route, Record-Route als List&amp;e senden</translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation>Die Via-, Route- und Record-Route-Header können als Liste von durch Komma getrennten Werten oder als einzelne Werte übertragen werden.</translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation>SIP Erweiterungen</translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>deaktiviert</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>erlaubt</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>erforderlich</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>bevorzugt</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation>Definiert die Art der Unterstützung für 100rel extension (PRACK):&lt;br&gt;&lt;br&gt;\n&lt;b&gt;deaktiviert&lt;/b&gt;: 100rel extension wird nicht unterstützt\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;erlaubt&lt;/b&gt;: 100rel wird unterstützt (wird im &quot;supported header&quot; abgehender INVITEs übertragen). Eine Gegenstelle kann dann ein PRACK auf eine 1xx Antwort anfordern.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;erforderlich&lt;/b&gt;: 100rel wird angefordert (wird im &quot;require header&quot; abgehender INVITEs übertragen). Wenn die Gegenstelle ein INVITE sendet (=anruft) und darin signalisiert, dass sie 100rel unterstützt, dann fordert Twinkle beim senden einer 1xx-Antwort PRACK an. Unterstützt die Gwegenstelle 100rel nicht, kommt die Verbindung nicht zustande.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;bevorzugt&lt;/b&gt;: Wie &quot;erforderlich&quot;, ausser dass auch dann ein Gespräch zustande kommt, wenn die Gegenstelle 100rel nicht unterstützt.\n\nDiese Einstellung beeinflusst das Verhalten bei &quot;early media&quot; (z.B. &quot;Freizeichen&quot;). </translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation>Rufweitervermittlung (REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call &amp;transfer (incoming REFER)</source>\n        <translation type=\"obsolete\">GgSt darf vermi&amp;tteln (eingehender REFER)</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation>Wenn aktiviert, befolgt Twinkle die Anfrage der Gegenstelle (REFER), Sie zu einer anderen Gegenstelle weiterzuvermitteln. Dies kann für Sie Kosten verursachen.</translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation>Benutzer vor &amp;Vermittlung fragen</translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation>Wenn aktiviert, fragt Twinkle bei eingehender Vermittlungsanfrage (REFER) vor Abbau der bisherigen und Anwählen der neuen Verbindung. Im Gegensatz zum Fest- und GSM-Netz trägt bei SIP nicht der Vermittler, sondern der &quot;Anrufende&quot; (also der, der an die neue Gegenstelle weitervermittelt wird) die eventuellen Kosten für das neue vermittelte Gespräch.</translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation>T&amp;winkle hält Gespräch als Vermittelter</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation>Wenn aktiviert, übernimmt bei eingehender Vermittlungsaufforderung Twinkle es, den bisherigen Anruf zu halten. Normalerweise sollte die vermittelnde Gegenstelle dies tun. Siehe folgende Option. Standard: deaktiviert.</translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation>Twink&amp;le hält Gespräch als Vermittler </translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation>Wenn aktiviert, schaltet Twinkle als Vermittler das bisherige Gespräch in den Gehalten-Zustand, bevor es der Gegenstelle ein REFER schickt. So muss die Gegenstelle dies nicht tun - siehe vorherige Option.\nStandard: aktiviert.</translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation>Subscription &amp;für REFER automatisch erneuern, bis Vermittlung beendet</translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation>Alt+F</translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation>Während eines Vermittlungvorgangs sendet der Vermittelte NOTIFY-Mitteilungen über den Fortgang des Gesprächsaufbaus an den Vermittler, allerdings nur für eine kurze Zeitspanne, die der Vermittelte festlegt. Wenn aktiviert, sendet der Vermittler (Twinkle) automatisch SUBCRIBEs, um diese Zeit zu verlängern bis der Vermittlungsvorgang abgeschlossen ist.</translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>NAT Durchtunnelung</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation>&amp;NAT Durchtunnelung unnötig</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation>Wählen Sie diese Option, \nwenn sich zwischen Twinkle und Ihrem SIP-Proxy keine NAT (Router) befindet, \nwenn zwar eine NAT existiert, aber ein Application Level Gateway (ALG) im Router den SIP-Betrieb unterstützt, oder \nwenn Ihr SIP-Provider &quot;hosted NAT traversal&quot; unterstützt (ein Weg, wie der Provider Probleme mit NAT umgehen kann).\nIm Zweifelsfall sollten Sie zuerst versuchen, ob diese Einstellung bei Ihnen funktioniert, auch wenn Sie einen Router / NAT haben.</translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation>Fest voreingestellte &amp;Adresse in SIP-Telegrammen verwenden</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation>Wenn aktiviert, verwendet Twinkle in SIP-Telegrammen, also Headern und Body, die im nächsten Feld angegebene öffentliche Adresse anstatt der automatisch ermittelten Adresse Ihres Netzwerkanschlusses.&lt;br&gt;&lt;br&gt;\nWenn Sie diese Option verwenden, müssen Sie auch in Ihrer NAT die entsprechenden RTP-Ports auf Ihren Rechner durchleiten.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN</source>\n        <translation type=\"obsolete\">&amp;STUN aktivieren</translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation>Aktivieren Sie diese Option, wenn Ihr SIP-Provider einen STUN-Server zum Durchtunneln der NAT anbietet.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN-Server:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Der Domainname, IP-Adresse oder Hostname des STUN-Servers (gegebenenfalls incl &quot;:&lt;portnr&gt;&quot;, also z.B. &quot;stunsrv.de:10000&quot;).</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>Öffentl. &amp;Adresse:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation>Die öffentliche Adresse (IP, DynDNS-domain), unter der Ihre NAT(/Router) im Internet erreichbar ist. Diese Option ist nur bei unveränderlicher Adresse sinnvoll.</translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Telefonnummern</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation>Bei Telefonnumern nur User-Teil &amp;der URI anzeigen</translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation>Wenn eine URI eine Telefonnummer darstellt, dann nur den User-Teil anzeigen. Kommt z.B. ein Anruf von sip:12345@einprovider.com, dann zeigt Twinkle nur &quot;12345&quot; als Adresse. Twinkle betrachtet eine URI als &quot;Telefonnummer&quot;, wenn sie entweder den Zusatz &quot;user=phone&quot; enthält, oder wenn die nächste Option aktiv ist und Twinkle den User-Teil als Nummer einschätzt.</translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation>&amp;URI mit numerischem User-Teil ist Telefonnummer</translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation>Wenn aktiviert, betrachtet Twinkle jede SIP-Adresse als &quot;Telefonnummer&quot;, die nur Ziffern, *, #, + und Sonderzeichen (s.o.) im User-Teil hat. In abgehenden SIP-Mitteilungen hängt Twinkle an solche Adressen den Parameter &quot;user=phone&quot; an.\nAchtung: z.B. sipgate verändert(e) subtil sein Verhalten bei manchen Funktionen, sobald dieser Parameter mitgesendet wird.</translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation>Sonde&amp;rzeichen aus Wählstring entfernen</translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation>Telefonnumern werden oft unter Verwendung von Sonderzeichen wie &quot;(&quot;, &quot;)&quot;, &quot; &quot;(Leerzeichen), &quot;-&quot; usw. angegeben, um sie für Menschen leichter lesbar zu gestalten. Beim Wählen, insbesondere einer SIP-Adresse, dürfen diese Zeichen nicht mit angegeben werden. Um das Wählen durch Kopieren und Einfügen, direktes Anklicken im Adressbuch usw. zu vereinfachen, kann man Twinkle eine Liste mit unzulässigen Zeichen angeben, die vor dem eigentlichen Wählen automatisch zu löschen sind.</translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation>unzul. &amp;Sonderzeichen:</translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation>Liste aller Sonderzeichen, die Twinkle aus den zu wählenden Nummern entfernen soll.</translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation>Nummernkonvertierung</translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation>Suchausdruck</translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Ersetzung</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telphone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation>&lt;p&gt;\nOftmals ist das Format der Telefonnummern, das z.B. der Provider erwartet, nicht identisch mit dem Format der im Adressbuch gespeicherten Nummern. Beispielsweise könnten Ihre Nummern mit &quot;+&quot; und dem Ländercode beginnen, Ihr Provider erwartet aber &quot;00&quot; statt des &quot;+&quot;. Oder Sie sind an die lokale SIP-Installation in Ihrer Firma angeschlossen und müssen eine Amtsholziffer vorwählen.\nHier können Sie unter Verwendung von Such- und Ersetzungs-Mustern (nach Art regulärer Ausdrücke a la Perl) allgemeingültige Regeln zur Umwandlung von Telefonnummern einrichten.\n&lt;/p&gt;\n&lt;p&gt;\nBei jeden Wahlversuch versucht Twinkle, für die zu wählende Nummer (den User-Teil der vollen SIP-Adresse) einen passenden Ausdruck in der Liste der Suchmuster zu finden. Der zum ersten passenden Suchmuster gehörende Ersetzungsausdruck ersetzt die Original-Nummer, wobei durch &quot;(&quot; &quot;)&quot; umschlossene Platzhalter im Suchausdruck (z.B. &quot;([0-9]*)&quot; für &quot;beliebig viele Ziffern&quot;) die durch sie &quot;geschluckten&quot; Zeichen zur entsprechenden Variablen (z.B. &quot;$1&quot; für den ersten Platzhalter) im Ersetzungsausdruck transportieren (siehe `man 7 regex` oder konqueror:&quot;#regex&quot;). Wird kein passendes Suchmuster gefunden, bleibt die Nummer unverändert.\n&lt;/p&gt;\n&lt;p&gt;\nDie Regeln werden auch auf die Absenderangaben eingehender Rufe angewendet, um diese Nummern gleich in das von Ihnen gewünschte Format zu wandeln. (!!! &lt;i&gt;[bug? Amtsziffer 0.  d.Üs.]&lt;/i&gt; ) \n&lt;/p&gt;\n&lt;h3&gt;Beispiel 1&lt;/h3&gt;\n&lt;p&gt;\nAngenommen Ihr Ländercode ist &quot;49&quot; für Deutschland, und Sie haben auch viele Inlandnummern in Ihrem Adressbuch in internationalem Nummernformat gespeichert, also z.B. +49 911 2345678. Ihr Provider erwartet für innerdeutsche Gespräche aber 0911 2345678. Also möchten Sie die &apos;+49&apos; durch &apos;0&apos; ersetzen. Für Auslandsgespräche möchten Sie &apos;+&apos; durch &apos;00&apos; ersetzen.\n&lt;/p&gt;\n&lt;p&gt;\nSie benötigen hierzu folgende Regeln, in dieser Reihenfolge:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nSuchausdruck = \\+49([0-9]*) , Ersetzung =0$1&lt;br&gt;\nSuchausdruck = \\+([0-9]*) , Ersetzung = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Beispiel 2&lt;/h3&gt;\n&lt;p&gt;\nSie befinden sich an einer Telefonanlage und alle Nummern mit 0 als erste Ziffer sollen die Amtsholziffer 9 vorangestellt bekommen. \n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nSuchausdruck = 0[0-9]* , Ersetzung = 9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n( $&amp; ist eine spezielle Variable, die die gesamte Originalnummer überträgt)&lt;br&gt;\nAnmerkung: Sie können diese Regel nicht einfach nur als dritte nach denen aus Beispiel 1 angeben, denn es wird immer nur die erste zutreffende Regel angewendet. Stattdessen müssten die Ersetzungen der Regeln 1 und 2 in  &quot;90$1&quot; u. &quot;900$1&quot; geändert werden</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation>Regel in der Liste nach oben verschieben.</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation>Regel in der Liste nach unten verschieben.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Neu</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation>Neue Regel erzeugen.</translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>&amp;Löschen</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation>Die ausgewählte Regel löschen.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>B&amp;earbeiten</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation>Die ausgewählte Regel ändern.</translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation>Tippen Sie eine Nummer und klicken Sie &quot;Test&quot;, um das Ergebnis der Umwandlung durch die Regeln zu sehen.</translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Testen</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation>Die Regeln mit der Nummer links testen und Resultat anzeigen. </translation>\n    </message>\n    <message>\n        <source>for STUN</source>\n        <translation type=\"obsolete\">Sekunden</translation>\n    </message>\n    <message>\n        <source>Keep alive timer for the STUN protocol. If you have enabled STUN, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"obsolete\">Zeitgeber für das STUN-Protokoll. Wenn STUN aktiviert ist, werden die STUN-keep-alive Datenpakete in diesem Zeitabstand von Twinkle gesendet. Damit der Router die Zuordnung zwischen interner und externer Adresse nicht aus der NAT-Adresstabelle löscht, frischen diese keep-alive-Pakete die Zuordnung in der NAT rechtzeitig auf. Dieser Wert ist daher von der eingesetzten NAT abhängig und sollte nicht zu gross gezählt werden.</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation>Wenn ein Anruf eingeht, beginnt dieser Zeitgeber abzulaufen. Wird der Ruf bis zum Ende der Zeitspanne nicht angenommen, sendet Twinkle &quot;480 User Not Responding&quot; und weist so den Anruf ab.</translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation>&amp;STUN NAT-keep-alive alle:</translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>&quot;&amp;Nicht erreichbar&quot; nach:</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation>&amp;Freizeichen:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Geben Sie hier den Namen der .wav-Datei für das Freizeichen dieses Benutzerprofils an.&lt;/p&gt;\n\n&lt;p&gt;Diese Einstellung ersetzt bei abgehendem Ruf von diesem Benutzerprofil die Auswahl für &quot;Freizeichen&quot; aus den Systemeinstellungen.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Geben Sie hier den Namen der .wav-Datei für den Klingelton dieses Benutzerprofils (=&quot;Nummer&quot;) an.&lt;/p&gt;\n\n&lt;p&gt;Diese Einstellung ersetzt bei Anrufen an dieses Benutzerprofil die Auswahl &quot;Klingelton&quot; aus den Systemeinstellungen.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Klingelton:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn ein Gespräch durch Sie beendet wird.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der abgesendeten SIP BYE Anforderung werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; enthält die request-URI des BYE. &lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn ein eingehender Ruf nicht zustande kommt, also das Klingeln endet ohne dass &quot;abgehoben&quot; wurde.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der abgesendeten SIP failure Antwort werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; enthält den Statuscode  der abgesendeten SIP failure Antwort. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt;enthält &quot;reason phrase&quot;, also die &quot;Fehler&quot;ursache in Klartext.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn ein Gespräch durch die Gegenstelle beendet wird.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der eingehenden SIP BYE Anforderung werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; enthält die request-URI des BYE. &lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn das Gespräch durch die Gegenstelle angenommen wird.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der eingehenden &quot;200 OK&quot; Mitteilung werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; enthält &quot;reason phrase&quot;&lt;br&gt; \n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn Sie einen Anruf entgegennehmen.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der gesendeten &quot;200 OK&quot; Antwort werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; enthält &quot;reason phrase&quot;&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.</translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation>Gespräch &amp;lokal beendet:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn ein abgehender Anruf nicht zustande kommt, z.B. wegen timeout, DND usw.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der empfangenen SIP failure Antwort werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; enthält den Statuscode  der abgesendeten SIP failure Antwort.&lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; enthält &quot;reason phrase&quot;, also die &quot;Fehler&quot;ursache in Klartext.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gestartet, wenn Sie einen Anruf tätigen.\n&lt;/p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Inhalte aller SIP header der abgesendeten SIP INVITE Anforderung werden in Environment Variablen ans Script übergeben.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;.&lt;br&gt;\n&lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;.&lt;br&gt;\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; enthält die request-URI des INVITE.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; enthält den Namen des aktuell genutzten Benutzerprofils.</translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation>Abgehe&amp;nder Anruf angenommen:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation>Eingehender Anruf er&amp;folglos:</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>E&amp;ingehender Anruf:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation>Gesp&amp;rächsende durch Gegenstelle: </translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation>Eingehender Anruf &amp;angenommen:</translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>Ausgehender Anr&amp;uf:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation>Aus&amp;gehender Anruf erfolglos:</translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>ZRTP/SRTP-V&amp;erschlüsselung aktivieren</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unecrypted.</source>\n        <translation>Wenn aktiviert, versucht Twinkle bei allen abgehenden und ankommenden Geprächen die Sprachdaten zu verschlüsseln. Hierzu muss natürlich die Gegenstelle ebenfalls ZRTP/SRTP unterstützen, andernfalls bleibt das Gespräch unverschlüsselt.</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>ZRTP-Einstellungen</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation>&amp;Nur verschlüsseln, wenn Gegenstelle ZRTP-Unterstützung im SDP meldet </translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation>Eine ZRTP-fähige SIP-Gegenstelle kann diese Fähigkeit schon während des Gesprächsaufbaus mitteilen. Wenn aktiviert, versucht Twinkle nur bei solchen Gegenstellen, eine Verschlüsselung auszuhandeln.</translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation>ZRTP-Unterstützung &amp;im SDP mitteilen</translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation>Wenn aktiviert, meldet Twinkle der Gegenstelle beim Gesprächsaufbau im SDP, dass es ZRTP unterstützt.</translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation>&amp;Warnen, wenn Gegenstelle auf unverschlüsselt umschaltet</translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation>Die Gegenstelle kann während eines verschlüsselten Gesprächs ein ZRTP-go-clear Komando senden und damit die Verschlüsselung stoppen. Wenn aktiviert, macht Twinkle in diesem Fall mit einer Warnmeldung auf das Sicherheitsproblem aufmerksam.</translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation>Dynamische Nutzdatenkennung %1 mehrfach vergeben.</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Sie müssen den Namensteil Ihrer SIP-Benutzerkennung angeben.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Sie müssen den domain-Teil (den Teil rechts nach @) Ihrer SIP-Benutzerkennung angeben.\nHäufig identisch mit der Domain Ihres SIP-Providers.\n\nFür direct-IP-to-IP, also ohne SIP-Provider, ist dies der (dyndns-)Name oder die öffentliche IP Ihres PC. </translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Ungültiger Benutzername.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Ungültige Domäne.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation>Ungültiger Wert für Registrar.</translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation>Ungültiger Wert für ausgehenden Proxy.</translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation>Wert für öffentliche IP-Adresse fehlt.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Ungültiger Wert für STUN-Server.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Klingeltöne</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Klingelton auswählen</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Signaltöne</translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Alle Dateien</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation>Auswahl Script bei &quot;eingehendem Ruf&quot;</translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation>Auswahl Script bei &quot;eingehender Ruf angenommen&quot;</translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation>Auswahl Script bei &quot;eingehender Ruf erfolglos&quot;</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation>Auswahl Script bei &quot;abgehendem Ruf&quot;</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation>Auswahl Script bei &quot;abgehender Ruf angenommen&quot;</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation>Auswahl Script bei &quot;abgehender Ruf erfolglos&quot;</translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation>Auswahl Script bei &quot;Gespräch lokal beendet&quot;</translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation>Auswahl Script bei &quot;Gespräch durch Gegenstelle beendet&quot;</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Anrufbeantworter</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation>Gegenstelle wählt Codecs bei eingehendem Ru&amp;f</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation>Wenn aktiviert: Bei ankomendem Anruf richtet sich Twinkle bevorzugt nach der Liste erlaubter Codecs von der Gegenstelle (SDP offer). Konkret wird der erste Codec der Ggst.-Wunschliste verwendet, der auch von Twinkle in der aktuellen Einstellung unterstützt wird.\nWenn deaktiviert, verwendet Twinkle den ertsen Codec der eigenen Liste, der auch von der GgSt. untersützt wird.</translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation>Gegenstelle wählt Codecs bei a&amp;bgehendem Ruf</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation>Wenn aktiviert: Bei abgehendem Ruf richtet sich Twinkle bevorzugt nach der Liste erlaubter Codecs von der Gegenstelle (SDP answer). Konkret wird der erste Codec der Ggst.-Wunschliste verwendet, der auch von Twinkle in der aktuellen Einstellung unterstützt wird.\nWenn deaktiviert, verwendet Twinkle den ertsen Codec der eigenen Liste, der auch von der GgSt. untersützt wird, also in der SDP-Answer-Liste steht.</translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation>Datenanordnung (codeword &amp;packing order):</translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation>RFC 3551</translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation>ATM AAL2</translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation>Es gibt 2 Methoden, die G.726 codewords in ein RTP-Paket anzuordnen. Standard ist RFC 3551. Einige SIP-Provider nutzen allerdings ATM AAL2. Wenn die Tonübertragung bei Verwendung des G.726-Codecs gestört ist, versuchen Sie hier die andere Einstellung.</translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Ersetzt</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extenstion is supported.</source>\n        <translation>Wenn aktiviert, unterstützt Twinkle Replaces-Extension bei PRACK.</translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation>Vermittlung mit Rückfrage verwendet &quot;Address of Record&quot;</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endoint.</source>\n        <translation>Eine Vermittlung mit Rückfrage sollte die Contact-URI als Zieladresse nutzen, um der vermittelten GgSt die neu zu schaltende Verbindung mitzuteilen. Diese Adresse kann allerdings evtl. nicht global gültig d.h. &quot;route-&quot;bar sein. Das vermittelte Gespräch kommt dann beim neuen Ziel nicht an. \nAlternativ kann Twinkle die AoR (Address of Record) nutzen. Nachteil hierbei: diese ist bei mehreren unter gleichem SIP-Benutzerkonto angemeldeten Endgeräten nicht eindeutig, so dass von der vermittelten GgSt (eigentlich vom Provider) alle Endgeräte angesprochen werden und einen Anuf signalisieren.</translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Datenschutz</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Datenschutzoptionen</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation>&amp;Sende &quot;P-Preferred-Identity Header&quot; bei &quot;Absender verbergen&quot;</translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Wenn aktiviert, wird zusammen mit der Absenderangabe ein &quot;P-Preferred-Identity Header&quot; beim INVITE gesendet, falls &quot;Absender verbergen&quot; aktiv ist.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDieses Script wird gerufen, wenn ein INVITE (Anruf) ankommt. &lt;br&gt;\nBitte lesen Sie im Handbuch unter &quot;/usr/share/doc/packages/twinkle/...&quot; oder &quot;https://mfnboer.home.xs4all.nl/twinkle/&quot; die ausführliche Beschreibung!\n&lt;/p&gt;\n&lt;h3&gt;Rückgabewerte&lt;/h3&gt; -   print nach STDOUT (z.B. `echo &quot;action=dnd&quot;`), ein Wert pro Zeile: &lt;br&gt;\n&lt;tt&gt;action=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;&lt;/tt&gt;\n&lt;blockquote&gt;\n&lt;i&gt;continue&lt;/i&gt; - Anrufverarbeitung normal fortsetzen (default)&lt;br&gt;\n&lt;i&gt;reject&lt;/i&gt; - Ruf abweisen&lt;br&gt;\n&lt;i&gt;dnd&lt;/i&gt; - Ruf ablehnen mit Hinweis &quot;do not disturb&quot;&lt;br&gt;\n&lt;i&gt;redirect&lt;/i&gt; - Ruf umleiten nach &lt;tt&gt;contact&lt;/tt&gt; (siehe dort)&lt;br&gt;\n&lt;i&gt;autoanswer&lt;/i&gt; - Ruf &quot;automatisch&quot; annehmen&lt;br&gt;\n&lt;/blockquote&gt;\n&lt;br&gt;\n&lt;tt&gt;reason=&amp;lt;string&amp;gt;   &lt;/tt&gt;für dnd und reject (Anzeige bei GgSt)&lt;br&gt;\n&lt;tt&gt;contact=&amp;lt;Umleitadresse&amp;gt; &lt;/tt&gt;für redirect&lt;br&gt;\n&lt;tt&gt;caller_name=&amp;lt;neuer Displayname des Anrufers&amp;gt;   &lt;/tt&gt;ersetzt evtl. vorh. displayname aus INVITE&lt;br&gt;\n&lt;tt&gt;ringtone=&amp;lt;Dateiname des .wav file&amp;gt;   &lt;/tt&gt;Klingelton, speziell f. diesen Anruf (nur bei &lt;i&gt;continue&lt;/i&gt; ;-)&lt;br&gt;\n&lt;tt&gt;display_msg=&amp;lt;belieb. Hinweis für Detailanzeige Hauptfenster&amp;gt;&lt;/tt&gt;&lt;br&gt;\n&lt;tt&gt;end   &lt;/tt&gt;Twinkle wertet alle Rückgaben aus, schliesst STDOUT des Scripts(!), und arbeitet weiter&lt;br&gt;\n&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\n&lt;h3&gt;Environment Variablen&lt;/h3&gt;\n&lt;p&gt;\nDie Werte aller SIP header des eingehenden INVITE werden in Environmentvariablen ans Script übergeben. Aufbau der Variablennamen: &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; - z.B. SIP_FROM enthält Wert des &quot;from header&quot;.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. &lt;br&gt;\nSIPREQUEST_METHOD=INVITE. &lt;br&gt;\nSIPREQUEST_URI enthält request-URI des INVITE.&lt;br&gt;\nTWINKLE_USER_PROFILE enthält Name des Benutzerprofils, für das der Ruf einging.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation>Anrufbeantworter Nr/Adr:</translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation>Die SIP-Adresse bzw. Telefonnr., unter der Ihr vom Provider zur Verfügung gestellter Anrufbeantworter abrufbar ist. Oft gibt der Provider zwei Nummern an, eine zum Abruf über beliebige Telefone (zB. &quot;0049 211 58000111&quot;) und eine zum SIP-Abruf (zB. &quot;50000&quot;) - dann sollte hier die SIP-Nummer angegeben werden.</translation>\n    </message>\n    <message>\n        <source>Unsollicited</source>\n        <translation>Asterisk-Modus</translation>\n    </message>\n    <message>\n        <source>Sollicited</source>\n        <translation>RFC 3842</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsollicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsollicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Sollicited&lt;/H3&gt;\n&lt;p&gt;\nSollicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation>&lt;H2&gt;Message waiting indication Typ&lt;/H2&gt;\n&lt;p&gt;\nWenn Ihr SIP-Provider &quot;message waiting indication&quot; (MWI, Benachrichtigung über aufgezeichnete Nachrichten) anbietet, kann Twinkle Sie über neue und schon abgehörte Nachrichten auf Ihrem SIP-Anrufbeantworter informieren. Abhängig von Ihrem Provider bzw. dem von Ihnen genutzten Anrufbeantworterdienst müssen Sie hier eines der folgenden Verfahren einstellen:\n&lt;/p&gt;\n&lt;H3&gt;Asterisk&lt;/H3&gt;\n&lt;p&gt;\nAsterisk unterstützt im allg. &quot;unsollicited message waiting indication&quot;.\n&lt;/p&gt;\n&lt;H3&gt;RFC 3842&lt;/H3&gt;\n&lt;p&gt;\n&quot;Sollicited message waiting indication&quot; entsprechend RFC 3842 Spezifikation (z.B. für &quot;sipgate.de&quot;).\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation>&amp;MWI Typ:</translation>\n    </message>\n    <message>\n        <source>Sollicited MWI</source>\n        <translation>RFC 3842</translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation>Anmel&amp;dung gültig:</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation>Mailbox Ben&amp;utzername:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation>Der Domainname, IP-Adresse oder Hostname des Voice-Mailbox-Servers. Versuchen Sie die Voreinstellung (=Domain Ihres Benutzernamens), falls Ihr Provider nichts anderes mitgeteilt hat.</translation>\n    </message>\n    <message>\n        <source>For sollicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation>Bei RFC 3842 MWI meldet sich das Endgerät (Twinkle) für eine gewisse Dauer beim Server zum Empfang von Benachrichtigungen an (SUBSCRIBE), und sollte diese Anmeldung vor Ablauf erneuern. Ähnlich der &quot;expiry time&quot; / &quot;haltbar&quot; für REGISTER, siehe SIP-Server.</translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation>Ihr Benutzername zum Zugriff auf Ihre Voice-Mailbox (Anrufbeantworter). Wenn Ihr Provider nichts anderes mitteilt, versuchen Sie die Vorgabe (=Ihr SIP-Benutzername).</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation>Mailbox-&amp;Server:</translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation>Via Outbound-&amp;Proxy:</translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation>Wenn aktiviert, sendet Twinkle SIP-Anfragen an die Mailbox über den Outbound-Proxy.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation>Sie müssen einen Mailbox-Benutzernamen angeben.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation>Sie müssen den Mailbox-Server angeben.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation>Ungültiger Name für Mailbox-Server.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation>Ungültiger Mailbox-Benutzername.</translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation>Domain-&amp;Name benutzen für eindeutigen Contact-Header</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Freizeichentondatei auswählen.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Klingeltondatei auswählen.</translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation>Skriptdatei auswählen.</translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>Vor     Konvertierung: &lt;b&gt;%1&lt;/b&gt;&lt;br&gt;\nNach  Konvertierung: &lt;b&gt;%2&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Instant Message</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Präsenz</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation>&amp;Max. Anzahl IM-Fenster:</translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation>Hier können Sie die Anzahl gleichzeitig offener IM-Fenster für dieses Benutzerprofil als Empfänger begrenzen.&lt;br&gt;\nBei Erreichen der Obergrenze erhält jeder weitere Absender einer Instant Message den Hinweis &quot;468 Besetzt&quot;. &lt;br&gt;\nSie können diese Einstellung auf 0 setzen, wenn Sie keine ankommenden Instant Messages wünschen.</translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Ihre Präsenz</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>Erreichbarkeit beim Start &amp;veröffentlichen</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Wenn aktiviert, veröffentlicht Twinkle Ihren Online-Status als &quot;online&quot;, sobald das Benutzerprofil aktivieren. Beachten Sie, dass Sie trotz allem solange nicht erreichbar sind, bis Sie sich bei, SIP-Server angemeldet haben - siehe Menü &quot;Anmeldung&quot;, sowie hier &quot;SIP Server&quot; &quot;Bei Profilstart anmelden&quot;.</translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation>Kumpelpräsenz</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation>Erneut ve&amp;röffentlichen nach (Sek.):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation>Die Refreshzeit für die Veröffentlichung des Online-Status in Sekunden. Damit der &quot;presence server&quot; z.B. eine unterbrochene Verbindung schnell bemerkt, kann es sinnvoll sein, hier wesentlich kürzere Werte als den Standard &quot;3600&quot; einzutragen.</translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation>&quot;&amp;Subscribe&quot; erneut nach (Sek.):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation>Die Refreshzeit für die Anmeldung durch &quot;SUBSCRIBE&quot; zum Erhalten von Online-Status-Mitteilungen über die Ereichbarkeit der Buddies unter diesem Benutzerprofil. Standard &quot;3600&quot;.</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Übertragung/NAT</translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation>Verwende q-Wert bei der Anmeldung</translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation>Falls mehrere Geräte auf die gleiche SIP-Benutzerkennung angemeldet werden, kann der Provider den q-Wert dazu verwenden, die Reihenfolge festzulegen, in der ein eingehender Ruf an die Geräte zugetellt wird.</translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation>Der q-Wert darf zwischen 0,000 und 1,000 liegen. Ein höherer Wert bedeutet höhere Priorität. Das Gerät mit der höchsten Priorität wird als erstes angesprochen.</translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>SIP-Übertragung</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation>UDP</translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation>TCP</translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Übertragungsmodus (TCP oder UDP) für SIP. Bei &quot;Automatisch&quot; wird TCP verwendet, falls die Größe der zu übertragenden Nachricht das Limit für UDP übersteigt.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>Übe&amp;rtragungsprotokoll:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation>UDP &amp;Grenzwert:</translation>\n    </message>\n    <message>\n        <source> bytes</source>\n        <translation> Bytes</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Nachrichten, die größer sind als der Grenzwert, werden über TCP gesendet, kleinere über UDP.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN (does not work for incoming TCP)</source>\n        <translation>&amp;STUN benutzen (wirkungslos für eingehende TCP-Verbindungen)</translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation>TCP-V&amp;erbindung aufrecht erhalten</translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation>Wenn aktiviert: Twinkle hält die TCP-Verbindung aufrecht, die bei der Registrierung verwendet wurde. So kann der SIP-Proxy diese Verbindung weiterhin benutzen, um ankommende Anfragen an Twinkle weiterzuleiten. Durch Senden von &quot;Application ping Paketen&quot; wird ständig überprüft, ob die Verbindung weiterhin besteht.</translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation>&amp;Sende &quot;compositing indication&quot; beim Schreiben einer Nachricht.</translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation>Wenn aktiviert, sendet Twinkle eine &quot;compositing indication&quot; wenn Sie eine Nachricht tippen. So kann der Empfänger erkennen, dass Sie gerade dabei sind, eine Nachricht zu verfassen.</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation>&amp;Qualität:</translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation>Bytes</translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Assistent</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Der Domainname, IP-Adresse oder Hostname des STUN-Servers.\n\nTwinkle versucht, unter der hier genannten Domain die korrekten Daten beim DNS-Server zu erfragen (RFC 2782).\nDaher genügt bei Providern, die dies unterstützen, die Domain des Anmeldeservers als Angabe.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN-Server:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Der Nutzername, den Sie von Ihrem Provider zugewiesen bekommen haben. Dieser ist der erste Teil Ihrer vollständigen SIP-Adresse &lt;b&gt;nutzername&lt;/b&gt;@domain.com .  Bei vielen Providern wird dieser -eigentlich falsch- als Telefonnummer bezeichnet.\n&lt;br&gt;&lt;br&gt;\n*DATEN FÜR DIESES FELD SIND ZWINGEND NOTWENDIG.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Domäne*:</translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Wählen Sie Ihren SIP-Provider aus, und tragen Sie dann Ihren SIP-Benutzernamen, gegebenenfalls Absendernamen, Anmeldenamen und Passwort ein.&lt;br&gt;\nWenn Ihr SIP-Provider nicht in der Liste erscheint, wählen Sie &lt;b&gt;Anderer&lt;/b&gt; und tragen Sie die Angaben entsprechend der von Ihrem Provider erhaltenen Daten ein.\n&lt;p&gt;\nPraktisch überall in Twinkle bekommen Sie mit &lt;b&gt;Umschalt-F1&lt;/b&gt; oder &lt;b&gt;rechtem Mausklick&lt;/b&gt; Hilfetexte wie diesen zu den einzelnen Feldern und Knöpfen.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>&amp;Anmeldename:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>Ihr &amp;Name:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ihr SIP-Anmeldename. Häufig identisch mit Ihrem SIP-Nutzernamen, dann leerlassen. Falls nicht, wird Ihr Provider dies mitteilen.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Die Domain oder IP-Adresse, unter der Sie von Ihrem Provider geführt werden bzw. im Internet erreichbar sind. Dies ist der zweite Teil ihrer vollständigen SIP-Adresse nutzername@&lt;b&gt;domain.com&lt;/b&gt;.  Bei vielen Providern identisch mit der Domain des Providers.\nFür direct-IP-to-IP (siehe Handbuch) ist hier die Adresse (DynDNS oder IP) einzutragen, unter der &lt;b&gt;Ihr Rechner&lt;/b&gt; zu erreichen ist.\n&lt;br&gt;&lt;br&gt;\n*DATEN FÜR DIESES FELD SIND ZWINGEND NOTWENDIG.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Ihr Absendername oder Pseudonym. Dieses Feld wird nur als Teil der Absenderangaben zur angerufenen/rufenden Gegernstelle übertragen und dort evtl angezeigt. Beliebige Angabe, nicht zwingend erforderlich.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>SIP-Pro&amp;xy:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>Die Domain, IP-Adresse oder Hostname Ihres SIP-Proxy. Wenn dieser mit Ihrer Benutzerdomain identisch ist, lassen Sie dieses Feld leer.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>&amp;SIP Service Provider (Umschalt-F1 für Hilfe):</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Passwort:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>Ben&amp;utzername *:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ihr Anmeldepasswort. Wenn Sie dieses Feld leerlassen, müssen Sie das Passwort bei jeder Anmeldung in den dann erscheinenden Requester eintragen. </translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Abbru&amp;ch</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Keiner (direkt IP zu IP)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Anderer</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Benutzerprofil Wizard: </translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Sie müssen den Namensteil Ihrer SIP-Benutzerkennung angeben.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Sie müssen den domain-Teil (den Teil rechts nach @) Ihrer SIP-Benutzerkennung angeben.\nHäufig identisch mit der Domain Ihres SIP-Providers.\n\nFür direct-IP-to-IP, also ohne SIP-Provider, ist dies der (dyndns-)Name oder die öffentliche IP Ihres PC. </translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Ungültiger Wert für SIP-Proxy.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Ungültiger Wert für STUN-Server.</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Ja</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Nein</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation>Annehmen</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Abweisen</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/lang/twinkle_fr.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.0\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Fiche de contact</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation>&amp;Remarque:</translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation>Titre du contact.</translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Prénom du contact.</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>&amp;Prénom:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation>Vous pouvez saisir une remarque sur le contact ici.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Téléphone:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation>T&amp;itre:</translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Numéro de téléphone ou une adresse SIP du contact.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Nom du contact. </translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Nom:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Vous devez saisir un nom.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>Vous devez saisir un numéro de téléphone ou une adresse SIP.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nom</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Téléphone</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"unfinished\">Remarque</translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Authentification</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>L&apos;utilisateur dont l&apos;identification est requise.</translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Le profil d&apos;utilisateur dont l&apos;identification est requise.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil d&apos;utilisateur:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Utilisateur:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>Mot de &amp;passe:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Votre mot de passe pour authentification.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Votre nom d&apos;authentification SIP. Souvent il est le même que votre nom d&apos;utilisateur SIP. Cependant, il peut être différent.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>&amp;Utilisateur:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation>Login nécessaire pour Realm:</translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation>Le Realm pour lequel vous devez vous identifier.</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Avatar</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Téléphone:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Nom de votre avatar.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>&amp;Montrer la disponibilité</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Cochez cette case si vous voulez voir la disponibilité de votre avatar. Ceci fonctionnera uniquement si votre fournisseur d&apos;accès offre un service de présence.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Nom:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>Adresse SIP de votre avatar.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Vous devez saisir un nom.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Téléphone invalide.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Echec de la sauvegarde de la liste d&apos;avatars: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Disponobilité</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>inconnu</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>déconnecté</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>connecté</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>demande rejetée</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>non publié</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>impossible de publier</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>demande échouée</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Cliquez à droite pour ajouter un avatar.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>Echec de l&apos;accès à la carte son</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>Echec de la création de la socket UDP (RTP) sur le port %1</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Echec de la création de &quot;audio receiver thread&quot;.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Echec de la création de &quot;audio transmitter thread&quot;.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>utilisateur local</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>utilisateur distant</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>échec</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>inconnu</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>entrant</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>sortant</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Déconnexion</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>déconnecter toutes les interfaces</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation type=\"obsolete\">C&apos;est simplement votre nom complet, ex: Pierre Dupond. Il est utilisé pour l&apos;affichage. Quand vous ferez un appel, ceci sera montré à votre correspondant.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation type=\"obsolete\">&amp;Votre nom:</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"obsolete\">Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Clavier</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>Touche de fonction A. Normalement pas nécessaire.</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>Touche de fonction B. Normalement pas nécessaire.</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>Touche de fonction C. Normalement pas nécessaire.</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Etoile (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Point (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>Touche de fonction D. Normalement pas nécessaire.</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Fermer</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n</context>\n<context>\n    <name>FreeDeskSysTray</name>\n    <message>\n        <source>Show/Hide</source>\n        <translation type=\"obsolete\">Montrer/Cacher</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"obsolete\">Quitter</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Failed to create a UDP socket (SIP) on port %1</source>\n        <translation type=\"obsolete\">Impossible de créer un socket UDP (SIP) sur le port %1</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Les profils suivant sont pour l&apos;utilisateur %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Des utilisateurs différents doivent avoir des profils différents.</translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Impossible de trouver une interface réseau. Twinkle utilisera 127.0.0.1 comme adresse IP locale. Quand vous vous connecterez au réseeau vous devrez redémarrer Twinkle pour utiliser la bonne adresse IP.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Ligne %1: appel entrant pour %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Appel tranféré par %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation>Ligne %1: appel annulé par le correspondant.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation>Ligne %1: Le correspondant a raccroché.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Ligne %1: réponse SDP du correspondant non supportée.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Ligne %1: réponse SDP du correspondant manquante.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation>Ligne %1: Contenu entrant non supporté.</translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Ligne %1: ACK non reçu, l&apos;appel sera annulé.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Ligne %1: PRACK non reçu, l&apos;appel sera annulé.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Ligne %1:  PRACK échoué.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Ligne %1:  impossible d&apos;annuler l&apos;appel.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Ligne %1: Le correspondant a répondu.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Ligne %1: appel échoué.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>L&apos;appel peut être redirigé vers:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation>Ligne %1: fin d&apos;appel.</translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Leitung %1: appel établi.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Réponse à la demande des capacités du correspondant: %1 %2</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Capacités du correspondant %1</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation>Types de corps acceptés:</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>inconnu</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Encodage accepté:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Langages accepté:</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Demandes authorisées:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation>Extensions supportées:</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation>aucun</translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Type de correspondant:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation>Ligne %1: récupération de l&apos;appel échoué.</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, connexion échouée: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, connexion réussie (expire %2 sec.)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, enregistrement échoué: echec STUN</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, déconnexion réussie: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation>%1, recherche des connexions échouée: %2 %3</translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: vous n&apos;êtes pas connecté</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: voici la liste des connexions</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: recherche des connexions...</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Ligne %1: rediriger la demande à </translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Redirection de la demande à : %1</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Ligne %1: DTMF détecté:  </translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation>Evénnement DTMF invalid (%1)</translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Ligne %1: envoi DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation>Ligne %1: le correspondant ne supporte pas les événnements DTMF.</translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation>Ligne %1: notification reçue.</translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Evénnement: %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Statut: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Raison: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Progression: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Ligne %1: transfert d&apos;appel échoué.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Ligne %1: transfert d&apos;appel réussi.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Ligne %1: transfert d&apos;appel en cours.</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation>Aucune nouvelle notification ne sera reçue.</translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Ligne %1: transfert d&apos;appel vers %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Demande de transfert de %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation>Ligne %1: Transfert d&apos;appel échoué. Recherche de l&apos;appel original.</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Redirection d&apos;appel</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil utilisateur:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Utilisateur:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Authorisez vous la redirection de l&apos;appel vers la destination suivante?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation>Si vous ne voulez plus qu&apos;on vous pose cette question, vous devez modifier les paramètres dans la section &quot;protocole SIP&quot; du profil d&apos;utilisateur.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation>Demande de redirection</translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation>Authorisez-vous la redirection de la demande %1 vers la destination suivante?</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Transfert d&apos;appel</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation>Demande de transfert d&apos;appel reçue depuis:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Authorisez-vous le transfert de l&apos;appel vers la destination suivante?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation>Info:</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Attention:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation>Critique:</translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Firewall / NAT Analyse...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Annuler</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Ligne %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Cliquez sur &quot;Verr Num&quot; pour confirmer un SAS correcte.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>L&apos;utilisateur distant sur la ligne %1 a invalidé la cryptographie.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Ligne %1: SAS confirmé.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation>Ligne %1: SAS confiirmation de la remise à zéro.</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Ligne %1: appel rejeté.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Ligne %1: appel redirigé.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Lancement de la conférence échoué.</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Outre-passer le fichier de vérouillage et démarrer quand-même?</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1, STUN demande rejetée: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, STUN demande échouée.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, echec du statut du message vocal.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, rejet du statut du message vocal.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, la boîte vocale n&apos;existe pas.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, statut du message vocal terminé. </translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, déconnexion échouée: %2 %3</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation>Demande de transfert d&apos;appel reçue.</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation>S&apos;il y a des utilisateurs de différents domaines, activez cette option dans votre profil d&apos;utilisateur (protcole SIP)</translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation>Utilisez le nom de domaine pour créer une entête de contact unique</translation>\n    </message>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>Impossible de créer un socket %1 (SIP) sur le port %2</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Accepté par le réseau</translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Echec de l&apos;enregistrement de la pièce jointe: %1</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Selection d&apos;adresse</translation>\n    </message>\n    <message>\n        <source>Name</source>\n        <translation type=\"obsolete\">Nom</translation>\n    </message>\n    <message>\n        <source>Type</source>\n        <translation type=\"obsolete\">Type</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"obsolete\">Téléphone</translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>Montrer uniquement les adresses &amp;SIP</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Cochez cette case quand vous voulez uniquement voir les contacts avec une adresse SIP, i.e. commençant par: &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>&amp;Recharger</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Recharge la liste d&apos;adresse de KAddressbook.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation>La liste d&apos;adresse est tiré de &lt;b&gt;KAddressBook&lt;/b&gt;. Les contacts pour lesquels vous n&apos;avez pas renseignés de numéro de téléphone ne sont pas montrés ici. \nPour ajouter, supprimer, ou modifier une information de contact, vous devez utiliser KaddressBook.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>Carnet d&apos;adresses &amp;local</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"obsolete\">Remarque</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Contacts dans le carnet d&apos;adresses local de Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Ajouter</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Ajouter un nouveau contact au carnet d&apos;adresses local.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Supprimer</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Supprimer un contact du carnet d&apos;adresses local.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Editer</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Editer un contact du carnet d&apos;adresses local.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Il semble qu&apos;il n&apos;y ait aucun contact avec un numéro de téléphone dans &lt;b&gt;KAddressBook&lt;/b&gt;, le carnet d&apos;adresses de KDE. Twinkle récupère tous les contacts avec un numéro de téléphone de KAddressBook. Pour gérer vos contacts utilisez KadressBook.&lt;/p&gt;\n&lt;p&gt;Comme alternative, vous pouvez utiliser le carnet d&apos;adresses local de Twinkle.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation>Etes-vous sûr de vouloir supprimer le contact &apos;%1&apos; du carnet d&apos;adresses local?</translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation>Supprimer un contact.</translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Nom du profil</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Saisissez un nom de profil:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Le nom de votre profil&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nUn profil contient vos parmêtres utilisateur, ex: votre nom d&apos;utilisateur et mot de passe. Vous devez donner un nom à chaque profil.\n&lt;br&gt;&lt;br&gt;\nSi vous avez plusieurs comptes SIP, vous pouvez créer différents profils. Quand vous lancez Twinkle, il vous proposera la liste des profils pour sélectionner celuique vous voulez utiliser.\n&lt;br&gt;&lt;br&gt;\nPour vous rappeler facilement de vos profils, vous pouvez utiliser votre nom d&apos;utilisateur SIP comme nom de profil. ex: &lt;b&gt;exemple@exemple.com&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Impossible de trouver le dossier .twinkle dans votre dossier home.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Ce profil existe déjà.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Renomer le profil %1 en:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - Historique des appels</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Heure</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>Ent/Sort</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>de/à</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Sujet</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Statut</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Détails</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Détails de l&apos;appel sélectionné.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Voir</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>Appels &amp;entrants</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Sélectionnez cette option pour voir les appels entrants.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>Appels &amp;sortants</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Sélectionnez cette option pour voir les appels sortants.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>Appels &amp;répondus</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Sélectionnez cette option pour voir les appels répondus.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>Appels en &amp;absence</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Sélectionnez cette option pour voir les appels en absence.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>Seulement l&apos;&amp;utilisateur actif</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Sélectionnez cette option pour voir seulement les appels de cet utilisateur.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Vider</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Vider la totalité de l&apos;historique.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Nota:&lt;/b&gt; ceci supprimera &lt;b&gt;tous&lt;/b&gt; les enregistrements, y compris les enregistrements non affichés en fonction des options sélectionnées.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Fermer cette fenêtre.</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Début de l&apos;appel:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Appel abouti:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Fin de l&apos;appel:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Durée de l&apos;appel:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Direction:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>de:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>à:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Répondre à:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Demandé par:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Sujet:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Terminé par:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Statut:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Interface de l&apos;utilisateur distant:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil d&apos;utilisateur:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>conversation</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Appel...</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Suppression</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Re:</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Adresse des appels sélectionnés.</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>&amp;Fermer (Esc)</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+F</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>Appel (&amp;Enter)</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Appel</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;à:</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Vous pouvez renseigner un sujet ici (optionnel). Il peut être montré à l&apos;appelant.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>L&apos;adresse de la personne vers que vous voulez appeler. Ceci peut être une adresse SIP comme &lt;b&gt;sip:exemple@exemple.com&lt;/b&gt; ou uniquement la partie utilisateur ou le numéro de téléphone d&apos;une adresse complète. Quand vous ne spécifiez pas une adresse complète, Twinkle complètera cette adresse en utilisant le domaine défini dans votre profil utilisateur.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>L&apos;utilisateur qui fera l&apos;appel.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Sujet:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;De:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>Cacher l&apos;&amp;identité</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Par cette option, vous demandez à votre fournisseur de services SIP de cacher votre identité vis à vis de votre correspondant. Ceci ne cachera que votre identité c&apos;est à dire votre adresse SIP et numéro de téléphone. Ceci &lt;/b&gt;ne&lt;b&gt; cache &lt;b&gt;pas&lt;/b&gt; votre &lt;b&gt;adresse IP&lt;/p&gt;.\n&lt;p&gt;&lt;b&gt;Attention: &lt;/b&gt;Tous les fournisseurs de services SIP ne supportent pas cette fonctionnalité !&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Tous les fournisseurs de services SIP ne supportent pas la fonctionnalité &quot;cacher l&apos;identité&quot;. Assurez vous que votre fournisseur de services SIP l&apos;authorise si vous en avez vraîment besoin.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Contenu du fichier de log (~/.twinkle/twinkle.log)</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Fermer</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+F</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Supprimer</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Fermer la fenêtre de log. Ceci &lt;b&gt;ne&lt;/b&gt; supprime &lt;b&gt;pas&lt;/b&gt; le fichier de log lui-même.</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - Messagerie instantanée</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;à:</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>L&apos;utilisateur qui enverra le message.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>L&apos;adresse de la personne à qui vous voulez envoyer un message. Ceci peut être une adresse SIP comme &lt;b&gt;sip:exemple@exemple.com&lt;/b&gt; ou uniquement la partie utilisateur ou le numéro de téléphone d&apos;une adresse complète. Quand vous ne spécifiez pas une adresse complète, Twinkle complètera cette adresse en utilisant le domaine défini dans votre profil utilisateur.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>Profil &amp;utilisateur:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Conversation</translation>\n    </message>\n    <message>\n        <source>The exchanged messages.</source>\n        <translation type=\"obsolete\">Les messages échangés.</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Saisissez votre message ici, puis appuyer sure &quot;Envoyer&quot; pour l&apos;envoyer.</translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Envoyer</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Envoyer le message.</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Echec de l&apos;envoi</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Notification de la réception</translation>\n    </message>\n    <message>\n        <source>Instant message toolbar</source>\n        <translation type=\"obsolete\">Barre d&apos;outil de la messagerie instantanée</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Envoi de fichier...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Envoyer un fichier</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>La taille de l&apos;image est réduite en prévisualisation</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Ouvrir avec %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Ouvrir avec...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Enregistrer la pièce jointe sous...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Le fichier existe déjà. Voulez-vous le remplacer ?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Impossible d&apos;enregistrer la pièce jointe.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 est un message texte.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>Envoi du message</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>&amp;Numéro:</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>L&apos;adresse de la personne vers que vous voulez appeler. Ceci peut être une adresse SIP comme &lt;b&gt;sip:exemple@exemple.com&lt;/b&gt; ou uniquement la partie utilisateur ou le numéro de téléphone d&apos;une adresse complète. Quand vous ne spécifiez pas une adresse complète, Twinkle complètera cette adresse en utilisant le domaine défini dans votre profil utilisateur.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>L&apos;utilisateur qui émettera l&apos;appel.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Utilisateur:</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Appeler</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Appeler l&apos;appel.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation>Indication de réponse automatique.</translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation>Indication de message en attente.</translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation>Indication de redirection d&apos;appel.</translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation>Indication &quot;ne pas déranger&quot;.</translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation>Indication d&apos;appel en absence.</translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation>Statut de la connexion.</translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation>Affichage</translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Statut de la ligne</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Ligne &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Cliquez pour basculer sur la ligne 1.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>De:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>à:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Sujet:</translation>\n    </message>\n    <message>\n        <source>Visual indication of line state.</source>\n        <translation type=\"obsolete\">Indication visuelle de l&apos;état de la ligne.</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call is on hold</source>\n        <translation type=\"obsolete\">Appel en attente</translation>\n    </message>\n    <message>\n        <source>Voice is muted</source>\n        <translation type=\"obsolete\">Voix coupée</translation>\n    </message>\n    <message>\n        <source>Conference call</source>\n        <translation type=\"obsolete\">Conférence</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Transfert d&apos;appel</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThe padlock indicates that your voice is encrypted during transport over the network.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nBoth ends of an encrypted voice channel receive the same SAS on the first call. If the SAS is different at each end, your voice channel may be compromised by a man-in-the-middle attack (MitM).\n&lt;/p&gt;\n&lt;p&gt;\nIf the SAS is equal at both ends, then you should confirm it by clicking this padlock for stronger security of future calls to the same destination. For subsequent calls to the same destination, you don&apos;t have to confirm the SAS again. The padlock will show a check symbol when the SAS has been confirmed.\n&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p&gt;\nLe clavier indique que votre voix est encrypté sur le réseau.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String (chaine d&apos;authentification courte)&lt;/h3&gt;\n&lt;p&gt;\nLes deux correspondants d&apos;une ligne encrypté reçoivent le même SAS lors du premier appel. Si le SAS est différent, votre ligne est compromise.\n&lt;/p&gt;\n&lt;p&gt;\nSi le SAS est égal, vous devez le confirmer en cliquant sur le clavier pour une meilleure sécurité des futurs appels vers cette même destination. Vous n&apos;aurez plus à confirmer le SAS pour cette même destination. Le clavier affichera un symbol de confirmation quand le SAS est confirmé.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation>SAS - Short authentication string</translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Durée de l&apos;appel</translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Ligne &amp;2:\n</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Cliquez pour basculer sur la ligne 2.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Fichier</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Edition</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>&amp;Appel</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Activer la ligne</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>&amp;Connexion</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>&amp;Services</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>&amp;Vue</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>Ai&amp;de</translation>\n    </message>\n    <message>\n        <source>Call Toolbar</source>\n        <translation type=\"obsolete\">Barre d&apos;outil d&apos;appel</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Quitter</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>&amp;Quitter</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>A propos de Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>A propos de &amp;Twinkle</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Appeler</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Répondre à l&apos;apppel entrant</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation>Raccrocher</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Rejeter l&apos;appel entrant</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Mettre un appel en attente, ou reprendre un appel en attente</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Redirection d&apos;appel entrant sans répondre</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Ouvrir le clavier pour siasir des digits du menu vocal</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Se connecter</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>Se &amp;connecter</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Se déconnecter</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>Se &amp;déconnecter</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Déconnecter cette interface</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Montrer les connexions</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>&amp;Montrer les connexions</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Possibilités du terminal</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Demande les capacités du terminal d&apos;un correspondant</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Ne pas déranger</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>Ne pas &amp;déranger</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Redirection d&apos;appel</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>&amp;Redirection d&apos;appel...</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Wahlwiederholung, wählt letzten Ruf erneut</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>A propos de Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>&amp;A propos de Qt</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Profil utilisateur</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>Profil &amp;utilisateur...</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation>Joindre une conférence 3 parties</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Rendre muet</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Transfert d&apos;appel</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Paramètres système</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>Paramètres &amp;système...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Tout déconnecter</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>&amp;Tout déconnecter</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Déconnecter toutes les interfaces connectées</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Réponse automatique</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>Réponse &amp;automatique</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>Historique d&apos;appel</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>&amp;Historique d&apos;appel...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Changer d&apos;utilisateur ...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Changer d&apos;utilisateur ...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Activer ou désactiver des utilisateurs</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>Qu&apos;est-ce que c&apos;est?</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>&amp;Qu&apos;est-ce que c&apos;est?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Ligne 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Ligne 2\n</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>libre</translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>numérotation</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>appel en cours, merci de patienter</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>appel entrant</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>établissement de l&apos;appel, merci de patienter</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>établi</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>établi (attente de données)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation>Raccrochage en cours, merci de patienter</translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>Etat inconnu</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Voix encryptée</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>Cliquez pour confirmer SAS.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation>Cliquez pour annuler la vérification SAS.</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Utilisateur:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Appel:</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Statut de la connexion:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>Connecté</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>Echoué</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>Non connecté</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Aucun utilisteur n&apos;est connecté.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>&quot;Ne pas dérangé&quot; activé pour:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Redirection activée pour:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>&quot;Réponse automatique&quot; activée pour:</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>&quot;Ne pas dérangé&quot; n&apos;est pas actif.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>La redirection n&apos;est pas active.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>la réponse automatique n&apos;est pas active.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>Vous n&apos;avez pas d&apos;appel en absence.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>1 appel en absence.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>%1 appels en absence.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Cliquez pour plus de détails .</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Démarrage des profiles d&apos;utilisateurs...</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Les profils suivant sont pour l&apos;utilisateur %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Voius pouvez seulement executer plusieurs profils pour différents utilisateurs.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation>Vous avez chazngé le port UDP de SIP. Pour prendre en compte les modifications, il est nécessaire de redémarrer Twinkle.</translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation>Consultation du transfert</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Cacher l&apos;identité</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Cliquez pour montrer les connexions.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 nouveau, 1 ancien message</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 nouveau, %2 ancien messages</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>1 nouveau message</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 nouveau messages</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>1  ancien message</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 anciens messages</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Messages reçus</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Pas de messages</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Statut de la boîte vocale:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Echec</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Inconnu</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Cliquez pour accéder à la boîte vocale.</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Cliquez pour activer/désactiver</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Cliquez pour activer</translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation>non enregistré</translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation>Vous devez enregistrer votre numéro de boîte vocale dans votre profil d&apos;utilisateur avant de pouvoir y accéder.</translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>La ligne est occupée. Impossible d&apos;accéder à la boîte vocale.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation>Le numéro %1 de boîte vocale est invalide. Merci d&apos;enregistrer un numéro valide dans votre profil d&apos;utilisateur.</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Appel</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <comment>call menu text</comment>\n        <translation type=\"obsolete\">&amp;Appel...</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Répondre</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Répondre</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Fin</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Fin</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Refuser</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">R&amp;efuser</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Attente</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Atte&amp;nte</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Redirect</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Re&amp;direction...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Dt&amp;mf...</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Possibilités du &amp;terminal...</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Rappeler</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Rappeler</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Conf</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Conférence</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Muet</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">M&amp;uet\n</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Transfert</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Transfert...</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Boîte vocale</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>&amp;Boîte vocale</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation>Accès boîte vocale</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Liste d&apos;avatars</translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>&amp;Message</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Msg</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>&amp;Message instantané...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Message instantané</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Appel...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>&amp;Editer...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Supprimer</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>Déc&amp;onnecté</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>&amp;Connecté</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>Mod&amp;ifier la disponibilité</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Ajouter un avatar...</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Echec de la sauvegarde de la liste d&apos;avatars: %1</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation>Vous pouvez créer une liste d&apos;avatar pour chaque profil. Vous ne pouvez voir la disponibilité de vos avatars et publier la votre que si votre fournisseur de service dispose d&apos;un serveur de présence.</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>&amp;Liste d&apos;avatars</translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation>&amp;Affichage</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation type=\"unfinished\">Appel</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation type=\"unfinished\">&amp;Répondre</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\">Répondre</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation type=\"unfinished\">&amp;Fin</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation type=\"unfinished\">Fin</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation type=\"unfinished\">R&amp;efuser</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\">Refuser</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation type=\"unfinished\">Atte&amp;nte</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation type=\"unfinished\">Attente</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation type=\"unfinished\">Re&amp;direction...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation type=\"unfinished\">Redirect</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation type=\"unfinished\">Dt&amp;mf...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation type=\"unfinished\">Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation type=\"unfinished\">Possibilités du &amp;terminal...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation type=\"unfinished\">&amp;Rappeler</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation type=\"unfinished\">Rappeler</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation type=\"unfinished\">&amp;Conférence</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation type=\"unfinished\">Conf</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation type=\"unfinished\">M&amp;uet\n</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation type=\"unfinished\">Muet</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation type=\"unfinished\">&amp;Transfert...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation type=\"unfinished\">Transfert</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - Conversion de numéro</translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Expression de recherche:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Remplacer:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation>Chaine au format Perl pour le remplacement du numéro.</translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation>Expression réguliuère au format Perl pour la vérification du format du numéro que vous voulez modifier.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>L&apos;expression de vérification ne doit pas être vide.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>La valeur de remplacement ne doit pas être vide.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Expression régulière invalide.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Redirection</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Rediriger les appels entrants vers</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Vous pouvez sélectionner jusqu&apos;a 3 destinations vers lesquelles rediriger l&apos;appel. Si la première destionationne répond pas, la deuxième sera essayée et ainsi de suite.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3ème destination:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2ème destination:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1ère destination:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - Selection de l&apos;interface réseau</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Selectionnez l&apos;interface réseau/adresse IP que vous voulez utiliser:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>Vous avez plusieurs adresses IP. Vous devez sélectionner ici l&apos;adresse IP qui doit être utilisée. Cette adresse IP sera incluse dans les messages SIP.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>Activer comme &amp;IP par défaut</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Utiliser l&apos;adresse IP sélectionnée comme adresse IP par défaut. La prochaine fois que vous démarrerez Twinkle, cette adresse IP sera automatiquement utilisée.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>Activer comme interface &amp;réseau par défaut</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>Utiliser l&apos;interface réseau sélectionné comme interface réseau par défaut. La prochaine fois que vous démarrerez Twinkle, cette interface sera automatiquement utilisée.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Si vous voulez supprimer ou modifier les paramètres par défaut, vous pourrez le faire par les paramètres système.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle -Sélectionner un profil utilisateur</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Sélectionner le(s) profil(s) à démarrer:</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation type=\"obsolete\">Profil utilisateur</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Cochez les cases des profils utilisateurs que vous voulez utilisez et appuyer sur &quot;Démarrer&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;New</source>\n        <translation type=\"obsolete\">&amp;Nouveau</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+N</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Créer un nouveau profil avec l&apos;éditeur de profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>&amp;Assistant</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Créer un nouveau profil avec l&apos;assistant de création de profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Editer</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Editer le profil sélectionné.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Supprimer</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Supprimer le profil sélectionné.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>Ren&amp;ommer</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Renommer le profil sélectionné.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>&amp;Utiliser par défaut</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Utiliser le profil sélectionné comme profil par défaut. La prochaine fois que vous démarrerez Twinkle, ces profils seront automatiquement démarrés.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Démarrer</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Démarrer Twinkle avec le profil sélectionné.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>Paramètres s&amp;ystème</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Editer les paramètres système.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Avant d&apos;utiliser Twinkle, vous devez créer un profil utilisateur.&lt;br&gt;Cliquez sur OK pour créer un profil.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"obsolete\">&lt;html&gt;Vous pouvez utiliser l&apos;éditeur de profil pour créer un profil. Avec l&apos;éditeur de profil vous pouvez modifier beaucoup de paramètres SIP, RTP et autres.&lt;br&gt;&lt;br&gt;Vous pouvez également utiliser l&apos;assistant pour créer un profil plus rapidement. Il vous proposera seulement quelques paramètres essentiels. Vous pourrez éditer ce profil plus tard en utilisant l&apos;éditeur de profil.&lt;br&gt;&lt;br&gt;Sélectionnez la méthode que vous préférez (débutants: l&apos;assistant est conseillé).&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Vous devrez ajuster les paramètres systèmes. Vous pouvez changer ces parmètres à tout moment.&lt;br&gt;&lt;br&gt;Cliquez sur OK pour voir et modifier les paramètres systèmes&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>Vous n&apos;avez sélectionné aucun profil à démarrer.\nMerci de sélectionner un profil.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Etes-vous sûr de vouloir supprimer le profil &apos;%1&apos; ?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Supprimer un profil</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Echec de la suppression de profil.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Echec du changement de nom de profil.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Si vous voulez supprimer ou modifier le profil par défaut, vous pourrez le faire par les paramètres système. &lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Impossible de trouver le dossier .twinkle dans le dossier home.</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>Editeur de &amp;profil</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\">Alt+E</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation type=\"unfinished\">Alt+A</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Selection d&apos;utilisateur</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>Tout &amp;sélectionner</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>Tout &amp;désélectionner</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation type=\"obsolete\">Utilisateur</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Se connecter</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Sélectionnez le profil que vous voulez connecter.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Se déconnecter</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Sélectionnez le profil que vous voulez déconnecter.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Déconnecter toutes les interfaces</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Sélectionnez le profil dont vous voulez déconnecter toutes les interfaces.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Ne pas déranger</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Sélectionnez le profil dont vous voulez activer &quot;ne pas déranger&quot;.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Réponse automatique</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Sélectionnez le profil dont vous voulez activer la réponse automatique.</translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Envoi de fichier</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Choisir le fichier à envoyer.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Fichier:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Sujet:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Le fichier n&apos;existe pas.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Envoi de fichier...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation>Twinkle - Redirection d&apos;appel</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Utilisateur:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation>Il y a 3 srvices de redirection:&lt;p&gt;\n&lt;b&gt;Inconditionnel:&lt;/b&gt; tous les appels sont redirigés&lt;p&gt;\n&lt;p&gt;\n&lt;b&gt;Occupé:&lt;/b&gt; Rediriger l&apos;appel si les 2 lignes sont occupées\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Pas de réponse:&lt;/b&gt; redirige un appel quand le décompte &quot;pas de réponse&quot; expire.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation>&amp;Inconditionnel</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation>&amp;Rediriger tous les appels</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt-A</translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation>Active le service de redirection inconditionelle.</translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation>Rediriger vers</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Vous pouvez sélectionner jusqu&apos;a 3 destinations vers lesquelles rediriger l&apos;appel. Si la première destionationne répond pas, la deuxième sera essayée et ainsi de suite.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3ème destination:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2ème destination:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1ère destination:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Occupé</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation>&amp;Rediriger les appels quand je suis occupé</translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation>Active le service de redirection &quot;occupé&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>&amp;Pas de réponse</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation>&amp;Rediriger les appels quand je ne réponds pas</translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation>Active le service de redirection sans réponse.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Accepter et enregistrer les modifications.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Annuler tous les changements et fermer la fenêtre.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>Vous avez saisi une destination invalide.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Paramètres du système</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Général</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Audio</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Sonneries</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Réseau</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Log</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Sélectionnez la catégorie dont vous voulez voir ou modifier les paramètres.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Carte son</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Sélectionnez la carte son qui jouera la sonnerie des appels entrants.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Sélectionnez la carte son à laquelle est connecté le microphone.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Sélectionnez la carte son à laquelle est connecté le haut parleur.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Haut-parleur:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Sonnerie:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Autre interface:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Microphone:</translation>\n    </message>\n    <message>\n        <source>When using ALSA, it is not recommended to use the default device for the microphone as it gives poor sound quality.</source>\n        <translation type=\"obsolete\">Avec ALSA, il n&apos;est pas recommandé d&apos;utiliser l&apos;interface par défaut pour le microphone car ceci dégrade la qualité du son.</translation>\n    </message>\n    <message>\n        <source>Reduce &amp;noise from the microphone</source>\n        <translation type=\"obsolete\">Réduire le &amp;bruit du microphone</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+B</translation>\n    </message>\n    <message>\n        <source>Recordings from the microphone can contain noise. This could be annoying to the person on the other side of your call. This option removes soft noise coming from the microphone.\n\nThe noise reduction algorithm is very simplistic. Sound is captured as 16 bits signed linear PCM samples. All samples between -50 and 50 are truncated to 0.</source>\n        <translation type=\"obsolete\">L&apos;enregistrement par l&apos;intermédiaire d&apos;un microphone peut contenir du bruit. Ceci peut être génant pour le correspondant. Cette option supprime ce bruit.\nL&apos;algoritme de réduction de bruit est très simple. Le son est enregistré en échantillons PCM 16 bits linéaires signés. Tous les échantillons entre -50 et 50 sont ramenés à zéro.</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Avancé</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation>OSS taille du &amp;fragment:</translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation>La période de lecture ALSA influence le comportement en temps réel de votre carte son. Si votre son disparait fréquemment en utilisant ALSA, vous devriez essayer différentes valeurs.</translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation>ALSA période de &amp;lecture (LS):</translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation>ALSA période de &amp;capture (MIC):</translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation>La taille du fragment OSS influence le comportement en temps réel de votre carte son. Si votre son disparait fréquemment en utilisant OSS, vous devriez essayer différentes valeurs.</translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation>La période de capture ALSA influence le comportement en temps réel de votre carte son. Si votre son disparait fréquemment en utilisant ALSA, vous devriez essayer différentes valeurs.</translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>Taille &amp;max. du fichier de log:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation>La taille maximum du fichier de log en Mo. Quand le fichier de log excède cette taille, une sauvegarde de ce fichier est effectué et le fichier est supprimé. Seulement un fichier de sauvegarde est conservé.</translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation>Sauvegarder les rapports de &amp;debug</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation>Indique si les rapports marqués &quot;débug&quot; seront archivés.</translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>Sauvegarder les rapports &amp;SIP</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation>Indique si les messages SIP douvent être sauvegardés.</translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>Sauvegarder les rapports S&amp;TUN</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation>Indique si les messages STUN douvent être sauvegardés.</translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation>Sauvegarder les rapports &amp;mémoire</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation>Indique si les rapports concernant la gestion mémoire seront archivés.</translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Tableau de bord</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Créer une icone dans le &amp;tableau de bord au démarrage</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation>Cochez cette case si vous voulez une icone dans le tableau de bord pour Twinkle. Cette icone est créée au démarrage de Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation>&amp;Rabattre dans le &amp;tableau de bord à la fermeture de la fenêtre principale</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation>Cochez cette case si vous voulez rabattre Twinkle dans le tableau de bord quand vous fermez la fenêtre principale.</translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Démarrage</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, this IP address will be automatically selected. This is only useful when your computer has multiple and static IP addresses.</source>\n        <translation type=\"obsolete\">La prochaine fois que vous lancerez Twinkle, cette adresse IP sera automatiquement sélectionnée. Ceci est utile uniquement si votre ordinateur à plusieurs IP statiques.</translation>\n    </message>\n    <message>\n        <source>Default &amp;IP address:</source>\n        <translation type=\"obsolete\">Adresse &amp;IP par défaut:</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, the IP address of this network interface be automatically selected. This is only useful when your computer has multiple network devices.</source>\n        <translation type=\"obsolete\">La prochaine fois que vous lancerez Twinkle, l&apos;adresse IP de cette interface réseau sera automatiquement sélectionnée. Ceci est utile uniquement si votre ordinateur à plusieurs interfaces réseau.</translation>\n    </message>\n    <message>\n        <source>Default &amp;network interface:</source>\n        <translation type=\"obsolete\">&amp;Réseau par défaut:</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation>&amp;Démarrer rabattu dans le tableau de bord</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation>La prochaine fois que vous lancerez Twinkle, il sera immédiatement rabattu dans le tableau de bord. Ceci fonctionne mieux si vous sélectionnez également un utilisateur par défaut.</translation>\n    </message>\n    <message>\n        <source>Default user profiles</source>\n        <translation type=\"obsolete\">Profil utilisateur par défaut</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation>Si vous utilisez toujours le(s) même(s) profile(s), vous pouvez le(s) définir comme &quot;défaut&quot;. La prochaine fois que vous lancerai Twinkle, vous n&apos;aurez pas à sélectionner de profil. Les profils par défaut seront lancés automatiquement.</translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Services</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>Mise en &amp;attente</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation>Avec cette option, un appel entrant sera accepté si une ligne est occupée. Si vous désactivez la mise en attente d&apos;appel, un appel entrant sera refusé quand une ligne est occupée.</translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation>Raccrocher les deux lignes à la fin d&apos;une &amp;conférence.</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation>Raccroche les deux lignes quand vous clickez sur le bouton &quot;Fin&quot; lors d&apos;une conférence. Quand cette option est désactivée, seule la ligne active est raccrochée et vous pourvez continuer à parler avec le correspondante de l&apos;autre ligne.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation>Nombre d&apos;appels &amp;maximum dans l&apos;historique des appels:</translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation>Le nombre d&apos;appels maximum qui seront enregistrés dans l&apos;historique des appels.</translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation>Affichage &amp;automatiquement de la fenêtre principale lors d&apos;un appel</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation>Quand la fenêtre principale est cachée, elle sera automatiqument affichée lors d&apos;un appel entrant après le nombre de secondes spécifié.</translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation>Délai d&apos;affichage automatique sur appel entrant.</translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>secs</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving SIP messages.</source>\n        <translation type=\"obsolete\">Le port UDP utilisé pour envoyer et recevoir des messages SIP.</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>Port &amp;RTP:</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation>Le port UDP utilisé pour envoyer et recevoir des RTP pour la première ligne. Celui de la seconde est plus grand de 2. Ex: Si le port 8000 est utilisé pour la première ligne, celui de la seconde sera 8002. Quand vous utilisez le transfert d&apos;appel, le port suivant (ex:8004) est alors également utilisé.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP UDP port:</source>\n        <translation type=\"obsolete\">Port &amp;SIP UDP :</translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Sonneries</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>&amp;Sonner lors d&apos;un appel entrant</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Indique si une sonnerie doit être jouée lors d&apos;un appel entrant.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>Sonneries par &amp;défaut</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Joue la sonnerie par défaut lors d&apos;un appel entrant.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>Sonnerie &amp;personnalisée</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Joue une sonnerie personnalisée lors d&apos;un appel entrant.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation>Saisissez le nom de fichier .wav que vous voulez utiliser comme sonnerie.</translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation>Sonneries de tonalité</translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation>Jouer une&amp; tonalité quand le réseau ne le fait pas</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Joue une tonalité pendant que vous attendez que votre correspondant décroche. &lt;/p&gt;\n&lt;p&gt;Suivant votre fournisseur de service, le réseau peut émettre une tonalité ou une annonce.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation>&amp;Tonalité par défaut</translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation>Joue la tonalité par défaut.</translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation>Tonalité p&amp;ersonnalisée</translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation>Joue la tonalité personnalisée.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation>Saisissez le nom de fichier .wav que vous voulez utiliser comme tonalité.</translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation>&amp;Voir le nom de l&apos;appel entrant</translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation>Ne pas &amp;tenir compte du nom d&apos;affichage reçu</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation>L&apos;appelant peut avoir défini un nom d&apos;affichage. Cochez cette case si vous voulez le remplacer par celui défini dans votre carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Visionner la &amp;photo pour l&apos;appel entrant</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation>Visionner la photo de l&apos;appelant dans votre carnet d&apos;adresses et l&apos;afficher pour les appels entrants.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Accepter et enregistrer les modifications.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Annuler tous les changements et fermer la fenêtre.</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default IP address combo</comment>\n        <translation type=\"obsolete\">aucune</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default network interface combo</comment>\n        <translation type=\"obsolete\">aucune</translation>\n    </message>\n    <message>\n        <source>Either choose a default IP address or a default network interface.</source>\n        <translation type=\"obsolete\">Choisissez soit une adresse IP par défaut soit une interface de réseau.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Sonneries</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Choisir une sonnerie</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Sonneries de tonalité</translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation>Choisir une sonnerie de tonalité</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>&amp;Valider les interfaces avant d&apos;utiliser</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation>&lt;p&gt;Twinkle valide l&apos;interface audio avant utilisation pour éviter d&apos;établir un appel sans canal audio.&lt;/p&gt;\n&lt;p&gt;Au démarrage de Twinkle, un avertissement vous prévient si l&apos;interface audio est inaccessible.&lt;/p&gt;\n&lt;p&gt;Si avant de faire un appel, le microphone ou le haut-parleur est invalide, un avertissement s&apos;affiche et l&apos;appel est bloqué.&lt;/p&gt;\n&lt;p&gt;Si avant de répondre à un appel, le microphone ou le haut-parleur est invalide, un avertissement s&apos;affiche et il est impossible de répondre à un appel.\n</translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation>Lors d&apos;un appel entrant, Twinkle essaiera de trouve le nom correspondant à l&apos;adresse SIP dans votre carnet d&apos;adresses. Ce nom sera affiché.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Sélectionner un fichier de sonnerie.</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Sélectionnez votre sonnerie de tonalité.</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation>Taille maximum allouée pour un message SIP entrant en UDP en octets (0-65535).</translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>Port &amp;SIP:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation>Taille max. du message SIP (&amp;TCP):</translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation>Le port UDP/TCP utilisé pour envoyer et recevoir des messages SIP.</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation>Taille max. du message SIP (&amp;UDP):</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation>Taille maximum allouée pour un message SIP entrant en TCP en octets (0-4294967295).</translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Répondre</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Refuser</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Capacités du terminal</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;De:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Récuperer les possibilités du terminal de</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;à:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>L&apos;adresse dont vous voulez demandes les possiblités (demande d&apos;OPTIONS). Ceci peut être une adresse SIP comme &lt;b&gt;sip:exemple@exemple.com&lt;/b&gt; ou uniquement la partie utilisateur ou le numéro de téléphone d&apos;une adresse complète. Quand vous ne spécifiez pas une adresse complète, Twinkle complètera cette adresse en utilisant le domaine défini dans votre profil utilisateur.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Transfert</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Transférer l&apos;appel vers</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;à:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>L&apos;adresse de la personne vers que vous voulez transférer l&apos;appel. Ceci peut être une adresse SIP comme &lt;b&gt;sip:exemple@exemple.com&lt;/b&gt; ou uniquement la partie utilisateur ou le numéro de téléphone d&apos;une adresse complète. Quand vous ne spécifiez pas une adresse complète, Twinkle complètera cette adresse en utilisant le domaine défini dans votre profil utilisateur.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Carnet d&apos;adresses</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Sélectionner une adresse du carnet d&apos;adresses.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Type of transfer</source>\n        <translation type=\"obsolete\">Type de transfert</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>Transfert &amp;direct</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation>Transfert l&apos;appel vers un tier sans consultation.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation>&amp;Transfert avec consultation</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation>Avant de transférer l&apos;appel vous pourrez consulter le tier vous-même.</translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation>Transfert vers l&apos;autre &amp;ligne</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation>Connecte le correspondant de la ligne active avec le correspondant de l&apos;autre ligne.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Impossible de créer le fichier de log %1.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Impossible d&apos;ouvrir le fichier %1 en lecture</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Erreur système pendant la lecture du fichier %1.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Impossible d&apos;ouvrir le fichier %1 en écriture</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Erreur système pendant l&apos;écriture du fichier %1.</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>Nombre trop important d&apos;erreurs de socket.</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Compilé à l&apos;aide de:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>Contributions:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Ce logiciel contient les logiciels tiers suivants:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation>Pour RTP, les librairies dynamiques suivantes sont rattachées:</translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Traduit en français par:&lt;br&gt;\n©20070614 Olivier Aufrère</translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Le dossier %1 n&apos;existe pas.</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Impossible d&apos;ouvrir le fichier %1.</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>%1 n&apos;est pas paramêtré pour votre dossier home.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Le dossier %1 (%2) n&apos;existe pas.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Impossible de créer le dossier %1.</translation>\n    </message>\n    <message>\n        <source>Lock file %1 already exist, but cannot be opened.</source>\n        <translation type=\"obsolete\">Le fichier de vérouillage %1 existe déjà mais ne peut être ouvert.</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>%1 en cours d&apos;execution\nLe fichier de vérouillage %2 existe déjà.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>Impossible de créer %1.</translation>\n    </message>\n    <message>\n        <source>Cannot write to %1 .</source>\n        <translation type=\"obsolete\">Impossible d&apos;écrire su %1.</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Erreur de syntaxe dans le fichier %1.</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Impossible de sauvegarder %1 sur %2</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>nom inconnu (interface utilisée)</translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Interface par défaut</translation>\n    </message>\n    <message>\n        <source>Anonymous</source>\n        <translation>Anonyme</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Attention:</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation>Transfert d&apos;appel - %1</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>La carte son ne peut pas être passée en full duplex.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>Impossible de paramètrer la taille du beffer de la carte son.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Impossible de paramètrer la carte son pour les canaux %1.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation>Impossible de paramètrer la carte son en enregistrement 16 bits.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation>Impossible de paramètrer la carte son en lecture 16 bits.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation>Impossible de paramètrer la carte son à un taux de sample %1</translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation>Echec de l&apos;ouverture d&apos;ALSE</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation>Impossible d&apos;ouvrir ALSA pour la lecture PCM</translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>Impossible de trouver le serveur STUN %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>Vous êtes derrière un NAT symétrique.\nSTUN ne fonctionnera pas.\nConfigurez une adresse IP publique pour votre profil utilisateur\net créez les attaches statiques suivantes (UDP) dans votre NAT.</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>IP publique: %1 -&gt; IP privée: %2 (SIP)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>IP publique: %1-%2 -&gt; IP privée: %3-%4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>Impossible d&apos;atteindre le serveur STUN: %1</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Port %1 (SIP)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>Echec de l&apos;identification du type NAT par STUN.</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Si vous êtes derrière un firemwall, vous devez ouvrir les ports UDP suivants.</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Ports %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation>Impossible d&apos;accéder à l&apos;interface %1 de la sonnerie.</translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>Impossible d&apos;accéder à l&apos;interface %1 du haut-parleur.</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>Impossible d&apos;accéder à l&apos;interface %1 du microphone.</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>Impossible d&apos;ouvrir ALSA pour la capture PCM</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Impossible de recevoir des connexions TCP entrantes.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Echec de la création du fichier %1</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Echec de l&apos;écriture de données dans %1</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Echec de l&apos;envoi du message.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Profil utilisateur</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil utilisateur:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Sélectionnez le profil que vous voulez éditer.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Utilisateur</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>Serveur SIP</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP Audio</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>Protocole SIP</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Format d&apos;adresse</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation>Décomptes</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Sonneries</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Sécurité</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Sélectionnez la catégorie dont vous voulez voir ou modifier les paramètres.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Accepter et enregistrer les modifications.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Annuler tous les changements et fermer la fenêtre.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>Compte SIP</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>Nom d&apos;&amp;utilisateur*:</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Domaine*:</translation>\n    </message>\n    <message>\n        <source>Or&amp;ganization:</source>\n        <translation>Or&amp;ganisation:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Le nom d&apos;utilisateur SIP donné par votre fournisseur de service. C&apos;est la partie &quot;utilisateur&quot; de votre adresse SIP, &lt;b&gt;utilisateur&lt;/b&gt;@domain.com.\nCeci peut être un numéro de téléphone.\n&lt;br&gt;&lt;br&gt;\nCe champ est obligatoire.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Le domaine de votre adresse SIP, username@&lt;b&gt;domain.com&lt;/b&gt;. A la place d&apos;un vrai nom de domaine, il est également possible de saisir le nom de l&apos;hôte ou l&apos;adresse IP de votre &lt;b&gt;proxy SIP&lt;/b&gt;. Si vous voulez uniquement des communications de PC à PC, saisissez  le nom de l&apos;hôte ou l&apos;adresse IP de votre ordinateur.\n&lt;br&gt;&lt;br&gt;\nCe champ est obligatoire.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>Vous pouvez remplir le nom de votre organisation. Quand vous ferez un appel, ceci sera montré à votre correspondant.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>C&apos;est simplement votre nom complet, ex: Pierre Dupond. Il est utilisé pour l&apos;affichage. Quand vous ferez un appel, ceci sera montré à votre correspondant.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Votre nom:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>Authentification SIP</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation>&amp;Nom d&apos;authentification:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>Mot de &amp;passe:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation>Le realm pour l&apos;authentification. Cette valeur vous est fournie par votre fournisseur de service SIP. Si vous laissez ce champ vide, Twinkle utilisera le nom d&apos;utilisateur et le mot de passe en cas de demande de realm.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Votre nom d&apos;authentification SIP. Souvent, c&apos;est le même que votre nom d&apos;utilisateur SIP. Cependant, il peut être différent.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Votre mot de passe d&apos;authentification.</translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation>Registrar (Serveur proxy) </translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation>&amp;Registrar:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation>Le nom d&apos;hôte, le nom de domaine ou l&apos;adresse IP de votre fournisseur de service. Si vous utilisez un proxy sortant qui est identique à votre fournisseur de service, vous pouvez laisser ce champ vide et seulement remplir l&apos;adresse du proxy sortant. </translation>\n    </message>\n    <message>\n        <source>&amp;Expiry:</source>\n        <translation>&amp;Expiration:</translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation>Le délai d&apos;expiration de l&apos;établissement de la connexion par Twinkle. Standard: 3600 (=1h).</translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>secondes</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Se &amp;connecter au démarrage</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt-C</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation>Indique si Twinkle doit automatiquement se connecter quand cous utilisez ce profil. Vous devrez désactivé ceci en cas de communication directe de téléphone IP à téléphone IP sans passer par proxy SIP.</translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation>Proxy sortant</translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>&amp;Utiliser le proxy sortant</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation>Indique si Twinkle doit utiliser un proxy sortant. Si un proxy sortant est utiliser, toutes les demandes SIP sont envoyées par ce proxy. Sans proxy sortant, Twinkle essaiera de résoudre l&apos;adresse SIP que vous avez saisie pour une invitation d&apos;appel, par exemple à une adresse IP et envoie la demande SIP à cette adresse.</translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>&amp;Proxy sortant: </translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation>&amp;Envoyer des demandes &quot;in-dialog&quot; au proxy</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation>Les demandes SIP incluses dans un dialogue SIP sont normalement envoyées dans les entêtes de contact échangées pendant l&apos;établissement de l&apos;appel. Si vous cochez cette case, cette adresse est ignorée et les demandes incluses dans le dialog sont également envoyées par le proxy de sortie.</translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation>&amp;Ne pas envoyer une demande au proxy si la destination peut être trouvée locallement.</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation>Quand vous cochez cette case, Twinkle essaiera d&apos;abord de trouver une adresse SIP par une adresse IP. S&apos;il y arrive, la demande SIP sera envoyée par ce mode. Dans le cas contraire, il enverra la demande SIP au proxy (nota: une demande incluse dans un dialogue serra uniquement envoyée au proxy si vous avez également cochez la case précédente)</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Le nom d l&apos;hôte, le nom de domaine ou l&apos;adresse IP de votre proxy de sortie.</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Codecs disponibles:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Liste des codecs disponibles.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Active un codec.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Désactive un codec.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Codecs actifs:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation>Liste des codecs actifs. Ceux sont les codecs qui seront utilisés pour la négociation lors de l&apos;établissement de l&apos;appel. L&apos;ordre des codecs est l&apos;ordre de préférence d&apos;utilisation.</translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation>Monte un codec dans la liste des codecs actifs, i.e. augmente sa priorité d&apos;utilisation.</translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation>Descend un codec dans la liste des codecs actifs, i.e. diminue sa priorité d&apos;utilisation.</translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation>&amp;G.711/G.726 taille des données:</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation>La taille des données préférée pour les codecs G.711 et G.726.</translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation>i&amp;LBC Type des données:</translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation>iLBC &amp;taille des données (ms):</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation>Le type de valeur dynamique (96 ou plus) devant être utilisé.</translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation>Taille préférée des données du paquet RTP pour iLBC.</translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation>&amp;Amélioration de la perception</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation>l&apos; &quot;amélioration de la perception&quot; (anglais: perceptual enhancement) est une partie du décodeur qui, quand elle est activée, essaie de réduire la perception du bruit produit par le décodage/encodage. Dans la plupart des cas, ceci rend le son assez différent de l&apos;original, mais le rend plus agréable.</translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation>Type des données pour la bande &amp;Ultra-Large:</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the audio being encoded is speech or silence/background noise. VAD is always implicitly activated when encoding in VBR, so the option is only useful in non-VBR operation. In this case, Speex detects non-speech periods and encode them with just enough bits to reproduce the background noise. This is called &quot;comfort noise generation&quot; (CNG).</source>\n        <translation type=\"obsolete\">En la sélectionnant, la détection de la parole (ie: voice activity detection ou VAD) détecte si le son encodé est de la voix ou du silence (bruit de fond). VAD est toujours implicitement activé en encodage VBR, cette option est donc uniquement utilisable pour les opérations non-VBR. Dans ce cas, Speex detecte les passages sans paroles et les encode avec juste le nombre de bits nécessaire pour reproduire le bruit de fond. Ceci est appelé la &quot;génération de bruit pour le confort&quot; (comfort noise generation CNG).</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation>Type des données pour la bande &amp;Large:</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation>Variable Bit-Rate (VBR) permet au codec d&apos;ajuster sa bande passante dynamiquement à la difficulté d&apos;encodage du son. Ceci permet à qualité constante, de réduire la bande passante nécessaire. Il y a cependant des désavantages: en ne spécifiant que la qualité, il n&apos;y à aucune garantie sur la bande passante moyenne finallement utilisée; pour certaines applications comme la VOIP, ce qui compte c&apos;est la bande passante maximum, qui doit être la plus basse possible.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation>La valeur dynamique (96 ou plus) à utiliser pour le speex à large bande (RFC 2833).</translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation>Co&amp;mplexité:</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation>La transmission discontinue est un ajout à VAD/VBR, qui permet d&apos;arrêter totalement la transmission quand le bruit de fond est stationnaire.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation>La valeur dynamique (96 ou plus) à utiliser pour le speex à petite bande (RFC 2833).</translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation>Avec Spexx, il est possible de faire varier le taux de compression de l&apos;encodeur. Ceci est possible en contrôlant comment la recherche est assurée avec un entier entre 1 et 10 d&apos;une manière similaire aux option -1 à -9 de gzip et bzip2. En utilisation normale, le niveau de bruit au taux 1 est entre 1 et 2 dB plus élevé que au taux 10, mais l&apos;utilisation du CPU au taux 10 est 5 fois plus grande que au taux 1. En pratique, Le meilleur compromis est entre 2 et 4, alors que des taux plus élevés sont souvent utilent pour encoder des sons autre que la voix comme les sonneries DTMF.</translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation>Type des données pour la bande &amp;Courte:</translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation>G.726 &amp;40 kb/s Type des données:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation>La valeur dynamique (96 ou plus) a utiliser pour G.726 40 kb/s.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation>La valeur dynamique (96 ou plus) a utiliser pour G.726 32 kb/s.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation>G.726 &amp;24 kb/s Type des données:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation>La valeur dynamique (96 ou plus) a utiliser pour G.726 24 kb/s.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation>G.726 &amp;32 kb/s Type des données:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation>La valeur dynamique (96 ou plus) a utiliser pour G.726 16 kb/s.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation>G.726 &amp;16 kb/s Type des données:</translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation>La valeur dynamique (96 ou plus) à utiliser pour les évenements DTMF (RFC 2833).</translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation>&amp;Volume DTMF:</translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation>Le volume de la sonnerie DTMF en dB.</translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation>La durée de la pose après la sonnerie DTMF.</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation>&amp;Durée DTMF: </translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation>D&amp;TMF Type des données:</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation>&amp;Pause DTMF:</translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation>Durée d&apos;une sonnerie DTMF.</translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>&amp;Méthode DTMF:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;&lt;h3&gt;RFC 2833&lt;/h3&gt;\nEnvoi une sonnerie DTMF comme un événement de téléphone (RFC 2833).&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Inband&lt;/h3&gt;\nSende DTMF inband (tatsächliche Töne, die Twinkle ins Tonsignal einmischt).&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Auto&lt;/h3&gt;\nWenn die Gegenstelle RFC 2833 unterstützt, dann DTMF-Töne als RFC 2833 telephone events senden, ansonsten inband.&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Out-of-band (SIP INFO)&lt;/h3&gt;\nSende DTMF nur out-of-band via  SIP INFO request.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Général</translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation>Redirection</translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation>&amp;Authoriser la redirection</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation>Indique si Twinkle doit rediriger une demande en cas de réception d&apos;une réponse 3XX.</translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation>Demander la &amp;permission de l&apos;utilisateur avant de rediriger</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation>Indique si Twinkle doit demander à l&apos;utilisateur avant de rediriger une demande en cas de réception d&apos;une réponse 3XX.</translation>\n    </message>\n    <message>\n        <source>Max re&amp;directions:</source>\n        <translation>Max. re&amp;directions:</translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation>Le nombre maximum de tentatives de redirection d&apos;adresse avant l&apos;abandon. Ceci évite qu&apos;une demande ne soit redirigé indéfiniement.</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Options du protocole</translation>\n    </message>\n    <message>\n        <source>Call &amp;Hold variant:</source>\n        <translation>Variante d&apos;&amp;attente d&apos;appel: </translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation>Indique si RFC 2543 (passer l&apos;adresse IP de l&apos;interface dans SDP à 0.0.0.0) ou RFC 3264 (utiliser les attributs de direction dans SDP) est utilisé pour mettre un appel en attente.</translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation>Authorise l&apos;absence de l&apos;&amp;entête de contact dans 200 OK lors de la connexion</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation>Une réponse &quot;200 OK&quot; lors d&apos;une connexion, doit comporter une entête de contact.Cependant, quelques fournisseurs de service, n&apos;incorporent pas d&apos;&apos;entête de contact ou en incorpore une erronée. Cette option authorise cette transgression.</translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation>&amp;Max-Forwards-Header onbligatoire</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation>Selon le RFC3261, l&apos;entête Max-Forwards est obligatoire, mais plusieurs implémentations n&apos;envoient pas cet entête. Si vous cochez cette case, Twinkle rejettera une demande SIP si Max-Forwards est manquant.</translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation>Mettre le délai d&apos;expi&amp;ration de la connexion dans l&apos;entête de contact</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation>Dans un message REGISTER, le temps d&apos;expiration de la connexion peut être inséré dans l&apos;entête de contact ou dans celle d&apos;expiration. Si vous cochez cette case, il sera inséreé dans celle de contact, dans le cas contraire, dans celle d&apos;expiration.</translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation>&amp;Utiliser les nom d&apos;entêtes courts</translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation>Indique si un nom d&apos;entête court doit être utilisé pour les entêtes courtes.</translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation>Authoriser les modification SDP pendant l&apos;établissement d&apos;un appel</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Une UAS SIP doit envoyer un SDP dans une réponse 1XX. Quand l&apos;appel est répondu, l&apos;UAS SIP doit envoyer le même SDP dans une réponse 200 OK selon le RFC 3261. Après réception du SDP, les SDP dans les réponses suivantes seront annulés.&lt;/p&gt;\n&lt;p&gt;En authorisant les modifications de SDP pendant l&apos;appel, Twinkle n&apos;annulera pas les SDP dans les réponses suivantes et modifiera le medium si le SDP a changé.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Twinkle crée une entête de contact unique en combinant le nom d&apos;utilisateur et de domaine SIP:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nDe cette façon, 2 profils utilisateur ayant le même nom d&apos;utilisateur mais des domaines différents, ont une adresse unique de contact et peuvent ainsi être activé simultanément.\n&lt;/p&gt;\n&lt;p&gt;\nDes proxy ne construisent pas l&apos;entête de contact de cette façon. Vous pouvez désactiver cette option pour avoir une entête comme ceci:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nCe format est utiliser par la plupart des téléphones SIP.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation>&amp;Encode Via, Route, Record-Route comme une liste</translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation>Les entêtes Via-, Route- et Record-Route peuvent être encodé comme une liste séparée par des virgules ou comme des occurrences mulitples de la même entête.</translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation>extensions SIP</translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>désactivé</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>supporté</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>requis</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>préféré</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation>Indique si l&apos;extension 100rel (PRACK) est supportée:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;désactivée&lt;/b&gt;: extension 100rel désactivée\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supportée&lt;/b&gt;: 100rel supportée (elle est insérée dans &quot;supported header&quot; d&apos;une INVITE sortante). Le correspondant peut alors exiger un PRACK dans une réponse  1xx.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;requise&lt;/b&gt;: 100rel requise (elle est insérée dans &quot;equire header&quot; d&apos;une INVITE sortante). Si une INVITE entrante indique qu&apos;elle supporte 100rel, alors Twinkle exigera un PRACK lors de l&apos;envoi d&apos;une reponse 1xx. Si le correspondant ne supporte pas 100rel, l&apos;appel ne se réalise pas.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;préférée&lt;/b&gt;: proche de requise, mais si l&apos;appel échoue suite à l&apos;absence de support de 100rel par le correspondant (420 réponses), une nouvelle tentativce d&apos;appel sans 100rel sera lancée.</translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation>Transfert d&apos;appel (REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call &amp;transfer (incoming REFER)</source>\n        <translation type=\"obsolete\">Authoriser le &amp;transfert d&apos;appel (REFER entrant)</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation>Indique si Twinkle doit transferer l&apos;appel en cas de réception d&apos;une demande REFER.</translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation>Demander l&apos;aut&amp;horisation d&apos;utilisateur avant de transférer</translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation>Indique si Twinkle doit demander à l&apos;utilisateur avant de transférer un appel en cas de réception d&apos;une demande REFER.</translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation>Mettre l&apos;appel avec un &amp;mandataire en attente pendant l&apos;établissement de l&apos;appel vers le destinataire du transfert</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation>Indique si Twinkle doit demander à l&apos;utilisateur avant de mettre l&apos;appel en attente en cas de réception d&apos;une réponse REFER pour un transfert.</translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation>Mettre l&apos;appel avec un ma&amp;ndant en attente avant d&apos;envoyer un REFER</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation>Indique si twinkle doit mettre l&apos;appel courant en attente pendant un transfert.</translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation>Ra&amp;fraichir la subsciption au refer automatiquement avant que le transfert d&apos;appel ne soit fini</translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation>Quand l&apos;appel est transféré, le mandant envoie des messages NOTIFY au mandataire à propos de la progression du transfert. Ces messages sont seulement envoyés pendante une courte période dans la durée est déterminée par le mandant. Si vous cochez cette case, le mandataire enverra automatiquement un SUBSCRIBE pour allonger cette durée si elle est trop courte (transfert non terminé).</translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>NAT Tranvsersal</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation>&amp;NAT traversal non nécessaire</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation>Cochez cette case quand il n&apos;y a pas d&apos;interface NAT entre vous et votre proxy SIP, ou quand votre fournisseur de service SIP gère lui même le NAT transversal.</translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation>&amp;Utiliser une adresse IP publique fixe dans les messages SIP</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation>Indique si Twinkle doit utiliser l&apos;adresse IP public spécifiée dans le champ suivant dans les messages SIP, i.e. dans les entêtes SIP et dans le corps SDP à la place de l&apos;adresse IP de votre interface réseau (privée).&lt;br&gt;&lt;br&gt;\nEn cochant cette case, vous devez créer un mappage NAT. Vous devez mapper les ports RTP sur l&apos;adresse IP public et sur l&apos;adresse IP privée de votre PC.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN</source>\n        <translation type=\"obsolete\">Utiliser &amp;STUN</translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation>Sélectionez cette option si votre fournisseur de service SIP propose un serveur STUN pour le NAT transversal.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>Serveur S&amp;TUN:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Le nom d l&apos;hôte, le nom de domaine ou l&apos;adresse IP du serveur STUN.</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>Adresse IP &amp;publique:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation>L&apos;adresse IP publique de votre NAT.</translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Numéros de téléphone</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation>&amp;Afficher seulement la partie utilisateur de l&apos;URI pour un numéro de téléphone</translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation>Si une URI indique un numéro de téléphone, alors seulement la partie utilisateur sera affichée. Ex: Si un appel arrive depuis sip:123456@twinklephone.com alors seulement  &quot;123456&quot; sera affiché. Une URI indique un numérode téléphone si elle contient le paramêtre &quot;user=phone&quot; ou si elle a une partie utilisateur numérique et que vous avez coché la case suivante.</translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation>Une &amp;URI avec une partie utilisateur numérique est un numéro de téléphone</translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation>Si vous cochez cette case, Twinkle considère une adresse SIP qui a une partie utilisateur constituée de chiffre,*,#,+ et des symboles spéciaux comme un numéro de téléphone. Dans un message sortant, Twinkle ajoutera le paramètre &quot;user=phone&quot; pour ce type d&apos;URI.</translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation>&amp;Supprimer les symboles spéciaux pour les chaines numériques</translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation>Les numéros de téléphone sont souvent écrit avec des caractères spéciaux pour les rendre plus facile à lire. Quand vous composez ce genre de numéro, les caractères spéciaux doivent être supprimés. Pour vous permettre de copier/coller ce type de numéro, Twinkle supprimera ces caractères à la numérotation.</translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation>&amp;Symboles spéciaux:</translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation>Les symboles spéciaux utilisables pour respecter le format usuel des numéros de téléphone mais qui doivent être supprimé à la numérotation.</translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation>Conversion des numéros</translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation>Expression de recherche</translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Remplacer</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telphone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation>&lt;p&gt;\nSouvent le format des numéros de téléphone que vous devez composer est différent du format eregistré dans votre carnet d&apos;adresses, ex: votre numéro commence par &quot;+&quot; suivi du code pays, mais votre fournisseur de service attend &quot;00&quot; à la plase de &quot;+&quot;, ou vous êtes au bureau et tous les numéros sortants doivent être précédés de &quot;0&quot;. Vous pouvez spécifier ici une conversion de numéro en expression régulière Perl ou chaine formatée.\n&lt;/p&gt;\n&lt;p&gt;\nPour tous les numéros que vous compsez, Twinkle essaiera de trouver une occurence dans la liste des expressions. Lors de la première concordance, le numéro sera remplacé en suivant le formatage par chaine. Si aucune occurence n&apos;est rencontrée, le numéro reste inchangé.\n&lt;/p&gt;\n&lt;p&gt;\nLes règes de conversion des numéro sont également appliquées aux appels entrant. Les numéros seroont donc affichés au format souhaité.\n&lt;/p&gt;\n&lt;h3&gt;Exemple 1&lt;/h3&gt;\n&lt;p&gt;\nEn considérant que votre code pays est 33 et que vous avez enregistré tous les numéros de votre carnet d&apos;adresses au format internationnal, ex +338712345678. Pour la numéroation des numéro de votre propre pays, vous voulez remplacer &quot;+33&quot; par &quot;0&quot;. Pour les numéros à l&apos;étranger, vous voulez remplacer &quot;+&quot; par &quot;00&quot;.\n&lt;/p&gt;\n&lt;p&gt;\nVoici les règles à créer:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =(sp)(sp)0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Exemple 2&lt;/h3&gt;\n&lt;p&gt;\nAu bureau, tous les numéros commençant par 0 doivent recevoir un préfixe 9 pour un appel sortant.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =(sp)(sp)9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation>Déplacer la règle de conversion sélectionnée vers le haut de la liste.</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation>Déplacer la règle de conversion sélectionnée vers le bas de la liste..</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Ajouter</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation>Ajouter une règle de conversion de numéro.</translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>&amp;Supprimer</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation>Supprimer la règle de conversion de numéro sélectionnée.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Editer</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation>Editer la règle de conversion de numéro sélectionnée..</translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation>Saisissez ici un numéro de téléphone et appuyer sur le bouton Test pour voir comment il est converti par la liste des règles de conversion de numéros.</translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Test</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation>Test la façon de convertir d&apos;un numéro par les règles de conversion.</translation>\n    </message>\n    <message>\n        <source>for STUN</source>\n        <translation type=\"obsolete\">pour STUN</translation>\n    </message>\n    <message>\n        <source>Keep alive timer for the STUN protocol. If you have enabled STUN, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"obsolete\">Garder actif le décompte pour le protocole STUN. Si vous avez activé STUN, Twinkle enverra des paquets à cet intervalle pour garder l&apos;adresse dans votre interface NAT active.</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation>Quand un appel entrant est reçu, le décompte est lancé. Si l&apos;utilisateur répond à l&apos;appel, le chronomètre est arrèté. Si le décompte se termine avant que l&apos;utilisateur ne réponde, Twinkle rejettera l&apos;appel avec un message &quot;480 User Not Responding&quot; (L&apos;utilisateur ne répond pas).</translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation>&amp;STUN NAT-keep-alive:</translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>&amp;Pas de réponse:</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation>&amp;Tonalité:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Entrez un nom de fichier wav qui sera utilisé comme tonalité pour cette utilisateur.&lt;/p&gt;\n\n&lt;p&gt;Cette tonalité est prioritaire sur celle définie dans les paramètres système.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Entrez un nom de fichier wav qui sera utilisé comme sonnerie pour cette utilisateur.&lt;/p&gt;\n\n&lt;p&gt;Cette sonnerie est prioritaire sur celle définie dans les paramètres système.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Sonnerie:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé au raccrochage.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP de la demande sortante SIP BYE sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contient la demande URI de BYE. &lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé en cas d&apos;échec d&apos;appel entrant.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP de la réponse d&apos;échec sortante SIP sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contient le code de statut de la réponse d&apos;échec.&lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contient la raison.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé en cas de raccrochage de la part du correspondant.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP de la demande entrante SIP BYE sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contient la demande URI de BYE. &lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé quand votre correspondant répond à votre appel.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP d&apos;un 200 OK entrant sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contient la raison.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé quand vous répondez à un appel entrant.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP d&apos;un 200 OK sortant sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contient la raison.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation>Appel raccroché locale&amp;ment:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé en cas d&apos;échec d&apos;appel sortant.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP de la réponse d&apos;échec entrante SIP sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contient le code de statut de la réponse d&apos;échec.&lt;br&gt;\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contient la raison.&lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nCe script est appelé quand vous passez un appel.\n&lt;/p&gt;\n&lt;h3&gt;Variables d&apos;environnement&lt;/h3&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP d&apos;un INVITE sortant sont passées en variables d&apos;environnement à votre script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;br&gt;\n&lt;b&gt;SIPSTATUS_CODE=INVITE&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contient la demande URI de INVITE. &lt;br&gt;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; contient le nom du profil utilisé.</translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation>Appel sortant répo&amp;ndu:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation>Appel entrant &amp;manqué:</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>Appel &amp;entrant:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation>Appel &amp;terminé par le correspondant: </translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation>Appel entrant répo&amp;ndu:</translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>Appel &amp;sortant:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation>Appel entrant &amp;échoué:</translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>Activer la cryptographie &amp;ZRTP/SRTP</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unecrypted.</source>\n        <translation>Quand ZRT/SRTP est activé, Twinkle essaiera de crypter les appels que vous émettez ou recevez. L&apos;encodage ne réussira que si le correspondant accepte le ZRTP/SRTP. Dans le cas contraire l&apos;appel ne sera pas crypté.</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>Paramètres ZRTP</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation>Encoder &amp;seulement si le correspondant accepte le ZRTP en SDP</translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation>Un terminal SIP supportant ZRTP doit l&apos;indiquer pendant l&apos;établissement de l&apos;appel. En cochant cette case, Twinkle n&apos;encryptera les appels que si le correspondant indique qu&apos;il accepte le ZRTP.</translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation>&amp;Indiquer le support de ZRTP dans SDP</translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation>Twinkle indiquera qu&apos;il accepte le ZRTP dans sa signature pendant l&apos;établissement de l&apos;appel.</translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation>Afficher un &amp;avertissement quand le correspondant désactive la cryptographie pendant l&apos;appel</translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation>Un correspondant d&apos;un appel encrypté doit envoyer une commande de fin ZRTP (go-clear) pour arréter la cryptographie. Quand Twinkle reçoit cette commande, un avertissement s&apos;affichera si cette case est cochée.</translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation>Le type %1 de charge utile dynamique est utilisé plu d&apos;une fois.</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Vous devez saisir un nom d&apos;utilisateur pour votre compte SIP.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Vous devez saisir un nom de domaine pour votre compte SIP.\n\nil est également possible de saisir le nom de l&apos;hôte ou l&apos;adresse IP de votre PC si vous voulez faire des appels directs de PC à PC.</translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Nom d&apos;utilisateur invalide.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Domaine invalide.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation>Registrar invalde.</translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation>Proxy sortant invalide.</translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation>Absence d&apos;adresse IP publique.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Absence de valeur pour le serveur STUN.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Sonneries</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Choisir une sonnerie</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Sonneries de tonalité</translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Tous les fichiers</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation>Sélectionner le scipt d&apos;appel entrant</translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation>Sélectionner le scipt d&apos;appel entrant répondu</translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation>Sélectionner le scipt d&apos;appel entrant échoué</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation>Sélectionner le scipt d&apos;appel sortant</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation>Sélectionner le scipt d&apos;appel sortant répondu</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation>Sélectionner le scipt d&apos;appel sortant échoué</translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation>Sélectionner le scipt de raccrochage local</translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation>Sélectionner le scipt de raccrochage local distant</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Boîte vocale</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation>&amp;Suivre la préférence de codec du correspondant pour les appels entrants</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation>Choisit le premier codec de la demande SDP qui est également dans la liste des codecs actifs.&lt;br&gt;\nSi vous désactivez cette option, c&apos;est le premier codec de la liste des codecs actifs qui est également dans la demande SDP qui est choisi.</translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation>&amp;Suivre la préférence de codec du correspondant pour les appels sortants</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation>Choisit le premier codec de la réponse SDP qui est également dans la liste des codecs actifs.&lt;br&gt;\nSi vous désactivez cette option, c&apos;est le premier codec de la liste des codecs actifs qui est également dans la réponse SDP qui est choisi.</translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation>Ordre de compression du codec (codeword &amp;packing order):</translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation>Il existe 2 standards de compression pour le codec G726 dans un paquet RTP. RFC 3551 est la compression par défaut. Quelques interfaces SIP utilisent ATM AAL2. Si vous avez une mauvaise qualité en G726 avec la compression RFC 3551, utilisez ATM AAL2.</translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Remplace</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extenstion is supported.</source>\n        <translation>Indique si Replaces-Extension est supporté.</translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation>Refer avec consultation vers &quot;Address of Record&quot;</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endoint.</source>\n        <translation>Un transfert avec consultation doit utiliser l&apos;URI du contact comme cible du refer. Cependant, une URI de contact ne doit pas être routable. L&apos;AoR doit être utlisée à la place. Un incovéniant est que l&apos;AoR peut router vers plusieurs destinataires alors que l&apos;URI de contact route vers un destinataire unique.</translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Confidentialité</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Options de confidentialité</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation>&amp;Envoyer &quot;P-Preferred-Identity Header&quot; quand l&apos;identité est cachée</translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Inclure un &quot;P-Preferred-Identity Header&quot; à votre identité dans une demande INVITE pour un appel à l&apos;identité cachée.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nVous pouvez personnaliser la façon dont Twinkle gère les appels entrants. Twinkle peut appel un script quand un appel arriver. Sur la base de la sortie du script, Twinkle accepte, rejette ou redirige l&apos;appel. Si l&apos;appel est accepté, la sonnerie peut également être presonnalisée par un script. Le script peut être un programme executable.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Nota:&lt;/b&gt; Twinkle est suspendu pendant que votre script tourne. Il est recommandé que votre script ne dure pas plus de 200 ms. Quand vous avez besoin de plus de temps, vous pouvez envoyer les paramètres suivis de &lt;b&gt;end&lt;/b&gt; et continuer d&apos;executer. Twinkle continuera quand il recevra le paramètre &lt;b&gt;end&lt;/b&gt;.(new line)\n&lt;/p&gt;\n&lt;p&gt;\nAvec votre script vous pouvez personnaliser la gestion d&apos;appel en renvoyant un ou plusieurs des paramètres suivants à stdout. Chaque paramètre doit être sur un ligne différente.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;adresse de redirection&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;non de l&apos;appelant à afficher&amp;gt;&lt;br&gt;\nringtone=&amp;lt;nom de fichier wav&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message à afficher sur l&apos;écran&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Paramètres&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - poursuivre la gestion d&apos;appel comme d&apos;habitude&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - rejeter l&apos;appel&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - rejeter l&apos;appel avec un message &quot;ne pas derranger&quot;&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - rediriger l&apos;appel vers l&apos;adresse de&lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - répondre automatiquement à l&apos;appel&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nQuand le script ne renvoie pas d&apos;action à stdout, alors l&apos;action par défaut se poursuit.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nAvec le paramètre &quot;reason&quot; vous pouvez affecter la valeur &quot;reject&quot; ou &quot;dnd&quot;. Ce sera montré au correspondant.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nCe paramètre ne tient pas compte du nom l&apos;appelant affiché.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nCe paramètre indique quel fichier wav utiliser comme sonnerie.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nLes valeurs de toutes les entêtes SIP d&apos;un message INVITE entrant sont passées en variable d&apos;environnement à votre script. Les nom de varibles sont les suivants:&lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contien le valeur de l&apos;entête from.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. la demande URI d&apos;une INVITE sera dans &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. le nom du profile utilisateur sera dans: &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation>Adresse de la &amp;boîte vocale:</translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation>L&apos;adresse SIP ou le numéro de téléphone pour accéder à votre boît vocale.</translation>\n    </message>\n    <message>\n        <source>Unsollicited</source>\n        <translation>Non désiré</translation>\n    </message>\n    <message>\n        <source>Sollicited</source>\n        <translation>RFC 3842</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsollicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsollicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Sollicited&lt;/H3&gt;\n&lt;p&gt;\nSollicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation>&lt;H2&gt;Type de message en attente&lt;/H2&gt;(new line)\n&lt;p&gt;(new line)\nSi votre fournisseur de service propose la signalisation d&apos;un message d&apos;attente , Twinkle peut vous indiquer qu&apos;un nouveau message est arrivé. Demander à votre fournisseur, quel type de signalisation est fourni.(new line)\n&lt;/p&gt;(new line)\n&lt;H3&gt;Non sollicité&lt;/H3&gt;(new line)\n&lt;p&gt;(new line)\nSignalisation de message en attente non sollicité .(new line)\n&lt;/p&gt;(new line)\n&lt;H3&gt;Sollicité&lt;/H3&gt;(new line)\n&lt;p&gt;(new line)\nSignalisation de message en attente sollicité com spécifié par le RFC 3842.(new line)\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation>Type &amp;MWI:</translation>\n    </message>\n    <message>\n        <source>Sollicited MWI</source>\n        <translation>RFC 3842</translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation>&amp;durée de souscription:</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation>Nom d&apos;&amp;utilisateur de boîte vocale:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation>Le nom d l&apos;hôte, le nom de domaine ou l&apos;adresse IP du serveur de boîte vocale.</translation>\n    </message>\n    <message>\n        <source>For sollicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation>Pour le RFC 3842 MWI sollicité, un terminal souscrit au message de statut pour une durée limité. Juste avant l&apos;expiration le terminal doit raffraichir la subcription.</translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation>Votre nom d&apos;utilisateur de boîte vocale.</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation>&amp;Server de boîte vocale:</translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation>Via &amp;proxy sortant</translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation>Cocher cette case si Twinkle doit envoyer les messages au serveur de boîte vocale par un proxy sortant.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation>Vous devez saisir un nom d&apos;utilisateur de boîte vocale.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation>Vous devez saisir un nom de serveur de boîte vocale</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation>Serveur de boîte vocale invalide.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation>Nom d&apos;utilisateur de boîte vocale invalide.</translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation>Utiliser le &amp;nom de domaine pour créer  une valeur unique de l&apos;entête de contact</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Sélectionnez votre sonnerie de tonalité.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Sélectionner un fichier de sonnerie.</translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation>Sélectionner un script.</translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>%1 convertit en %2</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Message instantané</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Présence</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation>Nombre &amp;maximum de sessions:</translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation>Quand ce nombre de sessions de message instantané est atteint, les nouvelles sessions de message entrant seront rejetées.</translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Votre présence</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>&amp;Publier la disponibilité au démarrage</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Publier la disponibilité au démarrage.</translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation>Présence d&apos;avatar</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation>Intervalle d&apos;&amp;actualisation de la publication (sec):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation>Période d&apos;actualisation de la publication de votre présence.</translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation>Intervalle d&apos;&amp;actualisation de la connexion (sec):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation>Période d&apos;actualisation de la connexion.</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Transport/NAT</translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation>Ajouter la q-value à la connexion</translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation>La q-value indique la priorité de l&apos;interface connectée. Si en plus de Twinkle, vous connectez une autre interface SIP pour ce compte, alors le réseau utilisera ces valeurs pour déterminer quelle interface tester en premier pour effectuer un appel.</translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation>La q-value est une valeur comprise ent 0.000 et 1.000. Une valeur superieure signifie une priorité plus importante.</translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>Transport SIP</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Mode de transport SIP. En mode auto, la taille du message détermine quel protocole de transport utiliser. Les messages plus grand que la limite de l&apos;UDP sont envoyés via TCP.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>Protocole de T&amp;ransport:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation>&amp;Limite UDP:</translation>\n    </message>\n    <message>\n        <source> bytes</source>\n        <translation type=\"obsolete\">octets</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Les messages plus grand que la limite sont envoyés via TCP. Les messages plus petits sont envoyés via UDP.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN (does not work for incoming TCP)</source>\n        <translation>Utiliser &amp;STUN (ne fonctionne pas pour le TCP entrant)</translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation>Connexion TCP p&amp;ercistante</translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation>Conserve la connexion TCP pendant l&apos;ouverture de l&apos;enregistrement de façon à ce que le proxy SIP puisse réutiliser cette connexion pour envoyer des requêtes. Des ping sont enoyés pour tester si la connexion est toujours en établie.</translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation>&amp;Envoi de l&apos;indication composite en un message.</translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation>Twinkle envoie une indication composite quand vous écrivez un message. Ainsi, l&apos;interlocuteur peut voir que vous êtes en train d&apos;écrire un message.</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation>AKA AM&amp;F:</translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation>A&amp;KA OP:</translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation>Champ de gestion de l&apos;authentification en AKAv1-MD5.</translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation>Clé de l&apos;opérateur en AKAv1-MD5.</translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation>Préinterprétati&amp;on</translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation>La préinterpretation améliore la qualité distante</translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation>Contrôle &amp;automatique du gain</translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation>Le contrôle automatique du gain (AGC) tient compte de la possible variation importante du volume d&apos;enregistrement entre deux paramètres. L&apos;AGC permet d&apos;ajuste le volume de référence. Ceci est très pratique car il supprime la nécessité d&apos;ajuster le gain du microphone manuellement. Un autre avantage est de permettre de régler le gain du microphone à un niveau assez bas pour éviter les coupures.</translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation>&amp;Niveau du contrôle automtaique du gain:</translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation>Ce niveau représente un pourcentage du gain du microphone. La valeur recommandée est 25%.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation>Détection de l&apos;activité de la &amp;voix</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation>La détection de l&apos;activité détecte si le signal d&apos;entrée est de la parole ou du silence/bruit de fond et ne transmet pas ce bruit.</translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation>Réduction du &amp;bruit</translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation>La réduction du bruit peut être utilisée pour réduire le niveau de bruit de fond dans le signal d&apos;entrée. Ceci améliore la qualité du son.</translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation>Suppression de l&apos;&amp;Echo</translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation>Dans toute communication VOIP, si le son venant du correspondant est sur amplificateur, il est enregistré par le microphone. Si le son enregistré par le micropohne est directement envoyé au correspondant, alors il entend sa voix en écho. La suppression de l&apos;écho est consue pour supprimer cet écho avant qu&apos;il ne soit transmis. Il est important de comprendre que la suppression de l&apos;écho est consue pour améliorer la qualite du son pour le correspondant.</translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation>Taux de compression varia&amp;ble</translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation>&amp;Transmission discontinue</translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation>&amp;Qualité:</translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation>Speex est un codec à perte, ce qui signifie qu&apos;il assure la compression au dépend de la fidélité au son d&apos;entrée. A la différence de quelques autres codecs, il est possible de contrôler l&apos;équilibre entre qualité et compression. Le pocessus d&apos;encodage Speex est contrôlé la plupart du temps par un paramêtre de qualité entre 0 et 10.</translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation>octets</translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation>Utiliser tel-URI comme &amp;numéro de téléphone</translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation>Transcrit un numéro de téléphone entré en tel-URI et non en sip-URI.</translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Assitant</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Le nom d l&apos;hôte, le nom de domaine ou l&apos;adresse IP du serveur STUN.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>Serveur S&amp;TUN:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Le nom d&apos;utilisateur SIP donné par votre fournisseur de service. C&apos;est la partie &quot;utilisateur&quot; de votre adresse SIP, &lt;b&gt;utilisateur&lt;/b&gt;@domain.com.\nCeci peut être un numéro de téléphone.\n&lt;br&gt;&lt;br&gt;\nCe champ est obligatoire.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Sélectionnez votre fournisseur de service SIP. S&apos;il n&apos;es pas dans la liste, sélectionnez &lt;b&gt;Autre&lt;/b&gt; et remplissez les paramètres que vous avez reçu de votre fournisseur.&lt;br&gt;&lt;br&gt;\nSi vous sélectionnez un des fournisseurs prédéfinis, vous n&apos;avez qu&apos;à saisir votre nom, nom d&apos;utilisateur, nom d&apos;authentification, et mot de passe.</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>Nom d&apos;&amp;authentification:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Votre nom:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Votre nom d&apos;authentification SIP. Souvent il est le même que votre nom d&apos;utilisateur SIP. Cependant, il peut être différent.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Le domaine de votre adresse SIP, username@&lt;b&gt;domain.com&lt;/b&gt;. A la place d&apos;un vrai nom de domaine, il est également possible de saisir le nom de l&apos;hôte ou l&apos;adresse IP de votre &lt;b&gt;proxy SIP&lt;/b&gt;. Si vous voulez uniquement des communications de PC à PC, saisissez  le nom de l&apos;hôte ou l&apos;adresse IP de votre ordinateur.\n&lt;br&gt;&lt;br&gt;\nCe champ est obligatoire.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>C&apos;est simplement votre nom complet, ex: Pierre Dupond. Il est utilisé pour l&apos;affichage. Quand vous ferez un appel, ceci sera montré à votre correspondant.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>Pro&amp;xy SIP:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>Le nom d&apos;hôte, le nom de domaine ou l&apos;adresse IP de votre proxy SIP. Si c&apos;est le même valeur que votre domaine, vous pouvez laisser ce champ vide. </translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>Fournisseur de service &amp;SIP:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>Mot de &amp;passe:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>Nom d&apos;&amp;utilisateur*:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Votre mot de passe pour authentification.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Annuler (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Aucun (appels directs d&apos;IP à IP)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Autre</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Assistant pour profil utilisateur: </translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Vous devez saisir un nom d&apos;utilisateur pour votre compte SIP.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Vous devez saisir un nom de domaine pour votre compte SIP.\n\nil est également possible de saisir le nom de l&apos;hôte ou l&apos;adresse IP de votre PC si vous voulez faire des appels directs de PC à PC.</translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Valeur invalide pour le proxy SIP.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Valeur invalide pour le serveur STUN.</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Oui</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Non</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\">Répondre</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\">Refuser</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/lang/twinkle_nl.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"nl\" sourcelanguage=\"en\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Adres</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation>Op&amp;merkingen:</translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation>Tussenvoegsel.</translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Voornaam.</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>Voor&amp;naam:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation>Hier kunt u opmerkingen kwijt.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefoon:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation>Tu&amp;ssenvoegsel:</translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Telefoonnummer of SIP adres.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Achternaam.</translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Achternaam:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>U moet een naam invullen.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>U moet een telefoonnummer of SIP adres invullen.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Telefoon</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"unfinished\">Opmerkingen</translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Authenticatie</translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>De gebruiker waarvoor authenticatie vereist is.</translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Het gebruikersprofiel van de gebruiker.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Gebruikersprofiel:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Gebruiker:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>W&amp;achtwoord:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Uw wachtwoord voor authenticatie.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Uw SIP gebruikersnaam voor authenticatie. Meestal is dit hetzelfde als uw SIP gebruikersnaam.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>&amp;Gebruikersnaam:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation>Realm:</translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation>De &quot;realm&quot; waarvoor u zich moet authenticeren.</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Vriend</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefoon:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Naam van uw vriend.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>&amp;Toon beschikbaarheid</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Vink deze optie aan als u de beschikbaarheid van uw vriend wilt zien. Dit werkt alleen als uw provider een presence agent heeft.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Naam:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>SIP adres van uw vriend.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>U moet een naam invullen.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Foutief telefoonnummer.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Opslaan van vriendenlijst is mislukt: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Beschikbaarheid</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>onbekend</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>offline</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>online</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>verzoek geweigerd</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>niet gepubliceerd</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>publicatie mislukt</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>verzoek mislukt</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Klik rechts om een vriend toe te voegen.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>Openen geluidskaart mislukt</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>UDP socket (RTP) creatie op port %1 mislukt</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Creatie van audio receiver thread mislukt.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Creatie van audio transmitter thread mislukt.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>lokale partij</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>andere partij</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>fout</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>onbekend</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>in</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>uit</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Deregistreren</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>deregistreer alle apparaten</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>Twinkle - Diamondcard User Profile</source>\n        <translation type=\"obsolete\">Twinkle - Diamondcard gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;With a Diamondcard account you can make worldwide calls to regular and cell phones. To sign up for a Diamondcard account click on the &quot;sign up&quot; link below. Once you have signed up you receive an account ID and PIN code. Enter the account ID and PIN code below to create a Twinkle user profile for your Diamondcard account.&lt;/p&gt;\n&lt;p&gt;For call rates see the sign up web page that will be shown to you when you click on the &quot;sign up&quot; link.&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p&gt;Met een Diamondcard account kunt u wereldwijd bellen naar vaste en mobiele telefoons. U kunt zich aanmelden voor een Diamondcard account door op de onderstaande &quot;aanmelden&quot; link te klikken. Na aanmelding ontvangt u een account ID en PIN code. Voer dit account ID en de PIN code hieronder in om een Twinkle gebruikersprofiel voor uw Diamondcard account te maken.&lt;/p&gt;\n&lt;p&gt;Beltarieven kunt u vinden op de aanmeldingspagina die u krijgt als u op de &quot;aanmelden&quot; link klinkt.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Your Diamondcard account ID.</source>\n        <translation type=\"obsolete\">Uw Diamondcard account ID.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation type=\"obsolete\">Dit is uw eigen naam. bijv. Jan Jansen. Als u iemand belt, kan deze naam getoond worden.</translation>\n    </message>\n    <message>\n        <source>&amp;Account ID:</source>\n        <translation type=\"obsolete\">&amp;Account ID:</translation>\n    </message>\n    <message>\n        <source>&amp;PIN code:</source>\n        <translation type=\"obsolete\">&amp;PIN code:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation type=\"obsolete\">U&amp;w naam:</translation>\n    </message>\n    <message>\n        <source>&lt;p align=&quot;center&quot;&gt;&lt;u&gt;Sign up for a Diamondcard account&lt;/u&gt;&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p align=&quot;center&quot;&gt;&lt;u&gt;Aanmelden voor een Diamondcard account&lt;/u&gt;&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"obsolete\">&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"obsolete\">Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation>Vul uw account ID in.</translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation>Vul uw PIN code in.</translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation>Een gebruikersprofiel met de naam %1 bestaat al.</translation>\n    </message>\n    <message>\n        <source>Your Diamondcard PIN code.</source>\n        <translation type=\"obsolete\">Uw Diamondcard PIN code.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages. To sign up for a Diamondcard account click on the &quot;sign up&quot; link below. Once you have signed up you receive an account ID and PIN code. Enter the account ID and PIN code below to create a Twinkle user profile for your Diamondcard account.&lt;/p&gt;\n&lt;p&gt;For call rates see the sign up web page that will be shown to you when you click on the &quot;sign up&quot; link.&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p&gt;Met een Diamondcard account kunt u wereldwijd bellen naar vaste en mobiele telefoons en SMS berichten versturen. U kunt zich aanmelden voor een Diamondcard account door op de onderstaande &quot;aanmelden&quot; link te klikken. Na aanmelding ontvangt u een account ID en PIN code. Voer dit account ID en de PIN code hieronder in om een Twinkle gebruikersprofiel voor uw Diamondcard account te maken.&lt;/p&gt;\n&lt;p&gt;Beltarieven kunt u vinden op de aanmeldingspagina die u krijgt als u op de &quot;aanmelden&quot; link klinkt.&lt;/p&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation>Twinkle - DTMF</translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Toetsen</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation>2</translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation>3</translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>A (normaal niet nodig).</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation>4</translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation>5</translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation>6</translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>B (normaal niet nodig).</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation>7</translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation>8</translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation>9</translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>C (normaal niet nodig).</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Ster (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation>0</translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Hekje (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>D (normaal niet nodig).</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation>1</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Sluiten</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n</context>\n<context>\n    <name>FreeDeskSysTray</name>\n    <message>\n        <source>Show/Hide</source>\n        <translation type=\"obsolete\">Toon/Verberg</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"obsolete\">Afsluiten</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Geen netwerkverbinding gevonden. Twinkle gebruikt nu 127.0.0.1 als lokaal IP adres. Als u weer een netwerkverbinging heeft, dan moet u Twinkle opnieuw starten om het correcte IP adres te gebruiken.</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Terminal eigenschappen van %1</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>onbekend</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation>geen</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, registratie mislukt: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, registratie geslaagd (duur = %2 seconden)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, registratie mislukt: STUN fout</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, deregistratie gesglaagd: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation>%1, opvragen registraties mislukt: %2 %3</translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: u bent niet geregistreerd</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: u heeft de volgende registraties</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: opvragen registraties...</translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Verzoek doorgewezen naar: %1</translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation>foutief DTMF signaal (%1)</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Gesprek doorverwezen</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Gebruikersprofiel:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Gebruiker:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Staat u toe dat u wordt doorverwezen naar de volgende bestemming?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation>Als u deze vraag niet meer wilt zien, dan kunt u dit aangeven in de SIP protocol instellingen van uw gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation>Verzoek doorverwezen</translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation>Staat u toe dat het %1 verzoek wordt doorverwezen naar de volgende bestemming?</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Gesprek doorverbonden</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation>Verzoek om gesprek door te verbinden van:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Staat u toe dat u wordt doorverbonden naar de volgende bestemming?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation>Info:</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Waarschuwing:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation>Ernstige fout:</translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Firewall / NAT verkennen...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Afbreken</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Lijn %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Klik op het hangslot om een juiste SAS te bevestigen.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>De andere partij op lijn %1 heeft encryptie uitgeschakeld.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Starten van conferentie mislukt.</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>U kunt alleen meerdere profielen voor verschillende gebruikers starten.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Lijn %1: inkomend gesprek voor %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Gesprek doorverbonden door %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation>Lijn %1: andere partij heeft gesprek geannuleerd.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation>Lijn %1: andere partij heeft gesprek beëindigd.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Lijn %1: SDP van andere partij wordt niet ondersteund.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Lijn %1: SDP van andere partij ontbreekt.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation>Lijn %1: niet-ondersteunde &quot;content type&quot; ontvangen van andere partij.</translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Lijn %1: geen ACK ontvangen, gesprek wordt afgebroken.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Lijn %1: geen PRACK ontvangen, gesprek wordt afgebroken.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Lijn %1: PRACK mislukt.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Lijn %1: annuleren van gesprek is mislukt.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Lijn %1: gesprek beantwoord.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Lijn %1: gesprek mislukt.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>U kunt het nogmaals proberen naar:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation>Lijn %1: gesprek beëindigd.</translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Lijn %1: gesprek verbonden.</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation>Geaccepteerde &quot;body&quot; typen:</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Geaccepteerde encoderingen:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Geaccepteerde talen:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation>Ondersteunde extensies:</translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Toesteltype:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation>Lijn %1: terughalen gesprek mislukt.</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Lijn %1: verzoek doorverwezen naar</translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Lijn %1: stuur DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation>Lijn %1: andere partij ondersteund geen DTMF &quot;telephone events&quot;.</translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation>Lijn %1: notificatie ontvangen.</translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Gebeurtenis: %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Toestand: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Reden: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Voortgang: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Lijn %1: doorverbinden gesprek mislukt.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Lijn %1: doorverbinden gesprek geslaagd.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Lijn %1: nog steeds bezig met doorverbinden gesprek.</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation>Er komen geen notificaties meer.</translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Lijn %1: doorverbinden gesprek met %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Verzoek tot doorverbinden door %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation>Lijn %1: doorverbinden mislukt. Oorspronkelijk gesprek wordt teruggehaald.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Lijn %1: SAS bevestigd.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation>Lijn %1: SAS bevestiging gewist.</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Lijn %1: gesprek afgewezen.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Lijn %1: gesprek doorverwezen.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Antwoord op verzoek om terminal eigenschappen: %1 %2</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>De volgende profielen zijn beiden voor gebruiker %1</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Toegestane verzoeken:</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Lijn %1: DTMF gedetecteerd:</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (SIP) on port %1</source>\n        <translation type=\"obsolete\">Opzetten UDP socket (SIP) op port %1 mislukt</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Wilt u het lock bestand overschrijven en opstarten?</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, voice mail status fout.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, voice mail status geweigerd.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, voice mailbox bestaat niet.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, voice mail status beëindigd.</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1, STUN verzoek mislukt: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, STUN verzoek mislukt.</translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, deregistratie mislukt: %2 %3</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation>Verzoek om gesprek door te verbinden.</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation>Als dit gebruikers in verschillende domeinen zijn, dan moet u de volgende optie in uw gebruikersprofiel (SIP protocol) aanzetten</translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation>Gebruik domeinnaam voor een unieke contact header</translation>\n    </message>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>SIP %1 poort kan niet geopend worden</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Geaccepteerd door netwerk</translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Opslaan van bijlage %1 mislukt</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation>Doorverbonden door: %1</translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation>Web browser kan niet geopend worden: %1</translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation>Configureer uw web browser in de systeeminstellingen.</translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Kies adres</translation>\n    </message>\n    <message>\n        <source>Name</source>\n        <translation type=\"obsolete\">Naam</translation>\n    </message>\n    <message>\n        <source>Type</source>\n        <translation type=\"obsolete\">Type</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"obsolete\">Telefoon</translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>&amp;Toon alleen SIP adressen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Vink deze optie aan als u alleen contacten met een SIP adres wilt zien, d.w.z. adressen die starten met &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>&amp;Herladen</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Haal de adressen opnieuw op uit KAddressbook.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation>&amp;KAddressBook</translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation>Deze lijst met adressen komt uit &lt;b&gt;KAddressBook&lt;/b&gt;. Contacten waarvoor u geen telefoonnummer heeft opgenomen staan niet in deze lijst. Om adresinformatie te wijzigen moet u KAddressBook gebruiken.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>&amp;Lokaal adresboek</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"obsolete\">Opmerkingen</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Adresgegevens uit het lokale adresboek van Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>To&amp;evoegen</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Voeg een adres toe aan het lokale adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Verwijderen</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Verwijder een adres uit het lokale adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Bewerk</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Wijzig adresgegevens.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;U heeft geen contacten met een telefoonnummer in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s adresboek applicatie. Twinkle haalt alle contacten met een telefoonnummer uit KAdressBook. Om uw contacten te beheren, moet u KAddressbook gebruiken.&lt;/p&gt;\n&lt;p&gt;Als alternatief kunt u het lokale adresboek van Twinkle gebruiken.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation>Weet je zeker dat je contact &apos;%1&apos; wilt verwijderen van je lokale adres boek?</translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation>Verwijder contact</translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Profielnaam</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Voer de naam voor uw profiel in:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>&lt;b&gt;De naam van uw profiel&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nEen profiel bevat uw gebruikersinstellingen, bijv. uw gebruikersnaam en paswoord. U moet elk profiel een naam geven.\n&lt;br&gt;&lt;br&gt;\nAls u meerde SIP accounts heeft, dan kunt u meerdere profielen maken. Als u Twinkle opstart dan krijgt u een lijst met alle profielnamen te zien. Uit deze lijst kunt u kiezen welk profiel u wilt starten.\n&lt;br&gt;&lt;br&gt;\nOm uw gebruikersprofielen makkelijk uit elkaar te houden kunt u uw SIP gebruikersnaam als profielnaam gebruiken, bijv. &lt;b&gt;example@example.com&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>.twinkle folder kan niet gevonden worden in uw thuis folder.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Profiel bestaat al.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Hernoem profiel &apos;%1&apos; naar:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - Gesprekshistorie</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Tijd</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>Van/Naar</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Onderwerp</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Status</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Gespreksdetails</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Details van het geselecteerde gesprek.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Toon</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>&amp;Inkomende gesprekken</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Vink deze optie aan om inkomende gesprekken te tonen.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>&amp;Uitgaande gesprekken</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Vink deze optie aan om uitgaande gesprekken te tonen.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>Be&amp;antwoorde gesprekken</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Vink deze optie aan om beantwoorde gesprekken te tonen.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>Ge&amp;miste gesprekken</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Vink deze optie aan om gemiste gesprekken te tonen.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>Alleen actieve &amp;profielen</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Vink deze optie aan om alleen gesprekken behorende bij de nu actieve profielen te tonen.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Wis</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Wis de gehele gespreksgeschiedenig.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Noot:&lt;/b&gt; hiermee wist u &lt;b&gt;alle&lt;/b&gt; gespreksgegevens, ook gegevens die niet getoond worden afhankelijk van de toon-opties.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Sluit dit venster.</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Begin:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Beantwoord:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Eind:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Duur:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Richting:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Van:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Naar:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Antwoord aan:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Doorverbonden door:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Onderwerp:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Beëindigd door:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Status:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Toestel partner:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Gebruikersprofiel:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>gesprek</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Bel...</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Wis</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Antw:</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>In/Uit</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Bel geselecteerd adres.</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>&amp;Sluiten</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>&amp;Bel</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation>Aantal gesprekken:</translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation>Totale duur:</translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Bellen</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Aan:</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Hier kunt u optioneel een onderwerp invoeren. Dit kan getoond worden aan de gebelde partij.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Het adres dat u wilt bellen. Dit kan een volledig SIP adres zijn zoals &lt;b&gt;sip:example@example.com&lt;/b&gt; of alleen een gebruikersnaam of telefoonnummer. Als u geen volledig adres invoert, dan zal Twinkle het adres afmaken met de waarde die u voor domein heeft ingevuld in uw gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Het gebruikersprofiel waarmee u het gesprek wilt maken.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>O&amp;nderwerp:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Van:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>&amp;Identiteit verbergen</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nMet deze optie verzoekt u uw SIP provider om uw identiteit verborgen te houden voor degene die u belt. Alleen uw SIP adres of telefoonnummer blijft geheim. Uw IP adres wordt &lt;b&gt;niet&lt;/b&gt; verborgen.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Waarschuwing:&lt;/b&gt; niet alle providers ondersteunen het verbergen van uw identiteit.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Niet alle SIP providers ondersteunen het verbergen van uw identiteit. Verzeker u ervan dat uw SIP provider dit ondersteunt als u dit nodig heeft.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation>Twinkle - Log</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Sluiten</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Wis</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Wis het log venster. Hiermee wist u &lt;b&gt;niet&lt;/b&gt; het log bestand zelf.</translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Inhoud van het log bestand (~/.twinkle/twinkle.log)</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - Instant bericht</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Aan:</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>De gebruiker die het bericht stuurt.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Het adres van de persoon aan wie u een bericht wilt sturen. Dit kan een volledig SIP adres zijn zoals &lt;b&gt;sip:example@example.com&lt;/b&gt; of een een telefoonnummer. Als u geen volledig adres opgeeft, dan zal Twinkle dit adres compleet maken door de domeinnaam uit uw gebruikersprofiel toe te voegen.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>&amp;Gebruikersprofiel:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Conversatie</translation>\n    </message>\n    <message>\n        <source>The exchanged messages.</source>\n        <translation type=\"obsolete\">De uitgewisselde berichten.</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Typ uw bericht en druk op &quot;zend&quot; om het te verzenden.</translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Zend</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Zend het bericht.</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Afleverfout</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Aflevernotificatie</translation>\n    </message>\n    <message>\n        <source>Instant message toolbar</source>\n        <translation type=\"obsolete\">Instant berichten</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Zend bestand...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Zend bestand</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>plaatje is verkleind voor preview</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Openen met %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Openen met...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Bijlage opslaan als...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Bestand bestaat al. Wilt u dit bestand overschrijven?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Opslaan bijlage mislukt.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 schrijft een bericht.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation>Lengte</translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>bericht wordt verstuurd</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation>Twinkle</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Het adres dat u wilt bellen. Dit kan een volledig SIP adres zijn zoals &lt;b&gt;sip:example@example.com&lt;/b&gt; of alleen een gebruikersnaam of telefoonnummer. Als u geen volledig adres invoert, dan zal Twinkle het adres afmaken met de waarde die u voor domein heeft ingevuld in uw gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Het gebruikersprofiel waarmee u het gesprek wilt maken.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Gebruiker:</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Bel</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Bel het adres.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation>Indicate automatisch beantwoord.</translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation>Indicatie doorverwijzen.</translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation>Indicatie niet storen.</translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation>Indicatie gemiste gesprekken.</translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation>Registratiestatus.</translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation>Scherm</translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Lijnstatus</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Lijn &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation>Alt+1</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Kliek hier om over te schakelen naar lijn 1.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Van:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Naar:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Onderwerp:</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>vrij</translation>\n    </message>\n    <message>\n        <source>Call is on hold</source>\n        <translation type=\"obsolete\">Gesprek staat in de wacht</translation>\n    </message>\n    <message>\n        <source>Voice is muted</source>\n        <translation type=\"obsolete\">Geluid is onderdrukt</translation>\n    </message>\n    <message>\n        <source>Conference call</source>\n        <translation type=\"obsolete\">Conferentiegesprek</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Gesprek doorverbonden</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThe padlock indicates that your voice is encrypted during transport over the network.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nBoth ends of an encrypted voice channel receive the same SAS on the first call. If the SAS is different at each end, your voice channel may be compromised by a man-in-the-middle attack (MitM).\n&lt;/p&gt;\n&lt;p&gt;\nIf the SAS is equal at both ends, then you should confirm it by clicking this padlock for stronger security of future calls to the same destination. For subsequent calls to the same destination, you don&apos;t have to confirm the SAS again. The padlock will show a check symbol when the SAS has been confirmed.\n&lt;/p&gt;</source>\n        <translation type=\"obsolete\">&lt;p&gt;\nHet hangslot geeft aan dat het geluidskanaal versleuteld is tijdens transport over het netwerk.\n&lt;/p&gt;\n&lt;h3&gt;SAS - Short Authentication String&lt;/h3&gt;\n&lt;p&gt;\nBeide kanten van een versleuteld geluidskanaal ontvangen dezelfde SAS bij het eerste gesprek. Als de SAS niet hetzelfde is aan beide kanten, dan kan uw geluidskanaal gecompromiteerd zijn door een &quot;man-in-the-middle&quot; aanval (MitM).\n&lt;/p&gt;\n&lt;p&gt;\nAls de SAS aan beide kanten hetzelfde is, dan moet u die bevestigen door op het hangslot te klikken voor hogere veiligheid van toekomstige gesprekken naar dezelfde bestemming. Voor toekomstige gesprekken naar deze bestemming, hoeft u de SAS dan niet nogmaals te bevestigen. Het hangslot toont een verificatie symbool als de SAS bevestigd is.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation>Short authentication string</translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation>Audio codec</translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation>0:00:00</translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Gespreksduur</translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Lijn &amp;2:</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation>Alt+2</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Klik hier om over te schakelen naar lijn 2.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Bestand</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>Be&amp;werk</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>Ge&amp;sprek</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Activeer lijn</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>&amp;Registratie</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>&amp;Diensten</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>&amp;Toon</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>&amp;Help</translation>\n    </message>\n    <message>\n        <source>Call Toolbar</source>\n        <translation type=\"obsolete\">Gespreksbalk</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Afsluiten</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>&amp;Afsluiten</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>Over Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>Over &amp;Twinkle</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Iemand bellen</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation>F5</translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Beantwoord een binnenkomend gesprek</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation>F6</translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation>Beëindig een gesprek</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Wijs een binnenkomend gesprek af</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation>F8</translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Zet een gesprek in de wacht, of haal een gesprek uit de wacht</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Verwijs een binnenkomend gesprek naar een andere bestemming zonder te antwoorden</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Open een toetsenbord om cijfers in te voeren voor een stem gestuurd menu</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Registreren</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>&amp;Registreren</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Deregistreren</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>&amp;Deregistreren</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Deregistreer dit apparaat</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Toon registraties</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>&amp;Toon registraties</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Terminal eigenschappen</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Vraag terminal eigenschappen van iemand op</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Niet storen</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>&amp;Niet storen</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>&amp;Doorverwijzen...</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Herhaal het laatste gesprek</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>Over Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>Over &amp;Qt</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>&amp;Gebruikersprofiel...</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation>Verbind twee gesprekken in een 3-weg conferentie</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Onderdruk het geluid</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Gesprek doorverbinden</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>&amp;Systeeminstellingen...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Alles deregistreren</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>&amp;Alles deregistreren</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Deregistreer al uw geregistreerde apparaten</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Automatisch beantwoorden</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>&amp;Automatisch beantwoorden</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Log</translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation>&amp;Log...</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>Gespreks&amp;historie...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation>F9</translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Wijzig gerbuiker...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Wijzig gebruiker...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Activeer of de-actieveer gebruikers</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>Wat is dit?</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>&amp;Wat is dit?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Lijn 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Lijn 2</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>bellen</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>gesprek opzetten, wacht aub</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>inkomend gesprek</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>gesprek verbinden, wacht aub</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>verbonden</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>verbonden (wacht op media)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation>gesprek afbreken, wacht aub</translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>onbekend</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Geluid is versleuteld</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>Klik om SAS te bevestigen.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation>Klik om SAS bevestiging te wissen.</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Registratiestatus:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>Geregistreeerd</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>Mislukt</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>Niet geregistreerd</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Niemand is geregistreerd.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>Niet storen actief voor:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Doorverwijzen actief voor:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>Automatisch beantwoorden actief voor:</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>Niet storen is niet actief.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>Doorverwijzen is niet actief.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>Automatisch beantwoorden is niet actief.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>Geen gemiste gesprekken.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>U heeft 1 gemist gesprek.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>U heeft %1 gemiste gesprekken.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Klik hier om de gesprekshistorie te zien.</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Opstarten gebruikersprofielen...</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>U kunt alleen meerdere profielen voor verschillende gebruikers starten.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation>U heeft de SIP UDP poort gewijzigd. Deze instelling wordt pas actief als u Twinkle opnieuw opstart.</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Gebruiker:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Bel:</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>De volgende profielen zijn beiden voor gebruiker %1</translation>\n    </message>\n    <message>\n        <source>Visual indication of line state.</source>\n        <translation type=\"obsolete\">Visuele indicatie van de lijnstatus.</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Doorverwijzen</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Systeeminstellingen</translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>Gesprekshistorie</translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>B&amp;el:</translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation>Ruggespraak doorverbinden</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Identiteit verbergen</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Klik om registraties te tonen.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 nieuw, 1 oud bericht</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 nieuw, %2 oude berichten</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>1 nieuw bericht</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 nieuwe berichten</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>1 oud bericht</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 oude berichten</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Er zijn berichten</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Geen berichten</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Voice mail status:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Fout</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Onbekend</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Klik voor toegang to voice mail.</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Klik om te activeren/deactiveren</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Klik om te activeren</translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation>niet ingesteld</translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation>Voor toegang tot voice mail moet u eerst uw voice mail nummer in uw gebruikersprofiel invullen.</translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>De lijn is bezet. Geen toegang tot voice mail.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation>Het voice mail nummer %1 is ongeldig. Vul een geldig nummer in in uw gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Bel</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <comment>call menu text</comment>\n        <translation type=\"obsolete\">&amp;Bel...</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Antw</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Antwoord</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Einde</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Einde</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Afwijzen</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">A&amp;fwijzen</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Wacht</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Wacht</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Verwijs</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Verwijs...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Dt&amp;mf...</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Terminal eigenschappen...</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Herh</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Herhaal</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Conf</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Conferentie</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Stil</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Stil</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Xfer</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Doorverbinden...</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Voice mail</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>&amp;Voice mail</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation>Bel voice mail</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation>Voice mail status.</translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Vrienden</translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>&amp;Bericht</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Bericht</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>Instant &amp;bericht...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Instant bericht</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Bel...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>Be&amp;werk...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Verwijderen</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>O&amp;ffline</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>&amp;Online</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>&amp;Wijzig beschikbaarheid</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Vriend toevoegen...</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Opslaan van vriendenlijst is mislukt: %1</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation>Voor elk gebruikersprofiel kunt u een vriendenlijst aanleggen. Om de beschikbaarheid van uw vrienden te zien en uw eigen beschikbaarheid te publiceren, moet uw provider over een presence server beschikken.</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>&amp;Vrienden</translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation>&amp;Scherm</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation>Handleiding</translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation>&amp;Handleiding</translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation>Aanmelden</translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation>&amp;Aanmelden...</translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation>Opwaarderen...</translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation>Balansoverzicht...</translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation>Gesprekkenoverzicht...</translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation>Admin center...</translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation>Opwaarderen</translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation>Balansoverzicht</translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation>Admin center</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation type=\"unfinished\">Bel</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation type=\"unfinished\">&amp;Antwoord</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation type=\"unfinished\">&amp;Einde</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation type=\"unfinished\">Einde</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation type=\"unfinished\">A&amp;fwijzen</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\">Afwijzen</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation type=\"unfinished\">&amp;Wacht</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation type=\"unfinished\">Wacht</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation type=\"unfinished\">&amp;Verwijs...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation type=\"unfinished\">Verwijs</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation type=\"unfinished\">Dt&amp;mf...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation type=\"unfinished\">Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation type=\"unfinished\">&amp;Terminal eigenschappen...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation type=\"unfinished\">&amp;Herhaal</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation type=\"unfinished\">Herh</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation type=\"unfinished\">&amp;Conferentie</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation type=\"unfinished\">Conf</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation type=\"unfinished\">&amp;Stil</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation type=\"unfinished\">Stil</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation type=\"unfinished\">&amp;Doorverbinden...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation type=\"unfinished\">Xfer</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Match expressie:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Vervang:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation>Formaat string (Perl syntax) voor het vervangen van het nummer in.</translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation>Reguliere expressie (Perl syntax) voor het matchen van het nummerformaat dat u wilt modificeren.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - Nummerconversie</translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>Match expressie mag niet leeg zijn.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>Vervangwaarde mag niet leeg zijn.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Ongeldige reguliere expressie.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Doorverwijzen</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Verwijs inkomend gesprek naar</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>U kan tot 3 bestemmingen invoeren waarnaar u een gesprek wilt doorverwijzen. Als de eerste bestemming niet opneemt, dan wordt de tweede bestemming geprobeerd enz.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3e bestemming:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2e bestemming:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1e bestemming:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - Kies NIC</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Kies de netwerk interface/IP adres die u wilt gebruiken:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>U heeft meerdere IP adressen. Hier moet u aangeven welk IP adres gebruikt moet worden. Dit adres wordt gebruikt in de SIP berichten.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>Default &amp;IP</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Het geselecteerde IP adres wordt het default IP adres. De volgende keer dat u Twinkle start wordt dit IP adres automatisch geslecteerd.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>Default &amp;NIC</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>De geslecteerde netwerk interface wordt de default interface. De volgende keer dat u Twinkle opstart wordt deze interface automatisch geslecteerd.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Als u de default later wilt verwijderen of wijzigen, dan kan dat via de systeeminstellingen.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle - Kies gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Selecteer de gebruikersprofielen die u wilt activeren:</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation type=\"obsolete\">Gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Vink de gebruikersprofielen aan die u wilt activeren.</translation>\n    </message>\n    <message>\n        <source>&amp;New</source>\n        <translation type=\"obsolete\">&amp;Nieuw</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+N</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Maak een nieuw profiel met de profielen editor.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>Wi&amp;zard</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Maak een nieuw profiel met de wizard.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>Be&amp;werk</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Bewerk het huidige profiel.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Verwijderen</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Verwijder het huidige profiel.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>&amp;Hernoemen</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Hernoem het huidige profiel.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>&amp;Default</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Markeer de geselecteerde profielen als default profielen. De volgende keer dat u Twinkle opstart, zullen deze profielen automatisch gestart worden.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Start</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Start Twinkle met de geselecteerde profielen.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>S&amp;ysteeminstellingen</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Wijzig de systeeminstellingen.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Voordat u Twinkle kunt gebruiken, moet u een gebruikerspofiel maken.&lt;br&gt;Klik op OK om een gebruikersprofiel te maken.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"obsolete\">&lt;html&gt;U kunt de profieleditor gebruiken om een gebruikersprofiel te maken. Met de profieleditor kunt u diverse instellingen met betrekking tot het SIP protocol, RTP en vele andere zaken wijzigen.&lt;br&gt;&lt;br&gt;Met de wizard kunt u snel een gebruikersprofiel maken. De wizard vraagt u alleen om een aantal essentiële instellingen. Als u een gebruikersprofiel met de wizard maakt, dan kun u deze op een later tijdstip alsnog met de profieleditor wijzigen.&lt;br&gt;&lt;br&gt;Kies op welke wijze u een gebruikersprofiel wilt maken.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;U kunt nu de systeeminstellingen wijzigen. Deze instellingen kunt u altijd wijzigen op een later tijdstip.&lt;br&gt;&lt;br&gt;Klik op OK om de systeeminstellingen te bekijken en eventueel te wijzigen.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>U heeft geen gebruikersprofiel geselecteerd.\nKies eerst een gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Weet u zeker dat u profiel &apos;%1&apos; wilt verwijderen?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Verwijder profiel</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Profiel verwijderen mislukt.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Profiel hernoemen mislukt.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Als u de default later wilt verwijderen of wijzigen, dan kunt u dat doen via de systeeminstellingen.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>.twinkle folder kan niet gevonden worden in uw thuis folder.</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>&amp;Profieleditor</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation>Maak gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation>Ed&amp;itor</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation>Dia&amp;mondcard</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation>Bewerk gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation>Starten gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;You can create a Diamondcard account to make worldwide calls to regular and cell phones.&lt;br&gt;&lt;br&gt;Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"obsolete\">&lt;html&gt;U kunt de profieleditor gebruiken om een gebruikersprofiel te maken. Met de profieleditor kunt u diverse instellingen met betrekking tot het SIP protocol, RTP en vele andere zaken wijzigen.&lt;br&gt;&lt;br&gt;Met de wizard kunt u snel een gebruikersprofiel maken. De wizard vraagt u alleen om een aantal essentiële instellingen. Als u een gebruikersprofiel met de wizard maakt, dan kun u deze op een later tijdstip alsnog met de profieleditor wijzigen.&lt;br&gt;&lt;br&gt;U kunt een Diamondcard account aanmaken waarmee u wereldwijd kunt bellen naar vaste en mobiele telefoonnummers.&lt;br&gt;&lt;br&gt;Kies op welke wijze u een gebruikersprofiel wilt maken.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation>&amp;Diamondcard</translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones.</source>\n        <translation type=\"obsolete\">Maak een gebruikersprofiel voor een Diamondcard account. Met een Diamondcard account kunt u wereldwijd bellen naar vaste en mobiele telefoonnummers.</translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation>Maak een gebruikersprofiel voor een Diamondcard account. Met een Diamondcard account kunt u wereldwijd bellen naar vaste en mobiele telefoonnummers en SMS berichten versturen.\n</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"obsolete\">&lt;html&gt;U kunt de profieleditor gebruiken om een gebruikersprofiel te maken. Met de profieleditor kunt u diverse instellingen met betrekking tot het SIP protocol, RTP en vele andere zaken wijzigen.&lt;br&gt;&lt;br&gt;Met de wizard kunt u snel een gebruikersprofiel maken. De wizard vraagt u alleen om een aantal essentiële instellingen. Als u een gebruikersprofiel met de wizard maakt, dan kun u deze op een later tijdstip alsnog met de profieleditor wijzigen.&lt;br&gt;&lt;br&gt;U kunt een Diamondcard account aanmaken waarmee u wereldwijd kunt bellen naar vaste en mobiele telefoonnummers en SMS berichten versturen.&lt;br&gt;&lt;br&gt;Kies op welke wijze u een gebruikersprofiel wilt maken.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Kies gebruiker</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>&amp;Selecteer alles</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>&amp;Wis alles</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation type=\"obsolete\">Gebruiker</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Registreren</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Selecteer welke gebruikers u wilt registreren.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Deregistreren</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Kies de gebruikers die u wilt deregistreren.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Deregistreer alle apparaten</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Kies de gebruikers voor wie u alle apparaten wilt deregistreren.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Niet storen</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Kies de gebruikers voor wie u &apos;niet storen&apos; wilt aanzetten.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Automatisch beantwoorden</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Kies de gebruikers voor wie u &apos;automatisch beantwoorden&apos; wilt aanzetten.</translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation></translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Zend bestand</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Kies het bestand dat u wilt zenden.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Bestand:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>O&amp;nderwerp:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Bestand bestaat niet.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Zend bestand...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation>Twinkle - Doorverwijzen</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Gebruiker:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation>Er zijn 3 doorverwijsdiensten:&lt;p&gt;\n&lt;b&gt;Onvoorwaardelijk:&lt;/b&gt; alle inkomende gesprekken worden doorverwezen\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Bezet:&lt;/b&gt; een inkomend gesprek wordt doorverwezen als beide lijnen bezet zijn\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Geen antwoord:&lt;/b&gt; een inkomend gesprek wordt doorverwezen als u niet opneemt\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation>O&amp;nvoorwaardelijk</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation>&amp;Alle gesprekken doorverwijzen</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation>Activeer de onvoorwaardelijk doorverwijzen.</translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation>Doorverwijzen naar</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>U kan tot 3 bestemmingen invoeren waarnaar u een gesprek wilt doorverwijzen. Als de eerste bestemming niet opneemt, dan wordt de tweede bestemming geprobeerd enz.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3e bestemming:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2e bestemming:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1e bestemming:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Bezet</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation>Gesprekken &amp;doorverwijzen als alle lijnen bezet zijn</translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation>Activeer doorverwijzen bij bezet.</translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>Geen &amp;antwoord</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation>Gesprekken &amp;doorverwijzen als ik niet antwoord</translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation>Activeer doorverwijzen bij geen antwoord.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Sla de huidige instellingen op.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Maak uw wijzigingen ongedaan en sluit het venster.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>U heeft een ongeldige bestemming ingevoerd.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Systeeminstellingen</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Algemeen</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Audio</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Ring tones</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Netwerk</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Log</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Kies de categorie waarvoor u instellingen wilt wijzigen.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Geluidskaart</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Kies de geluidskaart voor het afspelen van de ring tone bij een binnenkomend gesprek.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Kies de geluidskaart waarmee uw microfoon is verbonden.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Kies de geluidskaart waarmee uw speaker of headset verbonden is.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Speaker:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Ring tone:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Ander apparaat:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Microfoon:</translation>\n    </message>\n    <message>\n        <source>When using ALSA, it is not recommended to use the default device for the microphone as it gives poor sound quality.</source>\n        <translation type=\"obsolete\">Als u ALSA gebruikt, dan is het niet aan te raden om het default apparaat als microfoon te gebruiken omdat die een matige geluidskwaliteit heeft.</translation>\n    </message>\n    <message>\n        <source>Reduce &amp;noise from the microphone</source>\n        <translation type=\"obsolete\">&amp;Verminder ruis van de microfoon</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+V</translation>\n    </message>\n    <message>\n        <source>Recordings from the microphone can contain noise. This could be annoying to the person on the other side of your call. This option removes soft noise coming from the microphone.\n\nThe noise reduction algorithm is very simplistic. Sound is captured as 16 bits signed linear PCM samples. All samples between -50 and 50 are truncated to 0.</source>\n        <translation type=\"obsolete\">Opnames van de microfoon kunnen ruis bevatten. Dit kan vervelend zijn voor de persoon aan de andere kant van de lijn. Deze instelling kan zachte ruis van de microfoon verwijderen.\n\nHet algoritme om ruis te verminderen is erg simplistisch. Geluid wordt gedigitaliseerd als 16 bits lineaire PCM samples. Alle samples tussen de waarden -50 en 50 worden afgerond naar 0.</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Expert instellingen</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation>OSS &amp;fragment grootte:</translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation>16</translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation>32</translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation>64</translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation>128</translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation>256</translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation>De ALSA afspeel periode grootte is van invloed op het real time gedrag van uw geluidskaart bij het afspelen van geluid. Als het geluid hapert bij het gebruik van ALSA, dan kunt u een andere waarde proberen.</translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation>ALSA afspeel-&amp;periode grootte:</translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation>&amp;ALSA opneem-periode grootte:</translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation>De OSS fragment grootte is van invloed op het real time gedrag van uw geluidskaart. Als het geluid hapert bij het gebruik van OSS, dan kunt u een andere waarde proberen.</translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation>De ALSA opneem-periode grootte is van invloed op het real time gedrag van uw geluidskaart bij het opnemen van geluid. Als het geluid aan de andere kant van de lijn hapert bij het gebruik van ALSA, dan kunt u een andere waarde proberen.</translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>&amp;Max log grootte:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation>De maximum omvang van het log bestand in MB. Als de log file groter wordt dan deze omvang, dan wordt een backup van de log file gemaakt en wordt de huidige log file gewist. Er wordt slechts één backup log bestand bewaard.</translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation>MB</translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation>Log &amp;debug meldingen</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation>Plaats &quot;debug&quot; meldingen in het log bestand.</translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>Log &amp;SIP meldingen</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation>Plaats SIP meldingen in het log bestand.</translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>Log S&amp;TUN meldingen</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation>Plaats STUN meldingen in het log bestand.</translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation>Log g&amp;eheugen meldingen</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation>Plaats meldingen met betrekking tot geheugenbeheer in het log bestand.</translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Systeemvak</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Maak een icoon in het &amp;systeemvak bij opstarten</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation>Met deze optie wordt er een icoon in het systeemvak geplaatst bij het opstarten van Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation>&amp;Verbergen in systeemvak bij sluiten van het hoofdvenster</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation>Met deze optie verbert Twinkle zich in het systeemvak als u het hoofdvenster sluit.</translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Opstarten</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, this IP address will be automatically selected. This is only useful when your computer has multiple and static IP addresses.</source>\n        <translation type=\"obsolete\">De volgende keer dat u Twinkle opstart wordt dit IP adres automatisch geselecteerd. Dit is alleen handig als uw computer meerdere statische IP adressen heeft.</translation>\n    </message>\n    <message>\n        <source>Default &amp;IP address:</source>\n        <translation type=\"obsolete\">Default &amp;IP adres:</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle, the IP address of this network interface be automatically selected. This is only useful when your computer has multiple network devices.</source>\n        <translation type=\"obsolete\">De volgende keer dat u Twinkle opstart wordt het IP adres van deze netwerk interface automatisch geselecteerd. Dit is alleen handig als uw computer meerdere netwerk interfaces heeft.</translation>\n    </message>\n    <message>\n        <source>Default &amp;network interface:</source>\n        <translation type=\"obsolete\">Default &amp;netwerk interface:</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation>Verborgen ops&amp;tarten in systeemvak</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation>De volgende keer dat u Twinkle opstart, zal Twinkle zich onmiddelijk verbergen in het systeemvak. Dit werkt het beste als u ook een default gebruikersprofiel selecteert.</translation>\n    </message>\n    <message>\n        <source>Default user profiles</source>\n        <translation type=\"obsolete\">Default gebruikersprofielen</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation>Als u altijd dezelfde gebruikersprofielen gebruikt, dan kunt u deze profielen als default markeren. De volgdende keer dat u Twinkle opstart, wordt u dan niet meer gevraagd om een gebruikers profiel te kiezen. De default profielen worden automatisch opgestart.</translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Diensten</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>&amp;Wisselgesprek</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation>Met wisselgesprek kunt een inkomend gesprek ontvangen als slechts één lijn bezet is. Als u wisselgesprek uitschakelt, dan wordt een binnekomend gesprek automatisch geweigerd als één lijn bezet is.</translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation>Hang &amp;beide lijnen op bij het beëindigen van een 3-weg conferentiegesprek.</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation>Hang beide lijnen op als u een 3-weg gesprek beëindigd. Als u deze optie uitschakelt, dan zal alleen de actieve lijn worden opgehangen. U kunt dan doorpraten met de partij op de andere lijn.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation>&amp;Maximum aantal gesrpekken in gesprekshistorie:</translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation>Het maximum aantal gesprekken dat in de gesprekshistorie wordt bewaard.</translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation>Toon hoofdvenster &amp;automatisch bij een inkomend gesprek na</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation>Als het hoofdvenster verborgen is, dan wordt deze automatisch getoond bij een inkomend gesprek na het aantal aangegeven seconden.</translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation>Het aantal seconden waarna het hoofdvenster getoond moet worden.</translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>s</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving SIP messages.</source>\n        <translation type=\"obsolete\">De UDP poort voor het sturen en ontvangen van SIP berichten.</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>&amp;RTP poort:</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation>De UDP poort wordt gebruikt voor het sturen en ontvangen van RTP op de eerste lijn. De UDP poort voor de tweede lijn is 2 hoger. Voorbeeld: als poort 8000 wordt gebruikt voor de eerste lijn, dan wordt 8002 voor de tweede lijn gebruikt. Als u de dienst &quot;doorverbinden&quot; gebruikt dan wordt de volgende even poort (bijv. 8004) ook gebruikt.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP UDP port:</source>\n        <translation type=\"obsolete\">&amp;SIP UDP poort:</translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Ring tone</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>&amp;Speel ring tone bij inkomend gesprek</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Geeft aan dat een ring tone gespeeld moet worden bij een inkomend gesprek.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>&amp;Default reing tone</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Speel de default ring tone bij een inkomend gesprek.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>&amp;Eigen ring tone</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Speel een eigen ring tone bij een inkomend gesprek.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation>De bestandsnaam van een .wav bestand dat u als ring tone wilt afspelen.</translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation>Ring back tone</translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation>S&amp;peel ring back tone als het netwerk geen ring back tone speelt</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nSpeel een ring back tone (de toon die u hoort als u iemand belt) als u wacht op antwoord van de gebelde partij.\n&lt;/p&gt;\n&lt;p&gt;\nAfhankelijk van uw SIP provider, kan het netwerk een ring back tone spelen.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation>De&amp;fault ring back tone</translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation>Speel de default ring back tone.</translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation>Ei&amp;gen ring back tone</translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation>Speel een eigen ring back tone.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation>De bestandsnaam van een .wav bestand dat u als ring back tone wilt afspelen.</translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation>Naam op&amp;zoeken voor een inkomend gesprek</translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation>Gevonden naam heeft voor&amp;rang op ontvangen naam</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation>De beller kan een naam meesturen met een inkomend gesprek. Als u deze optie selecteert, dan zal Twinkle toch de naam uit uw adresboek tonen als deze gevonden wordt.</translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Opzoeken &amp;foto bij inkomend gesprek</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation>Zoek de foto van de beller op in uw adresboek en toon het bij een inkomend gesprek.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Sla de instellingen op.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Maak alle wijzigingen ongedaan en sluit het venster.</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default IP address combo</comment>\n        <translation type=\"obsolete\">geen</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default network interface combo</comment>\n        <translation type=\"obsolete\">geen</translation>\n    </message>\n    <message>\n        <source>Either choose a default IP address or a default network interface.</source>\n        <translation type=\"obsolete\">Kies óf een default IP adres óf een default netwerk interface.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Ring tones</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Kies ring tone</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Ring back tones</translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation>Kies ring back tone</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>&amp;Valideer apparaten voor gebruik</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation>&lt;p&gt;\nTwinkle valideert de geluidsapparaten voor gebruik om te voorkomen dat een gesprek zonder geluidskanaal wordt opgezet.\n&lt;p&gt;\nBij het opstarten geeft Twinkle een waarschuwing als een geluidsapparaat niet beschikbaar is.\n&lt;p&gt;\nAls voor het maken van een gesprek, de microfoon of speaker niet beschikbaar zijn, dan krijgt u een waarschuwing en kunt u het gesprek niet maken.\n&lt;p&gt;\nAls voor het beantwoorden van een inkomend gesprek, de microfoon of speaker onbeschikbaar zijn, dan krijgt u een waarschuwing en kunt u het gesprek niet beantwoorden.</translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation>Bij een inkomend gesprek, probeert Twinkle de naam van de beller op te zoeken in het adresboek. Deze naam wordt dan getoond.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Selecteer ring tone bestand.</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Selecteer ring back tone bestand.</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation>Maximum omvang (0-65535) van een inkomend SIP bericht over UDP in bytes.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>&amp;SIP poort:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation>Max. omvang SIP bericht (&amp;TCP):</translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation>De TCP/UDP poort die wordt gebruikt voor SIP verkeer.</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation>Max. omvang SIP bericht (&amp;UDP):</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation>Maxmimum grootte (0-4294967295) van een SIP bericht over TCP in bytes.</translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation>Command voor opstarten w&amp;eb browser:</translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation>Het commando waamee uw web browser kan worden opgestart. Als u dit veld leeg laat, dan zal Twinkle zelf proberen om uw standaard web browser op te starten.</translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation type=\"unfinished\">512</translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation type=\"unfinished\">1024</translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Antwoord</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Afwijzen</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation>Inkomend gesprek</translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Terminal Eigenschappen</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Van:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Opvragen terminal eigenschappen van</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Aan:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Het adres waarvoor u de eigenschappen (OPTION verzoek) wilt weten. Dit kan een volledig SIP adres zijn zoals &lt;b&gt;sip:example@example.com&lt;/b&gt; of alleen een gebruikersnaam of telefoonnummer. Als u geen volledig adres invoert, dan zal Twinkle het adres afmaken met de waarde die u voor domein heeft ingevuld in uw gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Doorverbinden</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Doorverbinden naar</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Aan:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Het adres waarnaar u wilt doorverbinden. Dit kan een volledig SIP adres zijn zoals &lt;b&gt;sip:example@example.com&lt;/b&gt; of alleen een gebruikersnaam of telefoonnummer. Als u geen volledig adres invoert, dan zal Twinkle het adres afmaken met de waarde die u voor domein heeft ingevuld in uw gebruikersprofiel.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresboek</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Kies een adres uit het adresboek.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Type of transfer</source>\n        <translation type=\"obsolete\">Doorverbindmethode</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>&amp;Blind doorverbinden</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation>Doorverbinden van het gesprek naar een derde partij zonder dat u die derde partij eerst zelf raadpleegt.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation>Doorverbinden met &amp;ruggespraak</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation>Alvorens een gesprek door te verbinden naar een derde partij, houdt u eerst ruggespraak met die partij.</translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation>Doorverbinden naar andere &amp;lijn</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation>Verbind de persoon op deze lijn door met de persoon op de andere lijn.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Folder %1 bestaat niet.</translation>\n    </message>\n    <message>\n        <source>Lock file %1 already exist, but cannot be opened.</source>\n        <translation type=\"obsolete\">Lock bestand %1 bestaat al, maar kan niet geopend worden.</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>%1 is al actief.\nLock bestand %2 bestaat al.</translation>\n    </message>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Aanmaken van log bestand %1 mislukt.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Bestand kan niet geopend worden om te lezen: %1</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Bestandssysteem fout tijdens lezen van file %1 .</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Bestand niet geopend worden om te schrijven: %1</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Bestandssysteem fout tijdens schrijven van bestand %1 .</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>Excessief aantal socket fouten.</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Gebouwd met ondersteuning voor:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>Bijdragen:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Deze software bevat de volgende software van derden:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation>* GSM codec van Jutta Degener en Carsten Bormann, Universiteit van Berlijn</translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation>* G.711/G.726 codecs van Sun Microsystems (public domain)</translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation>* iLBC implementatie uit RFC 3951 (www.ilbcfreeware.org)</translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation>* Delen van het STUN project op http://sourceforge.net/projects/stun</translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation>* Delen van libsrv op http://libsrv.sourceforge.net/</translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation>Voor RTP zijn de volgende dynamische libraries gelinkt:</translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Nederlandse vertaling door Michel de Boer</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Betand %1 kan niet geopend worden.</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>%1 heeft niet de waarde van uw thuis folder.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Folder %1 (%2) bestaat niet.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Folder %1 kan niet aangemaakt worden.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>%1 kan niet aangemaakt worden.</translation>\n    </message>\n    <message>\n        <source>Cannot write to %1 .</source>\n        <translation type=\"obsolete\">Kan niet schrijven naar %1 .</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Syntactische fout in bestand %1 .</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Backuppen van %1 naar %2 mislukt</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>naam onbekend (apparaat bezet)</translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Default apparaat</translation>\n    </message>\n    <message>\n        <source>Anonymous</source>\n        <translation>Anoniem</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Waarschuwing:</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation>Doorverbinden - %1</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>Geluidskaart werkt niet in full duplex modus.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>De buffergrootte van de geluidskaart kan niet ingesteld worden.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Geluidskaar ondersteunt geen %1 kanalen.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation>Geluidskaart ondersteunt geen 16 bits opname.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation>Geluidskaar ondersteunt geen 16 bits afspelen.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation>Geluidskaar ondersteunt sampling rate %1 niet</translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation>Openen ALSA stuurprogramma mislukt</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation>ALSA stuur programma voor PCM afspelen kan niet geopend worden</translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>IP adres van STUN server niet gevonden: %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>U bevindt zich achter een symmetrische NAT.\nSTUN zal niet werken.\nConfigureer uw publieke IP adres in uw gebruikersprofiel\nen creëer de volgende statische UDP mapping in uw NAT.</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>publiek IP: %1 --&gt; privé IP: %2 (SIP signalering)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>publiek IP: %1-%2 --&gt; privé IP: %3-%4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>STUN server onbereikbaar: %1</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Poort %1 (SIP signalering)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>Verkenning van NAT type via STUN is mislukt.</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Als u zich achter een firewall bevindt dan moet u de volgende UDP poorten open zetten.</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Poorten %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation>Ring tone apparaat niet beschikbaar (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>Speaker niet beschikbaar (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>Microfoon niet beschikbaar (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>ALSA stuurapparaat kan niet geopend worden voor openemen</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Kan geen inkomende TCP verbindingen ontvangen.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Creëren van bestand %1 mislukt</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Schrijven naar bestand %1 mislukt</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Zenden bericht mislukt.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation>Kan geen lock zetten op %1 .</translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Gebruikersprofiel</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Gebruikersprofiel:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Kies het gebruikersprofiel dat u wilt bewerken.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Gebruiker</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>SIP server</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP audio</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>SIP protocol</translation>\n    </message>\n    <message>\n        <source>NAT</source>\n        <translation type=\"obsolete\">NAT</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Adresformaat</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation>Timers</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Ring tones</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation>Scripts</translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Beveiliging</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Kies de categorie die u wilt bewerken.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Bewaar uw wijzigingen.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Maak alle wijzigingen ongedaan en sluit het venster.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>SIP account</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>Ge&amp;bruikersnaam*:</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation type=\"vanished\">&amp;Domein*:</translation>\n    </message>\n    <message>\n        <source>Or&amp;ganization:</source>\n        <translation type=\"vanished\">Or&amp;ganisatie:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>De SIP gebruikersnaam die u van uw provider heeft gekregen. Dit het gebruikersdeel in uw SIP adres, &lt;b&gt;gebruikersnaame&lt;/b&gt;@domein.nl Dit kan een telefoonnummer zijn.\n&lt;br&gt;&lt;br&gt;\nDit is een verplicht veld.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Dit is het domein deel van uw SIP adres, gebruikersnaame@&lt;b&gt;domein.nl&lt;/b&gt;. In plaats van een echt domein kan dit ook de host naam of het IP van uw &lt;b&gt;SIP proxy&lt;/b&gt; zijn. Als u direct van IP adres naar IP adres wilt bellen, dan vult u hier de host naam of IP adres van uw computer in.\n&lt;br&gt;&lt;br&gt;\nDit is een verplicht veld.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>Hier kunt u de naam van uw organisatie invullen. Als u iemand belt, dan kan dit getoond worden.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Dit is uw eigen naam. bijv. Jan Jansen. Als u iemand belt, kan deze naam getoond worden.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>U&amp;w naam:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>SIP authenticatie</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation>&amp;Realm:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Paswoord:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation>De authenticatie realm. Deze waarde moet verstrekt worden door uw SIP provider. Als u dit veld leeg laat, dan zal Twinkle automatisch de realm gebruiken die de SIP proxy stuurt. Als u de realm niet weet, laat dit veld dan leeg.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Uw SIP gebruikersnaam voor authenticatie. Meestal is dit hetzelfde als uw SIP gebruikersnaam.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Uw paswoord voor authenticatie.</translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation>Registratie server</translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation>&amp;Registratie server:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation>De host naam, domein naam of IP adres van uw registratie server. Als u een uitgaande proxy gebruikt die tevens uw registratie server is, dan kunt u dit veld leeg laten en alleen het adres voor de uitgaande proxy invullen.</translation>\n    </message>\n    <message>\n        <source>&amp;Expiry:</source>\n        <translation type=\"vanished\">&amp;Interval:</translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation>Het registratie interval dat Twinkle zal aanvragen.</translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>sec</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Re&amp;gistreer tijdens opstarten</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation>Geeft aan of Twinkle zich automatisch moet registrerent als u dit gebruikersprofiel start. U moet deze optie uitschakelen als u direct van IP adres naar IP adres wilt bellen zonder tussenkomst van een SIP proxy.</translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation>Uitgaande Proxy</translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>&amp;Gebruik uitgaande proxy</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation>Geeft aan dat Twinkle een uitgaande proxy moet gebruiken. Als een uitgaande proxy gebruikt wordt, dan worden alle SIP berichten naar die proxy gestuurd. Zonder uitgaande proxy, zal Twinkle zelf proberen om een SIP adres naar een IP adres te veralen.</translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>Uitgaande &amp;proxy:</translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation>&amp;Stuur &quot;in-dialog SIP request&quot; naar de proxy</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation>SIP verzoeken binnen een SIP dialog worden normaliter naar het adres uit de contact-header gestuurd. Dit adres wordt tijdens de gespreksopbouw bepaald. Als u deze optie aanvinkt, dan zal Twinkle dat adres negeren en zullen in-dialog requests ook naar de uitgaande proxy gestuurd worden.</translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation>Stuur een &amp;verzoek niet naar de proxy als de bestemming lokaal bepaald kan worden.</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation>Als u deze optie aanvinkt, dan zal Twinkle eerst zelf proberen om een SIP adres naar een IP adres te vertalen. Als dat lukt, dan zal het SIP verzoek naar dat IP adres worden gestuurd. Als dat niet lukt dan wordt het verzoek naar de proxy gestuurd (letop: als het om een in-dialog request gaat, dan moet ook de voorgaande optie aan staan, als u wilt dat deze naar de proxy gaat.)</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Host naam, domein of IP adres van uw uitgaande proxy.</translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation>Codecs</translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Beschikbare codecs:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation>G.711 A-law</translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation>G.711 u-law</translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation>GSM</translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation>speex-nb (8 kHz)</translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation>speex-wb (16 kHz)</translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation>speex-uwb (32 kHz)</translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Lijst met beschikbare codecs.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Verplaats een codec van de lijst met beschikbare codecs naar de lijst met actieve codecs.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Verplaats een codec van de lijst met actieve codecs naar de lijst met beschikbare codecs.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Actieve codecs:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation>Lijst met actieve codecs. Deze codecs worden gebruikt in de media onderhandeling tijdens de gespreksopbouw. De volgorde van de codecs geeft de voorkeur aan.</translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation>Schuif een codec omhoog in de lijst met actieve codec, d.w.z. verhoog de voorkeur.</translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation>Verschuif een codec omlaag in de lijst met actieve codecs, d.w.z. verlaag de voorkeur.</translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation>&amp;G.711/G.726 payload grootte:</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation>De gewenste payload grootte voor de G.711 en G.726 codecs.</translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation>ms</translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation>&amp;iLBC</translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation>iLBC</translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation>i&amp;LBC payload type:</translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation>iLBC &amp;payload grootte (ms):</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor iLBC.</translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation>20</translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation>30</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation>De gewenste payload grootte voor iLBC.</translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation>&amp;Speex</translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation>Speex</translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation>Perceptual &amp;enhancement</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation>Perceptual enhancement tracht de ruis geproduceert door het coderings/decoderings proces te verminderen (een subjectieve verbetering).</translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation>U&amp;ltra wide band payload type:</translation>\n    </message>\n    <message>\n        <source>&amp;VAD</source>\n        <translation type=\"obsolete\">&amp;VAD</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the audio being encoded is speech or silence/background noise. VAD is always implicitly activated when encoding in VBR, so the option is only useful in non-VBR operation. In this case, Speex detects non-speech periods and encode them with just enough bits to reproduce the background noise. This is called &quot;comfort noise generation&quot; (CNG).</source>\n        <translation type=\"obsolete\">Voice activity detection detecteert of de opgenomen audio spraak of stilte dan wel achtergrondruis is. VAD staat atlijd impliciet aan als VBR wordt gebruikt. VAD is dus alleen nutting als VBR uitstaat. De Speex codec stuurt dan slechts een paar bits tijdens stilte periodes. Dit heet comfort noise generation (CNG).</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation>&amp;Wide band payload type:</translation>\n    </message>\n    <message>\n        <source>V&amp;BR</source>\n        <translation type=\"obsolete\">V&amp;BR</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation>Met variable bit-rate (VBR) past de codec de bit-rate dynamisch aan aan de complexiteit van de opgenomen audio.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor speex wide band.</translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation>Co&amp;mplexiteit:</translation>\n    </message>\n    <message>\n        <source>DT&amp;X</source>\n        <translation type=\"obsolete\">DT&amp;X</translation>\n    </message>\n    <message>\n        <source>Alt+X</source>\n        <translation type=\"obsolete\">Alt+X</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation>Discontinuous transmission (DTX) is een extra optie boven op VAD. Met DTX wordt helemaal niets gestuurd tijdens stilte periodes.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor speex narrow band.</translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation>Voor Speex kan de complexiteit van de encoder ingesteld worden. Deze instelling is vergelijkbaar met de -1 tot -9 opties voor gzip en bzip2 compressie. Voor normaal gebruik is het ruisniveau bij complexiteit 1 tussen de 1 en 2 dB hoger dan bij complexiteit 10. De benodigde CPU kracht is bij complexiteit 10 ongeveer 5 keer zo hoog als bij complexiteit 1. In de praktijk is een complexiteit tussen 2 en 4 genoeg. Hogere complexiteit kan nuttig zijn bij het versturen van niet-spraak audio, bijvoorbeeld DTMF tonen.</translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation>&amp;Narrow band payload type:</translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation>G.726</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation>G.726 &amp;40 kbps payload type:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor G.726 40 kbps.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor G.726 32 kbps.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation>G.726 &amp;24 kbps payload type:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor G.726 24 kbps.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation>G.726 &amp;32 kbps payload type:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation>Het dynamische payload type (96 of hoger) voor G.726 16 kbps.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation>G.726 &amp;16 kbps payload type:</translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation>DT&amp;MF</translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation>Het dynamische payload type (96 of hoger) voor DTMF (RFC 2833).</translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation>DTMF vo&amp;lume:</translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation>Het volume van de DTMF toon in dB.</translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation>Pauze na het sturen van een DTMF toon.</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation>DTMF &amp;duur:</translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation>DTMF payload &amp;type:</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation>DTMF &amp;pauze:</translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation>dB</translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation>Duur van een DTMF toon.</translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>DTMF t&amp;ransport:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation>Auto</translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation>RFC 2833</translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation>Inband</translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation>Out-of-band (SIP INFO)</translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Stuur DTMF tonen als RFC 2833 events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Stuur DTMF inband toon.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;Als de andere partij RFC 2833 ondersteund dan wordt RFC 2833 gebruikt, anders wordt de DTMF toon inband gestuurd.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nStuur DTMF out-of-band in een SIP INFO verzoek.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Algemeen</translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation>Doorverwijzen</translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation>St&amp;a doorverwijzen toe</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation>&amp;Vraag toestemming voor doorverwijzen</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation>Geeft aan of Twinkle de gebruiker om toestemming moet vragen alvorens een verzoek door te verwijzen bij een 3XX antwoord.</translation>\n    </message>\n    <message>\n        <source>Max re&amp;directions:</source>\n        <translation type=\"vanished\">Max &amp;doorverwijzingen:</translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation>Het maximaal aantal doorverwijzingen dat Twinkle probeert om een verzoek af te leveren. Dit maximum voorkomt dat een verzoek eeuwig wordt doorverwezen.</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Protocol opties</translation>\n    </message>\n    <message>\n        <source>Call &amp;Hold variant:</source>\n        <translation type=\"vanished\">Wac&amp;ht variant:</translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation>RFC 2543</translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation>RFC 3264</translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation>Geeft aan of RFC 2543 (zet media IP adres in SDP op 0.0.0.0) of RFC 3264 (gebruikt &quot;direction&quot; attribuut in SDP) gebruikt moet worden om een gesprek in de wacht te plaatsen.</translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation>Ontbrekende Contact header &amp;in 200 OK op REGISTER toegestaan</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation>Een 200 OK antwoord op een REGISTER verzoek moet een Contact header bevatten. Sommige registratie servers stoppen echter geen of een foutieve Contact header de 200 OK. Met deze optie staat u een dergelijke afwijking van de specificaties toe.</translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation>&amp;Max-Forwards header verplicht</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation>Volgens RFC 3261 is de Max-Forwards header verplicht. Veel implementaties sturen deze header echter niet. Als u deze optie aanvinkt, dan zal Twinkle een SIP verzoek zonder Max-Forwards weigeren.</translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation>&amp;Registratie interval in contact header</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation>In een REGISTER verzoek kan het registratie interval zowel in de Contact header als in de Expires header geplaatst worden. Als u deze optie aanvinkt, dan wordt het interval in de Contact header geplaatst, anders in de Expires header.</translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation>&amp;Compacte header namen</translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation>Geeft aan of compacte header namen gebruikt moeten worden voor headers die een compacte naam hebben.</translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation>SDP wijziging tijdens gespreksopbouw toestaan</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Een SIP UAS kan SDP in een 1XX antwoord sturen voor bijvoorbeeld een ring back tone. Als het gesprek beantwoord wordt, dan moet de SIP UAS dezelfde SDP in de 200 OK sturen volgens RFC 3261: &lt;i&gt;Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/i&gt;&lt;/p&gt;\n&lt;p&gt;Door een SDP wijziging toe te staan, zal Twinkle de SDP in de 200 OK niet negeren in dit geval en de media parameters aanpassen. Als de SDP wijzigt, dan moet die wel een nieuwe versienummer in de o= lijn hebben.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nTwinkle maakt een unieke contact header waarde door de SIP gebruikersnaam te combineren met de domeinnaam:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;gebruikersnaam_domeinnaam@ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nZo krijgen 2 gebruikersprofielen met dezelfde gebruikersnaam, maar verschillende domeinnamen, toch een uniek contact adres. Hierdoor kunnen beide profielen tegelijkertijd geactiveerd worden.\n&lt;/p&gt;\n&lt;p&gt;\nSommige proxies vinden dit niet leuk. U kunt deze optie uitzetten voor een meer gangbare contact header:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;gebruikersnaam@ip&lt;/tt&gt;\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation>Cod&amp;eer Via, Route, Record-Route als lijst</translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation>De Via, Route en Record-Route headers kunnen als een lijst met door komma&apos;s gescheiden waarden worden gecodeerd of als meerdere voorkomens van dezelfde header.</translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation>SIP extensies</translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation>&amp;100 rel (PRACK):</translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>uitgeschakeld</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>ondersteund</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>vereist</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>voorkeur</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation>Geeft aan of de 100rel extensie (PRACK) wordt ondersteund:\n&lt;b&gt;uitgeschakeld&lt;/b&gt;: 100rel extensie is uitgeschakeld\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;ondersteund&lt;/b&gt;: 100rel wordt ondersteund (wordt toegevoegd aan de supported header in een INVITE).\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;vereist&lt;/b&gt;: 100rel is vereist (wordt toegevoegd aan de require header in een INVITE). Als een inkomende INVITE ondersteuning aangeeft voor 100rel, dan zal Twinkle een PRACK eisen bij het sturen van een 1XX antwoord. Een gesprek mislukt als de andere partij 100rel niet ondersteund.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;voorkeur&lt;/b&gt;: Vergelijkbaar met &lt;b&gt;vereist&lt;/b&gt;, maar als het gesprek mislukt omdat de andere partij 100rel niet ondersteund (420 antwoord), dan wordt het gesprek nogmaals opgezet zonder de 100rel eis.</translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation>REFER</translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation>Doorverbinden (REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call &amp;transfer (incoming REFER)</source>\n        <translation type=\"obsolete\">Doorverbinden &amp;toestaan (inkomende REFER)</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation>Geeft aan of Twinkle een gesprek moet doorverbinden als een REFER verzoek wordt ontvangen.</translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation>&amp;Vraag toestemming voor doorverbinden</translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation>Geeft aan of Twinkle de gebruiker om toestemming moet vragen alvorens een gesprek door te verbinden als een REFER verzoek ontvangen wordt.</translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation>Zet de partij die u doorverbindt in de &amp;wacht tijdens doorverbinden</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation>Geeft aan of Twinkle het huidige gesprek in de wacht moet zetten als een REFER verzoek is ontvangen.</translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation>&amp;Zet andere partij in de wacht voor sturen REFER</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation>Geeft aan of Twinkle de andere partij in de wacht moet zetten als u een gesprek doorverbindt.</translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation>Automatisch verversen van re&amp;fer subscriptie tijdens doorverbinden</translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation>Alt+F</translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation>Tijdens doorverbinden, stuurt de partij die wordt doorverbonden NOTIFY berichten naar de partij die doorverbindt. Deze NOTIFY berichten geven de voortgang van het doorverbinden aan. Deze berichten worden voor een bepaalde tijdsduur gestuurd. Als u deze optie aanvinkt, dan zal Twinkle automatisch een SUBSCRIBE verzoek sturen om de tijdsduur te verlengen als deze verloopt voordat het doorverbinden klaar is.</translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>NAT oplossing</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation>Geen &amp;NAT</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation>Kies deze optie als er geen router met netwerk adres translatie (NAT) zit tussen u en uw SIP proxy of als uw SIP provider &quot;hosted NAT traversal&quot; biedt.</translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation>St&amp;atisch publiek IP adres in SIP berichten</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation>Geeft aan of Twinkle een statisch IP adres in de SIP berichten moet plaatsen, bijv. in headers en SDP in plaats van het prive IP adres van uw netwerk interface.&lt;br&gt;&lt;br&gt;\nAls u deze optie kiest, dan moet u teven een adres vertaling in uw NAT router aanbrengen. U moet dezelfde RTP poorten op het publieke en prive adres gebruiken.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN</source>\n        <translation type=\"obsolete\">&amp;STUN</translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation>Kies deze optie als uw SIP provider een STUN server aanbiedt.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation type=\"vanished\">S&amp;TUN server:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Host naam, domeinnaam of IP adres van de STUN server.</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>&amp;Publiek IP adres:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation>Het publieke IP adres van uw NAT router.</translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Telefoonnummers</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation>Toon alleen gebruikers&amp;deel van een URI voor een telnr</translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation>Als een URI een telefoonnummer is, toon dan alleen het gebruikersdeel. Voorbeeld: een gesprek komt binnen van sip:123456@twinklephone.com, dan wordt alleen &quot;123456&quot; getoond. Een URI is een telefoonnummer als het de parameter &quot;user=phone&quot; bevat of als het gebruikersdeel numeriek is en u vinkt de volgende optie aan.</translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation>URI met &amp;numeriek gebruikersdeel is een telefoonnumer</translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation>Als u deze optie aanvinkt, dan ziet Twinkle een SIP adres dat bestaat uit cijfers, *, #, + en speciale symbolen als een telefoonnummer. In een uitgaand bericht, zal Twinkle dan de parameter &quot;user=phone&quot; toevoegen.</translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation>Ve&amp;rwijder speciale symbolen van numerieke strings</translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation>Telefoonnummers worden vaak opgeschreven met speciale symbolen zoals streepjes voor de leesbaarheid. Als u een dergelijk nummer draait, dan moeten de speciale symbolen niet gedraaid worden. Om ervoor te zorgen dat u makkelijke telefoonnumers kan knippen en plakken naar Twinkle, kan Twinkle deze symbolen verwijderen als u op de &quot;bel&quot; knop drukt.</translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation>&amp;Speciale symbolen:</translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation>De speciale symbolen die in een telefoonnummer kunnen staan voor de leesbaarheid, maar die verwijderd moeten worden bij het bellen.</translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation>Nummer conversie</translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation>Match expressie</translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Vervang</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telphone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation type=\"vanished\">&lt;p&gt;\nVaak is het formaat van een telefoonnummer dat u moet draaien anders dan het formaat van het nummer in uw adresboek, bijv. uw nummers starten met een +-teken gevolgd door een landencode, maar uw provider verwacht &apos;00&apos; in plaats van het +-teken, of u bent op kantoor en u moet eerst een &apos;9&apos; draaien om naar buiten te bellen. Hier kunt u nummerformaatconversie definieren m.b.v. reguliere expressies en vervang strings (Perl syntax).\n&lt;/p&gt;\n&lt;p&gt;\nVoor elk nummer dat u draait probeer Twinkle een match te vinden in de lijst met match expressies. Voor de eerste match die gevonden wordt, wordt het nummer vervangen door de vervang string. Als er geen match is, dan blijft het nummer onveranderd.\n&lt;/p&gt;\n&lt;p&gt;\nNummerconversie wordt ook toegepast op inkomende gesprekken, zodat de nummers getoond worden zoals u dat wilt.\n&lt;/p&gt;\n&lt;h3&gt;Voorbeeld 1&lt;/h3&gt;\n&lt;p&gt;\nUw landencode is 31 en u heeft alle nummers in uw adresboek in volledig internationaal formaat opgeslagen, bijv. +318712345678. Om nummers binnen Nederland te draaien wilt u de &apos;+31&apos; vervangen door &apos;0&apos;. Voor het draaien van buitenlandse nummers wilt u de &apos;+&apos; vervangen door &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nDe volgende match/vervang regels doen dit voor u:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expressie = \\+31([0-9]*) , Vervang =  0$1&lt;br&gt;\nMatch expressie = \\+([0-9]*) , Vervang = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Voorbeeld 2&lt;/h3&gt;\n&lt;p&gt;\nU bent op kantoor en alle nummers met een 0 moeten voorafgegaan worden door een 9 voor een buitenlijn.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expressie = 0[0-9]* , Vervang =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation>Schuif de geselecteerde conversie omhoog in de lijst.</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation>Schuif de geselecteerde conversie omlaag in de lijst.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>To&amp;evoegen</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation>Voeg een nummerconversie toe.</translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>&amp;Verwijder</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation>Verwijder de geselcteerde nummerconversie.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Bewerk</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation>Bewerk de geselecteerde nummerconversie.</translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation>Voer een telefoonnummer in en druk op de Test-knop om te zien hoe het nummer geconverteerd wordt door de lijst van nummerconversies.</translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Test</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation>Test hoe een nummer geconverteerd wordt door de nummerconversies.</translation>\n    </message>\n    <message>\n        <source>for STUN</source>\n        <translation type=\"obsolete\">STUN</translation>\n    </message>\n    <message>\n        <source>Keep alive timer for the STUN protocol. If you have enabled STUN, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"obsolete\">Keep alive timer voor het STUN protocol. Als u STUN aan heeft gezet, dan zal Twinkle keep alive pakketjes sturen met deze snelheid om de adresbindingen in uw NAT router in leven te houden.</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation>Als een gesprek binnenkomt, dan wordt deze timer gestart. Als de gebruiker antwoordt, dan wordt de timer gestopt. Als de timer afloopt voordat de gebruiker antwoordt, dan weigert Twinkle het gesprek met &quot;480 User Not Responding&quot;.</translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation>NAT &amp;keep alive:</translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>Gee&amp;n antwoord:</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation>Ring &amp;back tone:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nNaam van het .wav bestand dat u gespeeld wilt hebben als ring back tone voor dit gebruikersprofiel.\n&lt;/p&gt;\n&lt;p&gt;\nDeze ring back tone heeft voorrang boven de ring back tone in de systeeminstellingen.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nNaam van het .wav bestand dat u gespeeld wilt hebben als ring tone voor dit gebruikersprofiel.\n&lt;/p&gt;\n&lt;p&gt;\nDeze ring back tone heeft voorrang boven de ring tone in de systeeminstellingen.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Ring tone:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als u een gesprek beëindigd.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de uitgaande SIP BYE verzoek worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; bevat request-URI van de BYE. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als een inkomend gesprek mislukt.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\nDe waarden van alle SIP headers van de uitgaande SIP fout indicatie worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; bevat de status code de fout indicatie. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; bevat de foutmelding. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als de andere partij het gesprek beëindigd.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de inkomende SIP BYE worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; bevat de request-URI van de BYE. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als de andere partij uw gesprek beantwoordt.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de inkomende 200 OK worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; bevat de status melding. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als u een inkomend gesprek beantwoordt.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de uitgaande 200 OK worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; bevat de status melding. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation>Gesprek beëindigd &amp;door uzelf:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als een uitgaand gesprek mislukt.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de inkomende SIP foutmelding worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; bevat de foutcode. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; bevat de foutmelding. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nDit script wordt aangeroepen als u een nummer draait.\n&lt;/p&gt;\n&lt;h2&gt;Variabelen&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de uitgaande SIP INVITE worden via variabelen aan uw script doorgegeven.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; bevat request-URI van de INVITE. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation>Uitgaand gesprek bea&amp;ntwoord:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation>Inkomend gesprek &amp;mislukt:</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>&amp;Inkomend gesprek:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation>Gesp&amp;rek beëindigd door ander:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation>Inkomend gesprek be&amp;antwoord:</translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>Ui&amp;tgaand gesprek:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation>Uit&amp;gaand gesprek mislukt:</translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>ZRTP/SRTP &amp;encryptie</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unecrypted.</source>\n        <translation type=\"vanished\">Als u ZRTP/SRTP aanzet, dan zal Twinkle proberen om het audiokanaal van uw gesprekken te versleutelen. Versleuteling lukt alleen als uw gesprekspartner ook ZRTP/SRTP ondersteunt. Als uw gesprekspartner geen ZRTP/SRTP ondersteund, dan blijft het audiokanaal onversleuteld.</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>ZRTP instellingen</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation>Allee&amp;n versleutelen als de andere partij ZRTP ondersteuning in SDP aangeeft</translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation>Een SIP toestel kan tijdens de gespreksopbouw aangeven of het ZRTP ondersteunt. Met deze optie zal Twinkle de audio alleen proberen te versleutelen als de andere partij ondesteuning voor ZRTP gesignaleerd heeft.</translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation>S&amp;ignaleer ZRTP ondersteuning in SDP</translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation>Twinkle zal tijdens gespreksopbouw aangeven dat het ZRTP ondersteunt.</translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation>&amp;Popup waarschuwing als de andere partij versleuteling uitzet</translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation>Als de andere partij tijdens een versleuteld gesprek een ZRTP go-clear commando stuurt om de versleuteling te stoppen, dan zal Twinkle een waarschuwing geven.</translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation>Dynamische payload type %1 is meer malen gebruikt.</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>U moet een gebruikersnaam voor uw SIP account invullen.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>U moet een domeinnaam voor uw SIP account invullen.\nDit kan de host naam of IP adres van uw PC zijn als u direct van PC naar PC wilt bellen.</translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Ongeldige gebruikersnaam.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Ongeldig domein.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation>Ongeldige waarde voor de registratie server.</translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation>Ongeldige waarde voor de uitgaande proxy.</translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation>Publiek IP adres ontbreekt.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Ongeldige waarde voor STUN server.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Ring tones</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Kies ring tone</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Ring back tones</translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Alle bestanden</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation>Kies script voor inkomende gesprekken</translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation>Kies script voor beantwoording inkomend gesprek</translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation>Kies script voor mislukken inkomend gesprek</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation>Kies script voor uitgaande gesprekken</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation>Kies script voor beantwoording uitgaande gesprek</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation>Kies script voor mislukken uitgaande gesprek</translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation>Kies script voor beëindigen gesprek door uzelf</translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation>Kies script voor beëindigen gesprek door ander</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation>Co&amp;decs</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation>Geeft aan of Twinkle een verzoek moet doorverwijzen als een 3XX antwoord wordt ontvangen.</translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation>Authenticatie &amp;naam:</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Voice mail</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation>&amp;Volg codec voorkeur van de beller bij inkomende gesprekken</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation>&lt;p&gt;\nVolg de voorkeur van de beller (SDP aanbod) bij inkomende gesprekken. Neem de eerste codec uit het SDP aanbod dat ook in de lijst van actieve codecs voorkomt.\n&lt;p&gt;\nAls u deze optie uitschakeld, dan neemt Twinkle de eerste codec uit de actieve codec lijst die ook in het SDP aanbod voorkomt.</translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation>Volg &amp;codec voorkeur van de gebelde bij uitgaande gesprekken</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation>&lt;p&gt;\nVolg de voorkeur van de gebelde (SDP antwoord) bij uitgaande gesprekken. Neem de eerste codec uit het SDP antwoord dat ook in de lijst van actieve codecs voorkomt.\n&lt;p&gt;\nAls u deze optie uitschakelt, dan neemt Twinkle de eerste codec uit de actieve codec lijst die ook in het SDP antwoord voorkomt.</translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Replaces</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extenstion is supported.</source>\n        <translation type=\"vanished\">Geeft aan of de Replaces-extensie ondersteund wordt.</translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation>Begeleid doorverbinden maar AoR (Address of Record)</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endoint.</source>\n        <translation type=\"vanished\">Bij begeleid doorverbinden, is de contact URI de doorverbindbestemming. Een contact URI kan echter niet globaal routeerbaar zijn. Als alternatief kan dan de AoR (Address of Record) gebruikt worden. Een nadeel van het gebruik van de AoR is dat deze routeerbaar kan zijn naar meerdere eindpunten in het geval van SIP forking. De contact URI routeert altijd naar een uniek eindpunt.</translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Privacy</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Privacy opties</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation>&amp;Stuur P-Preferred-Identity header bij anonieme gesprekken</translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Stuur de P-Preferred-Identity header in een INVITE verzoek, als u uw identiteit verbergt bij het maken van een gesprek.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nU kunt het gedrag waarmee Twinkle inkomende gesprekken afhandelt aanpassen met een script dat Twinkle aanroept als een gesprek binnenkomt. Afhankelijk van de output van het script accepteert of weigert Twinkle het gesprek of verwijst het door.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Let op:&lt;/b&gt; Twinkle staat stil als het script loopt. Het is aanbevolen dat uw script niet langer dan 200 ms loopt. Als u meer tijd nodig heeft, dan kunt u de parameters sturen gevolgd door &lt;b&gt;end&lt;/b&gt;. Twinkle gaat verder zodra het &lt;b&gt;end&lt;/b&gt; ontvangt, terwijl u script blijft draaien.\n&lt;/p&gt;\n&lt;p&gt;\nU kunt Twinkle sturen door de volgende parameters naar stdout te schrijven. Elk op een nieuwe regel.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;adres voor doorverwijzen&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;toon deze naam&amp;gt;&lt;br&gt;\nringtone=&amp;lt;naam van .wav bestand&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;toon bericht op scherm&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - handel gesprek af op normale wijze&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - weiger gesprek&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - weiger gesprek met niet-storen indicatie&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - verwijs gesprek door naar &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatisch antwoorden&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nAls een script geen actie op stdout zet, dan is de actie &quot;continue&quot;\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nMet de reason parameter, zet u de SIP reason string voor reject of dnd. Dit kan getoond worden aan de gebruiker.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nToon deze naam in plaats van de display naam.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nDe ring tone die gespeeld moet worden als de actie &quot;continue&quot; is.\n&lt;/p&gt;\n&lt;h2&gt;Variables&lt;/h2&gt;\n&lt;p&gt;\nDe waarden van alle SIP headers van de inkomende INVITE worden via variabelen aan uw script doorgegeven. De variabele namen zijn als volgt samengesteld &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; Bijv. SIP_FROM bevat de waarde van de from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; bevat de request-URI van de INVITE. &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; bevat de gebruikersprofielnaam.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation>&amp;Voice mail adres:</translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation>SIP adres of telefoonnummer van uw voice mail.</translation>\n    </message>\n    <message>\n        <source>Unsollicited</source>\n        <translation type=\"vanished\">Unsollicited</translation>\n    </message>\n    <message>\n        <source>Sollicited</source>\n        <translation type=\"vanished\">Sollicited</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsollicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsollicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Sollicited&lt;/H3&gt;\n&lt;p&gt;\nSollicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation type=\"vanished\">&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nAls uw provider de dienst aanbiedt waarmee u uw voice mail status kunt zien, dan kan Twinkle laten zien hoeveel nieuwe voice mail berichten er op u wachten. Er zijn 2 methoden waarop deze dienst kan worden aangeboden.\n&lt;/p&gt;\n&lt;H3&gt;Unsollicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk biedt unsollicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Sollicited&lt;/H3&gt;\n&lt;p&gt;\nSollicited message waiting indication zoals gespecificeerd in RFC 3842.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation>&amp;MWI type:</translation>\n    </message>\n    <message>\n        <source>Sollicited MWI</source>\n        <translation type=\"vanished\">Sollicited MWI</translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation>Aanmeldings&amp;duur:</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation>Mailbox &amp;gebruikersnaam:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation>De host naam, domeinnaam of IP adres van uw voice mailbox server.</translation>\n    </message>\n    <message>\n        <source>For sollicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation type=\"vanished\">Twinkle meldt zich voor een bepaalde periode aan bij de voice mailbox server. Net voordat deze periode verstrijkt, zal Twinkle zich opnieuw aanmelden.</translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation>Uw gebruikersnaam voor toegang tot uw mailbox.</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation>Mailbox &amp;server:</translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation>Via uitgaande &amp;proxy</translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation>Vink deze optie aan als Twinkle de SIP berichten naar de mailbox server via de uitgaande proxy moet sturen.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation>U moet een mailbox gebruikersnaam invullen.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation>U moet een mailbox server invullen</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation>Ongeldige mailbox server.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation>Ongeldige mailbox gebruikersnaam.</translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation>Codeword &amp;packing volgorde:</translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation>RFC 3551</translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation>ATM AAL2</translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation>Er zijn 2 methoden om G.726 codewords in een RTP pakket te stoppen. RFC 3551 is de default methode. Sommige SIP apparaten gebruiken de ATM AAL2 methode echter. Als bij het gebruik van G.726 met RFC 3551 packing de geluidskwaliteit slecht is, probeer dan ATM AAL2 packing.</translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation>&amp;Gebruik domeinnaam voor een unieke contact header</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Selecteer ring back tone bestand.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Selecteer ring tone bestand.</translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation>Selecteer script bestand.</translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>%1 wordt geconverteerd naar %2</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Instant bericht</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Beschikbaarheid</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation>&amp;Maximum aantal sessies:</translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation>Als u het maximum aantal berichtensessies actief heeft, dan zullen inkomende berichten voor nieuwe sessies geweigerd worden.</translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Uw beschikbaarheid</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>&amp;Publiceer beschikbaarheid bij opstarten</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Publiceer uw beschikbaarheid bij opstarten.</translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation>Beschikbaarheid van vrienden</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation>Publicatie &amp;interval (sec):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation>Verversingssnelheid van beschikbaarheidspublicaties.</translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation>Aan&amp;meldingsinterval (sec):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation>Verversingsnelheid van beschikbaarheidsaanmeldingen.</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Transport/NAT</translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation>Voeg q-waarde toe aan registratie</translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation>De q-waarde is de prioriteit van een apparaat. Als u naast Twinkle nog andere SIP apparaten bij het netwerk registreert voor deze gebruiker, dan kan het netwerk deze waarde gebruiken om te bepalen op welk apparaat een gesprek als eerste afgeleverd moet worden.</translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation>De q-waarde is een waarde tussen 0,000 en 1,000. Een hogere waarde betekent een hogere prioriteit.</translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>SIP transport</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation>UDP</translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation>TCP</translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Transport modus voor SIP. In auto modus bepaalt de berichtgrootte welk transport protocol gebruikt wordt. Berichten groter dan de UDP drempel worden via TCP verstuurd. Kleinere berichten worden via UDP gestuurd.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>T&amp;ransport protocol:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation>UDP &amp;drempel:</translation>\n    </message>\n    <message>\n        <source> bytes</source>\n        <translation type=\"obsolete\"> bytes</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Berichten groter dan de drempel worden via TCP verstuurd. Kleinere berichten worden via UDP verstuurd.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN (does not work for incoming TCP)</source>\n        <translation type=\"vanished\">&amp;STUN (werkt niet voor inkomend TCP verkeer)</translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation>P&amp;ersistente TCP verbinding</translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation>De TCP verbinding die opgezet wordt tijdens registratie blijft open, zodat de SIP proxy deze verbinding kan gebruiken om binnenkomende verzoeken te sturen. Ping pakketten worden gestuurd om te testen of de verbinding nog bestaat.</translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation>&amp;Zend indicaties als u een bericht aan het schrijven bent.</translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation>Twinkle stuurt een compositie indicatie als u een bericht aan het schrijven bent. De ontvanger van uw bericht kan dan zien dat u bezig bent met schrijven.</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation>AkA AM&amp;F:</translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation>A&amp;KA OP:</translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation>&quot;Authentication management field&quot; voor AKAv1-MD5 authenticatie.</translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation>&quot;Operator variant key&quot; voor AKAv1-MD5 authenticatie.</translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation>V&amp;oorbewerking</translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation>Voorbewerking (verbetert de geluidskwaliteit voor uw gesprekspartner)</translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation>&amp;Automatische sterkteregeling</translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation>Automatische sterkteregeling versterkt zachte signalen en dempt luide signalen die door de microfoon worden opgenomen.</translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation>Niveau automatische sterkterege&amp;ling:</translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation>Een waarde rond 25% is aanbevolen voor een goede geluidskwaliteit.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation>&amp;Voice activity detection</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation>&quot;Voice activity detection&quot; detecteteert of een signaal spraak of stilte/ruid bevat. Stilte/ruis wordt niet over het netwerk gestuurd waardoor minder bandbreedte gebruikt wordt.</translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation>Ruisonderdrukki&amp;ng</translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation>Ruisonderdrukking vermindert achtergrondruis in het microfoonsignaal.</translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation>Acoustic &amp;Echo Cancellation</translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation>Spraak uit de speakers wordt opgevangen door de microfoon waardoor uw gesprekspartner een echo waarneemt. &quot;Acoustic echo cancellation&quot; verwijdert deze echo.</translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation>Variable &amp;bit-rate</translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation>Discontinuous &amp;Transmission</translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation>&amp;Kwaliteit:</translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation>Speex comprimeert het geluidssignaal ten koste van de kwaliteit. Hoe meer compressie, hoe minder bandbreedte nodig is, maar hoe slechter de geluidskwaliteit. Deze kwaliteitsfactor (0 tot 10) bepaalt de trade-off tussen kwaliteit en compressie.</translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation>bytes</translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation>Vestuur telefoon&amp;nummer als tel-URI</translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation>Expandeer een telefoonnummer naar een tel-URI in plaats van een sip-URI.</translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation>Accep&amp;teer doorverbindverzoek (inkomende REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation>Doorverbinden toestaan tijdens opbouw ruggespraak gesprek</translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation>Bij doorverbinden met ruggespraak, verbindt u normaal pas door nadat u ruggespraak heeft gehouden. Met deze optie kunt u al doorverbinden terwijl het gesprek voor ruggespraak nog in opbouw is. Dit is een niet-standaard implementatie die mogelijk niet werkt met alle SIP apparaten.</translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation>NAT &amp;keep alive</translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation>Stuur UDP NAT keep alive pakketten.</translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation>Als u STUN of NAT keep alive aan heeft gezet, dan zal Twinkle keep alive pakketjes sturen met deze snelheid om de adresbindingen in uw NAT router in leven te houden.</translation>\n    </message>\n    <message>\n        <source>Do&amp;main*:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Organi&amp;zation:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>E&amp;xpiry:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call Hold &amp;variant:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max redirections:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extension is supported.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Asserted-Identity header when hiding user identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use STUN (does not wor&amp;k for incoming TCP)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>STUN ser&amp;ver:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Unsolicited</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Solicited</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Solicited MWI</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Wizard</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>De host naam, domeinnaam of IP adres van de STUN server.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN server:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>De SIP gebruikersnaam die u van uw provider heeft gekregen. Dit het gebruikersdeel in uw SIP adres, &lt;b&gt;gebruikersnaame&lt;/b&gt;@domein.nl Dit kan een telefoonnummer zijn.\n&lt;br&gt;&lt;br&gt;\nDit is een verplicht veld.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Domein*:</translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Kies uw SIP provider. Als uw SIP provider niet in de lijst voorkomt, kies dan &lt;b&gt;Anders&lt;/b&gt; en vul de instellingen in die uw van uw provider hebt gekregen.&lt;br&gt;&lt;br&gt;\nAls u een voorgedefinieerde SIP provider kiest, dan hoeft u alleen uw eigen naam, gebruikersnaam, authenticatie naam en paswoord in te voeren.</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>&amp;Authenticatie naam:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>U&amp;w naam:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Uw SIP gebruikersnaam voor authenticatie. Meestal is dit hetzelfde als uw SIP gebruikersnaam.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Dit is het domein deel van uw SIP adres, gebruikersnaame@&lt;b&gt;domein.nl&lt;/b&gt;. In plaats van een echt domein kan dit ook de host naam of het IP van uw &lt;b&gt;SIP proxy&lt;/b&gt; zijn. Als u direct van IP adres naar IP adres wilt bellen, dan vult u hier de host naam of IP adres van uw computer in.\n&lt;br&gt;&lt;br&gt;\nDit is een verplicht veld.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Dit is uw eigen naam. bijv. Jan Jansen. Als u iemand belt, kan deze naam getoond worden.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>SIP pro&amp;xy:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>De host naam, domeinnaam of IP adres van uw SIP proxy. Als deze hetzelfde is als uw domeinnaam, dan mag u dit veld leeg laten.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>&amp;SIP provider:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Paswoord:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>Ge&amp;bruikersnaam*:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Uw paswoord voor authenticatie.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>Ann&amp;uleren</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Geen (directe IP naar IP gesprekken)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Anders</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Gebruikersprofiel wizard:</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>U moet een gebruikersnaam voor uw SIP account invullen.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>U moet een domeinnaam voor uw SIP account invullen.\nDit kan de host naam of IP adres van uw PC zijn als u direct van PC naar PC wilt bellen.</translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Ongeldige waarde voor SIP proxy.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Ongeldige waared voor STUN server.</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Ja</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Nee</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\">Afwijzen</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/lang/twinkle_ru.ts",
    "content": "<?xml version=\"1.0\" ?><!DOCTYPE TS><TS language=\"ru\" version=\"2.1\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Карточка абонента</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation>&amp;Описание:</translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation>Префикс контакта.</translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Имя контакта.</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>&amp;Имя:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation>Вы можете вписать сюда любое описание этого контакта.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Телефон:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation>&amp;Префикс имени:</translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Телефонный номер или SIP адрес контакта.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Фамилия контакта.</translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Фамилия:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Вы должны заполнить поле имя.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>Вы должны заполнить поле номер телефона или SIP адрес.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation>Телефон</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation>Описание</translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Аутентификация</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation>пользователь</translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>Пользователь, для которого запрашивается аутентификация.</translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation>профиль</translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Профиль пользователя, для которого запрашивается аутентификация.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Профиль пользователя:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Пользователь:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Пароль:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ваш пароль для аутентификации.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ваше аутентификационное имя для SIP. В большинстве случаем совпадает с именем пользователя SIP. Вы можете ввести здесь другое имя при необходимости.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>&amp;Имя пользователя:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation>Имя требуемое для области:</translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation>область</translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation>Область, для которой нужна авторизация.</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Друзья</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Телефон:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Имя вашего друга.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>&amp;Показать доступность</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Выберите эту опцию если вы хотите видеть доступность вашего друга. Это работает, только если ваш провайдер предоставляет услугу проверки доступности.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Имя:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>SIP адрес вашего друга.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Вы должны заполнить имя.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Неправильный телефон.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Ошибка сохранения списка друзей: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Доступность</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>неизвестно</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>не в сети</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>в сети</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>ошибка запроса</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>запрос отклонён</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>не опубликован</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>ошибка публикации</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Правый клик для добавления друзей.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>Ошибка открытия звуковой карты</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>Ошибка создания UDP сокета (RTP) на порте %1</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Ошибка создания принимаемого аудиопотока.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Ошибка создания передаваемого аудиопотока.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>локальный пользователь</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>удалённый пользователь</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>ошибка</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>неизвестно</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>входящий</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>исходящий</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Разрегистрация</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>разрегистрировать все устройства</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation>Заполните ваш идентификатор учётной записи.</translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation>Заполните ваш PIN код.</translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation>Профиль пользователя с именем %1 уже существует.</translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation>Twinkle - DTMF</translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Цифровая клавиатура</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation>2</translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation>3</translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>Нецифровое A. Обычно не нужно.</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation>4</translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation>5</translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation>6</translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>Нецифровое B. Обычно не нужно.</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation>7</translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation>8</translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation>9</translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>Нецифровое C. Обычно не нужно.</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Звезда (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation>0</translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Решётка (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>Нецифровое D. Обычно не нужно.</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation>1</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Закрыть</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>Ошибка создания  %1 сокета (SIP) на порте %2</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Перехватить файл блокировки и запуститься сейчас?</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Следующие профили как для пользователя %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Вы можете запускать только многократные профили для разных пользователей.</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation>Если эти пользователи для разных доменов, то включите следующий пункт в вашем профиле пользователя (протокол SIP)</translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation>Используйте доменное имя для создания уникального заголовка контакта</translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Не могу найти сетевой интерфейс. Twinkle будет использовать 127.0.0.1 как локальный IP адрес. Когда вы будете соединены с сетью, перезапустите Twinkle для использования правильного IP адреса.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Линия %1: входящий звонок для %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Звонок переведён к %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation>Линия %1: удалённая сторона прервала звонок.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation>Линия %1: удалённая сторона закончила звонок.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Линия %1: SDP ответ от удалённой стороны не поддерживается.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Линия %1: SDP ответ от удалённой стороны отсутствует.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation>Линия %1: Неподдерживаемый тип содержимого в ответе от удалённой стороны.</translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Линия %1: не получен ACK, звонок прерван.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Линия %1: не получен PRACK, звонок будет прерван.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Линия %1: PRACK ошибка.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Линия %1: Ошибка завершения звонка.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Линия %1: удалённая сторона ответила на звонок.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Линия %1: Ошибка звонка.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>Звонок будет переведён к:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation>Линия %1: соединение завершено.</translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Линия %1: соединение установлено.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Ответ на запрос органичений терминала: %1 %2</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Ограничения терминала от %1</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation>Разрешённые типы содержимого:</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>неизвестный</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Подтверждённые кодировки:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Подтверждённые языки:</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Разрешённые запросы:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation>Поддерживаемые расширения:</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation>нету</translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Тип конечной точки:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation>Линия %1: ошибка получения звонка.</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, ошибка регистрации: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, регистрация завершена (устаревание = %2 секунд)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, ошибка регистрации: ошибка STUN</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, разрегистрация состоялась: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, ошибка разрегистрации: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation>%1, ошибка проверки регистрации: %2 %3</translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: вы не зарегистрированы</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: вы имеете следующие регистрации</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: проверка регистрации...</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Линия %1: перенаправление запроса к</translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Перенаправление запроса к: %1</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Линия %1: определён DTMF:</translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation>неправильное телефонное DTMF событие (%1)</translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Линия %1: посылка DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation>Линия %1: удалённая сторона не поддерживает DTMF события.</translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation>Линия %1: получено оповещение.</translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Событие %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Состояние: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Причина: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Прогресс: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Линия %1: ошибка передачи звонка.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Линия %1: Перевод звонка завершён.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Линия %1: перевод звонка.</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation>Никакие дальнейшие уведомления не будут получены.</translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Линия %1. перевод звонка к %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Перевод запрошен %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation>Линия %1: Ошибка передачи звонка. Получение оригинального звонка.</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1, ошибка STUN запроса: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, Ошибка STUN запроса.</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Перенаправление звонка</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Профиль пользователя:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Пользователь:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Вы разрешаете переводить звонки к следующему направлению?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation>Если вы не хотите, чтобы вас об этом ещё раз спрашивало, то вы должны изменить настройки в разделе протокола SIP в профиле пользователя.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation>Запрос перенаправления</translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation>Вы разрешаете перевести запрос %1 к следующему назначению?</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Передача звонка</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation>Запрос перевода звонка получен от:</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation>Запрос перевода звонка получен.</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Вы разрешаете передачу звонка к следующему назначению?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation>Информация:</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Внимание:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation>Критическое:</translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Обнаружение файервола/NAT...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Прервать</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Линия %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Нажмите на замок для подтвердения корректности SAS.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>Удалённый пользователь на линии %1 запретил шифрование.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Линия %1: SAS подтверждён.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation>Линия %1: сброс SAS подтверждения.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, ошибка статуса голосовой почты.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, статус голосовой почты не принят.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, ящик голосовой почты не существует.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, прерван статус голосовой почты.</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Разрешён сетью</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Линия %1: звонок сброшен.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Линия %1: звонок перенаправлен.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Ошибка запуска конференции.</translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Ошибка сохранения вложения сообщения: %1</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation>Перевод: %1</translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation>Не могу открыть веб-браузер: %1</translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation>Настройте веб-браузер в настройках системы.</translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Выбор адреса</translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation>Адресная книга &amp;KDE</translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation>Этот список адресов получен из &lt;b&gt;Адресной книги KDE&lt;/b&gt;. Контакты, которые не содержат телефонного номера, здесь не показываются. Для добавления, удаления или модификации адресной информации вы должны использовать  Адресную книгу KDE.</translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>&amp;Показать только SIP адреса</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Отметьте эту опцию если вы ходите видеть только контакты с SIP адресами, те которые начинаются с &lt;b&gt;sip:&lt;/b&gt;&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>&amp;Обновить</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Перезагрузить список адресов из Адресной книги KDE.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>&amp;Локальная адресная книга</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Контакты из локальной адресной книги Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Добавить</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Добавить новый контакт в локальную адресную книгу.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Удалить</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Удалить контакт из локальной адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Редактировать</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Редактировать контакт из локальной адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;У Вас нет ни одного контакта с телефонным номером в  &lt;b&gt;KAddressBook&lt;/b&gt;(приложении KDE адресная книга). Twinkle получает все контакты с телефонными номерами из Адресной книги KDE. Для управления вашими контактами вы должны использовать KAddressBook.&lt;p&gt;Как альтернативу вы можете использовать локальную адресную книгу Twinkle.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation>Вы уверены, что хотите удалить контакт &apos;%1&apos; из локальной адресной книги?</translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation>Удалить контакт</translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Имя профиля</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Введите имя вашего профиля:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Имя вашего профиля&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nПрофиль содержит ваши пользовательские настройки такие как имя пользователя и пароль. Вы должны дать каждому профилю отдельное имя\n&lt;br&gt;&lt;br&gt;\nЕсли у вас несколько различных учётных записей SIP,  вы можете создать несколько профилей. При запуске Twinkle покажет вам список профилей и вы сможете выбрать какой использовать при этом запуске.\n&lt;br&gt;&lt;br&gt;\nЧтобы легко различать свои профили вы можете использовать ваше SIP имя пользователя как название профиля, например, &lt;b&gt;example@example.com&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Не могу найти каталог .twinkle  в вашем домашнем каталоге.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Профиль уже существует.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Переименовываю профиль &apos;%1&apos; в:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - История звонков</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Время</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>Входящий/исходящий</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>Откуда/Куда</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Тема</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Статус</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Детали звонка</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Детали выбранной записи звонка.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Показать</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>&amp;Входящие звонки</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Выберите эту опцию для показа входящих звонков.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>&amp;Исходящие звонки</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Выберите эту опцию для показа исходящих звонков.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>&amp;Отвеченные звонки</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Выберите эту опцию для показа отвеченных звонков.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>&amp;Пропущенные звонки</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Выберите эту опцию для показа пропущенных звонков.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>Только профиль данного &amp;пользователя</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Выберите эту опцию для показа звонков, ассоциированных с этим профилем пользователя.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>О&amp;чистить</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Очистить всю историю звонков.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Заметка:&lt;/b&gt; очищает &lt;b&gt;все&lt;/b&gt; записи, записи удаляются безвозратно, и вы никогда не увидите их в любых вариантах просмотра.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>&amp;Закрыть</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Закрыть это окно.</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>&amp;Звонить</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Звонить на выбранный адрес.</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Звонить...</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Удалить</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Звонок начат:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Звонок отвечен:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Звонок закончен:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Продолжительность звонка:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Направление:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Откуда:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Куда:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Ответить на:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Ссылаться на:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Тема:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Источник:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Статус:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Конечное устройство:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Профиль пользователя:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>разговор</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Ответ:</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation>Количество звонков:</translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation>###</translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation>Общая продолжительность звонка:</translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation>%1 звонит</translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Звонить</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Кому:</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Опционально вы можете ввести сюда тему. Возможно она будет показана удалённой стороне при звонке.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Адрес куда вы хотите позвонить. Это может быть полный SIP адрес, такой как  &lt;b&gt;sip:example@example.com&lt;/b&gt;, или просто часть имени пользователя или телефонный номер. Если вы не указываете полного адреса, Twinkle дополнит этот адрес, используя значение домена из вашего профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Пользователь, от которого вы будете производить звонок.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Тема:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Откуда:</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>&amp;Звонить анонимно</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nС этой опцией вы запрашиваете у своего провайдера SIP услуг убрать ваши идентификационные данные из данных, направляемых к удалённой стороне. Эта опция только прячет ваши идентификационные данные такие как: ваш SIP адрес, телефонный номер, имя пользователя. Она &lt;b&gt;не&lt;/b&gt; скрывает ваш IP адрес. \n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Внимание:&lt;/b&gt; не все провайдеры поддерживают анонимные звонки.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Звонить</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Не все SIP провайдеры поддерживают анонимные звонки. Уточните у своего провайдера действительно ли он поддерживает это.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation>Twinkle - Журнал</translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Содержимое текущего журнала (~/.twinkle/twinkle.log)</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Закрыть</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>О&amp;чистить</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Очистить окно журнала. Это &lt;b&gt;не &lt;/b&gt; стирает файл журнала и не очищает его.</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - Мгновенное сообщение</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Кому:</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>Пользователь, от которого вы будете посылать сообщения.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Адрес пользователя, которому вы посылаете сообщение. Это может быть полный SIP адрес, такой как  &lt;b&gt;sip:example@example.com&lt;/b&gt;, или просто часть имени пользователя, или телефонный номер. Если вы не указываете полного адреса, Twinkle дополнит этот адрес, используя значение домена из вашего профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>&amp;Профиль пользователя:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Общение</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Введите ваше сообщение и нажмите &quot;Отправить&quot; для его отсылки.</translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Отправить</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Отправить сообщение.</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Ошибка доставки</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Сообщение доставки</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Отправить файл...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Отправить файл</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>размер картинки уменьшен для просмотра</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Открыть в %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Открыть в...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Сохранить вложение как...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Файл уже существует. Перезаписать данный файл?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Ошибка сохранения вложения.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 пишет сообщение.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation>Размер</translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>отправка сообщения</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation>Twinkle</translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Список друзей</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation>Вы можете создать раздельные листы друзей для каждого профиля. Вы можете только видеть доступность друзей и публиковать вашу личную доступность, если провайдер поддерживает функцию доступности на сервере.</translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>&amp;Позвонить:</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Адрес куда вы хотите позвонить. Это может быть полный SIP адрес, такой как  &lt;b&gt;sip:example@example.com&lt;/b&gt;, или просто часть имени пользователя, или телефонный номер. Если вы не указываете полного адреса, Twinkle дополнит этот адрес, используя значение домена из вашего профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Набрать</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Набрать адрес.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Пользователь:</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Пользователь, от которого вы будете производить звонок.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation>Индикатор автоответа.</translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation>Индикатор перенаправления звонков.</translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation>Индикатор &quot;Не тревожить&quot;.</translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation>Индикатор ожидания сообщения.</translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation>Индикатор пропущенных звонков.</translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation>Статус регистрации.</translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation>Монитор</translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Статус линий</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Линия &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation>Alt+1</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Нажмите для переключения к линии 1.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Откуда:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Куда:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Тема:</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation>простой</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Передача звонка</translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation>sas</translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation>Короткая строка авторизации</translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation>g711a/g711a</translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation>Аудио кодек</translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation>0:00:00</translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Продолжительность звонка</translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation>sip:от</translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation>sip:до</translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation>тема</translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation>фото</translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Линия &amp;2:</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation>Alt+2</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Нажмите для переключения к линии 2.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Файл</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Редактировать</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>З&amp;вонить</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Активировать линию</translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>&amp;Чат</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>&amp;Регистрация</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>&amp;Сервисы</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>&amp;Показать</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>&amp;Справка</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Выход</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>&amp;Выход</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>О программе Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>&amp;О программе Twinkle</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Позвонить кому-либо</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation>F5</translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Ответить на входящий звонок</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation>F6</translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation>Завершить звонок</translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Отклонить входящий звонок</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation>F8</translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Поставить звонок на удержание или ответить на второй звонок</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Перенаправить входящий звонок без ответа</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Открывает панель цифровых клавиш для использования в голосовых меню</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Регистрация</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>&amp;Регистрация</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Разрегистрация</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>&amp;Разрегистрация</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Разрегистрация этого устройства</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Показать регистрации</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>&amp;Показать регистрации</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Ограничения терминала</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Запрос органичений терминала от кого-либо</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Не беспокоить</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>&amp;Не беспокоить</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Перенаправление звонка</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>&amp;Перенаправление звонка...</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Повторить последний звонок</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>О Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>О &amp;Qt</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Профиль пользователя</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>&amp;Профиль пользователя...</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation>Объединить два звонка в единую 3-х стороннюю конференцию</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Отключить микрофон</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Переключить звонок на другого абонента</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Системные настройки</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>&amp;Системные настройки...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Разрегистрировать всех</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>Разрегистрировать &amp;всех</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Разрегистрировать все ваши зарегистрированные устройства</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Автоответ</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>&amp;Автоответ</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Журнал</translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation>&amp;Журнал...</translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>История звонков</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>&amp;История звонков...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation>F9</translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Сменить пользователя ...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Сменить пользователя ...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Активировать или деактивировать пользователей</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>Что это?</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>Что &amp;это?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Линия 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Линия 2</translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation>&amp;Монитор</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Голосовая почта</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>&amp;Голосовая почта</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation>Доступ к голосовой почте</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Чат</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>Мгновенное &amp;сообщение...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Сообщения</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>Список &amp;друзей</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Звонить...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>&amp;Редактировать...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Удалить</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>&amp;Не в сети</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>&amp;В сети</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>&amp;Изменить доступность</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Добавить друзей...</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>простой</translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>набор номера</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>производится звонок, пожалуйста, ждите</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>входящий звонок</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>установка соединения, пожалуйста, ожидайте</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>установлено</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>установлено (ожидание медиапотока)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation>завершение соединения, пожалуйста, ожидайте</translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>неизвестное состояние</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Звук зашифрован</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>Нажмите для подтверждения SAS.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation>Нажмите для очистки проверки SAS.</translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation>Передача  с разговором</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Пользователь:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Звонок:</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Звонить анонимно</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Статус регистрации:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>Зарегистрирован</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>Oшибка</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>Не зарегистрирован</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Нажмите для показа регистрации.</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Нет зарегистрированных пользователей.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 новое, 1 старое сообщение</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 новое, %2 старых сообщений</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>1 новое сообщение</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 новых сообщений</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>1 старое сообщение</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 старых сообщений</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Ожидание сообщений</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Нет сообщений</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Статус голосовой почты:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Ошибка</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Неизвестно</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Нажмите для доступа к голосовой почте.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>Не беспокоить активно для:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Перенаправление активно для:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>Автоответ активен для:</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Нажмите для активации/деактивации</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Нажмите для активации</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>Не беспокоить не активно.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>Перенаправление не активно.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>Автоответ не активен.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Нажмите для просмотра подробностей истории звонков.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>У вас нет пропущенных звонков.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>У вас есть 1 пропущенный звонок.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>У вас %1 пропущенных звонков.</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Запуск профиля пользователя...</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Следующие профили как для пользователя %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Вы можете запускать только многократные профили для разных пользователей.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation>Вы изменили SIP UDP порт. Эта настройка применится только после перезапуска Twinkle.</translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation>не предусмотрен</translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation>Вы должны предоставить адрес голосовой почты в вашем профиле перед доступом к ней.</translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>Линия занята. Не могу получить доступ к голосовой почте.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation>Адрес голосовой почты %1 не является верным. Пожалуйста, предоставьте правильный адрес в вашем профиле пользователя.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Ошибка сохранения списка друзей: %1</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation>Алмазная карта</translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation>Справочник</translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation>&amp;Справочник</translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation>Зарегистрироваться</translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation>&amp;Зарегистрироваться...</translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation>Перезарядка...</translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation>История баланса...</translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation>История звонков...</translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation>Центр админа...</translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation>Перезарядка</translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation>История баланса</translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation>Центр админа</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation>Звон</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation>&amp;Ответить</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation>Ответ</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation>&amp;Завершить</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation>Заверш</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation>&amp;Отклонить</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Отклон</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation>&amp;Удержать</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation>Удерж</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation>П&amp;еренаправить...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation>Направ</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation>&amp;Dtmf...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation>Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation>&amp;Ограничения терминала...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation>&amp;Повторить</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation>Повт</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation>&amp;Конференция</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation>Конф</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation>&amp;Выключить звук</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation>Вык.Зв</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation>Пере&amp;ключить...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation>Перекл</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - Преобразователь номеров</translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Совпадение:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Замена:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation>Perl стиль формата строки для замены номера.</translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation>Perl стиль регулярного выражения совпадения формата номера, который вы хотите изменить.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>Совпадающее выражение не может быть пустым.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>Заменяющее значение не может быть пустым.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Неправильное регулярное выражение.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Перенаправление</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Перенаправление входящего звонка к</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Вы можете определить до 3 направлений, куда будут перенаправлятся звонки. Если первое направление не отвечает на вызов, то звонок перейдёт на второе направление и так далее.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3-й выбор направления:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2-й выбор направления:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1-й выбор направления:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - Выбор сетевого интерфейса</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Выберите сетевой интерфейс/IP адрес, который вы будете использовать:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>В вашей системе несколько IP адресов. Здесь вы должны выбрать какой из IP адресов нужно использовать. Этот IP адрес будет использоваться вне SIP сообщений (заголовки ip пакетов).</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>Установить как &amp;IP по умолчанию</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Сделать выбранный IP адрес адресом по умолчанию. В следующий раз когда Twinkle будет запускаться, этот IP адрес будет выбран автоматически.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>Выбрать как сетевую карту по &amp;умолчанию</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>Сделать выбранный сетевой интерфейс интерфейсом по умолчанию. В следующий раз когда Twinkle будет запускатся, этот интерфейс будет выбран автоматически.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Если вы захотите удалить или изменить профиль по умолчанию позже, то вы можете сделать это через системные настройки.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle - Выбор пользовательского профиля</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Выберите пользовательский(ие) профиль(ли) для запуска:</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Поставьте отметки на профилях, которые вы хотите запустить, и нажмите запуск.</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Создание нового профиля с помощью редактора профилей.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>&amp;Мастер</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Создание нового профиля с помощью мастера профилей.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Изменить</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Редактировать выделенный профиль.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Удалить</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Удалить выделенный профиль.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>Пер&amp;еименовать</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Переименовать выделенный профиль.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>&amp;Уст. по умолчанию</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Сделать выбранный профиль как профиль по умолчанию. При следующих запусках Twinkle этот профиль будет автоматически запущен.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Запуск</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Запустить Twinkle с выбраным профилем.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>С&amp;истемные настройки</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Редактировать системные настройки.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Перед тем как использовать Twinkle, вы должны создать пользовательский профиль.&lt;br&gt;Нажмите OK для создания профиля.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>&amp;Редактор профилей</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Далее вы можете определить системные настройки. Вы можете изменить эти настройки позже.&lt;br&gt;&lt;br&gt;Нажмите OK для просмотра и настройки системных настроек.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>Вы не выбрали ни одного профиля для запуска.\nПожалуйста, выберите профиль.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Вы уверены, что хотите удалить профиль &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Удалить профиль</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Ошибка удаления профиля.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Ошибка переименования профиля.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Если вы захотите удалить или изменить профиль по умолчанию позже, то вы можете сделать это через системные настройки.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Не могу найти .twinkle папку в вашей домашней директории.</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation>Создать профиль</translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation>&amp;Редактор</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation>&amp;Алмазная карта</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation>Изменить профиль</translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation>Автозапуск профиля</translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation>А&amp;лмазная карта</translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation>Создать профиль для учётной записи Алмазной карты. С аккаунта Алмазной карты Вы можете по всему миру делать звонки на обычные и мобильные телефоны, и отправлять SMS сообщения.</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation>&lt;html&gt;Для создания нового профиля вы можете использовать редактор профилей. В редакторе профилей вы можете гибко изменить настройки для SIP протокола, RTP и множество других тонких настроек.&lt;br&gt;&lt;br&gt;Как альтернативу вы можете использовать мастер для быстрой настройки. Мастер спросит у вас только самые необходимые настройки. Если вы создаёте профиль мастером, то позже сможете редактировать его редактором профилей.&lt;br&gt;&lt;br&gt;</translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation>Вы можете создать учётную запись Алмазной карты чтобы делать звонки по всему миру на обычные и мобильные телефоны, и отправлять SMS сообщения.&lt;br&gt;&lt;br&gt;</translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation>Выберите метод который вы будете использовать.&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Выбор пользователя</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>&amp;Выбрать все</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>О&amp;чистить все</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation>назначение</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Регистрация</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Выберите пользователей, которых надо зарегистрировать.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Разрегистрация</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Выберите пользователей, которых надо разрегистрировать.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Разрегистрировать все устройства</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Выберите пользователей, для которых вы хотите &quot;разрегистрировать все устройства&quot;.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Не беспокоить</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Выберите пользователей, для которых вы хотите включить &quot;не беспокоить&quot;.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Автоответ</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Выберите пользователей, для которых вы хотите включить &quot;автоответ&quot;.</translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Отправить файл</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Выберите файл для отправки.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Файл:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Тема:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Отправить</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Файл не существует.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Отправить файл...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation>Twinkle - Перенаправление звонков</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Пользователь:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation>Существует 3 сервиса перенаправления&lt;p&gt;\n&lt;b&gt;Безусловный:&lt;/b&gt; перенаправляет все звонки\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Занятый:&lt;/b&gt; перенаправляет звонок ели обе линии заняты\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Без ответа:&lt;/b&gt; перенаправляет звонок когда срабатывает таймер на отсутствие ответа\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation>&amp;Безусловный</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation>&amp;Перенаправление всех звонков</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation>Активировать сервис безусловной переадресации.</translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation>Перенаправить к</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Вы можете определить до 3 направлений, куда будут перенаправлятся звонки. Если первое направление не отвечает на вызов, то звонок перейдёт на второе направление и так далее.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3-й выбор направления:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2-й выбор направления:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1-й выбор направления:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Занятый</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation>&amp;Перенаправлять звонки когда я занят</translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation>Активировать перенаправление когда я занят.</translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>&amp;Нет ответа</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation>&amp;Перенаправлять звонки когда я не отвечаю</translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation>Активировать перенаправление когда я не отвечаю.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Принять и сохранить изменения.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Отменить все измения и закрыть окно.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>Вы ввели неверное направление.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Системные настройки</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Основные</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Аудио</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Сигналы</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Сеть</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Журнал</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Выберите категорию для просмотра и модификации настроек.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Принять и сохранить изменения.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Отменить все измения и закрыть окно.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Звуковая Карта</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Выберите звуковую карту для проигрывания сигнала вызова для входящих звонков.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Выберите звуковую карту, к которой подключен ваш микрофон.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Выберите звуковую карту, к которой подключены ваши колонки или наушники.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Колонки:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Сигнал вызова:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Другое устройство:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Микрофон:</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>&amp;Проверять устройства перед использованием</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation>&lt;p&gt;\nTwinkle проверяет аудиоустройства перед использованием, чтобы предотвратить работу звука в вызове.\n&lt;p&gt;\nЕсли включено, Twinkle проверяет, доступно ли указанное аудиоустройство при запуске.\n&lt;p&gt;\nЕсли микрофон или громкоговорители выглядят неисправными, будет отображаться предупреждающее сообщение, и вызовы не будут разрешены.\n&lt;p&gt;\nКроме того, когда обнаружен входящий вызов, и звуковое устройство не в порядке, будет отображаться предупреждение, и вызов не будет принят.</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Дополнительно</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation>OSS &amp;размер фрагмента:</translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation>16</translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation>32</translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation>64</translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation>128</translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation>256</translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation>Установка периода воспроизведения ALSA влияет на задержку звука на звуковой карте. Если у вас есть проблемы с сбросом или пропуском звука, попробуйте другое значение.</translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation>ALSA размер периода &amp;проигрывания:</translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation>ALSA размер периода &amp;записи:</translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation>Размер фрагмента OSS влияет на задержку звука на звуковой карте. Если у вас есть проблемы со сбросом или пропуском звука, попробуйте другое значение.</translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation>Размер периода записи ALSA влияет на задержку звука. Если контрагент жалуется на частое прерывание звука, попробуйте использовать другое значение.</translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>&amp;Максимальный размер журнала:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation>Максимальный размер файла журнала в МБ. Когда журнал достигает этого размера, создаётся архивная копия и текущий журнал удаляется. Остаётся только архивная копия.</translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation>МБ</translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation>Записывать &amp;отладочные отчёты</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation>Указывает будут ли записываться отладочные сообщения в журнал.</translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>Записывать &amp;SIP отчёты</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation>Указывает будут ли записываться SIP сообщения в журнал.</translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>Записывать S&amp;TUN отчёты</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation>Указывает будут ли записываться STUN сообщения в журнал.</translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation>Записывать отчёты п&amp;амяти</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation>Указывает будут ли записываться сообщения, касающиеся управления памятью, в журнал.</translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Системный лоток</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Создавать значок в &amp;системном лотке при запуске</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation>Включите эту опцию если вы хотите видеть значок Twinkle в системном лотке. Значок в системном лотке создаётся при старте Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation>&amp;Прятать в системном лотке при закрытии главного окна</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation>Включите эту опцию если вы хотите, чтобы Twinkle прятался в системном лотке, когда вы закрываете основное окно.</translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Запуск</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation>При &amp;запуске свернуться в системный лоток</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation>При следующих запусках Twinkle будет постоянно прятаться в системном лотке. Это удобно когда вы выбрали профиль пользователя по умолчанию.</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation>Если вы всегда используете одинаковый(ые) профиль(ли), тогда вы можете отметить этот профиль(ли) как профиль по умолчанию. При следующем запуске Twinkle он не будет спрашивать выбор профиля для запуска. Профиль по умолчанию загружается автоматически.</translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Сервисы</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>&amp;Ожидание звонка</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation>При ожидании звонка если одна из линий занята, то входящий звонок будет разрешён. Если вы запретите ожидание звонка, то если одна из линий занята, входящие звонки будут отклонены.</translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation>Прерывать &amp;обе линии при завершении 3-х сторонней конференции.</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation>Прерывать обе линии при завершении 3-х сторонней конференции. Если эта опция отключена, только активная линия будет прервана, и вы сможете продолжить разговор с удалённой стороной на другой линии.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation>&amp;Максимальное количество звонков в истории:</translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation>Максимальное количество звонков, которые будут сохранятся в истории звонков.</translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation>&amp;Автоматически показывать главное окно при входящем звонке после</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation>Когда главное окно скрыто, оно будет автоматически показано при входящем звонке после указанного количества секунд.</translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation>Число секунд, после которого главное окно будет показано.</translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>секунд</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation>Максимально разрешённый размер (0-65535) в байтах входящего SIP сообщения через UDP.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>&amp;SIP порт:</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>&amp;RTP порт:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation>Макс. размер SIP сообщения (&amp;TCP):</translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation>UDP/TCP порт используемый для отправки и получения SIP сообщений.</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation>Макс. размер SIP сообщения (&amp;UDP):</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation>Максимально разрешённый размер (0-4294967295) в байтах для входящего SIP сообщения поверх TCP.</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation>UDP порт, используемый для отправки и приёма RTP для первой линии. UDP порт для второй линии на 2 больше. Если для первой линии используется порт 8000, значит вторая линия использует порт 8002. Когда вы используете передачу звонка, используется следующий свободный порт (8004).</translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Звонок</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>&amp;Играть звонок при входящем вызове</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Указывает будет ли проигрываться сигнал вызова при входящем вызове.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>&amp;Стандартный звонок</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Проигрывать сигнал вызова по умолчанию при входящем звонке.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>С&amp;вой звонок</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Проигрывать свой сигнал по умолчанию при входящем звонке.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation>Определите имя файла из *.wav файлов, который будет проигрываться как сигнал вызова.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Выберите файл сигнала вызова.</translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation>Гудок вызова</translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation>И&amp;грать гудок когда сеть не играет сигнала вызова</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nВоспроизведение мелодии звонка для вызывающего абонента, если телефонная сеть не обеспечивает такой тон.\n&lt;/p&gt;\n&lt;p&gt;\nНаличие этого тона зависит от вашего провайдера.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation>С&amp;тандартный гудок вызова</translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation>Играть стандартный гудок при исходящем вызове.</translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation>С&amp;вой гудок вызова</translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation>Играть свой гудок вызова.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation>Определите файл из .wav  файлов, который будет проигрываться как гудок вызова.</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Выберите файл гудка вызова.</translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation>&amp;Искать имя при входящем звонке</translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation>При входящем звонке Twinkle будет пытаться найти имя в вашей адресной книге, которое соответствует входящему SIP адресу. Это имя будет отображено.</translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation>За&amp;менять полученное имя абонента</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation>Звонящий может предоставить отображаемое имя. Отметьте эту опцию если вы хотите заменить его данные данными из вашей адресной книги.</translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Искать &amp;фотографию при входящем звонке</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation>Искать фото звонящего в вашей адресной книге и отобразить его при входящем звонке.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Сигналы вызова</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Выберите сигнал вызова</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Гудок вызова</translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation>Выберите гудок вызова</translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation>Команда &amp;веб-браузера:</translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation>Команда для запуска веб-браузера. Если оставить это поле пустым, то Twinkle будет пытаться использовать веб-браузер по умолчанию.</translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation>512</translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation>1024</translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation>Совет: при треске с PulseAudio установите размер периода проигрывания на максимум.</translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation>Включить входящие звонки OSD</translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Ответить</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Отклонить</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation>Входящий звонок</translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Ограничения клиента</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Откуда:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Получить ограничения клиента от</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Откуда:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Адрес или номер партнёра, параметры которого должны быть определены (запрос OPTION). Это может быть полный SIP-адрес, например &lt;b&gt;sip:example@example.com&lt;/b&gt; или только пользователь, или номер телефона с полного адреса. Если адрес не завершён, Twinkle добавит адрес домена из вашего профиля.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Переключение</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Переключить звонок к</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Кому:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Адрес абонента, которому вы хотите перевести звонок. Это может быть полный адрес вида  &lt;b&gt;sip:example@example.com&lt;/b&gt; , только пользовательская часть или номер телефона из полного адреса. Когда вы не указываете полный адрес, Twinkle дополняет адрес, используя имя домена из вашего профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Адресная книга</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Выберите адрес из адресной книги.</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>&amp;Слепой перевод</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation>Передача звонка другому абоненту без разговора с ним.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation>П&amp;ередача  с разговором</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation>Перед передачей звонка другому абоненту вы можете предварительно переговорить с ним.</translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation>Передача на другую &amp;линию</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation>Подключить удалённую сторону на активную линию с удалённой стороной на другой линии.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Anonymous</source>\n        <translation>Неизвестный</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Внимание:</translation>\n    </message>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Ошибка создания файла журнала: %1 .</translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Не могу открыть файл для чтения: %1</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Ошибка файловой системы при чтении файла %1 .</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Не могу открыть файл для записи: %1</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Ошибка файловой системы при записи файла %1 .</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>Большое число ошибок сокета.</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Собрано с поддержкой для:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>При участии:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Данная программа содержит следующее програмное обеспечение от третьих сторон:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation>* GSM кодек от Jutta Degener and Carsten Bormann, University of Berlin</translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation>* G.711/G.726 кодек от Sun Microsystems (public domain)</translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation>* iLBC реализация по RFC 3951 (www.ilbcfreeware.org)</translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation>* Часть из STUN проекта на http://sourceforge.net/projects/stun</translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation>* Часть из libsrv на http://libsrv.sourceforge.net/</translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation>Для RTP следующие динамические библиотеки были связаны:</translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Перевод на русский язык:  Ходоренко Михаил chodorenko@mail.ru, Логинов Алексей alexl@mageia.org</translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Директория %1 не существует.</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Не могу открыть файл %1 .</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>%1 не установлена в вашу домашнюю директорию.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Директория %1 (%2) не существует.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Не могу создать директорию %1 .</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>%1 уже запушен.\nфайл блокировки %2 уже существует.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>Не могу создать %1 .</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Синтаксическая ошибка в файле %1 .</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Ошибка создания резервной копии %1 в %2</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>неизвестное имя (устройство занято)</translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Устройство по умолчанию</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation>Не могу получить доступ к устройству звукового сигнала (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>Не могу получить доступ к аудиовыходу (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>Не могу получить доступ к  входу микрофона (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Не могу получить входящие TCP соединения.</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation>Перевод звонка - %1</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>Звуковая карта не может установить полнодуплексный режим.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>Не могу установить размер буфера на звуковой карте.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Звуковая карта не может быть установлена для %1 каналов.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation>Не могу настроить звуковую карту для 16-битной записи.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation>Не могу настроить звуковую карту для 16-битного проигрывания.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation>Не могу настроить частоту дискретизации звуковой карты %1</translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation>Ошибка открытия ALSA драйвера</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation>Не могу открыть ALSA драйвер для воспроизведения PCM</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>Не могу открыть ALSA драйвер для захвата PCM</translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>Не могу определить имя STUN сервера: %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>Вы находитесь за симметричным NAT.\nSTUN не работает.\nВы должны установить общедоступный IP-адрес в профиле пользователя\nи сопоставьте свои NAT (UDP) порты.</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>внешний IP: %1 --&gt; внутренний IP: %2 (SIP оповещение)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>внешний IP: %1-%2 --&gt; внутренний IP: %3-%4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>Не могу получить доступ к STUN серверу: %1</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Если вы находитесь за файерволом, вам нужно открыть следующие UDP порты.</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Порт %1 (SIP оповещение)</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Порты %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>Ошибка определения типа NAT через STUN.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Ошибка создания файла %1</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Ошибка записи данных в файл %1</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Ошибка отправки сообщения.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation>Не могу заблокировать %1 .</translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Профиль пользователя</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Профиль пользователя:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Выберите профиль пользователя для редактирования.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Пользователь</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>SIP сервер</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Голосовая почта</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Сообщения</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Доступность</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP аудио</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>SIP протокол</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Транспорт/NAT</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Формат адреса</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation>Таймеры</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Звуковые сигналы</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation>Скрипты</translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Безопасность</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Выберите категорию для просмотра и модификации настроек.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Принять и сохранить изменения.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Отменить все измения и закрыть окно.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>Учётная запись SIP</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>&amp;Имя пользователя*:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>SIP имя пользователя, предоставленное вашим провайдером.  Пользовательская часть вашего SIP адреса, &lt;b&gt;username&lt;/b&gt;@domain.com  может быть вашим телефонным номером.\n&lt;br&gt;&lt;br&gt;\nЭто поле является обязательным.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Доменная часть вашего SIP адреса, username@&lt;b&gt;domain.com&lt;/b&gt;. Вместо реального домена также может быть именем машины или IP адресом вашего &lt;b&gt;SIP proxy&lt;/b&gt;. Если вы звоните напрямую с компьютера на компьютер, то заполните именем или IP адресом вашей машины\n&lt;br&gt;&lt;br&gt;\nЭто поле является обязательным.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>Вы можете заполнить имя вашей организации. Когда вы будете осуществлять звонок, оно может быть показано удалённой стороне.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Это просто ваше полное имя, например, John Doe. Оно используется в качестве отображаемого имени. Когда Вы делаете звонок, это имя может быть указано в качестве вызывающей стороны.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Ваше имя:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>SIP аутентификация</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation>&amp;Область:</translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation>Аутентификационное &amp;имя:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Пароль:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation>Область аутентификации. Значение должно быть предоставлено вашим SIP провайдером. Если вы оставите это поле пустым, то Twinkle будет использовать имя пользователя и пароль для любой области, которая требует авторизации.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ваше аутентификационное имя для SIP. В большинстве случаем совпадает с именем пользователя SIP. Вы можете ввести здесь другое имя при необходимости.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ваш пароль для аутентификации.</translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation>Регистратор</translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation>&amp;Регистратор:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation>Имя компьютера, доменное имя или IP адрес вашего регистратора. Если вы используете исходящий прокси, совпадающий с вашим регистратором, вы можете оставить его пустым.</translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation>Время устаревание регистрации, после которого Twinkle перепошлёт запрос заново.</translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>секунд</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Ре&amp;гистрироваться при запуске</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation>Указывает, должен ли Twinkle автоматически регистрироваться, когда запускается профиль пользователя. Если отключить эту опцию, то соединения будут происходить напрямую между IP клиентами без обращения к SIP прокси.</translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation>Добавить q-значение к регистрации</translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation>Q-значение указывает приоритет вашего зарегистриванного устройства. Если кроме Twinkle вы регистрируете эту учётную запись на другом SIP  устройстве, то сеть может определять по этому значению какому устройству перенаправить вызов в первую очередь.</translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation>Q-значение может изменятся в пределах между 0.000 и 1.000. Большее значение равно большему приоритету (для примера 1 &gt; 0.1 &gt; 0.01).</translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation>Исходящий прокси</translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>&amp;Использовать исходящий прокси</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation>Указывает, должен ли Twinkle автоматически регистрироваться при запуске этого профиля. Вы должны отключить это, если хотите совершать прямые вызовы между IP-адресами без прокси-сервера SIP.</translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>Исходящий &amp;прокси:</translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation>&amp;Посылать запросы к прокси</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation>Запросы SIP обычно отправляются на адрес в контактах, которыми обмениваются во время разговора. Если это поле отмечено, то этот адрес игнорируется, и все запросы также отправляются на исходящие прокси.</translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation>&amp;Не посылать запрос к прокси если назначение может быть определено локально.</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation>Если вы отметите эту опцию, Twinkle сначала попытается определить SIP адрес как IP адрес. Если сможет, то SIP запрос будет послан напрямую. Только если он не сможет определить адрес, Twinkle пошлёт SIP запрос на прокси (замечание: запрос будет послан только в том случае, если вы также выбрали предыдущий параметр.)</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Имя сервера, доменное имя или IP адрес вашего исходящего прокси.</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation>Ко&amp;деки</translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation>Кодеки</translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Доступные кодеки:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation>G.711 A-law</translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation>G.711 u-law</translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation>GSM</translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation>speex-nb (8 кГц)</translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation>speex-wb (16 кГц)</translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation>speex-uwb (32 кГц)</translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Список доступных кодеков.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Переместите кодек из списка доступных кодеков в список активных кодеков.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Переместите кодек из списка активных кодеков в список доступных кодеков.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Активные кодеки:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation>Список активных кодеков. Эти кодеки будут использовании при согласовании медиа потоков при установке соединения. Очерёдность кодеков - это приоритетность их использования.</translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation>Перемещение кодеков вверх по списку увеличивает их приоритет при использовании.</translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation>Перемещение кодеков вниз по списку понижает их приоритет при использовании.</translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation>&amp;G.711/G.726 payload размер:</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation>Приоритетный payload размер для G.711 и G.726 кодеков.</translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation>мс</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation>&amp;Следовать настройкам кодеков удалённой стороны при входящем звонке</translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation>Alt+F</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation>&lt;p&gt;\nЕсли включено, Twinkle будет отдавать приоритет предложениям SDP при входящих вызовах. В частности, первый кодек, предлагаемый контрагентом, также входит в список локальных кодеков.\n&lt;p&gt;\nЕсли отключено, Twinkle использует первый кодек в своем собственном списке, который также поддерживается контрагентом.</translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation>Следовать &amp;настройкам кодеков удалённой стороны при исходящем звонке</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation>&lt;p&gt;Если включено, Twinkle будет управлять исходящим вызовом со списком предпочтительных кодеков у контрагента (ответ SDP). В частности, первый кодек будет использоваться в списке предпочтительных кодеков-контрагентов, который также поддерживается текущим языком Twinkle.\n&lt;p&gt;\nЕсли отключено, Twinkle использует первый кодек из своего собственного списка, который также поддерживается контрагентом. То есть. указан в списке SDP-ответов.</translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation>&amp;iLBC</translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation>iLBC</translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation>i&amp;LBC payload тип:</translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation>iLBC &amp;payload размер (мс):</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для iLBC.</translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation>20</translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation>30</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation>Приоритетный payload размер для iLBC.</translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation>&amp;Speex</translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation>Speex</translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation>&amp;Улучшенное восприятие</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation>Улучшение воспринимаемого качества звука является частью декодера, который пытается уменьшить (воспринимаемый) шум, создаваемый процессом кодирования/декодирования. В большинстве случаев эта функция приводит к большему объективно отклоняющемуся звуку от оригинала (SNR), но она по-прежнему звучит лучше (субъективные улучшения).</translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation>&amp;Крайний широкополосный payload тип:</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation>&amp;Широкополосный payload тип:</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation>Переменная полоса пропускания (VBR) позволяет кодеку настраивать объём данных, необходимых для передачи вызова персонажу аудиосигнала. В то время как, например, некоторые резкие гласные или очень переменные проходы нуждаются в большой частоте дискретизации и высокой битовой скорости, поэтому достаточно мягких согласных с небольшим количеством потока данных. Благодаря VBR можно добиться лучшего качества звука с заданной скоростью передачи данных или с более низкой скоростью передачи данных для данного качества связи. Недостатком является то, что с качеством вы не можете предсказать, какой большой битрейт будет на самом деле. А также в приложениях реального времени (таких как VoIP) максимальная пропускная способность, которую должен обрабатывать канал связи, является решающей.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для широкополосного speex.</translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation>&amp;Сложность:</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation>Непоследовательное вещание - это расширение функций VAD/VBR, когда можно полностью прекратить отправку данных в случае молчания.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для узкополосного speex.</translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation>В случае Speex можно изменить сложность кодировщика. Он используется для определения глубины поиска в диапазоне от 1 до 10. Аналогичный принцип вводится в программах сжатия gzip и bzip2 с выбором от -1 до -9. В нормальных условиях шум при сложности составляет от 1 до 1 дБ, выше, чем комплекс 10. Однако загрузка процессора примерно в 5 раз выше, чем сложность 1. На практике установка между 2 и 4. хороша. Более высокие настройки подходят для передачи тонов DTMF или музыкального сигнала.</translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation>&amp;Узкополосный payload тип:</translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation>G.726</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation>G.726 &amp;40 кбит payload тип:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для G.726 40 кбит.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для G.726 32 кбит.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation>G.726 &amp;24 кбит payload тип:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для G.726 24 кбит.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation>G.726 &amp;32 кбит payload тип:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для G.726 16 кбит.</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation>G.726 &amp;16 кбит payload тип:</translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation>&amp;Кодовое слово порядка опакечивания:</translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation>RFC 3551</translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation>ATM AAL2</translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation>Существует два метода упаковки пакетов данных G.726 в пакет RTP. По умолчанию это RFC 3551. Некоторые провайдеры SIP используют ATM AAL2. Если передача звука скомпрометирована при использовании кодека G.726, вы можете попробовать различные настройки здесь.</translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation>DT&amp;MF</translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation>Значение динамического типа (96 или выше) чтобы использовать для событий DTMF (RFC 2833).</translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation>DTMF ур&amp;овень:</translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation>Уровень мощности тона DTMF в дБ.</translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation>Пауза после DTMF тона.</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation>DTMF &amp;длина:</translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation>DTMF payload &amp;тип:</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation>DTMF &amp;пауза:</translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation>дБ</translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation>Продолжительность посылки DTMF тона.</translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>DTMF т&amp;ранспорт:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation>Авто</translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation>RFC 2833</translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation>Внутриполосный</translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation>Внеполосный (SIP INFO)</translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Отправляет тональные сигналы DTMF в виде телефонных событий в соответствии с RFC 2833.&lt;/p&gt;\n&lt;h2&gt;Аудио&lt;/h2&gt;\n&lt;p&gt;Он передает DTMF-диапазон (фактические тона, которые Twinkle добавляет к аудиосигналу).&lt;/p&gt;\n&lt;h2&gt;Авто&lt;/h2&gt;\n&lt;p&gt;Если контрагент поддерживает RFC 2833, тональные сигналы DTMF используются в соответствии со стандартом RFC 2833, в противном случае как в полосе.\n&lt;/p&gt;\n&lt;h2&gt;SIP INFO&lt;/h2&gt;\n&lt;p&gt;\nОтправляет DTMF вне диапазона по запросу SIP INFO.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Основные</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Опции протокола</translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation>RFC 2543</translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation>RFC 3264</translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation>Указывает, используется ли RFC 2543 (настройка IP-адреса в SDP до 0.0.0.0) или RFC 3264 (используйте направленные атрибуты в SDP) для удержания вызова.</translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation>&amp;Позволить отсутствие заголовка контакта в 200 OK для регистрации</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation>Ответ 200 OK для запроса REGISTER должен содержать заголовок Contact. Некоторые поставщики либо не отправляют заголовок Contact, либо ошибаются. Эта опция допускает такое отклонение от спецификаций.</translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation>Заголовок &amp;Max-Forwards обязателен</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation>Согласно RFC3261, требуется заголовок Max-Forward. Часто, однако, он не отправляется. Если включено, Twinkle отклонит запросы SIP, которые не содержит заголовок Max-Forward.</translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation>&amp;Добавлять время окончания регистрации в заголовок контакта</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation>Время истечения срока регистрации в запросе REGISTER может передаваться как в заголовке Contact, так и в заголовке Expires. Если включено, Twinkle отправляет его в заголовок Contact, иначе в заголовке Expires.</translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation>&amp;Использовать компактные имена заголовков</translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation>Если включено, короткая форма будет использоваться для заголовков, если таковые имеются.</translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation>Разрешать изменения SDP при установке вызова</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;SIP UAS может отправлять SDP в 1XX-ответе на звук вызова, например мелодию звонка. Если вызов получен, SIP UAS согласно RFC 3261 должен отправить тот же SDP в ответ 200 OK. После того, как SDP будет принят, все ответы SDP должны быть отброшены.&lt;/p&gt;\n&lt;p&gt;Если SDP разрешено изменять во время вызова, Twinkle не будет игнорировать SDP в следующих ответах, но при необходимости изменит свойства потока RTP. Изменённый SDP должен иметь новый номер версии в строке «o =».&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation>Используйте доменное &amp;имя для создания значения уникального заголовка контакта</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Если включено, Twinkle создаёт уникальное значение для заголовка контакта, используя комбинацию имени пользователя SIP и имени домена:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nЭто позволяет использовать несколько профилей пользователей с одним и тем же именем пользователя, но с разными доменами, которые затем имеют разные контактные адреса, и поэтому эти профили могут использоваться одновременно.\n&lt;/p&gt;\n&lt;p&gt;\nНекоторые прокси-серверы не могут обрабатывать такие заголовки контактов. Если этот параметр отключен, Twinkle отправляет заголовок контакта в следующем формате:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nЭтот формат используется большинством SIP-телефонов.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation>&amp;Кодировать канал, маршрут, запись маршрута как список</translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation>Via, Route and Record-Route-заголовки могут быть закодированы как список значений, разделённых запятыми, или как одно значение, каждое в своем заголовке.</translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation>Перенаправление</translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation>&amp;Разрешить перенаправление</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation>Указывает, должен ли Twinkle перенаправить запрос на 3XX-ответ.</translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation>&amp;Спрашивать права пользователя для перенаправления</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation>Если включено, Twinkle спрашивает, запрашивает ли пользователь 3XX, чтобы исходящий вызов был перенаправлен на новый пункт назначения.</translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation>Номер переадресации исходящих вызовов, после которого Twinkle прекращает попытки установить соединение. Предотвращает перегрузку при перенаправлении.</translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation>Расширения SIP</translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>отключен</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>поддержан</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>необходим</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>предпочтён</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation>Определяет, как продвигать расширение 100rel (PRACK):&lt;br&gt;&lt;br&gt;\n&lt;b&gt;выключено&lt;/b&gt;: расширение 100rel не поддерживается\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;включено&lt;/b&gt;: поддерживается 100rel (добавлено в исходящий INVITE в качестве поддерживаемого заголовка). Затем контрагент может запросить PRACK для ответа 1xx.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;требуется&lt;/b&gt;: требуется 100rel. Запрос вставляется в требуемый заголовок в исходящем INVITE. Если INVITE отмечен как «100rel», тогда Twinkle будет требовать PRACK в ответе 1xx, если 100rel-копия не поддерживает соединение.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;предпочитаемый&lt;/b&gt;. Также как «требуется», на всякий случай, если вызов завершился неудачно, потому что партнер не поддерживает 100rel (ответ 420), тогда Twinkle пытается повторно подключить вызов, не требуя 100rel.</translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation>&amp;100 выпусков (PRACK):</translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Замены</translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation>Рефер</translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation>Перевод звонка (рефер)</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation>Если включено, Twinkle следует за обратным запросом (REFER), чтобы перенаправить вызов на другой адрес.</translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation>С&amp;прашивать права пользователя для передачи</translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation>Alt+K</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation>Если включено, Twinkle запрашивает запрос пересылки при получении запроса (REFER).</translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation>&amp;Удерживать звонок с посредником при настройке звонка в цель передачи</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+W</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation>Если включено, Twinkle удерживает существующий вызов, когда входящий запрос REFER при перенаправлении.</translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation>&amp;Удерживать вызов с посредником перед отправкой рефера</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation>Указывает, должен ли Twinkle удерживать вызов при пересылке.</translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation>&amp;Автоматически обновлять подписку на событие рефера пока перевод звонка не окончен</translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation>Во время переадресации вызова агент пересылки отправляет сообщение NOTIFY на маршруте пересылки на тот, чей вызов переадресован. Но только на короткое время. Это определяется тем, кто перенаправляется. Если этот параметр включен, (Twinkle) автоматически отправляет SUBCRIBE, чтобы продлить это время до завершения процесса перенаправления.</translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation>Добавлять рефера в AoR (адрес записи)</translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Защита</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Опции защиты</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation>&amp;Отправлять заголовок P-Preferred-Identity когда личность пользователя скрывается</translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Включать заголовок P-Preferred-Identity с вашей личностью в запросе INVITE для вызова со скрытием личности.</translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>Транспорт SIP</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation>UDP</translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation>TCP</translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Режим передачи для SIP. В автоматическом режиме размер сообщения определяет, какой транспортный протокол используется. Сообщения, превышающие границы UDP, отправляются через TCP. Меньшие сообщения отправляются через UDP.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>Протокол т&amp;ранспорта:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation>&amp;Порог UDP:</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Сообщения, превышающие указанную границу, отправляются через TCP. Меньшие сообщения через UDP.</translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>Пересечение NAT</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation>Пересечение &amp;NAT не нужно</translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation>Выберите этот вариант, если между Twinkle и вашим SIP-прокси отсутствует NAT, или если ваш провайдер может самостоятельно преодолеть NAT.</translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation>&amp;Использовать статически настроенный публичный IP-адрес внутри SIP сообщений</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation>Если включено, Twinkle будет использовать общедоступный IP-адрес внутри сообщений SIP (в заголовке SIP и теле SDP) вместо IP-адреса вашего сетевого интерфейса.&lt;br&gt;&lt;br&gt;\nЕсли вы выберете эту опцию, вам также необходимо направить соответствующие порты RTP на ваш компьютер на устройстве NAT.</translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation>Выберите эту опцию если ваш провайдер SIP предлагает STUN-сервер для обхода NAT.</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Имя сервера, доменное имя или IP адрес STUN сервера.</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>&amp;Публичный IP-адрес:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation>Публичный IP-адрес вашего NAT.</translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Номера телефонов</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation>&amp;Отображать только пользовательскую часть URI для телефонного номера</translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation>Если URI указывает номер телефона, отображается только пользовательская часть адреса. К примеру, если звонок поступает из sip: 12345@voipprovider.com, Twinkle будет отображать только как «12345». URI считается номером телефона, если он содержит параметр «пользователь=телефон» или если активна следующая опция, а пользовательская часть адреса является числовой.</translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation>Телефонный номер - это &amp;URI с числовой пользовательской частью</translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation>Если этот параметр включен, Twinkle рассматривает каждый SIP-адрес в качестве номера телефона, если он содержит только цифры, *, #, + и специальные символы в исходящем сообщении. В исходящих SIP-сообщениях Twinkle отмечает такие адреса с параметром «пользователь=телефон».</translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation>&amp;Удалять спецсимволы из строк числового набора</translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation>Чтобы упростить чтение чисел, их часто печатают с использованием специальных символов, таких как «(», «)», «» (пустой символ), «-». При наборе номера, особенно любых SIP-адресах, эти символы могут не передаваться. Чтобы упростить набор для копирования/вставки, Twinkle может автоматически удалять эти символы при наборе.</translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation>&amp;Спецсимволы:</translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation>Специальные символы, которые могут быть частью телефонного номера для красивого форматирования, но должны быть удалены при наборе номера.</translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation>Преобразователь номеров</translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation>Совпадение</translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Замена</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation>Переместить выбранное правило преобразования номера вверх в списке.</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation>Переместить выбранное правило преобразования номера вниз в списке.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Добавить</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation>Добавить правило преобразования номера.</translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>&amp;Удалить</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation>Удалить выбранное правило преобразования номера.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Редактировать</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation>Редактировать выбранное правило преобразования номера.</translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation>Напечатайте телефонный номер здесь и нажмите кнопку Теста чтобы увидеть, как он был преобразован используя список правил преобразования номера.</translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Тест</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation>Протестировать как номер был преобразован используя правила преобразования номера.</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation>Этот таймер запускается при приёме входящего вызова. Если вызов не получен до конца времени задержки, то Twinkle отклоняет вызов с сообщением «480 пользователь не отвечает».</translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation>Охранять NAT д&amp;ействующим:</translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>&amp;Нет ответа:</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Выберите файл гудка вызова.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Выберите файл сигнала вызова.</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation>Гудок &amp;вызова:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nВведите имя .wav для звонка, вызываемого для этого пользователя.\n&lt;/p&gt;\n&lt;p&gt;\nЭтот тон заменяет мелодию звонка для вызывающего абонента из системной настройки.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nВведите имя .wav-файла для мелодии звонка в этом профиле пользователя.\n&lt;/p&gt;\n&lt;p&gt;\nЭтот параметр заменяет мелодию звонка на системные настройки.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Сигнал вызова:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт будет запущен, если вызов будет завершён вами.\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех заголовков SIP, отправленных на запросы SIP BYE, будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; содержит запрос-URI метода BYE.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation>Выберите файл скрипта.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт будет запущен, если входящий вызов не будет принят. То есть. звон заканчивается без «снятия».\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех заголовков SIP, отправленных в ответ на отказ SIP, будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; содержит код состояния, отправленный в ответ на отказ SIP.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; содержит «фразу причины», которая является причиной ошибки с использованием обычного текста.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт запускается, когда вызов завершается контрагентом.\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех SIP-записей SIP запросов SIP будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; содержит сигнал запроса-URI.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЗдесь вы можете редактировать, как Twinkle выполняет входящие звонки. Twinkle может запускать скрипт при входящих вызовах. В зависимости от вывода Twinkle он принимает, отклоняет или перенаправляет вызов. Сценарий также может повлиять на мелодию звонка. Это может быть любая исполняемая программа.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Примечание:&lt;/b&gt; Twinkle перестанет работать во время запуска скрипта. Мы рекомендуем, чтобы сценарий не запускался более 200 мс. Если вам нужно больше времени, вы можете отправить &lt;b&gt;end&lt;/b&gt; после отправки параметров и продолжения работы. Twinkle будет продолжать работать сам после принятия параметра &lt;b&gt;end&lt;/b&gt;.\n&lt;/p&gt;\n&lt;p&gt;\nС помощью вашего сценария вы можете настроить обработку вызовов, выведя один или несколько из следующих параметров в стандартный вывод. Каждый параметр должен находиться в отдельной строке.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;строка&amp;gt;&lt;br&gt;\ncontact=&amp;lt;адрес для перенаправления&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;имя вызывающего абонента для отображения&amp;gt;&lt;br&gt;\nringtone=&amp;lt;имя файла .wav&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;сообщение для отображения на дисплее&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Параметры&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - продолжить обычную обработку вызовов (по умолчанию)&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - отклонить вызов&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - отклонить вызов, используя «не беспокоить»&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - перенаправление вызовов на &lt;b&gt;контакт&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - автоматически принимать вызовы&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nКогда сценарий не записывает действие в stdout, действие по умолчанию продолжается.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nС параметром reason вы можете установить строку причины для отклонения или dnd. Это может быть показано дальнему конечному пользователю.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nЭтот параметр переопределит отображаемое имя вызывающего абонента.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nПараметр ringtone указывает файл .wav, который будет воспроизводиться в качестве мелодии звонка, когда действие будет продолжено.\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nЗначения всех входящих INVITE SIP-заголовков будут переданы этому скрипту. Структура переменных: &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; Например, SIP_FROM содержит значение «from header».\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. Запрос-URI INVITE будет принят&lt;b&gt;SIPREQUEST_URI&lt;/b&gt;Имя профиля пользователя будет принято&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт будет запущен, если вызов будет принят контрагентом.\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех заголовков SIP, входящих в сообщение «200 OK», будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; содержит &quot;фразу причины&quot;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт будет запущен, если будет получен входящий вызов.\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех исходящих ответов «200 OK» заголовка SIP будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; содержит &quot;фразу причины&quot;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation>Вызов про&amp;изошёл локально:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт будет работать, если исходящие вызовы не могут быть выполнены. Например, из-за таймаута, DND,\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех SIP-заголовков полученных ответов SIP будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; содержит код состояния, отправленный сбойным ответом SIP.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; включает в себя «фразу причины», сообщение об ошибке обычного текста.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nЭтот скрипт будет запущен, если будут сделаны какие-либо вызовы.\n&lt;/p&gt;\n&lt;h2&gt;Системные переменные&lt;/h2&gt;\n&lt;p&gt;\nСодержимое всех заголовков SIP, отправленных запросами SIP INVITE, будет передано этому скрипту с использованием следующих системных переменных:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; содержит запрос запроса запроса INVITE.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; содержит имя активного профиля пользователя.</translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation>Исход&amp;ящий звонок отвечен:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation>Входя&amp;щий звонок неудачен:</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>&amp;Входящий звонок:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation>Вызов прои&amp;зошёл удалённо:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation>В&amp;ходящий звонок отвечен:</translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>&amp;Исходящий звонок:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation>И&amp;сходящий звонок неудачен:</translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>&amp;Включить ZRTP/SRTP шифрование</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>ZRTP настройки</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation>&amp;Только шифровать аудио если удалённая сторона свидетельствует о поддержке ZRTP в SDP</translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation>Контрагент, поддерживающий ZRTP, может сообщить об этом в начале разговора. Если этот параметр включен, Twinkle пытается использовать шифрованную передачу вызова в таких случаях.</translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation>&amp;Определять поддержку SRTP в SDP</translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation>Если включено, Twinkle сообщает контрагенту при вызове вызов SDP, который поддерживает ZRTP.</translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation>&amp;Всплывающее предупреждение когда удалённая сторона отключает шифрование во время вызова</translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation>Контрагент может отправить команду ZRTP-go-clear во время зашифрованного вызова для отмены шифрования. Если этот параметр включен, Twinkle немедленно предупреждает вас об этой проблеме безопасности.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation>&amp;Адрес голосовой почты:</translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation>SIP адрес или телефонный номер для доступа к вашей голосовой почте.</translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation>&amp;MWI тип:</translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation>Продолжительность &amp;подписки:</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation>Почтовый &amp;ящик:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation>Имя сервера, доменное имя или IP адрес вашего сервера голосовой почты.</translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation>Ваше имя пользователя для доступа к вашей голосовой почте.</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation>Почтовый &amp;сервер:</translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation>Через исходящий &amp;прокси</translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation>Если включено, Twinkle отправляет SIP-сообщения на сервер голосовой почты через исходящие прокси.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation>&amp;Максимальное число сессий:</translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation>Это число ограничивает количество открытых сеансов мгновенных сообщений, при превышении этого значения новые сессии будут отвергнуты.</translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Ваша доступность</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>&amp;Публиковать доступность при запуске</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Публикует вашу доступность при запуске.</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation>Интервал &amp;обновления публикации (сек):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation>Частота обновлений публикации доступности.</translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation>Доступность друзей</translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation>Интервал обновления &amp;подписки (сек):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation>Частота обновлений подписки.</translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation>Динамический payload тип %1 использован более, чем один раз.</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Вы должны заполнить имя пользователя для вашей учётной записи SIP.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Вы должны заполнить доменное имя для вашей учётной записи SIP.\nЭто может быть имя сервера или IP адрес вашего компьютера при прямых звонках.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Неправильный домен.</translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Неправильное имя пользователя.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation>Неправильное значение для регистратора.</translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation>Неправильное значение для исходящего прокси.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation>Вы должны заполнить имя пользователя почтового ящика.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation>Вы должны заполнить сервер почтового ящика</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation>Неверный почтовый сервер.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation>Неправильное имя пользователя для почтового ящика.</translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation>Значение для внешнего IP адреса отсутствует.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Неправильное значение для STUN сервера.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Сигналы вызова</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Выберите сигнал вызова</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Гудок вызова</translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Все файлы</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation>Выберите скрипт входящего звонка</translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation>Выберите скрипт входящего отвеченного звонка</translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation>Выберите скрипт входящего звонка с ошибкой</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation>Выберите скрипт исходящего звонка</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation>Выберите скрипт исходящего отвеченного звонка</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation>Выберите скрипт исходящего звонка с ошибкой</translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation>Выберите локальный скрипт</translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation>Выберите удалённый скрипт</translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>%1 конвертирован в %2</translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation>П&amp;остоянные TCP соединения</translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation>Храните открытые TCP-соединения, созданные во время регистрации, чтобы прокси-сервер SIP мог использовать это соединение для передачи входящих запросов. Приложение отправляет пакеты ping для определения того, продолжает ли соединение оставаться активным.</translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation>&amp;Посылать оповещение о наборе текста при вводе сообщения.</translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation>Twinkle посылает оповещения при наборе сообщения. Получатель будет видеть когда вы печатате.</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation>AKA AM&amp;F:</translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation>A&amp;KA OP:</translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation>Параметры управления аутентификацией для аутентификации AKAv1-MD5.</translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation>Операционная версия ключа аутентификации AKAv1-MD5.</translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation>Пред&amp;обработка</translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation>Предварительная обработка (улучшает качество звука на удалённой стороне)</translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation>&amp;Автоматический контроль усиления</translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation>Из-за большой разницы в громкости записи в разных настройках была введена функция автоматического регулирования усиления (AGC). AGC позволяет установить уровень сигнала на заданное значение. Это не требует ручной регулировки громкости микрофона вручную. Другим преимуществом является то, что настройка громкости микрофона обычно находится на более низком (консервативном) уровне, предотвращая вырезание большого количества звука.</translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation>Автоматический контроль уровня &amp;усиления:</translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation>Уровень автоматического регулирования громкости - это процент от максимального объёма микрофона. Рекомендуемое значение составляет около 25%.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation>&amp;Определение голосовой активности</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation>Если включено, распознавание голоса определяет, есть ли на аудиовходе голос или фоновый шум/шум.</translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation>&amp;Понижение шума</translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation>Подавление помех может использоваться для уменьшения помех во входном сигнале. Это приводит к улучшению качества речи.</translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation>&amp;Отмена акустического эха</translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation>Если при любой VoIP коммуникации звук воспроизводится на громкоговорителях громкой связи, он может развернуться в комнате и вернуться в микрофон. Если этот сигнал отправляется обратно вызывающему абоненту, он слышит эхо своего голоса. Функция подавления акустического эха предназначена для подавления этих звуков перед их отправкой вызывающему абоненту. Важно отметить, что эта функция предназначена для повышения качества передачи голоса, с другой стороны.</translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation>Переменный &amp;битрейт</translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation>&amp;Прерывистая передача</translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation>&amp;Качество:</translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation>Speex - это кодек потерь, что означает, что за счёт качества достигается снижение потока данных. В отличие от других голосовых кодеков, можно установить компромисс между качеством и битрейтом. Процесс кодирования для этого кодека обычно контролируется установкой параметра качества в диапазоне от 0 до 10.</translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation>байт</translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation>Использовать tel-URI для телефонного &amp;номера</translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation>Увеличьте номер телефона удалённого доступа до tel-URI, а не sip-URI.</translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation>&amp;Принимать запрос переадресации звонков (входящий рефер)</translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation>Разрешать перевод звонка когда идёт консультация</translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation>Если вы выполняете вспомогательную передачу, вы обычно переключаете вызов после консультации с контрагентом. После включения этой опции вы можете передать вызов, пока консультация всё ещё продолжается. Это нестандартная реализация, которая может не работать должным образом со всеми устройствами SIP.</translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation>Включить охрану NAT д&amp;ействующим</translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation>Отправлять UDP-пакеты для поддержки соединения через NAT.</translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation>Если у вас включен STUN или NAT, то Twinkle отправит пакеты обслуживания на этот интервал, чтобы сохранить отображение на вашем NAT.</translation>\n    </message>\n    <message>\n        <source>Do&amp;main*:</source>\n        <translation>До&amp;мен*:</translation>\n    </message>\n    <message>\n        <source>Organi&amp;zation:</source>\n        <translation>Органи&amp;зация:</translation>\n    </message>\n    <message>\n        <source>E&amp;xpiry:</source>\n        <translation>У&amp;старевание:</translation>\n    </message>\n    <message>\n        <source>Call Hold &amp;variant:</source>\n        <translation>&amp;Вариант удержания вызова:</translation>\n    </message>\n    <message>\n        <source>&amp;Max redirections:</source>\n        <translation>&amp;Максимум перенаправлений:</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extension is supported.</source>\n        <translation>Указывает, поддерживается ли расширение Replaces.</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</source>\n        <translation>Приходящая передача вызова должна использовать URI контакта в качестве адресата для связи нового соединения, перенаправленного на контрагента. Однако этот адрес не может быть глобально действительным. В качестве альтернативы можно использовать AoR (адрес записи). Недостатком является то, что с несколькими конечными устройствами AoR однозначен, тогда как URI контакта направляется на одно устройство.</translation>\n    </message>\n    <message>\n        <source>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Включать заголовок P-Asserted-Identity с вашей личностью в запросе INVITE для вызова со скрытием личности.</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Asserted-Identity header when hiding user identity</source>\n        <translation>&amp;Отправлять заголовок P-Asserted-Identity когда личность пользователя скрывается</translation>\n    </message>\n    <message>\n        <source>Use STUN (does not wor&amp;k for incoming TCP)</source>\n        <translation>Использовать STUN (не &amp;работает для входящего TCP)</translation>\n    </message>\n    <message>\n        <source>STUN ser&amp;ver:</source>\n        <translation>STUN сер&amp;вер:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects '00' instead of the '+', or you are at the office and all your numbers need to be prefixed with a '9' to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the '+31' and replace it by a '0'. For dialling numbers abroad you just want to replace the '+' by '00'.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation>&lt;p&gt;\nЧасто количество телефонных номеров, ожидаемых от провайдера VoIP, не совпадает с количеством, сохранённым в каталоге. К примеру, для номеров, начинающихся с «+» и национального кода страны, ваш провайдер ожидает «+» вместо «00». Или, если вы подключены к локальной сети SIP, вам необходимо предварительно указать номер доступа.\nМожно установить общепринятые правила для преобразования телефонных номеров с использованием шаблонов поиска и свопинга (с помощью регулярных выражений и языка Perl).\n&lt;/p&gt;\n&lt;p&gt;\nКаждый раз, когда вы набираете номер, Twinkle пытается найти соответствующие номера в списке шаблонов поиска для чисел со стороны пользователя SIP-адреса. Первое найденное совпадающее выражение - это исходное число, а позиция в («») в поисковом выражении (например, «([0-9] *)» для «любого числа чисел» заменяется символами соответствующих переменных. К примеру, «1 доллар» для первой позиции. См. также `man 7 regex` или konqueror:«#regex». Если совпадающий шаблон не найден, число остаётся неизменным.\n&lt;/p&gt;\n&lt;p&gt;\nПравила будут также применяться к номерам входящих вызовов. В зависимости от установленных правил они будут преобразованы в нужный формат.\n&lt;/p&gt;\n&lt;h3&gt;1. пример&lt;/h3&gt;\n&lt;p&gt;\nК примеру, ваш национальный код «420» для Чешской Республики, и в вашем каталоге у вас также есть много национальных номеров, хранящихся в международном формате. Например, позвоните по телефону +420 777 2345678. Тем не менее, провайдер VoIP ожидает по национальному звонку 0577 2345678. Вы хотите заменить «+420» на «0» и заменить «+» на «00» на внешние вызовы.\n&lt;/p&gt;\n&lt;p&gt;\nВ этом порядке требуются следующие правила:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nПоисковый запрос = \\ +49 ([0-9] *), Замена = 0 $ 1&lt;br&gt;\nПоисковый запрос = \\ + ([0-9] *), Замена = 00 $ 1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;2. пример&lt;/h3&gt;\n&lt;p&gt;\nВы находитесь на телефонной станции, и все номера с 0 в качестве первой цифры должны быть пронумерованы 9.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nПоисковый запрос = 0 [0-9] *, Замена = 9 $ &amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n($&amp; - специальная переменная, в которой хранится весь исходный номер)&lt;br&gt;\nПримечание. Это правило нельзя задать просто как третье правило для двух правил в предыдущем примере. Это будет использовать только первое, чтобы соответствовать поиску. Вместо этого замена в Правилах 1 и 2 должна быть изменена - «57$1» и «577$1»,</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</source>\n        <translation>Если включено, Twinkle пытается зашифровать аудиоданные во всех исходящих и входящих вызовах. Для того, чтобы вызов был зашифрован, контрагент должен также поддерживать шифрование ZRTP / SRTP. В противном случае вызов остаётся незашифрованным.</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation>&lt;H2&gt;Тип отображения ожидающего сообщения&lt;/H2&gt;\n&lt;p&gt;\nЕсли ваш SIP-провайдер предлагает оповещения о сохранённых сообщениях в вашей голосовой почте, Twinkle может рассказать вам о новых и уже услышанных сообщениях в вашей голосовой почте. Спросите своего провайдера, какой тип ожидающего сообщения используется\n&lt;/p&gt;\n&lt;H3&gt;Незапрошенный&lt;/H3&gt;\n&lt;p&gt;\nAsterisk поддерживает незапрошенные ожидающие сообщения.\n&lt;/p&gt;\n&lt;H3&gt;Запрошенный&lt;/H3&gt;\n&lt;p&gt;\nЗапрошенная индикация ожидающего сообщения в соответствии с RFC 3842.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Unsolicited</source>\n        <translation>Незапрошенный</translation>\n    </message>\n    <message>\n        <source>Solicited</source>\n        <translation>Запрошенный</translation>\n    </message>\n    <message>\n        <source>Solicited MWI</source>\n        <translation>Запрошенный MWI</translation>\n    </message>\n    <message>\n        <source>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation>Для запрошенного MWI, конечное устройство сообщает серверу о получении сообщения в течение определённого периода времени, и до истечения этого времени регистрация должна возобновиться.</translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Мастер</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Имя сервера, доменное имя или IP адрес STUN сервера.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN сервер:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>SIP имя пользователя, предоставленное вашим провайдером.  Пользовательская часть вашего SIP адреса, &lt;b&gt;username&lt;/b&gt;@domain.com  может быть вашим телефонным номером.\n&lt;br&gt;&lt;br&gt;\nЭто поле является обязательным.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Домен*:</translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Выберите вашего провайдера SIP услуг. Если вашего провайдера нет в списке, тогда выберите  &lt;b&gt;Другой&lt;/b&gt; и заполните поля данными полученными от вашего провайдера.&lt;br&gt;&lt;br&gt;\nЕсли вы выбрали одного из предопределённых провайдеров SIP услуг,  то вы должны только заполнить ваше имя, имя пользователя, имя аутентификации и пароль.</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>&amp;Аутентификационное имя:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Ваше имя:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ваше имя SIP аутентификации. Довольно часто совпадает с именем пользователя SIP. Однако может иметь и другое значение.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Доменная часть вашего SIP адреса , username@&lt;b&gt;domain.com&lt;/b&gt;. Вместо реального домена также может быть именем машины или IP адресом вашего &lt;b&gt;SIP proxy&lt;/b&gt;. Если вы звоните напрямую с компьютера на компьютер, то заполните именем или IP адресом вашей машины\n&lt;br&gt;&lt;br&gt;\nЭто поле является обязательным.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Это просто ваше полное имя, например John Doe. Оно используется в качестве отображаемого имени. Когда Вы делаете звонок, это имя может быть указано в качестве вызывающей стороны.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>SIP про&amp;кси:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>Имя компьютера, доменное имя или IP адрес вашего SIP прокси. Если оно совпадает со значением вашего домена, вы можете оставить его пустым.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>Провайдер &amp;SIP услуг:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Пароль:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>&amp;Имя пользователя*:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ваш пароль для аутентификации.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;Принять</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Отмена</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Отсутствует (Прямое IP - IP соединение)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Другой</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Помощник профиля пользователя:</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Вы должны заполнить имя пользователя для вашей учётной записи SIP.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Вы должны заполнить имя домена для вашей учётной записи SIP.\nЭто может быть имя или IP адрес  вашего компьютера если вы выполняете прямые звонки между компьютерами.</translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Неправильное значение для SIP прокси.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Неправильное значение для STUN сервера.</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Да</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Нет</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation>Ответить</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Отклонить</translation>\n    </message>\n</context>\n</TS>"
  },
  {
    "path": "src/gui/lang/twinkle_sk.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"sk\" sourcelanguage=\"en\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Záznam v adresári</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation>P&amp;oznámka:</translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation>Prostredné meno.</translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Krstné meno.</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>&amp;Krstné meno:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation>Sem môžete vložiť akúkoľvek poznámku o kontakte.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefón:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation>&amp;Prostredné meno:</translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Telefónne číslo alebo SIP adresa kontaktu.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Priezvisko kontaktu.</translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Priezvisko:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Musíte zadať meno.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>Musíte zadať telefónne číslo alebo SIP adresu.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation>Meno</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation>Telefón</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation>Poznámka</translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Prihlásenie</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation>user</translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>Používateľ, ktorý sa má prihlásiť.</translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation>profile</translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Profil používateľa, pre ktorého je vyžadované prihlásenie.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil používateľa:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Používateľ:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Heslo:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Vaše heslo na prihlásenie.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Vaše prihlasovacie SIP meno. Často je rovnaké ako vaše SIP meno, no môže sa aj líšiť.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>Meno &amp;používateľa:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation>Prihlásenie vyžadované pre realm:</translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation>realm</translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation>Realm, ku ktorému je vyžadované prihlásenie.</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Kontakt</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vybrať adresu z adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefón:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Meno vášho kontaktu.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>Zobraziť dostupno&amp;sť</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Vyberte túto možnosť, ak chcete vidieť dostupnosť vytvoreného kontaktu. Toto bude fungovať iba vtedy, ak váš poskytovateľ ponúka túto možnosť.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Meno:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>SIP adresa vášho kontaktu.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Musíte zadať meno.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Neplatné telefónne číslo.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Nepodarilo sa uložiť zoznam kontaktov: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Dostupnosť</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>neznáma</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>offline</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>online</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>požiadavka odmietnutá</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>nie je zverejnená</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>zverejnenie zlyhalo</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>požiadavka zlyhala</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Pravým kliknutím pridáte kontakt.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>Nepodarilo sa získať prístup ku zvukovej karte</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>Nepodarilo sa vytvoriť UDP socket (RTP) na porte %1</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Nepodarilo sa vytvoriť vlákno pre nahrávanie zvuku.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Nepodarilo sa vytvoriť vlákno pre prehrávanie zvuku.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>lokálny používateľ</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>vzdialený používateľ</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>chyba</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>neznámy</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>prichádzajúce</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>odchádzajúce</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Odregistrovať</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>odregistrovať všetky zariadenia</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"obsolete\">Zrušiť (Es&amp;c)</translation>\n    </message>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation>Zadajte ID vášho účtu.</translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation>Zadajte váš kód PIN.</translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation>Profil používateľa s menom %1 už existuje.</translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation>Twinkle - DTMF</translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Numerická klávesnica</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation>2</translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation>3</translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>Funkčný kláves A. Používaný zriedkavo.</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation>4</translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation>5</translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation>6</translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>Funkčný kláves B. Používaný zriedkavo.</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation>7</translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation>8</translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation>9</translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>Funkčný kláves C. Používaný zriedkavo.</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Hviezdička (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation>0</translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Mriežka (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>Funkčný kláves D. Používaný zriedkavo.</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation>1</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Zavrieť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n</context>\n<context>\n    <name>FreeDeskSysTray</name>\n    <message>\n        <source>Show/Hide</source>\n        <translation type=\"obsolete\">Zobraziť/Minimalizovať</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"obsolete\">Ukončiť</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Nasledujúce profily používajú rovnakého používateľa %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Viac profilov môžete používať iba pri rôznych používateľoch.</translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Nepodarilo sa nájsť sieťové rozhranie. Twinkle použije 127.0.0.1 ako svoju lokálnu IP adresu. Ak sa pripojíte k sieti neskôr, musíte Twinkle spustiť znovu, aby začal používať správnu IP adresu.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Linka %1: prichádzajúci hovor pre %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Volanie prepojené používateľom %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation>Linka %1: protistrana prerušila hovor.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation>Linka %1: hovor ukončený protistranou.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Linka %1: SDP odpoveď protistrany nie je podporovaná.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Linka %1: chýbajúca SDP odpoveď protistrany.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation>Linka %1: Typ obsahu v odpovedi protistrany nie je podporovaný.</translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Linka %1: žiadny ACK od protistrany, volanie bude ukončené.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Linka %1: žiadny PRACK od protistrany, volanie bude ukončené.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Linka %1:  PRACK zlyhalo.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Linka %1: chyba pri pokuse o ukončenie hovoru.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Linka %1: protistrana prijala hovor.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Linka %1: volanie zlyhalo.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>Hovor môže byť presmerovaný na:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation>Linka %1: hovor ukončený.</translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Linka %1:spojenie nadviazané.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Odpoveď protistrany na požiadavku na výpis schopností: %1 %2</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Schopnosti protistrany %1</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation>Akceptované &quot;body types&quot;:</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>neznáme</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Akceptované kódovania:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Akceptované jazyky:</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Povolené požiadavky:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation>Podporované rozšírenia:</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation>žiadne</translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Typ koncového zariadenia:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation>Linka %1: chyba pri pokuse o obnovenie hovoru.</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, neúspešné prihlásenie: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, registrácia úspešná (platná na %2 sekúnd)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, registrácia skončila s chybou: zlyhanie STUN</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, úspešné odhlásenie: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation>%1, chyba pri požiadavke na registráciu: %2 %3</translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: nie ste registrovaný</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: sú aktívne nasledujúce registrácie</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: prebieha požiadavka na registrácie...</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Linka %1: požiadavka presmerovaná na</translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Presmerovať požiadavku na: %1</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Linka %1: detegované DTMF:</translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation>neplatná udalosť DTMF (%1)</translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Linka %1: odosiela sa DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation>Linka %1: protistrana nepodporuje udalosti DTMF.</translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation>Linka %1: prijatá notifikácia.</translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Udalosť: %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Stav: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Dôvod: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Priebeh: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Linka %1: prepojenie hovoru zlyhalo.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Linka %1: hovor bol úspešne prepojený.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Linka %1: prepojenie hovoru stále prebieha.</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation>Nebudú prijímané ďalšie notifikácie.</translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Linka %1: hovor sa prepája na %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Prepojenie vyžiadal %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation>Linka %1: Prepojenie hovoru zlyhalo. Obnovujem pôvodný hovor.</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Presmerovávam hovor</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil používateľa:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Používateľ:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Súhlasíte, aby bol hovor presmerovaný na túto destináciu?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation>Ak na túto otázku už ďalej nechcete odpovedať, musíte zmeniť nastavenia v sekcii protokol SIP v profile používateľa.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation>Presmerovávam požiadavku</translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation>Má byť požiadavka %1 presmerovaná na nasledujúcu destináciu?</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Prepájanie hovoru</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation>Požiadavka na prepojenie hovoru prijatá od:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Povoliť prepojenie hovoru k nasledujúcej destinácii?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation>Info:</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Varovanie:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation>Kritické:</translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Detekcia firewallu/NATu...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Prerušiť</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Linka %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Pre potvrdenie správneho hesla SAS kliknite na ikonu zámku.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>Protistrana na linke %1 deaktivovala šifrovanie.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Linka %1: SAS potvrdené.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation>Linka %1: SAS potvrdenie vymazané.</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Linka %1: hovor odmietnutý.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Linka %1: hovor presmerovaný.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Nepodarilo sa zahájiť konferenciu.</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Ignorovať súbor so zámkom a spustiť aj tak?</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1, STUN požiadavka zlyhala: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, STUN požiadavka zlyhala.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, chyba stavu hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, odmietnutý stav hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, hlasová schránka neexistuje.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, ukončený prenos z hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, deregistrácia zlyhala: %2 %3</translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation>Prijatá požiadavka na prepojenie hovoru.</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation>Ak sú toto používatelia pre rôzne domény, aktivujte nasledujúcu voľbu v profile vášho používateľa (Protokol SIP)</translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation>Použiť doménové meno pre vytvorenie jedinečnej kontaktnej hlavičky</translation>\n    </message>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>Zlyhalo vytvorenie %1 socketu (SIP) na porte %2</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Akceptované sieťou</translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Zlyhalo uloženie prílohy správy: %1</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation>Prepojil: %1</translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation>Nemôžem otvoriť webový prehliadač: %1</translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation>Nastavte váš webový prehliadač v nastaveniach systému.</translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Výber adresy</translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>Zobraziť iba adresy &amp;SIP</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Ak je aktivované, budú zobrazené iba kontakty, ktoré obsahujú platnú adresu SIP (začínajúcu na &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;).</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>Aktua&amp;lizovať</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Znovu načítať zoznam adries z KAddressbook.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation>&amp;KAddressBook</translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation>Tento zoznam kontaktov pochádza z &lt;b&gt;KAddressBook&lt;/b&gt;. Kontakty, ktoré neobsahujú telefónne číslo alebo adresu SIP tu nie sú uvedené. Pre vytvorenie alebo úpravu kontaktov použite program KAddressBook.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>&amp;Miestny adresár</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"obsolete\">Poznámka</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Kontakty v lokálnom adresári Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>Prid&amp;ať</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Pridať nový kontakt do miestneho adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>O&amp;dstrániť</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Vymazať vybraný kontakt z miestneho adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Upraviť</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Upraviť vybraný kontakt v miestnom adresári.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation>Zdá sa, že &lt;p&gt;&lt;b&gt;KAddressBook&lt;/b&gt; neobsahuje žiadne záznamy s telefónnymi číslami, ktoré by Twinkle mohol načítať. Twinkle načítava z KAddressBook všetky kontakty s telefónnym číslom. Použite prosím tento program pre úpravu alebo pridanie vašich kontaktov.&lt;/p&gt;\n&lt;p&gt;Druhou možnosťou je použiť miestny adresár v Twinkle.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation>Naozaj odstrániť kontakt &apos;%1&apos; z miestneho adresára?</translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation>Odstrániť kontakt</translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Názov profilu</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Zadajte názov profilu:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Názov vášho profilu&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nProfil obsahuje všetky nastavenia používateľa, napr. meno používateľa alebo heslo. Každý profil musí byť pomenovaný.\n&lt;br&gt;&lt;br&gt;\nAk máte viac SIP účtov, môžete si vytvoriť niekoľko profilov. Pri spustení vám Twinkle zobrazí zoznam názvov profilov, z ktorých si môžete vybrať tie, ktoré chcete spustiť.\n&lt;br&gt;&lt;br&gt;\nPre jednoduchšie zapamätanie profilu ich môžete pomenovať podľa vášho používateľského účtu, napr. &lt;b&gt;example@example.com&lt;/b&gt;\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Nepodarilo sa nájsť adresár .twinkle vo vašom domovskom adresári.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Profil s týmto názvom už existuje.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Premenovať profil &apos;%1&apos; na:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - Zoznam volaní</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Čas</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>Prichádzajúce/Odchádzajúce</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>Protistrana</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Predmet</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Stav</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Podrobnosti hovoru</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Podrobnosti vybraného hovoru.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Zobraziť</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>&amp;Prichádzajúce hovory</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Zaškrtnite túto voľbu pre zobrazenie prichádzajúcich hovorov.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>&amp;Odchádzajúce hovory</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Zaškrtnite túto voľbu pre zobrazenie odchádzajúcich hovorov.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>&amp;Prijaté hovory</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Zaškrtnite túto voľbu pre zobrazenie prijatých hovorov.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>&amp;Zmeškané hovory</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Zaškrtnite túto voľbu pre zobrazenie zmeškaných hovorov.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>&amp;Iba aktívne používateľské profily</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Ak je táto voľba aktivovaná, budú zobrazené iba hovory, ktoré boli uskutočnené pod profilom tohto používateľa.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Vyčistiť</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Vymazať celú históriu volaní.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Poznámka:&lt;/b&gt; Týmto sa vymažú všetky záznamy. Vrátane tých, ktoré momentálne - v závislosti od vybraných parametrov nastavení - nemusia byť zobrazené.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Zavrieť toto okno.</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Začatý hovor:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Odpovedané na volanie:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Hovor ukončený:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Trvanie hovoru:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Smer:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Od:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Kam:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Odpovedať na:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Cez:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Predmet:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Ukončil:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Stav:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Zariadenie protistrany:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil používateľa:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>rozhovor</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Volať...</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Odstrániť</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Odp:</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>Za&amp;vrieť</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>&amp;Volať</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Zavolať na vybranú adresu.</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation>Počet hovorov:</translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation>###</translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation>Celkové trvanie hovorov:</translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation>%1 volá</translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Volanie</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>K&amp;am:</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Tu môžete voliteľne zadať predmet hovoru, ktorý môže byť zobrazený volanému účastníkovi.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vybrať adresu z adresára.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa, na ktorú chcete zavolať. Toto môže byť plnohodnotná adresa SIP ako napr. &lt;b&gt;sip:priklad@priklad.com&lt;/b&gt; alebo len meno používateľa, príp. telefónne číslo. Ak nie je zadaná úplná adresa, Twinkle ju doplní o doménové meno aktuálneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Používateľ, ktorý uskutoční hovor.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Predmet:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Od:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>&amp;Skryť identitu volajúceho</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Touto voľbou dávate najavo vášmu poskytovateľovi SIP, že si neželáte, aby boli protistrane zaslané informácie o vašej identite. Napr. vaša adresa SIP alebo telefónne číslo. Vaša IP adresa bude protistrane &lt;b&gt;vždy&lt;/b&gt; zobrazená.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Upozornenie:&lt;/b&gt; Túto možnosť nepodporujú všetci poskytovatelia!&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Nie všetci poskytovatelia SIP umožňujú skrytie identity. Ak túto funkciu naozaj potrebujete, uistite sa, že ju váš poskytovateľ SIP ponúka.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation>Twinkle - Protokol</translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Obsah aktuálneho súboru s protokolom (~/.twinkle/twinkle.log)</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Zavrieť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Vyčistiť</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Vyčistiť okno s protokolom. Obsah samotného súboru s protokolom odstránený &lt;b&gt;nebude&lt;/b&gt;.</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - Textová správa</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Komu:</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>Používateľ, ktorý pošle správu.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa používateľa, ktorému chcete poslať správu. Môže to byť buď adresa SIP, ako napríklad &lt;b&gt;sip:priklad@priklad.com&lt;/b&gt; alebo len používateľ, príp. telefónne číslo. Ak neuvediete celú adresu, Twinkle doplní adresu doménou z profilu vášho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výber adresy z adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>&amp;Profil používateľa:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Konverzácia</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Sem napíšte správu a stlačením tlačidla &quot;Odoslať&quot; ju odošlite.</translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Odoslať</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Odoslať správu.</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Doručenie zlyhalo</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Potvrdenie o doručení</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Odoslať súbor...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Odoslať súbor</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>obrázok je v náhľade zmenšený</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Otvoriť s %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Otvoriť s...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Uložiť prílohu ako...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Súbor už existuje. Chcete tento súbor prepísať?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Chyba pri ukladaní prílohy.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 píše správu.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation>Veľkosť</translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>odosielanie správy</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation>Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>&amp;Volané číslo:</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa, na ktorú chcete zavolať. Toto môže byť plnohodnotná adresa SIP ako napr. &lt;b&gt;sip:priklad@priklad.com&lt;/b&gt; alebo len meno používateľa, príp. telefónne číslo. Ak nie je zadaná úplná adresa, Twinkle ju doplní o doménové meno aktuálneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Používateľ, ktorý uskutoční volanie.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Používateľ:</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Volať</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Volať adresu.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vybrať adresu z adresára.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation>Indikátor automatického príjmu hovoru.</translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation>Indikátor presmerovania hovoru.</translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation>Indikátor stavu nerušiť.</translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation>Indikátor zmeškaných hovorov.</translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation>Stav registrácie.</translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation>Správy</translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Stav linky</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Linka &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation>Alt+1</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Kliknite pre prepnutie na linku 1.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Od:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Kam:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Predmet:</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation>idle</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation>Hovor bude presmerovaný</translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation>sas</translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation>Krátky overovací reťazec</translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation>g711a/g711a</translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation>Audio kodek</translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation>0:00:00</translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Trvanie hovoru</translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation>sip:from</translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation>sip:to</translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation>subject</translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation>photo</translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Linka &amp;2:</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation>Alt+2</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Kliknite pre prepnutie na linku 2.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Súbor</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Upraviť</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>&amp;Hovor</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Vybrať linku</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>&amp;Registrácia</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>S&amp;lužby</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>&amp;Zobraziť</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>Po&amp;mocník</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Ukončiť</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>&amp;Ukončiť</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>O programe Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>O &amp;programe Twinkle</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Zavolať niekomu</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation>F5</translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Prijať prichádzajúci hovor</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation>F6</translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation>Ukončiť hovor</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Odmietnuť prichádzajúci hovor</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation>F8</translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Podržať hovor alebo pokračovať v podržanom hovore</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Presmerovať prichádzajúci hovor bez prijatia hovoru</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Otvoriť numerickú klávesnicu pre hlasové menu</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Registrovať sa</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>&amp;Registrovať sa</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Odregistrovať sa</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>&amp;Odregistrovať sa</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Odregistrovať toto zariadenie</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Zobraziť registrácie</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>&amp;Zobraziť registrácie</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Parametre protistrany</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Požiadavka na parametre protistrany</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Nerušiť</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>&amp;Nerušiť</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Presmerovanie hovoru</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>&amp;Presmerovanie hovoru...</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Opakované vytáčanie</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>O Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>O &amp;Qt</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Profil používateľa</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>&amp;Profil používateľa...</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation>Spojiť dva hovory do konferencie</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Vypnúť mikrofón</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Presmerovať hovor</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Nastavenia systému</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>&amp;Nastavenia systému...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Všetko odregistrovať</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>&amp;Všetko odregistrovať</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Odregistrovať všetky registrované zariadenia</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Automaticky prijímať volania</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>&amp;Automaticky prijímať volania</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Protokol</translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation>&amp;Protokol...</translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>História volaní</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>&amp;História volaní...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation>F9</translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Zmeniť používateľa...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Zmeniť používateľa...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Aktivovať alebo deaktivovať používateľov</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>Čo je toto?</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>Čo je &amp;toto?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Linka 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Linka 2</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>voľná</translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>vytáčam</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>pokus o nadviazanie spojenia, prosím čakajte</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>prichádzajúce volanie</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>nadväzujem hovor, prosím čakajte</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>nadviazané</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>nadviazané (čaká sa na zvuk)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation>zavesujem, prosím čakajte</translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>neznámy stav</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Hovor je zašifrovaný</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>Kliknite pre potvrdenie SAS.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation>Kliknite pre zrušenie overenia SAS.</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Používateľ:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Hovor:</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Stav registrácie:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>Registrovaný</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>Nepodarilo sa</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>Neregistrovaný</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Nie je prihlásený žiaden používateľ.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>Nerušiť aktivované pre:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Presmerovanie aktivované pre:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>Automatické prijímanie hovorov aktivované pre:</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>Nerušiť nie je aktívne.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>Presmerovanie volaní nie je aktivované.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>Automatické prijímanie hovorov nie je aktívne.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>Nemáte žiadne zmeškané hovory.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>1 zmeškaný hovor.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>%1 zmeškaných hovorov.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Kliknutím otvoríte podrobný zoznam hovorov.</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Štartujem profily používateľov...</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Nasledujúce profily používajú rovnakých používateľov %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Rôzne profily musia používať rôznych používateľov.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation>Bol zmenený port SIP UDP. Toto nastavenie bude aktívne až pri ďalšom spustení programu Twinkle.</translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation>Asistované presmerovanie</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Skryť identitu</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Kliknutím zobrazíte registrácie.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 nová, 1 stará správa</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 nové, %2 staré správy</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>1 nová správa</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 nových správ</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>1 stará správa</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 starých správ</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Prijatých správ</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Žiadne správy</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Stav hlasovej schránky:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Chyba</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Neznámy</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Kliknutím vstúpite do hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Kliknutím aktivujete/deaktivujete</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Kliknutím aktivujete</translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation>nie je poskytované</translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation>Skôr ako vám bude umožnené použiť hlasovú schránku je potrebné nastaviť jej adresu v profile používateľa.</translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>Hlasovú schránku nie je možné otvoriť. Linka je obsadená.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation>Adresa hlasovej schránky %1 je neplatná. Skontrolujte nastavenia vo vašom profile používateľa.</translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation>Zobrazenie čakajúcich správ.</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Hlasová schránka</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>&amp;Hlasová schránka</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation>Vstúpiť do hlasovej schránky</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Zoznam kontaktov</translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>S&amp;práva</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Msg</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>Textová &amp;správa...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Textová správa</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Volať...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>&amp;Upraviť...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Odstrániť</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>O&amp;ffline</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>&amp;Online</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>&amp;Zmeniť dostupnosť</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Pridať kontakt...</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Zlyhalo uloženie zoznamu kontaktov: %1</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation>Je možné vytvoriť oddelený zoznam kontaktov pre každý profil používateľa. Dostupnosť vašich kontaktov a vlastnú dostupnosť je možné zistiť a využívať len vtedy, ak to váš poskytovateľ podporuje.</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>Zoznam &amp;kontaktov</translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation>&amp;Správy</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation>Diamondcard</translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation>Príručka</translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation>Prír&amp;učka</translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation>Prihlásiť sa</translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation>&amp;Prihlásiť sa...</translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation>Dobiť kredit...</translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation>História zostatkov...</translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation>História volaní...</translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation>Administratívne centrum...</translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation>Dobiť kredit</translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation>História zostatkov</translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation>Administratívne centrum</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation>Volať</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation>Pri&amp;jať</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation>Prijať</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation>&amp;Zavesiť</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation>Zavesiť</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation>&amp;Odmietnuť</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Odmietnuť</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation>&amp;Podržať</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation>Podržať</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation>&amp;Presmerovať...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation>Presmerovať</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation>&amp;DTMF...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation>&amp;Parametre protistrany...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation>&amp;Opakovať</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation>Opakovať</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation>&amp;Konferencia</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation>Konferencia</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation>&amp;Stíšiť</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation>Stíšiť</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation>Prep&amp;ojiť...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation>Prepojiť</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - Konverzia tel. čísla</translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Hľadaný výraz:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Nahradiť:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation>Formátovací reťazec (štýl Perlu) s nahradeným číslom.</translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation>Regulárny výraz (štýl Perlu) pre nahrádzané číslo.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>Hľadaný výraz nesmie byť prázdny.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>Nahrádzaná hodnota nesmie byť prázdna.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Neplatný regulárny výraz.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Presmerovanie</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Prichádzajúci hovor presmerovať na</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Pre presmerovanie volania je možné zadať max. 3 čísla. Ak nebude hovor prijatý prvým cieľom, prebehne pokus o presmerovanie na druhý cieľ atď.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3. cieľ:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2. cieľ:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1. cieľ:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výber adresy z adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - Výber sieťového rozhrania</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Vyberte sieťové rozhranie/IP adresu, ktorú chcete použiť:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>Na vašom počítači je k dispozícii viac IP adries. Tu vyberiete tú, ktorú chcete používať. Táto IP adresa bude používať Twinkle vo vnútri správ SIP.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>&amp;Nastaviť ako predvolenú IP adresu</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Nastaviť vybranú IP adresu ako predvolenú. Twinkle túto IP adresu automaticky použije pri ďalšom spustení.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>Nastaviť ako &amp;predvolené rozhranie</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>Nastaviť vybrané sieťové rozhranie ako predvolené. Twinkle použije toto rozhranie pri ďalšom spustení.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Predvolené nastavenie je možné zmeniť kedykoľvek neskôr v nastaveniach systému.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle - Výber profilu používateľa</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Vyberte profily používateľov, ktoré majú byť aktivované:</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Označte profil používateľa, s ktorým by mal Twinkle pracovať a stlačte &quot;Spustiť&quot;.</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Pomocou editoru profilu vytvoriť nový profil používateľa.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>Spri&amp;evodca</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Vytvoriť nový profil používateľa pomocou sprievodcu.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>U&amp;praviť</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Upraviť zvolený profil používateľa.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Odstrániť</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Odstrániť zvolený profil používateľa.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>&amp;Premenovať</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Premenovať označený profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>Nastaviť ako pred&amp;volený</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Nastaviť vybrané profily ako predvolené. Twinkle ich použije automaticky pri ďalšom spustení.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Spustiť</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Spustiť Twinkle so zvolenými profilmi používateľmi.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>S&amp;ystémové nastavenia</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Upraviť nastavenia systému.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Skôr ako budete môcť Twinkle začať používať, musíte vytvoriť profil používateľa.&lt;br&gt;Kliknite OK pre založenie nového profilu.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;V ďalšom kroku môžete upraviť nastavenia systému. Môžete to urobiť aj kedykoľvek neskôr.&lt;br&gt;&lt;br&gt;Kliknite na OK pre prístup k nastaveniam systému.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>Nevybrali ste žiadny profil používateľa.\nVyberte prosím aspoň jeden profil.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Naozaj odstrániť profil používateľa &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Odstrániť profil</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Chyba pri odstraňovaní profilu.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Chyba pri premenovaní profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Predvolené nastavenie je možné kedykoľvek zrušiť alebo zmeniť v nastaveniach systému. &lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Nie je možné nájsť adresár .twinkle vo vašom domovskom adresári.</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>&amp;Editor profilu</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation>Vytvoriť profil</translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation>Ed&amp;itor</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation>Upraviť profil</translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation>Profil pri spustení</translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation>Vytvoriť profil pre účet Diamondcard. S účtom Diamondcard môžete volať na pevné linky, do mobilných sietí po celom svete a odosielať správy SMS.</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation>Tu si môžete zaregistrovať účet Diamondcard, s ktorým môžete volať na pevné linky, do mobilných sietí po celom svete a odosielať správy SMS.&lt;br&gt;&lt;br&gt;</translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation>Vyberte metódu, ktorú chcete použiť&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Výber používateľa</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>Vybrať &amp;všetko</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>O&amp;dznačiť všetko</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation>purpose</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Prihlásiť sa</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Vybrať používateľov, ktorých chcete zaregistrovať.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Odregistrovať</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Vybrať používateľov, ktorých chcete odregistrovať.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Odhlásiť všetky zariadenia</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Vybrať používateľov, pri ktorých chcete odhlásiť všetky zariadenia.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Nerušiť</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Vybrať používateľov, pri ktorých má byť aktivovaný režim &quot;Nerušiť&quot;.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Automaticky prijať volanie</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Vybrať profil používateľa, pre ktorého má byť aktivovaný režim &quot;Automaticky prijať volanie&quot;.</translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Odoslať súbor</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Výber súboru na odoslanie.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Súbor:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Predmet:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Súbor neexistuje.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Odoslať súbor...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation>Twinkle - Presmerovanie hovoru</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Používateľ:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation>Existujú 3 spôsoby presmerovania volania:&lt;p&gt;\n&lt;b&gt;Nepodmienené:&lt;/b&gt; presmerovať všetky hovory\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Obsadené:&lt;/b&gt; presmerovať hovor, ak sú obe linky obsadené\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Bez odpovede:&lt;/b&gt; presmerovať hovor po uplynutí čakacej doby\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation>N&amp;epodmienené</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation>&amp;Presmerovať všetky hovory</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation>Aktivovať službu presmerovaní všetkých hovorov.</translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation>Presmerovať na</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Môžete zadať až 3 ciele pre presmerovanie volaní. Ak nebude hovor prijatý prvým cieľom, bude použitý druhý cieľ, prípadne tretí.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>&amp;3. cieľ:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>&amp;2. cieľ:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>&amp;1. cieľ:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Vyberte adresu z adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Obsadené</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation>&amp;Presmerovať hovory, ak sú linky obsadené</translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation>Aktivovať presmerovanie, ak je linka obsadená.</translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>&amp;Bez odpovede</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation>&amp;Presmerovať hovor, ak naň nie je odpovedané</translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation>Aktivovať službu &quot;Presmerovať v neprítomnosti&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Prijať a uložiť všetky zmeny.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Neukladať vykonané zmeny a zavrieť okno.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>Zadali ste neplatnú cieľovú adresu.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Nastavenia systému</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Všeobecné</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Audio</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Tóny vyzváňania</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Sieť</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Protokol</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Vyberte skupinu vlastností, v ktorej chcete zmeniť nastavenia.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Zvuková karta</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Vyberte zvukovú kartu pre prehrávanie vyzváňacieho tónu prichádzajúceho volania.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Vyberte zvukovú kartu, ku ktorej máte pripojený mikrofón.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Vyberte zvukovú kartu pre slúchadlo alebo reproduktor.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Slúchadlo/reproduktor:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Tón vyzváňania:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Iné zariadenie:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Mikrofón:</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Pokročilé</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation>Veľkosť &amp;fragmentov OSS:</translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation>16</translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation>32</translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation>64</translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation>128</translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation>256</translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation>Nastavenie periódy prehrávania ALSA ovplyvňuje oneskorenie zvuku na zvukovej karte. Pri problémoch s vypadávaním alebo preskakovaním zvuku skúste použiť inú hodnotu.</translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation>Veľkosť &amp;periódy prehrávania ALSA:</translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation>Veľkosť periódy nahrávania &amp;ALSA:</translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation>Veľkosť fragmentu OSS ovplyvňuje oneskorenie zvuku na zvukovej karte. Pri problémoch s vypadávaním alebo preskakovaním zvuku skúste použiť inú hodnotu.</translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation>Veľkosť periódy nahrávania ALSA ovplyvňuje oneskorenie zvuku pri zaznamenávaní zvuku. Ak sa protistrana sťažuje na časté výpadky zvuku, skúste použiť inú hodnotu.</translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>&amp;Maximálna veľkosť protokolu:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation>Maximálna veľkosť súboru s protokolom v MB. Pri dosiahnutí tejto veľkosti súboru Twinkle vytvorí z aktuálneho súboru zálohu a začne zapisovať do nového súboru. Uchováva sa len posledný súbor so zálohou.</translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation>MB</translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation>Zapísať správy pre &amp;ladenie</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>ALt+L</translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation>Indikuje, či sa majú do protokolu zapisovať &quot;debug&quot; správy.</translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>Zapisovať do protokolu &amp;SIP hlásenia</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation>Indikuje, či majú byť správy SIP ukladané do protokolu.</translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>Zapisovať do protokolu hlásenia S&amp;TUN</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation>Indikuje, či majú byť správy STUN ukladané do protokolu.</translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation>Zapisovať hlásenia o pa&amp;mäti do protokolu</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation>Indikuje, či sa majú do protokolu zapisovať hlásenia o správe pamäte.</translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Systémová lišta</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Pri spustení vytvoriť &amp;ikonu v systémovej lište</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation>Zapnite túto voľbu, ak chcete vidieť ikonu Twinkle v systémovej lište. Tá sa vytvori pri spustení Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation>Skryť v systémovej lište pri zatvorení &amp;hlavného okna</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation>Povoľte, ak chcete, aby sa Twinkle pri zatvorení hlavného okna skryl do systémovej lišty.</translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Štart programu</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation>Spustiť minimalizované v &amp;systémovej lište</translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation>Pri budúcom štarte sa Twinkle ihneď skryje do systémovej lišty. Toto funguje najlepšie vtedy, ak vyberiete predvolený profil používateľa.</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation>Ak vždy používate tie isté profily, potom ich tu môžete označiť ako predvolené. Pri ďalšom spustení sa vás Twinkle nebude pýtať na to, ktoré profily má spustiť. Automaticky budú spustené predvolené profily.</translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Služby</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>Čakajúce hovor&amp;y</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation>Pri zapnutej funkcii čakajúcich hovorov sú prichádzajúce hovory prijaté vtedy, ak je obsadená iba jedna linka. Ak túto funkciu zakážete, prichádzajúce hovory budú odmietané aj keď je používaná iba jedna linka.</translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation>Zavesiť o&amp;be linky pri ukončení konferenčného hovoru.</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation>Ak je aktivované budú pri &quot;zavesení&quot; hovoru v konferencii ukončené obe linky. V opačnom prípade bude ukončený hovor len na aktívnej linke a je možné pokračovať v hovore na druhej linke.</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation>&amp;Maximálny počet záznamov v histórií volaní:</translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation>Maximálny počet hovorov, ktoré budú udržované v zozname hovorov.</translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation>Pri prichádzajúcom hovore &amp;automaticky zobraziť hlavné okno programu po uplynutí</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation>Ak je hlavné okno programu skryté, bude pri prichádzajúcom hovore po uplynutí zadaného času (s) automaticky zobrazené.</translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation>Čas v sekundách, po uplynutí ktorého bude zobrazené hlavné okno.</translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>sekúnd</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>&amp;RTP port:</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation>UDO port použitý pri posielaní a prijímaní RTP na prvej linke. UDP port pre druhú linku je o 2 čísla vyšší, napr. ak je pre prvú linku použitý port 8000, druhá linka používa port 8002. Ak použijete presmerovanie hovoru, použije sa nasledujúci párny port (napr. 8004).</translation>\n    </message>\n    <message>\n        <source>&amp;SIP UDP port:</source>\n        <translation type=\"obsolete\">&amp;SIP UDP port:</translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Tón vyzváňania</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>Pri prichádzajúcom hovore &amp;prehrávať vyzváňací tón</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Indikuje, či má pri prichádzajúcich hovoroch znieť vyzváňanie.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>&amp;Predvolený tón vyzváňania</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Prehrá predvolený tón vyzváňania pri prichádzajúcom hovore.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>&amp;Vlastný tón vyzváňania</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Pri prichádzajúcom hovore prehrať vlastný tón vyzváňania.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation>Zadajte názov súboru .wav s vlastným tónom vyzváňania.</translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation>Tón pre signalizáciu vyzváňania na volanej stanici</translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation>&amp;Prehrať tón pre vyzváňanie na volanej stanici, ak telefónna sieť žiaden tón neposkytuje</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Prehrať tón pre vyzváňanie na volanej stanici, ak telefónna sieť žiadny taký tón neposkytuje.&lt;/p&gt;\n\n&lt;p&gt;Prítomnosť tohto tónu závisí na vašom poskytovateľovi.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation>Predvolený tón pre vyzváňanie na &amp;volanej stanici</translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation>Prehrávať predvolený tón vyzváňania na volanej stanici.</translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation>Vla&amp;stný tón vyzváňania na volanej stanici</translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation>Použiť vlastný tón vyzváňania na volanej stanici.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation>Zadajte meno súboru .wav pre váš vlastný tón vyzváňania na volanej stanici.</translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation>Podľa čísla &amp;zistiť meno volajúceho</translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation>P&amp;repisovať meno volajúceho</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation>Volajúca protistrana môže posielať vlastné meno. Pri aktivovaní tejto voľby bude zobrazované meno prepísané menom z vášho adresára.</translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Hľadať &amp;fotografiu volajúceho</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation>Hľadať fotografiu vo vašom adresári a zobraziť ju pri prichádzajúcom hovore.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Akceptovať a uložiť zmeny v nastaveniach.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Vrátiť všetky zmeny a zavrieť okno.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Tóny vyzváňania</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Vybrať tón vyzváňania</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Tón vyzváňania na volanej stanici</translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation>Vybrať tón vyzváňania na volanej stanici</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>Overiť zvukové za&amp;riadenia pred ich použitím</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation>&lt;p&gt;Twinkle overuje zvukové zariadenia pred ich použitím, aby se nestalo, že počas hovoru nebude fungovať zvuk.\n&lt;p&gt;Twinkle pri štarte skontroluje a zobrazí varovanie,ak nie je dané audio zariadenie dostupné.&lt;/p&gt;\n&lt;p&gt;Ak sa pred začatím hovoru zistí, že mikrofón alebo reproduktor/slúchadlá nefunguje/ú, zobrazí sa varovanie a hovor nebude možné uskutočniť.&lt;/p&gt;\n&lt;p&gt;Rovnako v prípade, že je detegovaný prichádzajúci hovor a audio zariadenie nie je v poriadku, zobrazí sa varovanie a hovor nebude možné prijať.</translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation>Pri prichádzajúcom hovore sa Twinkle pokúsi nájsť k volajúcemu v adresári zodpovedajúci záznam. Ak sa to podarí, bude jeho meno zobrazené.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Vybrať súbor s tónom vyzváňania.</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Vybrať súbor vyzváňacieho tónu na protistrane.</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation>Maximálna povolená veľkosť prichádzajúcej SIP správy cez UDP v bajtoch(0-65535).</translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>&amp;SIP port:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation>Max. veľkosť SIP správy (&amp;TCP):</translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation>UDP/TCP port použitý pre odosielanie a prijímanie správ SIP.</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation>Max. veľkosť správy SIP (&amp;UDP):</translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation>Maximálna povolená veľkosť prichádzajúcej správy SIP cez TCP v bajtoch (0-4294967295).</translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation>Príkaz pre w&amp;ebový prehliadač:</translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation>Príkaz pre spustenie webového prehliadača. Ak toto pole necháte prázdne, Twinkle sa pokúsi zistiť váš predvolený prehliadač.</translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation>512</translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation>1024</translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation>Tip: pri praskajúcom zvuku s PulseAudio nastavte najvyššiu periódu prehrávania ALSA.</translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation>Povoliť OSD počas hovoru</translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Prijať</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Odmietnuť</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation>Prichádzajúci hovor</translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Parametre protistrany</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Od:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Získať parametre protistrany</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Adresa:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa alebo číslo protistrany, ktorej parametre sa majú zistiť (OPTION request). Môže to byť úplná adresa SIP ako &lt;b&gt;sip:priklad@priklad.com&lt;/b&gt; alebo len meno používateľa, príp. telefónne číslo z úplnej adresy. Ak nie je zadaná úplná SIP adresa, Twinkle doplní adresu o doménu z profilu používateľa.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výber adresy z adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Prepojenie</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Prepojiť hovor na</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Na:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adresa alebo číslo protistrany, na ktorú má byť hovor prepojený. Môže to byť úplná adresa SIP ako &lt;b&gt;sip:priklad@priklad.com&lt;/b&gt; alebo len meno používateľa, príp. telefónne číslo z úplnej adresy. Ak nie je zadaná úplná SIP adresa, Twinkle doplní adresu o doménu z profilu používateľa.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adresár</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Výber adresy z adresára.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>&amp;Slepé presmerovanie</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation>Hovor priamo presmerovať na nového účastníka, bez toho, aby bol najprv kontaktovaný.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation>Asistované p&amp;repojenie</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation>Pred presmerovaním hovoru najprv kontaktovať nového účastníka a volajúceho ohlásiť.</translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation>Presmerovať na inú &amp;linku</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation>Prepojiť hovor na aktívnej linke s hovorom na druhej linke.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation>F10</translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Chyba pri vytváraní súboru s protokolom %1.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Chyba pri otváraní súboru na čítanie: %1</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Chyba súborového systému pri čítaní súboru %1.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Nie je možné otvoriť súbor %1 pre zápis</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Chyba súborového systému pri zápise do %1.</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>Príliš veľký počet chýb socketu.</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Vytvorené s podporou pre:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>Prispievatelia:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Tento program obsahuje softvérové časti nasledovných tretích strán:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation>* GSM kodek od Jutta Degener a Carsten Bormann, University of Berlin</translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation>* G.711/G.726 kodeky od Sun Microsystems (public domain)</translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation>* iLBC implementácia RFC 3951 (www.ilbcfreeware.org)</translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation>* Časti zo STUN projektu na http://sourceforge.net/projects/stun</translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation>* Časti z libsrv na http://libsrv.sourceforge.net/</translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation>Pre RTP sú linkované nasledujúce dynamické knižnice:</translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Do slovenčiny preložil Jose Riha</translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Adresár &quot;%1&quot; neexistuje.</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Súbor %1 nie je možné otvoriť.</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>&quot;%1&quot; nie je vaším domovským adresárom.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Adresár %1 (%2) neexistuje.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Adresár %1 nie je možné vytvoriť.</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>%1 už beží.\nSúbor so zámkom %2 už existuje.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>Nie je možné vytvoriť %1.</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Chyba syntaxe v súbore %1.</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Chyba pri zálohovaní %1 do %2</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>neznámy názov (zariadenie sa používa)</translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Predvolené zariadenie</translation>\n    </message>\n    <message>\n        <source>Anonymous</source>\n        <translation>Anonymné</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Varovanie:</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation>Presmerovanie hovoru - %1</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>Zvuková karta nefunguje v režime full duplex.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>Nie je možné nastaviť veľkosť bufferu na zvukovej karte.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Zvukové zariadenie nie je možné nastaviť na %1 kanálov.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation>Audio zariadenie nie je možné nastaviť na 16 bitové nahrávanie.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation>Audio zariadenie nie je možné nastaviť na 16 bitové prehrávanie.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation>Nie je možné nastaviť vzorkovaciu frekvenciu audio zariadenia na %1</translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation>Chyba pri otváraní ovládača ALSA</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation>Nie je možné otvoriť ovládač ALSA pre prehrávanie PCM</translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>Nie je možné vyhľadať adresu STUN servera: %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>Nachádzate sa za symetrickým NATom.\nSTUN nebude fungovať.\nV profile používateľa nastavte verejnú IP adresu\na na vašom NATe namapujte statické (UDP) porty.</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>verejná IP: %1 --&gt; privátna IP: %2 (SIP signalizácia)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>verejná IP: %1 - %2 --&gt; privátna IP: %3 - %4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>Nie je možné pripojiť sa na STUN server: %1</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Port %1 (SIP signalizácia)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>Detekcia typu NATu pomocou STUN skončila s chybou.</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Ak sa nachádzate za firewallom, je potrebné otvoriť nasledujúce UDP porty.</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Porty %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation>Zvukové zariadenie %1 pre tón vyzváňania nie je dostupné.</translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>Zvukové zariadenie %1 pre reproduktory/slúchadlá nie je dostupné.</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>Zvukové zariadenie %1 pre mikrofón nie je dostupné.</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>Nie je možné otvoriť ovládač ALSA pre nahrávanie PCM</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Nie je možné prijímať prichádzajúce TCP spojenia.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Zlyhalo vytvorenie súboru %1</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Zlyhal zápis dát do súboru %1</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Zlyhalo odoslanie správy.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation>Nemôžem zamknúť %1.</translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Profil používateľa</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Profil používateľa:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Vyberte profil, ktorý chcete upraviť.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Používateľ</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>SIP server</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP audio</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>Protokol SIP</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Formát adresy</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation>Časovače</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Vyzváňacie tóny</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation>Skripty</translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Bezpečnosť</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Vyberte skupinu vlastností, v ktorej chcete zmeniť nastavenia.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Akceptovať a uložiť zmeny v nastaveniach.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Vrátiť všetky zmeny a zavrieť okno.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>Účet SIP</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>&amp;Meno používateľa*:</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation type=\"vanished\">&amp;Doména*:</translation>\n    </message>\n    <message>\n        <source>Or&amp;ganization:</source>\n        <translation type=\"vanished\">Or&amp;ganizácia:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Meno používateľa, ktoré vám pridelil váš poskytovateľ. Je to prvá časť vašej kompletnej SIP adresy, &lt;b&gt;menouzivatela&lt;/b&gt;@domena.com. Môže byť vo forme telefónneho čísla.\n&lt;br&gt;&lt;br&gt;\nToto pole je povinné.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Doménová časť vašej adresy SIP, menopouzivatela@&lt;b&gt;domena.com&lt;/b&gt;. Miesto skutočnej domény tu môže byť tiež názov hostiteľa alebo IP adresa vašej &lt;b&gt;SIP proxy&lt;/b&gt;. Pre priame volanie medzi IP adresami tu uveďte názov hostiteľa alebo IP adresu vášho počítača.\n&lt;br&gt;&lt;br&gt;\nToto pole je povinné.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>Tu je možné uviesť meno vašej organizácie. Ak niekomu voláte, môže byť tento údaj zobrazený protistrane.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Toto je vaše celé meno, napríklad Jozef Mrkva. Tento údaj je zobrazený volanej protistrane ako meno volajúceho.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>Vaše &amp;meno:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>Prihlasovacie údaje SIP</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation>&amp;Realm (sféra):</translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation>Prihlasovacie &amp;meno:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Heslo:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation>Hodnota &quot;realm&quot; (sféra) pre prihlásenie. Tento údaj vám musí poskytnúť váš poskytovateľ SIP. Ak toto pole necháte prázdne, Twinkle sa pokúsi o prihlásenie s vaším meno používateľa a heslom pri ľubovoľnom realme.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Vaše prihlasovacie SIP meno. Je často identické s vaším SIP menom používateľa. Môžu však byť aj rozdielne.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Vaše prihlasovacie heslo.</translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation>Registrar (prihlasovací server)</translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation>&amp;Registrar:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation>Názov hostiteľa, doménové meno alebo IP adresa prihlasovacieho servera. Ak používate odchádzajúcu proxy, ktorá je rovnaká ako váš prihlasovací server, je možné toto pole nechať prázdne a vyplniť iba adresu odchádzajúcej proxy.</translation>\n    </message>\n    <message>\n        <source>&amp;Expiry:</source>\n        <translation type=\"vanished\">&amp;Platnosť:</translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation>Doba platnosti registrácie v sekundách, ktorú si Twinkle vyžiada.</translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>sekúnd</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Re&amp;gistrovať sa pri štarte</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt+H</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation>Indikuje, či sa má Twinkle automaticky registrovať pri spustení tohto profilu. Toto by ste mali zakázať, ak chcete volať priamo medzi IP adresami bez SIP proxy.</translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation>Odchádzajúca proxy</translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>Po&amp;užiť odchádzajúcu-proxy</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation>Indikuje, či má Twinkle používať odchádzajúcu proxy. Ak sa používa odchádzajúca proxy, všetky požiadavky SIP sú posielané na túto proxy.Bez odchádzajúcej proxy sa Twinkle pokúsi použiť názov hostiteľa z adresy SIP a smerovať hovor priamo tam.</translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>Odchádzajúca &amp;proxy:</translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation>Poslať in-&amp;dialog požiadavky na proxy</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation>Požiadavky SIP sa bežne posielajú na adresu v kontaktných hlavičkách vymenených pri zostavovaní hovoru. Ak je toto pole zaškrtnuté, potom je táto adresa ignorovaná a všetky žiadosti sa takisto zasielajú na odchádzajúcu proxy.</translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation>N&amp;eposielať žiadosti SIP na proxy, ak je možné cieľ spojiť lokálne.</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+E</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation>Ak je táto voľba aktivovaná, najprv sa Twinkle pokúsi nájsť k cieľovej adrese zodpovedajúcu IP adresu a poslať požiadavku SIP priamo tam. Ak sa nepodarí IP adresu zistiť, je požiadavka poslaná na proxy. (Upozornenie: in-dialog žiadosti budú v tomto prípade posielané na proxy iba ak je aktivovaná aj predchádzajúca voľba.)</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Doménové meno, IP adresa alebo meno vašej odchádzajúcej proxy.</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation>Ko&amp;deky</translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation>Kodeky</translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Dostupné kodeky:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation>G.711 A-law</translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation>G.711 u-law</translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation>GSM</translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation>speex-nb (8 kHz)</translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation>speex-wb (16 kHz)</translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation>speex-uwb (32 kHz)</translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Zoznam dostupných kodekov.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Presunúť kodek zo zoznamu dostupných kodekov do zoznamu aktívnych kodekov.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Deaktivovať kodek.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Aktívne kodeky:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation>Zoznam aktívnych kodekov. Tieto budú použité pri dohodnutí parametrov hovoru počas zostavovania hovoru. Poradie tu uvedených kodekov je zároveň poradím, v akom budú kodeky preferované.</translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation>Presunúť tento kodek smerom hore v zozname kodekov, t. j. zvýšiť jeho prioritu.</translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation>Presunúť tento kodek smerom dole v zozname kodekov, t. j. znížiť jeho prioritu.</translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation>&amp;G.711/G.726 veľkosť payloadu:</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation>Uprednostňovaná veľkosť payloadu pre kodeky G.711 a G.726.</translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation>ms</translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation>&amp;iLBC</translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation>iLBC</translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation>i&amp;LBC typ payloadu:</translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation>iLBC veľkosť &amp;payloadu (ms):</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation>Dynamická hodnota typu pre iLBC (96 alebo viac).</translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation>20</translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation>30</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation>Uprednostňovaná veľkosť payloadu pre iLBC.</translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation>&amp;Speex</translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation>Speex</translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation>Vylepšenie &amp;kvality zvuku</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+K</translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation>Vylepšenie vnímanej kvality zvuku je súčasťou dekodéru, ktorá sa snaží znížiť (vnímaný) šum vzniknutý pri procese kódovania/dekódovania. Vo väčšine prípadov vedie táto funkcia k väčšiemu objektívnemu odchýleniu zvuku od originálu (z hľadiska SNR), ale i napriek tomu znie lepšie (subjektívne vylepšenie).</translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation>Typ payload&amp;u pre ultra široké pásmo:</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation>Typ payloadu pre široké pásm&amp;o:</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation>Variabilná šírka pásma (VBR) umožní danému kodeku prispôsobiť množstvo dát potrebných na prenos hovoru charakteru audio signálu. Kým napr. niektoré ostré samohlásky alebo veľmi premenlivé pasáže vyžadujú veľkú vzorkovaciu frekvenciu a tým aj veľký dátový tok, mäkké spoluhlásky si vystačia s malým dátovým tokom. Vďaka tomu VBC tak môže pri danej dátovej rýchlosti docieliť lepšej kvality zvuku alebo pri danej kvalite hovoru vystačiť s nižším dátovým tokom. Nevýhodou je, že pri zadanej kvalite nie je možné predpovedať aký veľký dátový tok v skutočnosti bude. A tiež, že v aplikáciách pracujúcich v reálnom čase (akým je práve VoIP) je rozhodujúca maximálna šírka pásma, ktorú musí zvládnuť komunikačný kanál.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation>Dynamická hodnota typu pre speex wide band (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation>Ko&amp;mplexita:</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation>Nesúvislé vysielanie je rozšírením funkčnosti VAD/VBR, kedy je možné úplne prestať odosielať dáta, keď je na linke ticho.</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation>Dynamická hodnota typu pre speex narrow band (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation>V prípade Speexu je možné meniť komplexitu kodéru. To slúži na zadanie hĺbky hľadania v rozsahu od 1 do 10. Podobný princíp je zavedený v kompresných programoch gzip a bzip2 s voľbou -1 až -9. Za normálnych podmienok je odstup šumu pri komplexite 1 o 1 až 2 dB vyšší ako pri komplexite 10. Avšak vyťaženie procesoru je asi 5x vyššie ako pri komplexite 1. V praxi sa osvedčilo nastavenie medzi 2 až 4. Vyššie nastavenia sú vhodné pre prenos tónov DTMF alebo hudby.</translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation>Typ payloadu pre úz&amp;ke pásmo:</translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation>G.726</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation>Typ payloadu pre G.726 &amp;40 kb/s:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation>Dynamická hodnota typu pre G.726 40 kb/s (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation>Dynamická hodnota typu pre G.726 32 kb/s (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation>Typ payloadu pre G.726 &amp;24 kb/s:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation>Dynamická hodnota typu pre G.726 24 kb/s (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation>Typ payloadu pre G.726 &amp;32 kb/s:</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation>Dynamická hodnota typu pre G.726 16 kb/s (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation>Typ payloadu pre G.726 &amp;16 kb/s:</translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation>DT&amp;MF</translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation>Dynamická hodnota typu pre udalosti DTMF (RFC 2833) (96 alebo vyšší).</translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation>H&amp;lasitosť DTMF:</translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation>Hlasitosť vysielaných DTMF tónov v dB.</translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation>Pauza medzi dvoma DTMF tónmi.</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation>Trvanie &amp;DTMF:</translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation>&amp;Typ payloadu DTMF:</translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation>&amp;Pauza DTMF:</translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation>dB</translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation>Doba trvania tónu DTMF.</translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>P&amp;renos DTMF:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation>Automaticky</translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation>RFC 2833</translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation>Zvukovo</translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation>Out-of-band (SIP INFO)</translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;&lt;h3&gt;RFC 2833&lt;/h3&gt;\nVysielať DTMF tóny ako telefónne udalosti podľa RFC 2833.&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Zvukovo&lt;/h3&gt;\nVysielať DTMF inband (skutočné tóny, ktoré Twinkle primieša do audio signálu).&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Auto&lt;/h3&gt;\nAk protistrana podporuje RFC 2833, sú použité DTMF tóny podľa štandardu RFC 2833, inak ako inband.&lt;/p&gt;\n&lt;p&gt;&lt;h3&gt;Out-of-band (SIP INFO)&lt;/h3&gt;\nVysielať DTMF out-of-band prostredníctvom požiadavky SIP INFO.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Všeobecné</translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation>Presmerovanie</translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation>Povoliť presmerov&amp;anie</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation>Indikuje, či má Twinkle presmerovať požiadavku pri odpovedi 3XX.</translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation>Vypýtať si povolenie od používateľa pred &amp;presmerovaním</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation>Ak je táto voľba aktivovaná, Twinkle sa pri prijatí požiadavky 3XX spýta používateľa, či má byť hovor presmerovaný na nový cieľ.</translation>\n    </message>\n    <message>\n        <source>Max re&amp;directions:</source>\n        <translation type=\"vanished\">Max. počet &amp;presmerovaní:</translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation>Počet presmerovaní odchádzajúcich volaní, po ktorom Twinkle ukončí pokusy o nadviazanie spojení. Zabraňuje zacykleniu pri presmerovaní.</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Nastavenie protokolu SIP</translation>\n    </message>\n    <message>\n        <source>Call &amp;Hold variant:</source>\n        <translation type=\"vanished\">Spôsob pridržania &amp;hovoru:</translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation>RFC 2543</translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation>RFC 3264</translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation>Indikuje, či bude pre podržanie hovoru použité RFC 2543 (nastavenie IP adresy SDP na 0.0.0.0) alebo RFC 3264 (použité smerové atribúty v SDP).</translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation>P&amp;ovoliť chýbajúcu kontaktnú hlavičku v 200 OK pri REGISTER</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation>Odpoveď 200 OK na požiadavku REGISTER musí obsahovať hlavičku Contact. Niektorí poskytovatelia buď hlavičku Contact neposielajú alebo ju posielajú chybnú. Táto voľba povolí toto odchýlenie od špecifikácie.</translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation>Hlavička &amp;Max-Forwards je povinná</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation>Podľa RFC3261 je hlavička Max-Forwards povinná. Často však nie je posielaná. Ak je táto voľba aktivovaná, Twinkle odmietne požiadavky SIP, ktoré hlavičku Max-Forwards neobsahujú.</translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation>Vložiť do kontaktnej hlavičky dobu platnosti &amp;registrácie</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation>Doba vypršania platnosti prihlásenia v požiadavke REGISTER môže byť prenášaná tak v hlavičke Contact ako aj v hlavičke Expires. Ak je táto voľba zapnutá, posiela ju Twinkle v hlavičke Contact, inak v hlavičke Expires.</translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation>Použiť &amp;kompaktné názvy hlavičiek</translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation>Indikuje, či má byť pre názvy hlavičiek použitá krátka forma, ak existuje.</translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation>Povoliť zmeny v SDP pri zostavovaní hovoru</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;SIP UAS môže odosielať SDP v 1XX odpovedi na zvuk na začiatku hovoru, napr. tón vyzváňania. Ak bude hovor prijatý, mal by SIP UAS podľa RFC 3261 poslať to isté SDP v odpovedi 200 OK. Po prijatí SDP by mali byť všetky nasledujúce odpovede SDP zahodené.&lt;/p&gt;\n&lt;p&gt;Povolením toho, že sa môže SDP počas zostavovania hovoru zmeniť, nebude v nasledujúcich odpovediach ignorovať SDP, ale zmení požadovaným spôsobom vlastnosti prúdu RTP. Zmenené SDP musí mat v riadku &quot;o=&quot; nové číslo verzie.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Ak je táto voľba zapnutá, Twinkle vytvorí jednoznačnú hodnotu kontaktnej hlavičky kombináciou SIP mena používateľa a doménového mena:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;pouzivatel_domena@lokalna_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nTo umožňuje vytvorenie viacerých používateľských profilov s rovnakým menom používateľa, ale rozdielnou doménou, ktoré potom majú odlišné kontaktné adresy a preto sa dajú tieto profily používať súčasne.\n&lt;/p&gt;\n&lt;p&gt;\nNiektoré proxy nemusia s takýmito kontaktnými hlavičkami vedieť pracovať. Ak je táto voľba deaktivovaná, Twinkle zasiela kontaktnú hlavičku v nasledovnom formáte:\n&lt;br&gt;\n&lt;tt&gt;&amp;nbsp;pouzivatel@lokalna_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nTento formát je používaný na väčšine SIP telefónov.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation>Via, Route a Record-Route poslať ako &amp;zoznam</translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation>Via-, Route- a Record-Route-Header môžu byť posielané zakódované ako zoznam čiarkou oddelených hodnôt alebo ako jednotlivé hodnoty, každá vo svojej hlavičke.</translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation>Rozšírenia SIP</translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation>&amp;100 rel (PRACK):</translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>deaktivované</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>podporované</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>vyžadované</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>uprednostňované</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation>Definuje spôsob podpory rozšírenia 100rel(PRACK):&lt;br&gt;&lt;br&gt;\n&lt;b&gt;deaktivované&lt;/b&gt;: rozšírenie 100rel nie je podporované\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;povolené&lt;/b&gt;: 100rel je podporované (je pridané do odchádzajúceho INVITE ako podporovaná hlavička). Protistrana si potom môže vyžiadať PRACK na 1xx odpoveď.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;vyžadované&lt;/b&gt;: 100rel je vyžadované. Požiadavka je vložená do hlavičky require v odchádzajúcom INVITE. Ak je v prichádzajúcom INVITE označené, že je 100rel podporované, potom Twinkle bude vyžadovať PRACK v odpovedi 1xx. Ak protistrana 100rel nepodporuje, nedôjde k nadviazaniu spojenia.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;uprednostňované&lt;/b&gt;: Podobné ako &quot;vyžadované&quot;, ale v prípade, že hovor zlyhá kvôli tomu, že protistrana nepodporuje 100rel (odpoveď 420), Twinkle sa pokúsi hovor znovu nadviazať bez vyžadovania 100rel.</translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation>REFER</translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation>Presmerovanie volania (REFER)</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation>Indikuje, či má Twinkle pri obdržaní požiadavky protistrany (REFER) presmerovať hovor na inú adresu.</translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation>Vypýtať si povolenie od používateľa pred &amp;presmerovaním</translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation>Indikuje, či sa má Twinkle spýtať používateľa na povolenie pri prichádzajúcej požiadavke na presmerovanie (REFER).</translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation>Podržať ho&amp;vor, kým sa zostavuje hovor s cieľom prepojenia</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation>Indikuje, či Twinkle pri prichádzajúcej požiadavke na presmerovanie (REFER) podrží aktuálny hovor.</translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation>Podržať hovor pred odos&amp;laním REFER</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation>Indikuje, či má Twinkle podržať hovor pri jeho presmerovaní.</translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation>Automaticky obnovovať registráciu &amp;pre REFER signál, kým nie je presmerovanie dokončené</translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation>Počas presmerovania hovoru posiela sprostredkovateľ presmerovania správ NOTIFY o postupe presmerovania tomu, koho hovor je presmerovávaný. Avšak iba na krátky čas. Tento čas určuje ten, kto je presmerovávaný. Ak je aktivovaná táto voľba, posiela sprostredkovateľ (Twinkle) automaticky SUBSCRIBE tak, aby sa tento čas predĺžil až do momentu, kedy je proces presmerovania ukončený.</translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>Prekonanie NATu (traversal)</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation>Prekonanie &amp;NATu (traversal) nie je potrebné</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation>Vyberte túto voľbu, ak sa medzi Twinkle a vašou SIP proxy nenachádza žiaden NAT alebo pokiaľ váš poskytovateľ dokáže sám NAT prekonať.</translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation>V správach SIP po&amp;užiť statickú verejnú IP adresu</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation>Indikuje, či má Twinkle použiť vo vnútri správ SIP (v hlavičke SIP a tele SDP) verejnú IP adresu, miesto IP adresy vášho sieťového rozhrania.&lt;br&gt;&lt;br&gt;\nAk si vyberiete túto voľbu, musíte tiež na vašom zariadení NAT nasmerovať zodpovedajúce RTP porty na váš počítač.</translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation>Vyberte túto voľbu, ak váš poskytovateľ SIP ponúka STUN server na premostenie vášho NATu.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation type=\"vanished\">Adresa S&amp;TUN servera:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Doménové meno, IP adresa alebo meno STUN servera.</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>Verejná IP &amp;adresa:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation>Verejná adresa vášho NATu.</translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Telefónne čísla</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation>Pri telefónnych číslach zobraziť iba používateľskú časť &amp;URI</translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation>Ak URI označuje telefónne číslo, bude zobrazená iba používateľská časť adresy. Napr. ak príde volanie od sip:12345@voipposkytovatel.com, Twinkle zobrazí ako adresu len &quot;12345&quot;. URI je považované za telefónne číslo, ak obsahuje parameter &quot;user=phone&quot; alebo ak je aktívna nasledujúca voľba a používateľská časť adresy je numerická.</translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation>&amp;URI s numerickou používateľskou časťou je telefónne číslo</translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation>Ak povolíte túto voľbu, Twinkle bude považovať každú SIP adresu za telefónne číslo, ak má v používateľskej časti iba číslice, *, #, + a špeciálne znaky. V odchádzajúcich SIP správach označí Twinkle takéto adresy parametrom &quot;user=phone&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation>&amp;Odstrániť z telefónneho čísla špeciálne znaky</translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation>Aby boli telefónne čísla ľahšie čitateľné, často sa píšu s použitím špeciálnych znakov ako napr. &quot;(&quot;, &quot;)&quot;, &quot; &quot; (prázdny znak), &quot;-&quot;. Pri vytáčaní nesmú byť tieto znaky vysielané. Ab bolo možné zjednodušiť vytáčanie pomocou copy/paste, Twinkle môže tieto znaky automaticky odstrániť.</translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation>Špeciálne &amp;znaky:</translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation>Zoznam všetkých špeciálnych znakov, ktoré má Twinkle z vytáčaných čísiel odstrániť.</translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation>Prevod čísiel</translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation>Vyhľadávaný výraz</translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Nahradiť</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telphone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation type=\"vanished\">&lt;p&gt;\nČasto sa formát telefónnych čísiel, ktoré očakáva poskytovateľ VoIP nezhoduje s formátom čísiel uložených v adresári. Napr. pri číslach začínajúcich na &quot;+&quot; a národným kódom krajiny očakáva váš poskytovateľ miesto &quot;00&quot; znak &quot;+&quot;. Alebo ste pripojený na miestnu sieť SIP a je potrebné najprv zadať predvoľbu pre volania smerom von.\nTu je možné pomocou vyhľadávacích a nahradzovaných vzorov (podľa štýlu regulárnych výrazov so syntaxom jazyku Perl) nastaviť všeobecne platné pravidlá pre konverziu telefónnych čísiel.\n&lt;/p&gt;\n&lt;p&gt;\nPri každom vytáčaní sa Twinkle pokúsi nájsť pre čísla z používateľskej časti adresy SIP zodpovedajúci výraz v zozname hľadaných vzorov. S prvým nájdeným vyhovujúcim výrazom je vykonaná úprava pôvodného čísla, pričom pozícia v &quot;(&quot; &quot;)&quot; v hľadanom výraze (napr. &quot;([0-9]*)&quot; pre &quot;ľubovoľný počet čísiel&quot;) je nahradená znakmi v zodpovedajúcich premenných. Napr. &quot;$1&quot; pre prvú pozíciu. Viď tiež `man 7 regex` alebo konqueror:&quot;#regex&quot;. Ak nie je nájdený žiaden zodpovedajúci hľadaný vzor, zostane číslo nezmenené.\n&lt;/p&gt;\n&lt;p&gt;\nPravidlá budú tiež použité na čísla v prichádzajúcich hovoroch. Podľa nastavených pravidiel budú pretransformované do želaného formátu.\n&lt;/p&gt;\n&lt;h3&gt;1. príklad&lt;/h3&gt;\n&lt;p&gt;\nNapr. váš národní kód je &quot;421&quot; pre Slovenskú republiku a vo vašom adresári máte tiež veľa vnútroštátnych čísiel uložených v medzinárodnom formáte. Napr.. +421 2 123 456. Avšak poskytovateľ VoIP očakáva pre vnútroštátny hovor číslo 02 123456. Chcete teda nahradiť &apos;+421&apos; číslom &apos;0&apos; a zároveň pre zahraničné hovory nahradiť &apos;+&apos; predvoľbou &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nNa to sú potrebné nasledujúce pravidlá uvedené v tejto postupnosti:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHľadaný výraz = \\+421([0-9]*) , Náhrada = 0$1&lt;br&gt;\nHľadaný výraz = \\+([0-9]*) , Náhrada = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;2. príklad&lt;/h3&gt;\n&lt;p&gt;\nNachádzate sa na telefónnej ústredni a všetkým číslam začínajúcim nulou má byť predradené číslo 9.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHľadaný výraz = 0[0-9]* , Náhrada = 9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n( $&amp; je špeciálna premenná, v ktorej je uložené celé pôvodné číslo)&lt;br&gt;\nPoznámka: Toto pravidlo nie je možné nastaviť jednoducho ako tretie pravidlo k dvom pravidlám z príkladu č. 1. Bude totiž použité vždy len prvé pravidlo, ktoré vyhľadávaniu vyhovie. Miesto toho by muselo byť zmenené nahrádzanie v pravidlách 1 a 2</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation>Posunúť vybrané pravidlo konverzie na vyššiu pozíciu.</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation>Posunúť vybrané pravidlo konverzie na nižšiu pozíciu.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Pridať</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation>Pridať nové konverzné pravidlo.</translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>&amp;Odstrániť</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation>Odstrániť vybrané konverzné pravidlo.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>U&amp;praviť</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation>Upraviť vybrané konverzné pravidlo.</translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation>Pre overenie funkčnosti vytvoreného konverzného pravidla sem napíšte skúšobné telefónne číslo a kliknite na Test.</translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Test</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation>Otestovať, ako bude číslo prevedené konverznými pravidlami.</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation>Tento časovač sa spustí pri prichádzajúcom hovore. Ak nebude na hovor do vypršania časového limitu odpovedané, Twinkle hovor odmietne so správou &quot;480 User Not Responding&quot;.</translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation>&amp;Keep alive NATu:</translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>&amp;Nedostupný po:</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation>&amp;Tón vyzváňania na strane volaného:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Zadajte meno súboru .wav pre indikáciu zvonenia na strane tohto používateľa.&lt;/p&gt;\n\n&lt;p&gt;Tento tón nahradí tón vyzváňania nastavený v systéme.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Zadajte meno súboru .wav pre tón vyzváňania v tomto profile používateľa.&lt;/p&gt;\n\n&lt;p&gt;Toto nastavenie nahradí tón vyzváňania tón nastavený v systéme.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Tón vyzváňania:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí, ak je nejaký hovor ukončený z vašej strany.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek odosielaných SIP BYE požiadaviek budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. \n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. \n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; obsahuje request-URI metódy BYE. \n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí, ak nebude prichádzajúci hovor prijatý. Tzn. ukončí sa vyzváňanie bez toho aby bolo &quot;zdvihnuté&quot;.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek odosielaných SIP failure požiadaviek budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. \n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; obsahuje stavový kód odosielanej SIP failure odpovede.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;, tedy príčinu chyby.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí, ak je hovor ukončený protistranou.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek odosielaných SIP BYE požiadaviek budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; obsahuje request-URI signálu BYE.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí, ak bude hovor prijatý protistranou.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek prichádzajúcich hlásení &quot;200 OK&quot; budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:&lt;/p&gt;\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí, ak bude prichádzajúci hovor prijatý.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek odchádzajúcich hlásení &quot;200 OK&quot; budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:&lt;/p&gt;\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation>Lokálne &amp;ukončenie hovoru:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí, ak nie je možné realizovať odchádzajúce volanie, napr. kvôli timeoutu, DND atď.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek odosielaných SIP failure požiadaviek budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;.\n&lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; obsahuje stavový kód odoslanej SIP failure odpovede.\n&lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; obsahuje &quot;reason phrase&quot;, tedy chybovou hlásenie vo forme jednoduchého textu.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTento skript sa spustí pri zahájení hovoru z vašej strany.\n&lt;/p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nObsahy všetkých SIP hlavičiek odosielaných SIP INVITE požiadaviek budú odovzdané tomuto skriptu prostredníctvom nasledujúcich systémových premenných:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;.\n&lt;b&gt;SIPREQUEST_URI&lt;/b&gt; obsahuje request-URI signálu INVITE.\n&lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt; obsahuje meno aktívneho používateľského profilu.</translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation>Hovor &amp;prijatý protistranou:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation>Prichádzajúce volanie bolo &amp;neúspešné:</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>&amp;Prichádzajúce volanie:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation>Ukončenie &amp;hovoru protistranou:</translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation>Prichádzajúce volanie &amp;prijaté:</translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>Odchádzajúce &amp;volanie:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation>Od&amp;chádzajúce volanie nebolo úspešné:</translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>Povoliť &amp;šifrovanie ZRTP/SRTP</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unecrypted.</source>\n        <translation type=\"vanished\">Ak je aktivované, Twinkle sa pokúsi pri všetkých odchádzajúcich a prichádzajúcich hovoroch šifrovať zvukové dáta. Aby bol hovor naozaj zašifrovaný musí aj protistrana podporovať šifrovanie ZRTP/SRTP. Inak ostane hovor nešifrovaný.</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>Nastavenia ZRTP</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation>Šifro&amp;vať iba ak protistrana potvrdí podporu ZRTP v SDP</translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation>Protistrana podporujúca ZRTP môže túto informáciu oznámiť už počas zostavovania hovoru. Ak je aktivovaná táto voľba, Twinkle sa v takýchto prípadoch pokúsi použiť šifrovaný prenos hovoru.</translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation>ZRTP podporu ohlasovať &amp;v SDP</translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation>Ak je táto voľba zapnutá, Twinkle pri zostavovaní hovoru ohlási protistrane pomocí SDP, že podporuje ZRTP.</translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation>&amp;Upozorniť, ak protistrana prepne na nešifrovaný prenos hovoru</translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation>Protistrana môže počas šifrovaného hovoru vyslať príkaz ZRTP-go-clear a tým šifrovanie vypnúť. Ak je táto voľba aktívna, Twinkle na tento bezpečnostný problém okamžite upozorní.</translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation>Dynamický typ payloadu %1 je použitý viac ako raz.</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Je potrebné zadať meno používateľa pre váš SIP účet.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Pre váš SIP účet musíte zadať doménové meno.\nPre priame volania medzi IP adresami je to doménové meno alebo verejná IP adresa vášho počítača.</translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Neplatné meno používateľa.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Neplatná doména.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation>Neplatné meno registrátora.</translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation>Neplatné meno odchádzajúcej proxy.</translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation>Chýba verejná IP adresa.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Neplatná hodnota pre STUN server.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Tóny vyzváňania</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Vyberte tón vyzváňania</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Tón pre signalizáciu vyzváňania na strane volaného</translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Všetky súbory</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri prichádzajúcom hovore</translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť po &quot;prijatí prichádzajúceho volania&quot;</translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri chybe počas prichádzajúceho hovoru</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri odchádzajúcom hovore</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri prijatí hovoru protistranou</translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri chybe počas odchádzajúceho hovoru</translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri vlastnom ukončení hovoru</translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation>Vyberte skript, ktorý sa má spustiť pri ukončení hovoru protistranou</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Hlasová schránka</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation>Pri prichádzajúcich hovoroch dávať prednosť kodekom &amp;protistrany</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation>Ak je táto voľba aktívna, Twinkle uprednostní pri prichádzajúcom hovore kodeky protistrany (SDP offer). Konkrétne bude použitý prvý kodek, ktorý je protistranou ponúkaný a tiež sa nachádza v zozname lokálnych kodekov.\nAk je voľba vypnutá, použije Twinkle prvý kodek vo vlastnom zozname, ktorý je tiež podporovaný protistranou.</translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation>Pri odchádzajúcich hovoroch dávať prednosť kodekom podľa &amp;preferencií protistrany</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation>Ak je táto voľba aktívna, Twinkle uprednostní pri odchádzajúcom hovore kodeky protistrany (SDP answer). Konkrétne bude použitý prvý kodek, ktorý je protistranou ponúkaný a tiež sa nachádza v zozname lokálnych kodekov.\nAk je voľba vypnutá, použije Twinkle prvý kodek vo vlastnom zozname, ktorý je tiež podporovaný protistranou, to znamená, že je uvedený v zozname SDP Answer.</translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation>Dátové poradie (codeword &amp;packing order):</translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation>RFC 3551</translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation>ATM AAL2</translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation>Existujú dve metódy balenia dátových paketov G.726 do RTP paketov. Štandardne je to RFC 3351. Niektorí poskytovatelia SIP však používajú ATM AAL2. Ak je prenos zvuku pri použití kodeku G.726 zarušený, môžte vyskúšať balenie ATM AAL2.</translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Rozšírenie Replaces</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extenstion is supported.</source>\n        <translation type=\"vanished\">Indikuje, či je podporované rozšírenie Replaces.</translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation>Asistovane prepájať na AoR (Address of Record)</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endoint.</source>\n        <translation type=\"vanished\">Asistované prepojenie by malo používať Contact URI ako cieľovú adresu pre informovanie presmerovávanej strany o novom spojení. Táto adresa nemusí byť však globálne platiť. Ako alternatívu je možné použiť AoR (Address of Record). Nevýhodou je, že pri viacerých koncových zariadeniach nie je AoR jednoznačné, zatiaľčo URI kontaktu vždy ukazuje na jediné zariadenie.</translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Súkromie</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Nastavenia ochrany súkromia</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation>&amp;Posielať hlavičku P-Preferred-Identity pri skrývaní identity používateľa</translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Ak je vybrané a zároveň je aktívna voľba &quot;skryť odosielateľa&quot;, bude spolu s údajom odosielateľa odoslaná pri požiadavke INVITE hlavička P-Preferred-Identity.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation>&lt;p&gt;\nTu môžete upraviť ako Twinkle obslúži prichádzajúce hovory. Twinkle môže pri prichádzajúcich hovoroch spustiť skript. V závislosti od výstupu skriptu Twinkle prijme, odmietne alebo presmeruje hovor. Skript môže ovplyvniť aj tón vyzváňania. Môže se jednať o ľubovoľný spustiteľný program.\n&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Poznámka:&lt;/b&gt; Twinkle prestane po dobu spustenia skriptu pracovať. Odporúčame, aby skript nebežal dlhšie ako 200 ms. Ak potrebujete viac času, môžete po odoslaní parametrov poslať &lt;b&gt;end&lt;/b&gt; a pokračovať v spúšťaní. Twinkle bude sám po prijatí parametra &lt;b&gt;end&lt;/b&gt; pokračovať.\n&lt;h3&gt;Vrátené hodnoty&lt;/h3&gt; -   print po STDOUT (napr. `echo &quot;action=dnd&quot;`), jedna hodnota na riadok: &lt;br&gt;\n&lt;tt&gt;action=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;&lt;/tt&gt;\n&lt;blockquote&gt;\n&lt;i&gt;continue&lt;/i&gt; - pokračovať v normálnom spracovaní hovoru (predvolené)&lt;br&gt;\n&lt;i&gt;reject&lt;/i&gt; - odmietnuť hovor&lt;br&gt;\n&lt;i&gt;dnd&lt;/i&gt; - odmietnuť hovor s poznámkou &quot;nerušiť&quot;&lt;br&gt;\n&lt;i&gt;redirect&lt;/i&gt; - presmerovať hovor na &lt;tt&gt;contact&lt;/tt&gt;&lt;br&gt;\n&lt;i&gt;autoanswer&lt;/i&gt; - hovor automaticky prijať&lt;br&gt;\n&lt;/blockquote&gt;\n&lt;br&gt;\n&lt;tt&gt;reason=&amp;lt;string&amp;gt;   &lt;/tt&gt;pre dnd a reject (zobrazenie na strane volaného/volajúceho)&lt;br&gt;\n&lt;tt&gt;contact=&amp;lt;presmerovacia adresa&amp;gt; &lt;/tt&gt;pre presmerovanie&lt;br&gt;\n&lt;tt&gt;caller_name=&amp;lt;nové zobrazované meno volajúceho&amp;gt;   &lt;/tt&gt;nahradí meno volajúceho&lt;br&gt;\n&lt;tt&gt;ringtone=&amp;lt;meno .wav súboru&amp;gt;   &lt;/tt&gt;tón vyzváňania zvlášť pre tento hovor (iba pri &lt;i&gt;continue&lt;/i&gt; ;-)&lt;br&gt;\n&lt;tt&gt;display_msg=&amp;lt;ľubovoľná poznámka pre zobrazenie detailov v hlavnom okne&amp;gt;&lt;/tt&gt;&lt;br&gt;\n&lt;tt&gt;end   &lt;/tt&gt;Twinkle vyhodnotí všetky vrátené hodnoty, uzatvorí STDOUT skriptu(!) a pokračuje ďalej&lt;br&gt;\n&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\n&lt;h3&gt;Systémové premenné&lt;/h3&gt;\n&lt;p&gt;\nHodnoty všetkých SIP hlavičiek prichádzajúceho INVITE budu odovzdané tomuto skriptu. Štruktúra premenných: &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; - napr. SIP_FROM obsahuje hodnotu &quot;from header&quot;.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. &lt;br&gt;\nSIPREQUEST_METHOD=INVITE. &lt;br&gt;\nSIPREQUEST_URI obsahuje request-URI signálu INVITE.&lt;br&gt;\nTWINKLE_USER_PROFILE obsahuje meno používateľského profilu, pre ktorý je prichádzajúce volanie určené.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation>&amp;Adresa hlasovej schránky:</translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation>SIP adresa alebo telefónne číslo pre prístup k vašej hlasovej schránke.</translation>\n    </message>\n    <message>\n        <source>Unsollicited</source>\n        <translation type=\"vanished\">Nevyžiadané</translation>\n    </message>\n    <message>\n        <source>Sollicited</source>\n        <translation type=\"vanished\">Vyžiadané</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsollicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsollicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Sollicited&lt;/H3&gt;\n&lt;p&gt;\nSollicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation type=\"vanished\">&lt;H2&gt;Typ indikácie čakajúcich správ&lt;/H2&gt;\n&lt;p&gt;\nAk váš poskytovateľ SIP ponúka upozornenie na uložené správy v hlasovej schránke, môže vás Twinkle informovať o nových aj už vypočutých správach vo vašej hlasovej schránke. Spýtajte sa vášho poskytovateľa, aký typ indikácie čakajúcich správ je používaný\n\n&lt;/p&gt;\n&lt;H3&gt;Nevyžiadané&lt;/H3&gt;\n&lt;p&gt;\nAsterisk podporuje nevyžiadané indikovanie čakajúcich správ.\n&lt;/p&gt;\n&lt;H3&gt;Vyžiadané&lt;/H3&gt;\n&lt;p&gt;\nVyžiadaná indikácia čakajúcich správ podľa RFC 3842.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation>Typ &amp;MWI:</translation>\n    </message>\n    <message>\n        <source>Sollicited MWI</source>\n        <translation type=\"vanished\">Vyžiadané MWI</translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation>Doba &amp;platnosti odberu:</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation>Meno používateľa &amp;hlasovej schránky:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation>Meno hostiteľa, doménové meno alebo IP adresa servera vašej hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>For sollicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation type=\"vanished\">Podľa špecifikácie MWI sa koncové zariadenie prihlási na serveri k príjmu správ na určitú dobu. Pred vypršaním tejto doby by sa prihlásenie malo znovu obnoviť.</translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation>Meno používateľa pre prístup k vašej hlasovej schránke.</translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation>&amp;Server hlasovej schránky:</translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation>Cez odchádzajúcu &amp;proxy</translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation>Ak je táto voľba aktivovaná, Twinkle zasiela správy SIP na server hlasovej schránky cez odchádzajúcu proxy.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation>Musíte vyplniť meno používateľa hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation>Musíte vyplniť server hlasovej schránky</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation>Neplatný server hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation>Neplatné meno používateľa hlasovej schránky.</translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation>Použiť doménové &amp;meno pre vytvorenie jednoznačnej hlavičky Contact</translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation>Vyberte súbor pre tón vyzváňania na druhej strane.</translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation>Vyberte súbor s tónom vyzváňania.</translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation>Vyberte súbor so skriptom.</translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>%1 konvertuje na %2</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Textová správa</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Prítomnosť</translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation>&amp;Maximálny počet sedení:</translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation>Ak je už otvorený tento počet sedení s textovými správami, nové prichádzajúce sedenia budú odmietnuté.</translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Vaše dostupnosti</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>&amp;Zverejniť dostupnosť pri štarte</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Zverejniť vašu dostupnosť pri spustení.</translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation>Prítomnosť kontaktov</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation>Interval &amp;odosielania stavu (s):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation>Frekvencia odosielania vlastnej dostupnosti.</translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation>&amp;Interval obnovenia príjmu dostupnosti (s):</translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation>Frekvencia príjmu dostupnosti kontaktov.</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Prenos/NAT</translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation>Pridať q-hodnotu k registrácii</translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation>Hodnota &apos;q&apos; určuje prioritu vášho registrovaného zariadenia. Ak je okrem Twinkle k VoIP účtu registrované aj iné SIP zariadenie, môže sieť využiť tieto hodnoty na určenie zariadenia, ktoré má byť prednostne oslovené pre obslúženie hovoru.</translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation>Hodnota &apos;q&apos; je medzi 0.000 and 1.000. Vyššia hodnota znamená vyššiu prioritu.</translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>Prenos SIP</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation>UDP</translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation>TCP</translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Režim prenosu SIP. V automatickom režime je prenosový protokol určený veľkosťou správy. Správy väčšie ako prah pre UDP sú posielané protokolom TCP. Menšie správy sú posielané pomocou UDP.</translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>P&amp;renosový protokol:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation>&amp;Prah použitia UDP:</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation>Správy väčšie ako špecifikovaný prah sú doručované protokolom TCP. Menšie správy sú doručované protokolom UDP.</translation>\n    </message>\n    <message>\n        <source>Use &amp;STUN (does not work for incoming TCP)</source>\n        <translation type=\"vanished\">Použiť &amp;STUN (nefunguje pre prichádzajúce TCP)</translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation>Tr&amp;valé TCP spojenie</translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation>Podržať otvorené TCP spojenie vytvorené počas registrácie tak, aby SIP proxy mohla využiť toto spojenie na vysielanie prichádzajúcich požiadaviek. Aplikácia odosiela pakety ping tak, aby sa zistilo, či je spojenie stále aktívne.</translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation>Pri písaní správ &amp;odosielať o tomto indikáciu.</translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation>Ak píšete správu, Twinkle o tomto informuje protistranu. Takto se príjemca môže dozvedieť, že niečo píšete.</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation>AKA AM&amp;F:</translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation>A&amp;KA OP:</translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation>Parametre autentizačného managementu pre AKAv1-MD5 autentizáciu.</translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation>Operátorova varianta kľúča pre autentizáciu AKAv1-MD5.</translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation>Pred&amp;spracovanie</translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation>Predspracovanie (vylepšuje kvalitu u príjemcu)</translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation>&amp;Automatické riadenie hlasitosti</translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation>Z dôvodu veľkého rozdielu hlasitostí nahrávania v rôznych konfiguráciách bola zavedená funkcia automatického riadenia hlasitosti (AGC - Automatic gain control). AGC umožňuje nastaviť úroveň signálu na prednastavenú hodnotu. Vďaka tomu nie je potrebné neustále nastavovať hlasitosť mikrofónu. Ďalšou výhodou je, že nastavenie hlasitosti mikrofónu je väčšinou na nižšej (konzervatívnej) úrovni, čím sa predchádza orezávaniu príliš hlasného zvuku.</translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation>&amp;Úroveň automatického riadenia hlasitosti:</translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation>Úroveň automatického riadenia hlasitosti predstavuje percentuálnu hodnotu maximálnej hlasitosti mikrofónu. Odporúčaná hodnota je kolem 25%.</translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation>Detekcia &amp;hlasu</translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation>Ak je táto voľba aktívna, detekcia hlasu zisťuje, či je na zvukovom vstupe hlas alebo ticho/šum na pozadí.</translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation>&amp;Potlačenie šumu</translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation>Potlačenie šumu môže byť použité na zníženie okolitých rušivých zvukov vo vstupnom signále. Toto vedie k lepšej kvalite hovoreného slova.</translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation>Potlačenie &amp;akustickej ozveny</translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation>Ak je pri VoIP komunikácii prichádzajúci zvuk prepnutý na hlasný odposluch v reproduktoroch, môže sa šíriť miestnosťou a byť snímaný mikrofónom. Ak je tento signál posielaný späť volajúcemu, stáva sa, že počuje ozvenu vlastného hlasu. Funkcia potlačenia akustickej ozveny je navrhnutá na potlačenie týchto zvukov pred tým, ako sú odoslané volajúcemu. Je dôležité si uvedomiť, že táto funkcia je určená pre zlepšenie kvality prenosu hlasu na druhej strane hovoru.</translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation>Premenlivý &amp;dátový tok</translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation>Diskontinuitný &amp;prenos</translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation>&amp;Kvalita:</translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation>Speex je stratový kodek, čo znamená, že je na úkor kvality možné docieliť redukciu dátového toku. Na rozdiel od iných hlasových kodekov je možné nastaviť kompromis medzi kvalitou a dátovým tokom. Kódovací proces pri tomto kodeku je spravidla riadený nastavením parametru kvality v rozsahu od 0 do 10.</translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation>bajtov</translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation>Použiť tel-URI pre &amp;telefónne číslo</translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation>Rozšíriť vytáčané telefónne číslo na tel-URI miesto sip-URI.</translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation>Prijímať žiadosti o &amp;prepojenie hovoru (prichádzajúci REFER)</translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation>Povoliť prepojenie hovoru počas asistovaného prepojenia</translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation>Ak robíte asistované prepojenie, tak obvykle prepojíte hovor po tom, čo prebehne konzultácia s protistranou. Akonáhle povolíte túto voľbu, potom môžete prepojiť hovor, kým konzultácia stále prebieha. Toto je neštandardná implementácia, ktorá nemusí správne pracovať so všetkými zariadeniami SIP.</translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation>Povoliť NAT &amp;keep alive</translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation>Posielať UDP pakety pre udržanie spojenia cez NAT.</translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation>Ak máte povolený STUN alebo NAT keep alive, bude Twinkle zasielať udržovacie pakety v tomto intervale tak, aby boli udržané mapovania na vašom NATe.</translation>\n    </message>\n    <message>\n        <source>Do&amp;main*:</source>\n        <translation>&amp;Doména*:</translation>\n    </message>\n    <message>\n        <source>Organi&amp;zation:</source>\n        <translation>Or&amp;ganizácia:</translation>\n    </message>\n    <message>\n        <source>E&amp;xpiry:</source>\n        <translation>&amp;Platnosť:</translation>\n    </message>\n    <message>\n        <source>Call Hold &amp;variant:</source>\n        <translation>Spôsob pridržania &amp;hovoru:</translation>\n    </message>\n    <message>\n        <source>&amp;Max redirections:</source>\n        <translation>Max. počet &amp;presmerovaní:</translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extension is supported.</source>\n        <translation>Indikuje, či je podporované rozšírenie Replaces.</translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</source>\n        <translation>Asistované prepojenie by malo používať Contact URI ako cieľovú adresu pre informovanie presmerovávanej strany o novom spojení. Táto adresa nemusí však globálne platiť. Ako alternatívu je možné použiť AoR (Address of Record). Nevýhodou je, že pri viacerých koncových zariadeniach nie je AoR jednoznačné, no URI kontaktu vždy ukazuje na jediné zariadenie.</translation>\n    </message>\n    <message>\n        <source>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation>Ak je vybrané a zároveň je aktívna voľba &quot;skryť odosielateľa&quot;, bude spolu s údajom odosielateľa odoslaná pri požiadavke INVITE hlavička P-Preferred-Identity.</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Asserted-Identity header when hiding user identity</source>\n        <translation>&amp;Posielať hlavičku P-Preferred-Identity pri skrývaní identity používateľa</translation>\n    </message>\n    <message>\n        <source>Use STUN (does not wor&amp;k for incoming TCP)</source>\n        <translation>Použiť &amp;STUN (nefunguje pre prichádzajúce TCP)</translation>\n    </message>\n    <message>\n        <source>STUN ser&amp;ver:</source>\n        <translation>Adresa S&amp;TUN servera:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation>&lt;p&gt;\nČasto sa formát telefónnych čísiel, ktoré očakáva poskytovateľ VoIP nezhoduje s formátom čísiel uložených v adresári. Napr. pri číslach začínajúcich na &quot;+&quot; a národným kódom krajiny očakáva váš poskytovateľ miesto &quot;00&quot; znak &quot;+&quot;. Alebo ste pripojený na miestnu sieť SIP a je potrebné najprv zadať predvoľbu pre volania smerom von.\nTu je možné pomocou vyhľadávacích a nahradzovaných vzorov (podľa štýlu regulárnych výrazov so syntaxom jazyku Perl) nastaviť všeobecne platné pravidlá pre konverziu telefónnych čísiel.\n&lt;/p&gt;\n&lt;p&gt;\nPri každom vytáčaní sa Twinkle pokúsi nájsť pre čísla z používateľskej časti adresy SIP zodpovedajúci výraz v zozname hľadaných vzorov. S prvým nájdeným vyhovujúcim výrazom je vykonaná úprava pôvodného čísla, pričom pozícia v &quot;(&quot; &quot;)&quot; v hľadanom výraze (napr. &quot;([0-9]*)&quot; pre &quot;ľubovoľný počet čísiel&quot;) je nahradená znakmi v zodpovedajúcich premenných. Napr. &quot;$1&quot; pre prvú pozíciu. Viď tiež `man 7 regex` alebo konqueror:&quot;#regex&quot;. Ak nie je nájdený žiaden zodpovedajúci hľadaný vzor, zostane číslo nezmenené.\n&lt;/p&gt;\n&lt;p&gt;\nPravidlá budú tiež použité na čísla v prichádzajúcich hovoroch. Podľa nastavených pravidiel budú pretransformované do žiadaného formátu.\n&lt;/p&gt;\n&lt;h3&gt;1. príklad&lt;/h3&gt;\n&lt;p&gt;\nNapr. váš národní kód je &quot;421&quot; pre Slovenskú republiku a vo vašom adresári máte tiež veľa vnútroštátnych čísiel uložených v medzinárodnom formáte. Napr.. +421 2 123 456. Avšak poskytovateľ VoIP očakáva pre vnútroštátny hovor číslo 02 123456. Chcete teda nahradiť &apos;+421&apos; číslom &apos;0&apos; a zároveň pre zahraničné hovory nahradiť &apos;+&apos; predvoľbou &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nNa to sú potrebné nasledujúce pravidlá uvedené v tomto poradí:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHľadaný výraz = \\+421([0-9]*) , Náhrada = 0$1&lt;br&gt;\nHľadaný výraz = \\+([0-9]*) , Náhrada = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;2. príklad&lt;/h3&gt;\n&lt;p&gt;\nNachádzate sa na telefónnej ústredni a všetkým číslam začínajúcim nulou má byť predradené číslo 9.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nHľadaný výraz = 0[0-9]* , Náhrada = 9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n( $&amp; je špeciálna premenná, v ktorej je uložené celé pôvodné číslo)&lt;br&gt;\nPoznámka: Toto pravidlo nie je možné nastaviť jednoducho ako tretie pravidlo k dvom pravidlám z príkladu č. 1. Bude totiž použité vždy len prvé pravidlo, ktoré vyhľadávaniu vyhovie. Miesto toho by muselo byť zmenené nahrádzanie v pravidlách 1 a 2</translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</source>\n        <translation>Ak je aktivované, Twinkle sa pokúsi pri všetkých odchádzajúcich a prichádzajúcich hovoroch šifrovať zvukové dáta. Aby bol hovor naozaj zašifrovaný musí aj protistrana podporovať šifrovanie ZRTP/SRTP. Inak ostane hovor nešifrovaný.</translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation>&lt;H2&gt;Typ indikácie čakajúcich správ&lt;/H2&gt;\n&lt;p&gt;\nAk váš poskytovateľ SIP ponúka upozornenie na uložené správy v hlasovej schránke, môže vás Twinkle informovať o nových aj už vypočutých správach vo vašej hlasovej schránke. Spýtajte sa vášho poskytovateľa, aký typ indikácie čakajúcich správ je používaný\n\n&lt;/p&gt;\n&lt;H3&gt;Nevyžiadané&lt;/H3&gt;\n&lt;p&gt;\nAsterisk podporuje nevyžiadané indikovanie čakajúcich správ.\n&lt;/p&gt;\n&lt;H3&gt;Vyžiadané&lt;/H3&gt;\n&lt;p&gt;\nVyžiadaná indikácia čakajúcich správ podľa RFC 3842.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>Unsolicited</source>\n        <translation>Nevyžiadané</translation>\n    </message>\n    <message>\n        <source>Solicited</source>\n        <translation>Vyžiadané</translation>\n    </message>\n    <message>\n        <source>Solicited MWI</source>\n        <translation>Vyžiadané MWI</translation>\n    </message>\n    <message>\n        <source>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation>Podľa špecifikácie MWI sa koncové zariadenie prihlási na serveri k príjmu správ na určitú dobu. Pred vypršaním tejto doby by sa prihlásenie malo znovu obnoviť.</translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Sprievodca</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Doménové meno, IP adresa alebo meno hostiteľa STUN servera.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN server:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Meno používateľa, ktoré vám bolo pridelené vaším poskytovateľom SIP. Je tiež prvou časťou vašej úplnej SIP adresy &lt;b&gt;pouzivatel&lt;/b&gt;@domena.com. Môže sa tiež jednať o telefónne číslo.\n&lt;br&gt;&lt;br&gt;\nTento údaj je povinný.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Doména*:</translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Vyberte vášho poskytovateľa SIP a uveďte tu vaše celé meno, vaše používateľské meno SIP, prípadne prihlasovacie meno a heslo.&lt;br&gt;\nAk váš poskytovateľ nie je v zozname, vyberte &lt;b&gt;Iný&lt;/b&gt; a uveďte požadované údaje.</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>&amp;Prihlasovacie meno:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>Vaše &amp;meno:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Vaše prihlasovacie meno SIP. Často sa zhoduje s vaším SIP menom používateľa. Môže však byť aj iné.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Doménová časť vašej úplnej SIP adresy pouzivatel@&lt;b&gt;domena.com&lt;/b&gt;. Okrem skutočnej domény sa môže tiež jednať o meno hostiteľa alebo IP adresu vašej &lt;b&gt;SIP proxy&lt;/b&gt;. Pre priame volania medzi IP adresami tu môžete zadať meno hostiteľa alebo IP adresu vášho počítača.\n&lt;br&gt;&lt;br&gt;\nTento údaj je povinný.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Vaše celé meno, napr. Jozef Mrkva. Používa sa len pre účely zobrazenia. Akonáhle uskutočníte hovor, toto meno môže byť zobrazené volanému.</translation>\n    </message>\n    <message>\n        <source>SIP pre&amp;xy:</source>\n        <translation type=\"vanished\">SIP pro&amp;xy:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>Doménové meno, IP adresa alebo meno vašej proxy. Ak sa zhoduje s doménou, je možné toto pole nechať prázdne.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>Poskytovateľ &amp;SIP VoIP služby:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Heslo:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>&amp;Meno používateľa*:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Vaše prihlasovacie heslo.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Zrušiť</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+Z</translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Žiadne (priame volanie medzi IP adresami)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Iný</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Sprievodca profilu používateľa:</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Musíte zadať meno používateľa pre váš SIP účet.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Je potrebné zadať doménové meno vášho SIP účtu (časť vpravo od symbolu &quot;@&quot;).\nV prípade priameho volania medzi IP adresami se môže jednať o meno hostiteľa alebo IP adresu vášho PC.</translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Neplatná hodnota pre SIP proxy.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Neplatná hodnota pre STUN server.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>SIP pro&amp;xy:</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Áno</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Nie</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation>Prijať</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Odmietnuť</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/lang/twinkle_sv.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"sv\" sourcelanguage=\"en\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation>Twinkle - Adresskort</translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation>Förnamn för kontakten.</translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation>&amp;Förnamn:</translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefon:</translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation>Telefonnummer eller SIP-adress för kontakten.</translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation>Efternamn för kontakten.</translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation>&amp;Efternamn:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Du måste ange ett namn.</translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation>Du måste ange ett telefonnummer eller SIP-adress.</translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation type=\"unfinished\">Namn</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Telefon</translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation>Twinkle - Autentisering</translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation>användare</translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation>Användaren för vilken autentisering begärs.</translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation>profil</translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation>Användarprofilen för användaren för vilken autentisering begärs.</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Användarprofil:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Användare:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Lösenord:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ditt lösenord för autentisering.</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ditt SIP-autentiseringsnamn. Ganska ofta är detta samma som ditt SIP-användarnamn. Det kan dock vara ett annat namn.</translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation>Användar&amp;namn:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation>Twinkle - Kompis</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation>&amp;Telefon:</translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation>Namnet på din kompis.</translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation>&amp;Visa tillgänglighet</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation>Kryssa för detta alternativ om du vill se tillgängligheten för din kompis. Detta kommer endast att fungera om din leverantör erbjuder en närvaroagent.</translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation>&amp;Namn:</translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation>SIP-adress till din kompis.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation>Du måste ange ett namn.</translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation>Ogiltig telefon.</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Misslyckades med att spara kompislista: %1</translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation>Tillgänglighet</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>okänd</translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation>frånkopplad</translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation>ansluten</translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation>begäran misslyckades</translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation>begäran avvisades</translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation>inte publicerad</translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation>misslyckades med att publicera</translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation>Högerklicka för att lägga till en kompis.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation>MIsslyckades med att öppna ljudkortet</translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation>Misslyckades med att skapa ett UDP-uttag(RTP) på port %1</translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation>Misslyckades med att skapa ljudmottagartråd.</translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation>Misslyckades med att skapa ljudsändartråd.</translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation>lokal användare</translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation>fjärranvändare</translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation>fel</translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>okänd</translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation>in</translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation>ut</translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation>Twinkle - Avregistrering</translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation>avregistrera alla enheter</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation type=\"obsolete\">Detta är helt enkelt ditt fullständiga namn, t.ex. Sven Svensson. Det används endast för visning. När du ringer ett samtal kan dock detta namn visas för motparten.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation type=\"obsolete\">&amp;Ditt namn:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"obsolete\">&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"obsolete\">&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation>Twinkle - DTMF</translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation>Knappsats</translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation>2</translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation>3</translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation>Över dekadiskt A. Behövs oftast inte.</translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation>4</translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation>5</translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation>6</translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation>Över dekadiskt B. Behövs oftast inte.</translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation>7</translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation>8</translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation>9</translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation>Över dekadiskt C. Behövs oftast inte.</translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation>Stjärna (*)</translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation>0</translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation>Fyrkant (#)</translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation>Över dekadiskt D. Behövs oftast inte.</translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation>1</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>S&amp;täng</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+T</translation>\n    </message>\n</context>\n<context>\n    <name>FreeDeskSysTray</name>\n    <message>\n        <source>Show/Hide</source>\n        <translation type=\"obsolete\">Visa/Göm</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"obsolete\">Avsluta</translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Failed to create a UDP socket (SIP) on port %1</source>\n        <translation type=\"obsolete\">Kunde inte skapa UDP-socket (SIP) på port %1</translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation>Strunta i låsfil och starta ändå?</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Följande profiler är båda för användaren %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Du kan endast köra flera profiler för olika användare.</translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation>Kan inte hitta ett nätverkskort. Twinkle kommer att använda 127.0.0.1 som lokal IP-adress. När du ansluter till nätverket så måste du starta om Twinkle för att använda den korrekta IP-adressen.</translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation>Linje %1: inkommande samtal för %2</translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation>Samtal kopplat av %1</translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation>Linje %1: SDP-svar från andra parten stöds inte.</translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation>Linje %1: Inget SDP-svar från andra parten.</translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation>Linje %1: inget ACK mottaget. Samtalet kommer avslutas.</translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation>Linje %1: inget PRACK mottaget. Samtalet kommer avslutas.</translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation>Linje %1: PRACK misslyckades.</translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation>Linje %1: misslyckades med att avbryta samtal.</translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation>Linje %1: motparten svarade på samtalet.</translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation>Linje %1: samtal misslyckades.</translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation>Samtalet kan vidarekopplas till:</translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation>Linje %1: samtal etablerat.</translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation>Svar på begäran om terminalförmågor: %1 %2</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation>Terminalförmågor för %1</translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation>okänt</translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation>Accepterade kodningar:</translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation>Accepterade språk:</translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation>Tillåtna begäran:</translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation></translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation>Typ av motpart:</translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation>%1, registrering misslyckades: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation>%1, registrering lyckades (går ut om = %2 sekunder)</translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation>%1, registrering misslyckades: STUN-problem</translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation>%1, avregistrering lyckades: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation>%1, avregistrering misslyckades: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation>: du är inte registrerad</translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation>: du har följande registreringar</translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation>: hämtar registreringar...</translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation>Linje %1: Skickar vidare till</translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation>Skickar vidare till: %1</translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation>Linje %1: DTMF detekterad:</translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation>Linje %1: skicka DTMF %2</translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation>Händelse: %1</translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation>Tillstånd: %1</translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation>Anledning: %1</translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation>Förlopp: %1 %2</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation>Linje %1: samtalskoppling misslyckades.</translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation>Linje %1: samtal kopplades.</translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation>Linje %1: samtalskoppling pågår fortfarande.</translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation>Linje %1: kopplar samtal till %2</translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation>Koppilng begärd av %1</translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation>%1. STUN-begäran misslyckades: %2 %3</translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation>%1, STUN-begäran misslyckades.</translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation>Vidarekoppling av samtal</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Användarprofil:</translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Användare:</translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation>Tillåter du samtalet att hänvisas till följande destination?</translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation type=\"unfinished\">Om du inte vill bli frågad detta igen måste du ändra inställningarna i sektionen SIP-protokoll för användarprofilen.</translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation>Tillåter du samtalet att kopplas vidare till följande destination?</translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Varning:</translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation>Brandvägg / NAT-identifiering...</translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation>Avbryt</translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation>Linje %1</translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation>Klicka på hänglåset för att bekräfta en korrekt SAS.</translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation>Motparten på linje %1 stängde av krypteringen.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation>Linje %1: SAS bekräftad.</translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation>%1, kan inte hämta status för röstbrevlåda.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation>%1, hämtning av status för röstbrevlåda avvisades.</translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation>%1, röstbrevlådan finns inte.</translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation>%1, hämtning av status för röstbrevlåda avslutad.</translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation>Linje %1: samtal avvisades.</translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation>Linje %1: samtal vidarekopplat.</translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation>Kunde inte starta konferens.</translation>\n    </message>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation>Misslyckades med att skapa ett %1-uttag (SIP) på port %2</translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation>Misslyckades med att spara meddelandebilaga: %1</translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation>Accepterad av nätverket</translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation>Twinkle - Välj adress</translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation>&amp;KAddressBook</translation>\n    </message>\n    <message>\n        <source>Name</source>\n        <translation type=\"obsolete\">Namn</translation>\n    </message>\n    <message>\n        <source>Type</source>\n        <translation type=\"obsolete\">Typ</translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"obsolete\">Telefon</translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation>&amp;Visa endast SIP-adresser</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation>Kryssa för detta alternativ när du endast vill se kontakter med SIP-adresser, alltså börjar med &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation>Upp&amp;datera</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation>Uppdatera listan över adresser från KAddressbook.</translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation>&amp;Lokal adressbok</translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation>Kontakter i den lokala adressboken för Twinkle.</translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Lägg till</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+L</translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation>Lägg till en ny kontakt i den lokala adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Ta bort</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation>Ta bort en kontakt från den lokala adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Redigera</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation>Redigera en kontakt från den lokala adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation>Twinkle - Profilnamn</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation>Ge din profil ett namn:</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Din profils namn&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nEn profil innehåller dina användarinställningar. T.ex. ditt användarnamn och lösenord. Du måste ge varje profil ett namn.\n&lt;br&gt;&lt;br&gt;\nOm du har flera SIP-konton kan du skapa flera profiler. När du startar Twinkle kommer du se en lista över dina profiler och kunna välja vilken av profilerna du vill använda.\n&lt;br&gt;&lt;br&gt;\nFör att enkelt komma ihåg dina profiler kan du använda ditt SIP-användarnamn som profilnamn. T.ex. &lt;b&gt;exempel@exempel.com&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Kan inte hitta katalogen .twinkle i din hemkatalog.</translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation>Profilen finns redan.</translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation>Döp om profilen &apos;%1&apos; till:</translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation>Twinkle - Samtalshistorik</translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation>Tid</translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation>In/Ut</translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation>Från/Till</translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation>Ämne</translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation>Status</translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation>Samtalsdetaljer</translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation>Detaljer för det valda samtalet.</translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation>Visa</translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation>&amp;Inkommande samtal</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation>Kryssa i denna ruta för att visa inkommande samtal.</translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation>&amp;Utgående samtal</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+U</translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation>Kryssa i denna ruta för att visa utgående samtal.</translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation>&amp;Besvarade samtal</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation>Kryssa i denna ruta för att visa besvarade samtal.</translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation>&amp;Missade samtal</translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation>Alt+M</translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation>Kryssa i denna ruta för att visa missade samtal.</translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation>Enbart aktuell &amp;användarprofil</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation>Kryssa i denna ruta för att enbart visa samtal associerade med aktuell användarprofil.</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Töm</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation>&lt;p&gt;Töm hela samtalshistoriken.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Observera:&lt;/b&gt; detta rensar bort &lt;b&gt;alla&lt;/b&gt; samtal. Även samtal som inte visas på grund av aktuella visningsalternativ.&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation type=\"obsolete\">&amp;Stäng</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation>Stäng detta fönster.</translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation>Samtal startade:</translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation>Samtal besvarades:</translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation>Samtal avslutades:</translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation>Samtalslängd:</translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation>Riktning:</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Från:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Till:</translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation>Svara till:</translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation>Kopplad av:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Ämne:</translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation>Avslutat av:</translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation>Status:</translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation>Enhet på andra sidan:</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Användarprofil:</translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation>konversation</translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation>Ring upp...</translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation>Ta bort</translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation>Sv:</translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation>St&amp;äng</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+Ä</translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation>&amp;Ring</translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation>Ring markerad adress.</translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation>Twinkle - Ring</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Till:</translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation>Om du vill kan du ange ämne här. Det kan visas för den du ringer.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adressen som du vill ringa. Detta kan vara en komplett SIP-adress som &lt;b&gt;sip:exempel@exempel.com&lt;/b&gt; eller bara användarnamnet eller telefonnumret i adressen. När du inte anger en komplett adress kommer Twinkle fylla ut adressen med domännamnet från din användarprofil.</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Användare som ringer upp.</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Ämne:</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Från:</translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation>&amp;Dölj identitet</translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation>Alt+D</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation>&lt;p&gt;\nMed denna inställning kan du be din SIP-leverantör dölja din identitet för den du ringer upp. Detta döljer bara din identitet. D.v.s. din SIP-adress eller telefonnummer. Det döljer &lt;b&gt;inte&lt;/b&gt; din IP-adress.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Varning:&lt;/b&gt; Inte alla SIP-leverantörer stöder dold identitet.\n&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation>Inte alla SIP-leverantörer stöder dold identitet. Se till att din SIP-leverantör stöder det om du verkligen behöver det.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation>Twinkle - Logg</translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation>Innehåll i nuvarande loggfil (~/.twinkle/twinkle.log)</translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation>&amp;Stäng</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation>&amp;Töm</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation>Töm loggfönstret. Detta tömmer &lt;b&gt;inte&lt;/b&gt; själva loggfilen.</translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation>Twinkle - Snabbmeddelande</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Till:</translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation>Användaren som ska skicka meddelandet.</translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation>&amp;Användarprofil:</translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation>Konversation</translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation>Skriv ditt meddelande här och tryck sedan på &quot;Skicka&quot; för att skicka det.</translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation>&amp;Skicka</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation>Skicka meddelandet.</translation>\n    </message>\n    <message>\n        <source>Instant message toolbar</source>\n        <translation type=\"obsolete\">Verktygsrad för snabbmeddelanden</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Skicka fil...</translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation>Skicka fil</translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation>bildstorleken är nedskalad i förhandsvisning</translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation>Leverans misslyckades</translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation>Leveransnotifiering</translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation>Öppna med %1...</translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation>Öppna med...</translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation>Spara bilaga som...</translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation>Filen finns redan. Vill du skriva över denna fil?</translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation>Misslyckades med att spara bilaga.</translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation>%1 skriver ett meddelande.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation>skickar meddelande</translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation>Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation>&amp;Ring:</translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adressen som du vill ringa. Detta kan vara en komplett SIP-adress som &lt;b&gt;sip:exempel@exempel.com&lt;/b&gt; eller bara användarnamnet eller telefonnumret i adressen. När du inte anger en komplett adress kommer Twinkle fylla ut adressen med domännamnet från din användarprofil.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation>Ring</translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation>Ring till adressen.</translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation>&amp;Användare:</translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation>Användaren som ska ringa samtalet.</translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation>Linjestatus</translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation>Linje &amp;1:</translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation>Alt+1</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation>Klicka för att växla till linje 1.</translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation>Från:</translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation>Till:</translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation>Ämne:</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation>Overksam</translation>\n    </message>\n    <message>\n        <source>Conference call</source>\n        <translation type=\"obsolete\">Konferenssamtal</translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation>sas</translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation>g711a/g711a</translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation>Ljudkodek</translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation>0:00:00</translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation>Samtalslängd</translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation>sip:från</translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation>sip:till</translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation>ämne</translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation>foto</translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation>Linje &amp;2:</translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation>Alt+2</translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation>Klicka för att växla till linje 2.</translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation>&amp;Arkiv</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Redigera</translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation>&amp;Ring</translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation>Aktivera linje</translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation>&amp;Registrering</translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation>&amp;Tjänster</translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation>&amp;Visa</translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation>&amp;Hjälp</translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation>Avsluta</translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation>&amp;Avsluta</translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+A</translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation>Om Twinkle</translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation>&amp;Om Twinkle</translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Ring</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <comment>call menu text</comment>\n        <translation type=\"obsolete\">&amp;Ring...</translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation>Ring någon</translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation>F5</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Svara</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Svara</translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation>Svara inkommande samtal</translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation>F6</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Adjö</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Lägg på</translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation>Esc</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Avvisa</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Avvisa</translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation>Avvisa inkommande samtal</translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation>F8</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Parkera</translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation>Pakera ett samtal eller återuppta ett parkerat samtal</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Koppla vidare</translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Koppla vidar&amp;e...</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation>Koppla vidare inkommande samtal utan att svara</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Dtmf...</translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation>Öppna knappsats för att mata in siffror för röstmenyer</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Registrera</translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation>&amp;Registrera</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Avregistrera</translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation>Avre&amp;gistrera</translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation>Avregistrera denna enhet</translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation>Visa registreringar</translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation>&amp;Visa registreringar</translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation>Terminalförmågor</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Terminalförmågor...</translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation>Begär terminalförmågor från någon</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Stör inte</translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation>Stör &amp;inte</translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation>Vidarekoppla samtal</translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation>Vidarekoppla sa&amp;mtal...</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Ring igen</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Ring &amp;igen</translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation>Upprepa senaste samtalet</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation>F12</translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation>Om Qt</translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation>Om &amp;Qt</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation>Användarprofil</translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation>&amp;Användarprofil...</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Konf</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Konferens</translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Tyst</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">&amp;Tyst</translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation>Stäng av mikrofonen</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <comment>toolbar text</comment>\n        <translation type=\"obsolete\">Koppla</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <comment>menu text</comment>\n        <translation type=\"obsolete\">Kopp&amp;la...</translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation>Koppla samtal</translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation>Systeminställningar</translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation>&amp;Systeminställningar...</translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation>Avregistrera alla</translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation>Avregistrera &amp;alla</translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation>Avregistera alla dina registrerade enheter</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Svara automatiskt</translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation>S&amp;vara automatiskt</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Logg</translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation>&amp;Logg...</translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation>Samtalshistorik</translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation>Samtals&amp;historik...</translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation>F9</translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation>Byt användare...</translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation>&amp;Byt användare...</translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation>Aktivera eller inaktivera användare</translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation>Vad är detta?</translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation>Vad är &amp;detta?</translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation>Shift+F1</translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation>Linje 1</translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation>Linje 2</translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation>overksam</translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation>ringer</translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation>försöker ringa samtal, vänta</translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation>inkommande samtal</translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation>etablerar samtal, vänta</translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation>etablerat</translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation>etablerat (väntar på media)</translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation>okänt tillstånd</translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation>Röstkanal är krypterad</translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation>Klicka för att bekräfta SAS.</translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Användare:</translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation>Ring:</translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation>Dölj identitet</translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation>Registreringsstatus:</translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation>Registrerad</translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation>Misslyckades</translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation>Inte registrerad</translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation>Klicka för att visa registreringar.</translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation>Inga användare är registrerade.</translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation>%1 nytt, ett gammalt meddelande</translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation>%1 nya, %2 gamla meddelanden</translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation>Ett nytt meddelande</translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation>%1 nya meddelanden</translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation>Ett gammalt meddelande</translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation>%1 gamla meddelanden</translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation>Meddelanden väntar</translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation>Inga meddelanden</translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation>&lt;b&gt;Status för röstbrevlåda:&lt;/b&gt;</translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation>Misslyckades</translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation>Okänt</translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation>Klicka för att komma åt röstmeddelanden.</translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation>Stör inte är aktivt för:</translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation>Vidarekoppling är aktivt för:</translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation>Svara automatiskt är aktivt för:</translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation>Klicka för att aktivera/inaktivera</translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation>Klicka för att aktivera</translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation>Stör inte är inte aktiverat.</translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation>Vidarekoppling är inte aktiverat.</translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation>Svara automatiskt är inte aktiverat.</translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation>Klicka för att se samtalshistorik för detaljer.</translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation>Du har inga missade samtal.</translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation>Du missade ett samtal.</translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation>Du missade %1 samtal.</translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation>Startar användarprofiler...</translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation>Följande profiler är båda för användaren %1</translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation>Du kan endast köra flera profiler för olika användare.</translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation>Linjen är upptagen. Kan inte komma åt röstmeddelanden.</translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation>Kompislista</translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation>&amp;Meddelande</translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Röstbrevlåda</translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation>&amp;Röstbrevlåda</translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation>F11</translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation>Chatt</translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation>Snabb&amp;meddelande...</translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Snabbmeddelande</translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation>&amp;Kompislista</translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation>&amp;Ring...</translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation>&amp;Redigera...</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Ta bort</translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation>Fr&amp;ånkopplad</translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation>A&amp;nsluten</translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation>&amp;Ändra tillgänglighet</translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation>&amp;Lägg till kompis...</translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation>Misslyckades med att spara kompislista. %1</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation type=\"unfinished\">Ring</translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation type=\"unfinished\">&amp;Svara</translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\">Svara</translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation type=\"unfinished\">&amp;Lägg på</translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation type=\"unfinished\">Adjö</translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation type=\"unfinished\">&amp;Avvisa</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\">Avvisa</translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation type=\"unfinished\">&amp;Parkera</translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation type=\"unfinished\">Koppla vidar&amp;e...</translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation type=\"unfinished\">Koppla vidare</translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation type=\"unfinished\">&amp;Dtmf...</translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation type=\"unfinished\">Dtmf</translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation type=\"unfinished\">&amp;Terminalförmågor...</translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation type=\"unfinished\">Ring &amp;igen</translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation type=\"unfinished\">Ring igen</translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation type=\"unfinished\">&amp;Konferens</translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation type=\"unfinished\">Konf</translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation type=\"unfinished\">&amp;Tyst</translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation type=\"unfinished\">Tyst</translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation type=\"unfinished\">Kopp&amp;la...</translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation type=\"unfinished\">Koppla</translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation>Twinkle - Nummerkonvertering</translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation>&amp;Matcha uttryck:</translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation>&amp;Ersätt:</translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation>Uttryck att matcha mot kan inte vara tomt.</translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation>Ersättningsvärde kan inte vara tomt.</translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation>Ogiltigt reguljärt uttryck.</translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation>Twinkle - Koppla vidare</translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation>Koppla vidare inkommande samtal till</translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation>Du kan ange upp till 3 destinationer att koppla vidare samtalet till. Om första destinationen inte svarar går man vidare till andra destinationen och så vidare.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>Destination i &amp;tredje hand:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>Destination i &amp;andra hand:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>Destination i &amp;första hand:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\">F12</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\">F11</translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation>Twinkle - Välj nätverkskort</translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation>Välj nätverkskort/IP-adress som du vill använda:</translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation>Du har flera IP-adresser. Här måste du välja vilken IP-adress som ska användas. Denna IP-adress kommer användas i alla SIP-meddelanden.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation>Ange som standard-&amp;IP</translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation>Alt+I</translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation>Gör den valda IP-adressen till standard-IP-adress. Nästa gång som du startar Twinkle kommer denna IP-adress att väljas automatiskt.</translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation>Ange som standard&amp;nätverkskort</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation>Gör det valda nätverkskortet till standardgränssnitt. Nästa gång som du startar Twinkle kommer detta nätverkskort att väljas automatiskt.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation>Om du vill ta bort eller ändra standardvärdet vid ett senare tillfälle så kan du göra det via systeminställningarna.</translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation>Twinkle - Välj användarprofil</translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation>Välj användarprofil(er) att köra:</translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation type=\"obsolete\">Användarprofil</translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation>Kryssa för de användarprofiler som du vill köra och tryck på Kör.</translation>\n    </message>\n    <message>\n        <source>&amp;New</source>\n        <translation type=\"obsolete\">&amp;Ny</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+N</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation>Skapa en ny profil med profilredigeraren.</translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation>&amp;Guide</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation>Skapa en ny profil med guiden.</translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Redigera</translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation>Alt+R</translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation>Redigera markerad profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation>&amp;Ta bort</translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation>Ta bort markerad profil.</translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation>Byt &amp;namn</translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation>Byt namn på markerad profil.</translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation>&amp;Ange som standard</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation>Gör de markerade profilerna till standardprofiler. Nästa gång som du startar Twinkle så kommer dessa profiler att köras automatiskt.</translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation>&amp;Kör</translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation>Alt+K</translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation>Kör Twinkle med markerade profiler.</translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation>S&amp;ysteminställningar</translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation>Alt+Y</translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation>Redigera systeminställningarna.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation>&lt;html&gt;Innan du kan använda Twinkle måste du skapa en användarprofil.&lt;br&gt;Klicka OK för att skapa en profil.&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation>&amp;Profilredigerare</translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation>Du valde inte någon användarprofil att köra.\nVälj en profil.</translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation>Är du säker på att du vill ta bort profilen &quot;%1&quot;?</translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation>Ta bort profil</translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation>Misslyckades med att ta bort profil.</translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation>Misslyckades med att byta namn på profil.</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation>Kan inte hitta katalogen .twinkle i din hemkatalog.</translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\">Alt+I</translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation type=\"unfinished\">Alt+M</translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation>Twinkle - Välj användare</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation>&amp;Välj alla</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation>&amp;Töm alla</translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation>syfte</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation type=\"obsolete\">Användare</translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation>Registrera</translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation>Välj användare som du vill registrera.</translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation>Avregistrera</translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation>Välj användare som du vill avregistrera.</translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation>Avregistrera alla enheter</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation>Välj användare för vilka du vill avregistrera alla enheter.</translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation>Stör inte</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation>Välj användare för vilka du vill aktivera &apos;stör ej&apos;.</translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation>Svara automatiskt</translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation>Välj användare för vilka du vill aktivera &quot;svara automatiskt&quot;.</translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation>Twinkle - Skicka fil</translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation>Välj fil att skicka.</translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation>&amp;Fil:</translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation>&amp;Ämne:</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation>Filen finns inte.</translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation>Skicka fil...</translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation>Användare:</translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation type=\"unfinished\">Du kan ange upp till 3 destinationer att koppla vidare samtalet till. Om första destinationen inte svarar går man vidare till andra destinationen och så vidare.</translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation>Destination i &amp;3:e hand:</translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation>Destination i &amp;2:a hand:</translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation>Destination i &amp;1:a hand:</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation>&amp;Upptagen</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation>&amp;Inget svar</translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation>Acceptera och spara alla ändringar.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation>Ångra dina ändringar och stäng fönstret.</translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation>Du har angivit en ogiltig destination.</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\">F11</translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\">F12</translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation>Twinkle - Systeminställningar</translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Generellt</translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation>Ljud</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Ringsignaler</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation>Nätverk</translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation>Logg</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Välj den kategori som du vill titta eller ändra inställningar för.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Acceptera och spara dina ändringar.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Ångra alla ändringar och stäng fönstret.</translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation>Ljudkort</translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation>Välj det ljudkort som ska användas för att spela upp ringsignalen när någon ringer.</translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation>Välj det ljudkort som din mikrofon är inkopplad till.</translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation>Välj det ljudkort som högtalaren är inkopplad till under samtal.</translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation>&amp;Högtalare:</translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Ringsignal:</translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation>Annan enhet:</translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation>&amp;Mikrofon:</translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation>&amp;Validera enheterna innan användning</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reduce &amp;noise from the microphone</source>\n        <translation type=\"obsolete\">Reducera &amp;brus från mikrofonen</translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"obsolete\">Alt+B</translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation>Avancerat</translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation>16</translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation>32</translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation>64</translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation>128</translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation>256</translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation>&amp;Maximal loggstorlek:</translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation>MB</translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation type=\"unfinished\">Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation>Logga &amp;SIP-rapporter</translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation>Alt+S</translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation>Logga S&amp;TUN-rapporter</translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation>Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation type=\"unfinished\">Alt+R</translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation>Aktivitetsfält</translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation>Skapa ikon i a&amp;ktivitetsfält vid uppstart</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation type=\"unfinished\">Alt+D</translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation>Uppstart</translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Default user profiles</source>\n        <translation type=\"obsolete\">Användarprofiler som standard</translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation>Tjänster</translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation>Samtal &amp;väntar</translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation type=\"unfinished\">Alt+B</translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\">Alt+B</translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation>s</translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation>&amp;RTP-port:</translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation>Ringsignal</translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation>S&amp;pela upp ringsignal vid inkommande samtal</translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation>Alt+P</translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation>Indikerar om en ringsignal ska spelas upp när ett samtal kommer in.</translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation>Stan&amp;dardringsignal</translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation>Spela upp standardringsignalen när ett samtal kommer in.</translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation>A&amp;npassad ringsignal</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+N</translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation>Spela upp en anpassad ringsignal när ett samtal kommer in.</translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\">Alt+T</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation>Slå upp &amp;foto för inkommande samtal</translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default IP address combo</comment>\n        <translation type=\"obsolete\">ingen</translation>\n    </message>\n    <message>\n        <source>none</source>\n        <comment>This is the &apos;none&apos; in default network interface combo</comment>\n        <translation type=\"obsolete\">ingen</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Ringsignaler</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Välj ringsignal</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation>&amp;SIP-port:</translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation type=\"unfinished\">512</translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation type=\"unfinished\">1024</translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation>Svara</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation>Avvisa</translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation>Twinkle - Terminalförmågor</translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation>&amp;Från:</translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation>Hämta terminalförmågor för</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Till:</translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation>Adressen som du vill fråga efter förmågor (OPTION-begäran). Detta kan vara en fullständig SIP-adress som &lt;b&gt;sip:exempel@exempel.se&lt;/b&gt; eller bara användardelen eller telefonnumret av den fullständiga adressen. När du inte anger en fullständig adress så kommer Twinkle att komplettera adressen genom att använda domänvärdet för din användarprofil.</translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation>Twinkle - Koppla</translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation>Koppla samtal till</translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation>&amp;Till:</translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation>Adressbok</translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation>Välj en adress från adressboken.</translation>\n    </message>\n    <message>\n        <source>Type of transfer</source>\n        <translation type=\"obsolete\">Typ av koppling</translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation>&amp;Blind koppling</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\">Alt+T</translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\">F10</translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation>Kan inte öppna fil för läsning: %1</translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation>Fel i filsystemet vid läsning från filen %1.</translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation>Kan inte öppna fil för skrivning: %1</translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation>Fel i filsystemet vid skrivning till filen %1.</translation>\n    </message>\n    <message>\n        <source>Anonymous</source>\n        <translation>Anonym</translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation>Varning:</translation>\n    </message>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation>Kunde inte skapa loggfil %1.</translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation>För stort antal uttagsfel (socket).</translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation>Byggd med stöd för:</translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation>Bidrag:</translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation>Den här programmet innehåller följande tredjepartsprogramvara:</translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation>* GSM codec från Jutta Degener och Carsten Bormann vid Berlins universitet</translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation>* G.711/G.726 kodekar från Sun Microsystems (public domain)</translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation>* iLBC implementation från RFC 3951 (www.ilbcfreeware.org)</translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation>* Delar av STUN-projektet från http://sourceforge.net/projects/stun</translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation>* Delar av libsrv från http://libsrv.sourceforge.net</translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation>För RTP länkas följande dynamiska bibliotek in:</translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation>Översatt till svenska av Daniel Nylander</translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation>Katalogen %1 finns inte.</translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation>Kan inte öppna filen %1.</translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation>%1 är inte satt till din hemkatalog.</translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation>Katalogen %1 (%2) finns inte.</translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation>Kan inte skapa katalogen %1.</translation>\n    </message>\n    <message>\n        <source>Lock file %1 already exist, but cannot be opened.</source>\n        <translation type=\"obsolete\">Låsfil %1 finns redan, men kan inte öppnas.</translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation>%1 körs redan.\nLåsfilen %2 finns redan.</translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation>Kan inte skapa %1.</translation>\n    </message>\n    <message>\n        <source>Cannot write to %1 .</source>\n        <translation type=\"obsolete\">Kan inte skriva till %1.</translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation>Syntaxfel i filen %1.</translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation>Kunde inte kopiera %1 till %2</translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation>okänt namn (enheten är upptagen)</translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation>Standardenhet</translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation>Kan inte komma åt högtalaren (%1).</translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation>Kan inte komma åt mikrofonen (%1).</translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation>Ljudkortet kan inte ställas in i full duplex-läge.</translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation>Kan inte ställa in buffertstorlek på ljudkortet.</translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation>Ljudkortet kan inte ställas in till %1 kanaler.</translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation>Kan inte slå upp STUN-server: %1</translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation>Du är bakom en symmetrisk NAT.\nSTUN kommer inte fungera.\nKonfigurera en publik IP-adress i användarprofilen\noch skapa följande statiska bindningar (UDP) i din NAT.</translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation>publik IP: %1 --&gt; privat IP: %2 (SIP-signalering)</translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation>publik IP: %1-%2 --&gt; privat IP: %3-%4 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation>Kan inte kontakta STUN-servern: %1</translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation>Om du är bakom en brandvägg behöver du öppna följande UDP-portar.</translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation>Port %1 (SIP-signalering)</translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation>Portar %1-%2 (RTP/RTCP)</translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation>STUN kunde inte detektera NAT-typ.</translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation>Misslyckades med att skapa filen %1</translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation>Misslyckades med att skriva data till filen %1</translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation>Kan inte ta emot inkommande TCP-anslutningar.</translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation>Kan inte öppna ALSA-drivrutin för PCM-fångst</translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation>Misslyckades med att skicka meddelande.</translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation>Twinkle - Användarprofil</translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation>Användarprofil:</translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation>Välj den profil du vill ändra.</translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation>Användare</translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation>SIP-server</translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation>Röstbrevlåda</translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation>RTP-ljud</translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation>SIP-protokoll</translation>\n    </message>\n    <message>\n        <source>NAT</source>\n        <translation type=\"obsolete\">NAT</translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation>Adressformat</translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation>Ringsignaler</translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation>Skript</translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation>Säkerhet</translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation>Välj en kategori för vilken du vill se eller ändra inställningar.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation>Acceptera och spara dina ändringar.</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation>Ångra alla dina ändringar och stäng fönstret.</translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation>SIP-konto</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>&amp;Användarnamn*:</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation type=\"vanished\">&amp;Domän*:</translation>\n    </message>\n    <message>\n        <source>Or&amp;ganization:</source>\n        <translation type=\"vanished\">Or&amp;ganisation:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>SIP-användarnamnet som din leverantör levererat till dig. Detta är användardelen av din SIP-adress, &lt;b&gt;användarnamn&lt;/b&gt;@domän.se. Detta kan vara ett telefonnummer.\n&lt;br&gt;&lt;br&gt;\nDetta fält är obligatoriskt.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Domändelen av din SIP-adress, användarnamn@&lt;b&gt;domän.se&lt;/b&gt;. Istället för en riktig domän så kan detta även vara värdnamnet eller IP-adressen för din &lt;b&gt;SIP-proxy&lt;/b&gt;. Om du vill använda direktkommunikation mellan IP-telefon och IP-telefon så kan du fylla i värdnamnet eller IP-adress för din dator.\n&lt;br&gt;&lt;br&gt;\nDetta fält är obligatoriskt.</translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation>Du kan fylla i namnet för din organisation. När du ringer ett samtal så kan dock detta visas för motparten.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Detta är helt enkelt ditt fullständiga namn, t.ex. Sven Svensson. Det används endast för visning. När du ringer ett samtal kan dock detta namn visas för motparten.</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Ditt namn:</translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation>SIP-autentisering</translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Lösenord:</translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ditt SIP-autentiseringsnamn. Ganska ofta är detta samma som ditt SIP-användarnamn. Det kan dock vara ett annat namn.</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ditt lösenord för autentisering.</translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation>sekunder</translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation>Re&amp;gistrera vid uppstart</translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation>Utgående proxy</translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation>&amp;Använd utgående proxy</translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation>Utgående &amp;proxy:</translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\">Alt+V</translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation type=\"unfinished\">Alt+T</translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation>Värdnamnet, domännamnet eller IP-adressen för din utgående proxy.</translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation>Ko&amp;dekar</translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation>Kodekar</translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation>Tillgängliga kodekar:</translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation>G.711 A-law</translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation>G.711 u-law</translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation>GSM</translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation>speex-nb (8 kHz)</translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation>speex-wb (16 kHz)</translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation>speex-uwb (32 kHz)</translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation>Lista över tillgängliga kodekar.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation>Flytta en kodek från listan för tillgängliga kodekar till listan för aktiva kodekar.</translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation>Flytta en kodek från listan för aktiva kodekar till listan för tillgängliga kodekar.</translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation>Aktiva kodekar:</translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation>ms</translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation type=\"unfinished\">Alt+P</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation>&amp;iLBC</translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation>iLBC</translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation>20</translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation>30</translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation>&amp;Speex</translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation>Speex</translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation type=\"unfinished\">Alt+R</translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;VAD</source>\n        <translation type=\"obsolete\">&amp;VAD</translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation>Alt+V</translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>V&amp;BR</source>\n        <translation type=\"obsolete\">V&amp;BR</translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation>Alt+B</translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DT&amp;X</source>\n        <translation type=\"obsolete\">DT&amp;X</translation>\n    </message>\n    <message>\n        <source>Alt+X</source>\n        <translation type=\"obsolete\">Alt+X</translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation>G.726</translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation>RFC 3551</translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation>ATM AAL2</translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation>DT&amp;MF</translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation>dB</translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation>DTMF-t&amp;ransport:</translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation>Auto</translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation>RFC 2833</translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation>Allmänt</translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation>Protokollalternativ</translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation>RFC 2543</translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation>RFC 3264</translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\">Alt+I</translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation type=\"unfinished\">Alt+M</translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\">Alt+B</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation>inaktiverad</translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation>stöds</translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation>krävs</translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation>föredras</translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation>Ersätter</translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation>REFER</translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation type=\"unfinished\">Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\">Alt+T</translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation>Integritet</translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation>Integritetsalternativ</translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation>NAT-traversering</translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation type=\"vanished\">S&amp;TUN-server:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation type=\"unfinished\">Värdnamnet, domännamnet eller IP-adressen för STUN-servern.</translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation>&amp;Publik IP-adress:</translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation>Telefonnummer</translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation>Ersätt</translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation>&amp;Lägg till</translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation>Ta &amp;bort</translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation>&amp;Redigera</translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation>&amp;Testa</translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>for STUN</source>\n        <translation type=\"obsolete\">för STUN</translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation>&amp;Inget svar:</translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation>&amp;Ringsignal:</translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation>&amp;Inkommande samtal:</translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation>&amp;Utgående samtal:</translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation>&amp;Aktivera ZRTP/SRTP-kryptering</translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation>Inställningar för ZRTP</translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation type=\"unfinished\">Du måste fylla i ett användarnamn för ditt SIP-konto.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation type=\"unfinished\">Du måste fylla i ett domännamn för ditt SIP-konto.\nDetta kan vara värdnamnet eller IP-adressen för din dator, om du vill ha direktsamtal, PC till PC.</translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation>Ogiltig domän.</translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation>Ogiltigt användarnamn.</translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Ogiltigt värde för STUN-server.</translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation>Ringsignaler</translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation>Välj ringsignal</translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation>Alla filer</translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation>Snabbmeddelande</translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation>Närvaro</translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation>Transport/NAT</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F</source>\n        <translation type=\"obsolete\">AKA AM&amp;F</translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation>A&amp;KA OP:</translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation>SIP-transport</translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation>UDP</translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation>TCP</translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation>T&amp;ransportprotokoll:</translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source> bytes</source>\n        <translation type=\"obsolete\"> byte</translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation>Din närvaro</translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation>&amp;Publicera tillgänglighet vid uppstart</translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation>Publicera din tillgänglighet vid uppstart.</translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation>%1 konverteras till %2</translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do&amp;main*:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Organi&amp;zation:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>E&amp;xpiry:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call Hold &amp;variant:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max redirections:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extension is supported.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Asserted-Identity header when hiding user identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use STUN (does not wor&amp;k for incoming TCP)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>STUN ser&amp;ver:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Unsolicited</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Solicited</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Solicited MWI</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation>Twinkle - Guide</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation>Värdnamnet, domännamnet eller IP-adressen för STUN-servern.</translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation>S&amp;TUN-server:</translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>SIP-användarnamnet som din leverantör levererat till dig. Detta är användardelen av din SIP-adress, &lt;b&gt;användarnamn&lt;/b&gt;@domän.se. Detta kan vara ett telefonnummer.\n&lt;br&gt;&lt;br&gt;\nDetta fält är obligatoriskt.</translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation>&amp;Domän*:</translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation>Välj din SIP-tjänsteleverantör. Om din SIP-tjänsteleverantör inte finns med i listan så ska du välja &lt;b&gt;Annan&lt;/b&gt; och fylla i inställningarna som du fått från din leverantör.&lt;br&gt;&lt;br&gt;\nOm du väljer en av de fördefinierade SIP-tjänsteleverantörerna så behöver du endast fylla i ditt namn, användarnamn, autentiseringsnamn och lösenord.</translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation>&amp;Autentiseringsnamn:</translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation>&amp;Ditt namn:</translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation>Ditt SIP-autentiseringsnamn. Ganska ofta är detta samma som ditt SIP-användarnamn. Det kan dock skilja sig.</translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation>Domändelen av din SIP-adress, användarnamn@&lt;b&gt;domän.se&lt;/b&gt;. Istället för en riktig domän så kan detta även vara värdnamnet eller IP-adressen för din &lt;b&gt;SIP-proxy&lt;/b&gt;. Om du vill använda direktkommunikation mellan IP-telefon och IP-telefon så kan du fylla i värdnamnet eller IP-adress för din dator.\n&lt;br&gt;&lt;br&gt;\nDetta fält är obligatoriskt.</translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation>Detta är helt enkelt ditt fullständiga namn, t.ex. Sven Svensson. Det används endast för visning. När du ringer ett samtal kan dock detta namn visas för motparten.</translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation>SIP-pro&amp;xy:</translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation>Värdnamnet, domännamnet eller IP-adressen för din SIP-proxy. Om detta är samma värde som för din domän så kan du lämna detta fält tomt.</translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation>&amp;SIP-tjänsteleverantör:</translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation>&amp;Lösenord:</translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation>A&amp;nvändarnamn*:</translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation>Ditt lösenord för autentisering.</translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation>&amp;OK</translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation>Alt+O</translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation>&amp;Avbryt</translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation>Ingen (direktsamtal IP till IP)</translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation>Annan</translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation>Guide för användarprofil:</translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation>Du måste fylla i ett användarnamn för ditt SIP-konto.</translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation>Du måste fylla i ett domännamn för ditt SIP-konto.\nDetta kan vara värdnamnet eller IP-adressen för din dator, om du vill ha direktsamtal, PC till PC.</translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation>Ogiltigt värde för SIP-proxy.</translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation>Ogiltigt värde för STUN-server.</translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation>&amp;Ja</translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation>&amp;Nej</translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\">Svara</translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\">Avvisa</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/lang/twinkle_xx.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\">\n<context>\n    <name>AddressCardForm</name>\n    <message>\n        <source>Twinkle - Address Card</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Remark:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Infix name of contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>First name of contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;First name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You may place any remark about the contact here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Infix name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Phone number or SIP address of contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Last name of contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Last name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a phone number or SIP address.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>AddressTableModel</name>\n    <message>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Phone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Remark</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>AuthenticationForm</name>\n    <message>\n        <source>Twinkle - Authentication</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>user</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The user for which authentication is requested.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>profile</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The user profile of the user for which authentication is requested.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;User name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Login required for realm:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>realm</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The realm for which you need to authenticate.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>BuddyForm</name>\n    <message>\n        <source>Twinkle - Buddy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Phone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Name of your buddy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Show availability</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option if you want to see the availability of your buddy. This will only work if your provider offers a presence agent.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP address your buddy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid phone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>BuddyList</name>\n    <message>\n        <source>Availability</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>offline</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>online</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>request failed</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>request rejected</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>not published</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>failed to publish</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click right to add a buddy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>CoreAudio</name>\n    <message>\n        <source>Failed to open sound card</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to create a UDP socket (RTP) on port %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to create audio receiver thread.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to create audio transmitter thread.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>CoreCallHistory</name>\n    <message>\n        <source>local user</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>remote user</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>failure</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>in</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>out</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DeregisterForm</name>\n    <message>\n        <source>Twinkle - Deregister</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>deregister all devices</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DiamondcardProfileForm</name>\n    <message>\n        <source>Fill in your account ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Fill in your PIN code.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A user profile with name %1 already exists.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DtmfForm</name>\n    <message>\n        <source>Twinkle - DTMF</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Keypad</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Over decadic A. Normally not needed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>4</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>5</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>6</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Over decadic B. Normally not needed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>7</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>8</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>9</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Over decadic C. Normally not needed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Star (*)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>0</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Pound (#)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Over decadic D. Normally not needed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GUI</name>\n    <message>\n        <source>Failed to create a %1 socket (SIP) on port %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Override lock file and start anyway?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If these are users for different domains, then enable the following option in your user profile (SIP protocol)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use domain name to create a unique contact header</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot find a network interface. Twinkle will use 127.0.0.1 as the local IP address. When you connect to the network you have to restart Twinkle to use the correct IP address.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: incoming call for %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call transferred by %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: far end cancelled call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: far end released call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end not supported.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: SDP answer from far end missing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: Unsupported content type in answer from far end.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: no ACK received, call will be terminated.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: no PRACK received, call will be terminated.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: PRACK failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: failed to cancel call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: far end answered call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The call can be redirected to:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call released.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call established.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Response on terminal capability request: %1 %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Terminal capabilities of %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accepted body types:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accepted encodings:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accepted languages:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allowed requests:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Supported extensions:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>none</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>End point type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call retrieve failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, registration failed: %2 %3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, registration succeeded (expires = %2 seconds)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, registration failed: STUN failure</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, de-registration succeeded: %2 %3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, de-registration failed: %2 %3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, fetching registrations failed: %2 %3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>: you are not registered</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>: you have the following registrations</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>: fetching registrations...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: redirecting request to</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirecting request to: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: DTMF detected:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>invalid DTMF telephone event (%1)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: send DTMF %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: far end does not support DTMF telephone events.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: received notification.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Event: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>State: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reason: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Progress: %1 %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call successfully transferred.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call transfer still in progress.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>No further notifications will be received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: transferring call to %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer requested by %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: Call transfer failed. Retrieving original call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed: %2 %3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, STUN request failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirecting call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be redirected to the following destination?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you don&apos;t want to be asked this anymore, then you must change the settings in the SIP protocol section of the user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirecting request</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do you allow the %1 request to be redirected to the following destination?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Request to transfer call received from:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Request to transfer call received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do you allow the call to be transferred to the following destination?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Info:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Critical:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Firewall / NAT discovery...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Abort</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click the padlock to confirm a correct SAS.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The remote user on line %1 disabled the encryption.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: SAS confirmation reset.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, voice mail status failure.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, voice mail status rejected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, voice mailbox does not exist.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1, voice mail status terminated.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accepted by network</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call rejected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line %1: call redirected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to start conference.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to save message attachment: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transferred by: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open web browser: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Configure your web browser in the system settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GetAddressForm</name>\n    <message>\n        <source>Twinkle - Select address</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;KAddressBook</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>This list of addresses is taken from &lt;b&gt;KAddressBook&lt;/b&gt;. Contacts for which you did not provide a phone number are not shown here. To add, delete or modify address information you have to use KAddressBook.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Show only SIP addresses</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option when you only want to see contacts with SIP addresses, i.e. starting with &quot;&lt;b&gt;sip:&lt;/b&gt;&quot;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Reload</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reload the list of addresses from KAddressbook.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Local address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Contacts in the local address book of Twinkle.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Add a new contact to the local address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delete a contact from the local address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Edit a contact from the local address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;You seem not to have any contacts with a phone number in &lt;b&gt;KAddressBook&lt;/b&gt;, KDE&apos;s address book application. Twinkle retrieves all contacts with a phone number from KAddressBook. To manage your contacts you have to use KAddressBook.&lt;p&gt;As an alternative you may use Twinkle&apos;s local address book.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete contact &apos;%1&apos; from the local address book?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delete contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GetProfileNameForm</name>\n    <message>\n        <source>Twinkle - Profile name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enter a name for your profile:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;The name of your profile&lt;/b&gt;\n&lt;br&gt;&lt;br&gt;\nA profile contains your user settings, e.g. your user name and password. You have to give each profile a name.\n&lt;br&gt;&lt;br&gt;\nIf you have multiple SIP accounts, you can create multiple profiles. When you startup Twinkle it will show you the list of profile names from which you can select the profile you want to run.\n&lt;br&gt;&lt;br&gt;\nTo remember your profiles easily you could use your SIP user name as a profile name, e.g. &lt;b&gt;example@example.com&lt;/b&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Profile already exists.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Rename profile &apos;%1&apos; to:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>HistoryForm</name>\n    <message>\n        <source>Twinkle - Call History</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Time</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>In/Out</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>From/To</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Subject</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Status</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call details</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Details of the selected call record.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>View</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Incoming calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option to show incoming calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Outgoing calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option to show outgoing calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Answered calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option to show answered calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Missed calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option to show missed calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Current &amp;user profiles only</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option to show only calls associated with this user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;Clear the complete call history.&lt;/p&gt;\n&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; this will clear &lt;b&gt;all&lt;/b&gt; records, also records not shown depending on the checked view options.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Clo&amp;se</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Close this window.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call selected address.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call start:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call answer:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call end:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Direction:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reply to:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Referred by:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Released by:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Status:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Far end device:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>conversation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Re:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Number of calls:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>###</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Total call duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>IncomingCallPopup</name>\n    <message>\n        <source>%1 calling</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>InviteForm</name>\n    <message>\n        <source>Twinkle - Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Optionally you can provide a subject here. This might be shown to the callee.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Hide identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nWith this option you request your SIP provider to hide your identity from the called party. This will only hide your identity, e.g. your SIP address, telephone number. It does &lt;b&gt;not&lt;/b&gt; hide your IP address.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Warning:&lt;/b&gt; not all providers support identity hiding.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Not all SIP providers support identity hiding. Make sure your SIP provider supports it if you really need it.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>LogViewForm</name>\n    <message>\n        <source>Twinkle - Log</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Contents of the current log file (~/.twinkle/twinkle.log)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Close</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>C&amp;lear</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MessageForm</name>\n    <message>\n        <source>Twinkle - Instant message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The user that will send the message.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;User profile:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Conversation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Type your message here and then press &quot;send&quot; to send it.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send the message.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delivery failure</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delivery notification</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send file</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>image size is scaled down in preview</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Open with %1...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Open with...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Save attachment as...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>File already exists. Do you want to overwrite this file?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to save attachment.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 is typing a message.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Size</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MessageFormView</name>\n    <message>\n        <source>sending message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MphoneForm</name>\n    <message>\n        <source>Twinkle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Buddy list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Call:</source>\n        <comment>Label in front of combobox to enter address</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Dial</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Dial the address.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;User:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The user that will make the call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto answer indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call redirect indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do not disturb indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Message waiting indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Missed call indication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Registration status.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Display</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line status</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line &amp;1:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to switch to line 1.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>From:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>To:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Subject:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transferring call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>sas</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Short authentication string</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>g711a/g711a</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Audio codec</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>0:00:00</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call duration</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>sip:from</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>sip:to</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>subject</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>photo</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line &amp;2:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to switch to line 2.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;File</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>C&amp;all</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate line</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Registration</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Services</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;View</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Help</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Quit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Quit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ctrl+Q</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>About Twinkle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;About Twinkle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call someone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F5</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Answer incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F6</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Release call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Esc</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reject incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F8</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Put a call on hold, or retrieve a held call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirect incoming call without answering</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Open keypad to enter digits for voice menu&apos;s</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Register</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Deregister</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister this device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Show registrations</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Show registrations</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Terminal capabilities</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Request terminal capabilities from someone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Do not disturb</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call &amp;redirection...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Repeat last call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>About Qt</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>About &amp;Qt</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;User profile...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Join two calls in a 3-way conference</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mute a call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>System settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;System settings...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister all</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister &amp;all</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister all your registered devices</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Auto answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Log...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call history</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call &amp;history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F9</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Change user ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Change user ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate or de-activate users</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>What&apos;s This?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>What&apos;s &amp;This?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Shift+F1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Display</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Access voice mail</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Msg</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Instant &amp;message...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Buddy list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Call...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Edit...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>O&amp;ffline</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Online</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Change availability</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Add buddy...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>idle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>dialing</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>attempting call, please wait</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>establishing call, please wait</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>established</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>established (waiting for media)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>releasing call, please wait</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown state</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Voice is encrypted</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to confirm SAS.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to clear SAS verification.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer consultation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hide identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Registration status:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Registered</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Not registered</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to show registrations.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>No users are registered.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 new, 1 old message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 new, %2 old messages</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>1 new message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 new messages</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>1 old message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 old messages</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Messages waiting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>No messages</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;b&gt;Voice mail status:&lt;/b&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failure</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Unknown</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to access voice mail.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do not disturb active for:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirection active for:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto answer active for:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to activate/deactivate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to activate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do not disturb is not active.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirection is not active.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto answer is not active.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Click to see call history for details.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You have no missed calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You missed 1 call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You missed %1 calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Starting user profiles...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The following profiles are both for user %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can only run multiple profiles for different users.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You have changed the SIP UDP port. This setting will only become active when you restart Twinkle.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>not provisioned</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must provision your voice mail address in your user profile, before you can access it.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The line is busy. Cannot access voice mail.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The voice mail address %1 is an invalid address. Please provision a valid address in your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to save buddy list: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Manual</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sign up</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Sign up...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call history...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Recharge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Balance history</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Admin center</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Bye</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Bye</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Reject</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Hold</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hold</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>R&amp;edirect...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Dtmf...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Dtmf</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Terminal capabilities...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Redial</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redial</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Conference</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Conf</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Mute</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mute</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Trans&amp;fer...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Xfer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>NumberConversionForm</name>\n    <message>\n        <source>Twinkle - Number conversion</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Match expression:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Replace:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Perl style format string for the replacement number.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Perl style regular expression matching the number format you want to modify.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Match expression may not be empty.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Replace value may not be empty.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid regular expression.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RedirectForm</name>\n    <message>\n        <source>Twinkle - Redirect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirect incoming call to</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectNicForm</name>\n    <message>\n        <source>Twinkle - Select NIC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select the network interface/IP address that you want to use:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Set as default &amp;IP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Set as default &amp;NIC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you want to remove or change the default at a later time, you can do that via the system settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectProfileForm</name>\n    <message>\n        <source>Twinkle - Select user profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select user profile(s) to run:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Tick the check boxes of the user profiles that you want to run and press run.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create a new profile with the profile editor.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Wizard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create a new profile with the wizard.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Edit the highlighted profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delete the highlighted profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ren&amp;ame</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Rename the highlighted profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Set as default</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Run</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Run Twinkle with the selected profiles.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>S&amp;ystem settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+Y</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Edit the system settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Before you can use Twinkle, you must create a user profile.&lt;br&gt;Click OK to create a profile.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Profile editor</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;Next you may adjust the system settings. You can change these settings always at a later time.&lt;br&gt;&lt;br&gt;Click OK to view and adjust the system settings.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You did not select any user profile to run.\nPlease select a profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Are you sure you want to delete profile &apos;%1&apos;?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Delete profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to delete profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to rename profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;If you want to remove or change the default at a later time, you can do that via the system settings.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot find .twinkle directory in your home directory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ed&amp;itor</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Dia&amp;mondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Modify profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Startup profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Diamondcard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;html&gt;You can use the profile editor to create a profile. With the profile editor you can change many settings to tune the SIP protocol, RTP and many other things.&lt;br&gt;&lt;br&gt;Alternatively you can use the wizard to quickly setup a user profile. The wizard asks you only a few essential settings. If you create a user profile with the wizard you can still edit the full profile with the profile editor at a later time.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can create a Diamondcard account to make worldwide calls to regular and cell phones and send SMS messages.&lt;br&gt;&lt;br&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose what method you wish to use.&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectUserForm</name>\n    <message>\n        <source>Twinkle - Select user</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Select all</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>C&amp;lear all</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>purpose</source>\n        <comment>No need to translate</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Register</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select users that you want to register.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select users that you want to deregister.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Deregister all devices</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select users for which you want to deregister all devices.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do not disturb</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;do not disturb&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select users for which you want to enable &apos;auto answer&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SendFileForm</name>\n    <message>\n        <source>Twinkle - Send File</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select file to send.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;File:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Subject:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>File does not exist.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send file...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SrvRedirectForm</name>\n    <message>\n        <source>Twinkle - Call Redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Unconditional</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Redirect all calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate the unconditional redirection service.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirect to</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;3rd choice destination:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;2nd choice destination:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;1st choice destination:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Busy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I am busy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate the redirection when busy service.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;No answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Redirect calls when I do not answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Activate the redirection on no answer service.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accept and save all changes.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Undo your changes and close the window.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You have entered an invalid destination.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F11</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F12</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysSettingsForm</name>\n    <message>\n        <source>Twinkle - System Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Audio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Network</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sound Card</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select the sound card for playing the ring tone for incoming calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select the sound card to which your microphone is connected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select the sound card for the speaker function during a call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Speaker:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Other device:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Microphone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Validate devices before usage</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Advanced</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>OSS &amp;fragment size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>16</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>32</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>64</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>128</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>256</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>ALSA &amp;play period size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;ALSA capture period size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max log size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>MB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log &amp;debug reports</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if reports marked as &quot;debug&quot; will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log &amp;SIP reports</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if SIP messages will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log S&amp;TUN reports</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if STUN messages will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Log m&amp;emory reports</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if reports concerning memory management will be logged.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>System tray</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Create &amp;system tray icon on startup</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Hide in system tray when closing main window</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+H</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Startup</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>S&amp;tartup hidden in system tray</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Services</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call &amp;waiting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hang up &amp;both lines when ending a 3-way conference call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Maximum calls in call history:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The maximum number of calls that will be kept in the call history.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Auto show main window on incoming call after</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Number of seconds after which the main window should be shown.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>secs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;SIP port:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;RTP port:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;TCP):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The UDP/TCP port used for sending and receiving SIP messages.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Max. SIP message size (&amp;UDP):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Play ring tone on incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if a ring tone should be played when a call comes in.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Default ring tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Play the default ring tone when a call comes in.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>C&amp;ustom ring tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Play a custom ring tone when a call comes in.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>P&amp;lay ring back tone when network does not play ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>D&amp;efault ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Play the default ring back tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cu&amp;stom ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Play a custom ring back tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Specify the file name of a .wav file that you want to be played as ring back tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Lookup name for incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ove&amp;rride received display name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Lookup &amp;photo for incoming call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Lookup the photo of a caller in your address book and display it on an incoming call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose ring back tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>W&amp;eb browser command:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>512</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>1024</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Tip: for crackling sound with PulseAudio, set play period size to maximum.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable in-call OSD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SysTrayPopup</name>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Incoming Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TermCapForm</name>\n    <message>\n        <source>Twinkle - Terminal Capabilities</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;From:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Get terminal capabilities of</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransferForm</name>\n    <message>\n        <source>Twinkle - Transfer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer call to</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;To:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address book</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select an address from the address book.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Blind transfer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer the call to a third party without contacting that third party yourself.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>T&amp;ransfer with consultation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Before transferring the call to a third party, first consult the party yourself.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transfer to other &amp;line</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Connect the remote party on the active line with the remote party on the other line.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>F10</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TwinkleCore</name>\n    <message>\n        <source>Anonymous</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Warning:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to create log file %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open file for reading: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>File system error while reading file %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open file for writing: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>File system error while writing file %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Excessive number of socket errors.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Built with support for:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Contributions:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>This software contains the following software from 3rd parties:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>* G.711/G.726 codecs from Sun Microsystems (public domain)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>* Parts of the STUN project at http://sourceforge.net/projects/stun</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>* Parts of libsrv at http://libsrv.sourceforge.net/</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>For RTP the following dynamic libraries are linked:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Translated to english by &lt;your name&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Directory %1 does not exist.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open file %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 is not set to your home directory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Directory %1 (%2) does not exist.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot create directory %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 is already running.\nLock file %2 already exists.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot create %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Syntax error in file %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to backup %1 to %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>unknown name (device is busy)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Default device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot access the ring tone device (%1).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot access the speaker (%1).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot access the microphone (%1).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot receive incoming TCP connections.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call transfer - %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to full duplex.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot set buffer size on sound card.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Sound card cannot be set to %1 channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits recording.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot set sound card to 16 bits playing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot set sound card sample rate to %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Opening ALSA driver failed</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM playback</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot open ALSA driver for PCM capture</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot resolve STUN server: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>public IP: %1 --&gt; private IP: %2 (SIP signaling)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>public IP: %1-%2 --&gt; private IP: %3-%4 (RTP/RTCP)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot reach the STUN server: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you are behind a firewall then you need to open the following UDP ports.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Port %1 (SIP signaling)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ports %1-%2 (RTP/RTCP)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>NAT type discovery via STUN failed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to create file %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to write data to file %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Failed to send message.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Cannot lock %1 .</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>UserProfileForm</name>\n    <message>\n        <source>Twinkle - User Profile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User profile:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select which profile you want to edit.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP server</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Voice mail</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Instant message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Presence</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>RTP audio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP protocol</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transport/NAT</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Address format</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Timers</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Scripts</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Security</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select a category for which you want to see or modify the settings.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accept and save your changes.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Undo all your changes and close the window.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP account</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP authentication</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Realm:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Authentication &amp;name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Registrar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Registrar:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The registration expiry time that Twinkle will request.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>seconds</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Re&amp;gister at startup</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+G</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Add q-value to registration</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Outbound Proxy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Use outbound proxy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+U</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Outbound &amp;proxy:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send in-dialog requests to proxy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Don&apos;t send a request to proxy if its destination can be resolved locally.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+D</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Co&amp;decs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Codecs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Available codecs:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.711 A-law</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.711 u-law</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>GSM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>speex-nb (8 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>speex-wb (16 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>speex-uwb (32 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>List of available codecs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of available codecs to the list of active codecs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move a codec from the list of active codecs to the list of available codecs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Active codecs:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;G.711/G.726 payload size:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The preferred payload size for the G.711 and G.726 codecs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>ms</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Follow codec preference from far end on incoming calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+F</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Follow codec &amp;preference from far end on outgoing calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+P</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;iLBC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>iLBC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>i&amp;LBC payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>iLBC &amp;payload size (ms):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for iLBC.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>20</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>30</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The preferred payload size for iLBC.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Speex</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Speex</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Perceptual &amp;enhancement</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Ultra wide band payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+V</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Wide band payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+B</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there&apos;s no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex wide band.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Co&amp;mplexity:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for speex narrow band.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that&apos;s similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Narrow band payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;40 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;24 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;32 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>G.726 &amp;16 kbps payload type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Codeword &amp;packing order:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>RFC 3551</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>ATM AAL2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DT&amp;MF</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF vo&amp;lume:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The power level of the DTMF tone in dB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The pause after a DTMF tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF &amp;duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF payload &amp;type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF &amp;pause:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>dB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Duration of a DTMF tone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>DTMF t&amp;ransport:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>RFC 2833</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Inband</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Out-of-band (SIP INFO)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>General</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Protocol options</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>RFC 2543</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>RFC 3264</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow m&amp;issing Contact header in 200 OK on REGISTER</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+I</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max-Forwards header is mandatory</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+M</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Put &amp;registration expiry time in contact header</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Use compact header names</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if compact header names should be used for headers that have a compact form.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow SDP change during call setup</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use domain &amp;name to create a unique contact header value</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+N</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Encode Via, Route, Record-Route as list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Allow redirection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should redirect a request if a 3XX response is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ask user &amp;permission to redirect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>disabled</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>supported</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>required</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>preferred</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;100 rel (PRACK):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Replaces</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>REFER</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call transfer (REFER)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+T</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should transfer a call if a REFER request is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>As&amp;k user permission to transfer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+K</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Hold call &amp;with referrer while setting up call to transfer target</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+W</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ho&amp;ld call with referee before sending REFER</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+L</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should put the current call on hold when you transfer a call.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Auto re&amp;fresh subscription to refer event while call transfer is not finished</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Attended refer to AoR (Address of Record)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Privacy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Privacy options</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Preferred-Identity header when hiding user identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP transport</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>UDP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>TCP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>T&amp;ransport protocol:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>UDP t&amp;hreshold:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>NAT traversal</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;NAT traversal not needed</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Use statically configured public IP address inside SIP messages</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose this option when your SIP provider offers a STUN server for NAT traversal.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Public IP address:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The public IP address of your NAT.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Telephone numbers</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Only &amp;display user part of URI for telephone number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;URI with numerical user part is a telephone number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Remove special symbols from numerical dial strings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Special symbols:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Number conversion</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Match expression</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Replace</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule upwards in the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Move the selected number conversion rule downwards in the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Add a number conversion rule.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Re&amp;move</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Remove the selected number conversion rule.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Edit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Edit the selected number conversion rule.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Test</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Test how a number is converted by the number conversion rules.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>NAT &amp;keep alive:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;No answer:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring back tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select ring tone file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring &amp;back tone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Ring tone:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Select script file.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call released locall&amp;y:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Outgoing call a&amp;nswered:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;failed:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Incoming call:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call released &amp;remotely:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Incoming call &amp;answered:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>O&amp;utgoing call:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Out&amp;going call failed:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Enable ZRTP/SRTP encryption</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>ZRTP settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Indicate ZRTP support in SDP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Twinkle will indicate ZRTP support during call setup in its signalling.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Popup warning when remote party disables encryption during call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Voice mail address:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The SIP address or telephone number to access your voice mail.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;MWI type:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Subscription &amp;duration:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;user name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your voice mailbox server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your user name for accessing your voice mailbox.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Mailbox &amp;server:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Via outbound &amp;proxy</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Maximum number of sessions:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your presence</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Publish availability at startup</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Publish your availability at startup.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Publication &amp;refresh interval (sec):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence publications.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Buddy presence</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Subscription refresh interval (sec):</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Refresh rate of presence subscriptions.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Dynamic payload type %1 is used more than once.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid domain.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid user name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for registrar.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for outbound proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox user name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a mailbox server</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid mailbox server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid mailbox user name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Value for public IP address missing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose ring tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Ring back tones</source>\n        <comment>Description of .wav files in file dialog</comment>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>All files</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose incoming call script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose incoming call answered script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose incoming call failed script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose outgoing call script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose outgoing call answered script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose outgoing call failed script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose local release script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose remote release script</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>%1 converts to %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>P&amp;ersistent TCP connection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send composing indications when typing a message.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>AKA AM&amp;F:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>A&amp;KA OP:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Authentication management field for AKAv1-MD5 authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Operator variant key for AKAv1-MD5 authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Prepr&amp;ocessing</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Preprocessing (improves quality at remote end)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Automatic gain control</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control &amp;level:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Voice activity detection</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Noise reduction</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Acoustic &amp;Echo Cancellation</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Variable &amp;bit-rate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Discontinuous &amp;Transmission</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Quality:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>bytes</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use tel-URI for telephone &amp;number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Accept call &amp;transfer request (incoming REFER)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Allow call transfer while consultation in progress</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Enable NAT &amp;keep alive</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Send UDP NAT keep alive packets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Do&amp;main*:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Organi&amp;zation:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>E&amp;xpiry:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Call Hold &amp;variant:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Max redirections:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Indicates if the Replaces-extension is supported.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Send P-Asserted-Identity header when hiding user identity</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Use STUN (does not wor&amp;k for incoming TCP)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>STUN ser&amp;ver:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects &apos;00&apos; instead of the &apos;+&apos;, or you are at the office and all your numbers need to be prefixed with a &apos;9&apos; to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the &apos;+31&apos; and replace it by a &apos;0&apos;. For dialling numbers abroad you just want to replace the &apos;+&apos; by &apos;00&apos;.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Unsolicited</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Solicited</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Solicited MWI</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>WizardForm</name>\n    <message>\n        <source>Twinkle - Wizard</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of the STUN server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>S&amp;TUN server:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Domain*:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Authentication name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Your name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>SIP pro&amp;xy:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;SIP service provider:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Password:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;User name*:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Your password for authentication.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;OK</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;Cancel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>None (direct IP to IP calls)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Other</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>User profile wizard:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a user name for your SIP account.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>You must fill in a domain name for your SIP account.\nThis could be the hostname or IP address of your PC if you want direct PC to PC dialing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for SIP proxy.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Invalid value for STUN server.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>YesNoDialog</name>\n    <message>\n        <source>&amp;Yes</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>&amp;No</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>incoming_call</name>\n    <message>\n        <source>Answer</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <source>Reject</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "src/gui/logviewform.cpp",
    "content": "#include \"logviewform.h\"\n\n#include <QScrollBar>\n#include <QTimer>\n#include \"audits/memman.h\"\n#include \"log.h\"\n\n/*\n *  Constructs a LogViewForm which is a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nLogViewForm::LogViewForm(QWidget* parent)\n\t: QDialog(parent)\n{\n\tsetupUi(this);\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nLogViewForm::~LogViewForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\nbool LogViewForm::isOnBottom() const\n{\n\tconst QScrollBar* vsb = logTextEdit->verticalScrollBar();\n\treturn (vsb->value() == vsb->maximum());\n}\n\nvoid LogViewForm::scrollToBottom()\n{\n\tQScrollBar* vsb = logTextEdit->verticalScrollBar();\n\tvsb->setValue(vsb->maximum());\n\tlogTextEdit->update();\n}\n\nvoid LogViewForm::show()\n{\n    if (isVisible()) {\n\t\traise();\n\t\treturn;\n\t}\n\n\tQString fname = log_file->get_filename().c_str();\n\tlogfile = new QFile(fname);\n\tMEMMAN_NEW(logfile);\n\tlogstream = NULL;\n\tif (logfile->open(QIODevice::ReadOnly)) {\n\t\tlogstream = new QTextStream(logfile);\n\t\tMEMMAN_NEW(logstream);\n        logTextEdit->setPlainText(logstream->readAll());\n\t}\n\tlog_file->enable_inform_user(true);\n\n\tQDialog::show();\n\n\t// Couldn't get it to scroll AND show contents(!) without this hack\n\tQTimer::singleShot(50, this, SLOT(scrollToBottom()));\n\traise();\n}\n\nvoid LogViewForm::closeEvent(QCloseEvent* ev)\n{\n\tlog_file->enable_inform_user(false);\n\t// logTextEdit->clear(); // causes crashes with Qt5\n\n\tif (logstream) {\n\t\tMEMMAN_DELETE(logstream);\n\t\tdelete logstream;\n\t\tlogstream = NULL;\n\t}\n\n\tlogfile->close();\n\tMEMMAN_DELETE(logfile);\n\tdelete logfile;\n\tlogfile = NULL;\n\n\tQDialog::closeEvent(ev);\n}\n\nvoid LogViewForm::update(bool log_zapped)\n{\n    if (!isVisible()) return;\n\n\tif (log_zapped) {\n\t\tclose();\n\t\tshow();\n\t\treturn;\n\t}\n\n\tif (logstream) {\n        QString s = logstream->readAll();\n\t\tif (!s.isNull() && !s.isEmpty()) {\n\t\t\tbool bottom = isOnBottom();\n\t\t\tlogTextEdit->appendPlainText(s);\n\t\t\tif (bottom)\n\t\t\t\tscrollToBottom();\n\t\t}\n\t}\n}\n\nvoid LogViewForm::clear()\n{\n\tlogTextEdit->clear();\n}\n"
  },
  {
    "path": "src/gui/logviewform.h",
    "content": "#ifndef LOGVIEWFORM_H\n#define LOGVIEWFORM_H\n\n#include <QDialog>\n#include <QCloseEvent>\n#include <QTextStream>\n\n#include \"ui_logviewform.h\"\n\nclass LogViewForm : public QDialog, protected Ui::LogViewForm\n{\n    Q_OBJECT\n\nprivate:\n    QFile* logfile;\n    QTextStream* logstream;\n\n    bool isOnBottom() const;\npublic slots:\n    void scrollToBottom();\n\npublic:\n    LogViewForm(QWidget* parent = 0);\n    ~LogViewForm();\n\npublic slots:\n    void show();\n    void closeEvent(QCloseEvent* ev);\n    void update(bool log_zapped);\n    void clear();\n\n};\n\n#endif // LOGVIEWFORM_H\n"
  },
  {
    "path": "src/gui/logviewform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>LogViewForm</class>\n <widget class=\"QDialog\" name=\"LogViewForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>599</width>\n    <height>472</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Log</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"0\" column=\"0\" colspan=\"3\">\n    <widget class=\"QPlainTextEdit\" name=\"logTextEdit\">\n     <property name=\"whatsThis\">\n      <string>Contents of the current log file (~/.twinkle/twinkle.log)</string>\n     </property>\n     <property name=\"lineWrapMode\">\n      <enum>QPlainTextEdit::NoWrap</enum>\n     </property>\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"2\">\n    <widget class=\"QPushButton\" name=\"closePushButton\">\n     <property name=\"text\">\n      <string>&amp;Close</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+C</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <spacer>\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>360</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item row=\"1\" column=\"1\">\n    <widget class=\"QPushButton\" name=\"clearPushButton\">\n     <property name=\"whatsThis\">\n      <string>Clear the log window. This does &lt;b&gt;not&lt;/b&gt; clear the log file itself.</string>\n     </property>\n     <property name=\"text\">\n      <string>C&amp;lear</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+L</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>logTextEdit</tabstop>\n  <tabstop>clearPushButton</tabstop>\n  <tabstop>closePushButton</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">qfile.h</include>\n </includes>\n <resources/>\n <connections>\n  <connection>\n   <sender>closePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>LogViewForm</receiver>\n   <slot>close()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>clearPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>LogViewForm</receiver>\n   <slot>clear()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/main.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"twinkle_config.h\"\n#include <QtDebug>\n#include <QtGlobal>\n\n#ifdef HAVE_KDE\n#include <kapplication.h>\n#include <kcmdlineargs.h>\n#endif\n\n#include <qapplication.h>\n#include <qtranslator.h>\n#include <qtextcodec.h>\n#include <QSettings>\n#include <QDir>\n\n#include \"mphoneform.h\"\n\n#include <iostream>\n#include <string>\n#include <cstdlib>\n#include <cstring>\n#include <ctime>\n#include <QtDebug>\n#include <unistd.h>\n\n#include \"address_book.h\"\n#include \"address_finder.h\"\n#include \"call_history.h\"\n#include \"cmd_socket.h\"\n#include \"events.h\"\n#include \"listener.h\"\n#include \"log.h\"\n#include \"protocol.h\"\n#include \"sender.h\"\n#include \"transaction_mgr.h\"\n#include \"twinkleapplication.h\"\n#include \"user.h\"\n#include \"util.h\"\n#include \"phone.h\"\n#include \"gui.h\"\n#include \"qt_translator.h\"\n#include \"command_args.h\"\n#include \"sockets/connection_table.h\"\n#include \"sockets/interfaces.h\"\n#include \"sockets/socket.h\"\n#include \"threads/thread.h\"\n#include \"utils/mime_database.h\"\n#include \"audits/memman.h\"\n#include <QLibraryInfo>\n\nusing namespace std;\nusing namespace utils;\n\n// Class to initialize the random generator before objects of\n// other classes are created. Initializing just from the main function\n// is too late.\nclass t_init_rand {\npublic:\n\tt_init_rand();\n};\n\nt_init_rand::t_init_rand() { srand(time(NULL)); }\n\n// Initialize random generator\nt_init_rand init_rand;\n\n// Language translator for the core of Twinkle\nt_translator *translator = NULL;\n\n// Indicates if application is ending (because user pressed Quit)\nbool end_app;\n\n// Memory manager for memory leak tracing\nt_memman \t\t*memman;\n\n// IP address on which the phone is running\nstring user_host;\n\n// Local host name\nstring local_hostname;\n\n// SIP UDP socket for sending and receiving signaling\nt_socket_udp *sip_socket;\n\n// SIP TCP socket for sending and receiving signaling\nt_socket_tcp *sip_socket_tcp;\n\n// SIP connection table for connection oriented transport\nt_connection_table *connection_table;\n\n// Event queue that is handled by the transaction manager thread\n// The following threads write to this queue\n// - UDP listener\n// - transaction layer\n// - timekeeper\nt_event_queue\t\t*evq_trans_mgr;\n\n// Event queue that is handled by the UDP sender thread\n// The following threads write to this queue:\n// - phone UAS\n// - phone UAC\n// - transaction manager\nt_event_queue\t\t*evq_sender;\n\n// Event queue that is handled by the transaction layer thread\n// The following threads write to this queue\n// - transaction manager\n// - timekeeper\nt_event_queue\t\t*evq_trans_layer;\n\n// Event queue that is handled by the phone timekeeper thread\n// The following threads write into this queue\n// - phone UAS\n// - phone UAC\n// - transaction manager\nt_event_queue\t\t*evq_timekeeper;\n\n// The timekeeper\nt_timekeeper\t\t*timekeeper;\n\n// The transaction manager\nt_transaction_mgr\t*transaction_mgr;\n\n// The phone\nt_phone\t\t\t*phone;\n\n// User interface\nt_userintf\t\t*ui;\n\n// Log file\nt_log\t\t\t*log_file;\n\n// System config\nt_sys_settings\t\t*sys_config;\n\n// Call history\nt_call_history\t\t*call_history;\n\n// Local address book\nt_address_book\t\t*ab_local;\n\n// Mime database\nt_mime_database\t*mime_database;\n\n/** Command arguments. */\nt_command_args g_cmd_args;\n\n// Thread id of main thread\npthread_t\t\tthread_id_main;\n\n// Indicates if LinuxThreads or NPTL is active.\nbool\t\t\tthreading_is_LinuxThreads;\n\nQSettings*      g_gui_state;\n\n/**\n * Parse arguments passed to application\n * @param argc [in] Number of arguments\n * @param argv [in] Array of arguments\n * @param cli_mode [out] Indicates if Twinkle must run in CLI mode.\n * @param override_lock_file [out] Indicates if an existing lock file must be overriden.\n * @param config_files [out] User profiles passed on the command line.\n * @param remain_argc [out] The number of arguments not parsed by this function.\n * @param remain_argv [out] The arguments not parsed by this function.\n * \tremain_argv[0] == argv[0]\n */\nvoid parse_main_args(int argc, char **argv, bool &cli_mode, bool &override_lock_file,\n\t\t     list<string> &config_files, int &remain_argc, char **&remain_argv) \n{\n\tcli_mode = false;\n\toverride_lock_file = false;\n\tconfig_files.clear();\n\t\n\t// Initialize the remaining arguments with the first argument (application name)\n\t// from the original arguments.\n\tremain_argv = (char**)malloc(argc * sizeof(char*));\n\tremain_argv[0] = argv[0];\n\tremain_argc = 1;\n\n\tfor (int i = 1; i < argc; i++) {\n\t\tif (strcmp(argv[i], \"-h\") == 0 || strcmp(argv[i], \"--help\") == 0) {\n\t\t\t// Help\n\t\t\tcout << \"Usage: twinkle [options]\\n\\n\";\n\t\t\tcout << \"Options:\\n\";\n\t\t\tcout << \" -c\";\n\t\t\tcout << \"\\t\\tRun in command line interface (CLI) mode\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --share <dir>\";\n\t\t\tcout << \"\\tSet the share directory.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" -f <profile>\";\n\t\t\tcout << \"\\tStartup with a specific profile. You will not be requested\\n\";\n\t\t\tcout << \"\\t\\tto choose a profile at startup. The profiles that you created\\n\";\n\t\t\tcout << \"\\t\\tare the .cfg files in your .twinkle directory.\\n\";\n\t\t\tcout << \"\\t\\tYou may specify multiple profiles separated by spaces.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --force\";\n\t\t\tcout << \"\\tIf a lock file is detected at startup, then override it\\n\";\n\t\t\tcout << \"\\t\\tand startup.\\n\";\n\t\t\tcout << endl;\n#if 0\n\t\t\t// DEPRECATED\n\t\t\tcout << \" -i <IP addr>\";\n\t\t\tcout << \"\\tIf you have multiple IP addresses on your computer,\\n\";\n\t\t\tcout << \"\\t\\tthen you can supply the IP address to use here.\\n\";\n\t\t\tcout << endl;\n#endif\n\t\t\tcout << \" --sip-port <port>\\n\";\n\t\t\tcout << \"\\t\\tPort for SIP signalling.\\n\";\n\t\t\tcout << \"\\t\\tThis port overrides the port from the system settings.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --rtp-port <port>\\n\";\n\t\t\tcout << \"\\t\\tPort for RTP.\\n\";\n\t\t\tcout << \"\\t\\tThis port overrides the port from the system settings.\\n\";\n\t\t\tcout << endl;\n#if 0\n\t\t\t// DEPRECATED\n\t\t\tcout << \" --nic <NIC>\";\n\t\t\tcout << \"\\tIf you have multiple NICs on your computer,\\n\";\n\t\t\tcout << \"\\t\\tthen you can supply the NIC name to use here (e.g. eth0).\\n\";\n\t\t\tcout << endl;\n#endif\n\t\t\tcout << \" --call <address>\\n\";\n\t\t\tcout << \"\\t\\tInstruct Twinkle to call the address.\\n\";\n\t\t\tcout << \"\\t\\tWhen Twinkle is already running, this will instruct the running\\n\";\n\t\t\tcout << \"\\t\\tprocess to call the address.\\n\";\n\t\t\tcout << \"\\t\\tThe address may be a full or partial SIP URI. A partial SIP URI\\n\";\n\t\t\tcout << \"\\t\\twill be completed with the information from the user profile.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \"\\t\\tA subject may be passed by appending '?subject=<subject>'\\n\";\n\t\t\tcout << \"\\t\\tto the address.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \"\\t\\tExamples:\\n\";\n\t\t\tcout << \"\\t\\ttwinkle --call 123456\\n\";\n\t\t\tcout << \"\\t\\ttwinkle --call sip:example@example.com?subject=hello\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --cmd <cli command>\\n\";\n\t\t\tcout << \"\\t\\tInstruct Twinkle to execute the CLI command. You can run\\n\";\n\t\t\tcout << \"\\t\\tall commands from the command line interface mode.\\n\";\n\t\t\tcout << \"\\t\\tWhen Twinkle is already running, this will instruct the running\\n\";\n\t\t\tcout << \"\\t\\tprocess to execute the CLI command.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \"\\t\\tExamples:\\n\";\n\t\t\tcout << \"\\t\\ttwinkle --cmd answer\\n\";\n\t\t\tcout << \"\\t\\ttwinkle --cmd mute\\n\";\n\t\t\tcout << \"\\t\\ttwinkle --cmd 'transfer 12345'\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --immediate\";\n\t\t\tcout << \"\\tThis option can be used in conjunction with --call or --cmd\\n\";\n\t\t\tcout << \"\\t\\tIt indicates the the command or call is to be performed\\n\";\n\t\t\tcout << \"\\t\\timmediately without asking the user for any confirmation.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --set-profile <profile>\\n\";\n\t\t\tcout << \"\\t\\tMake <profile> the active profile.\\n\";\n\t\t\tcout << \"\\t\\tWhen using this option in conjunction with --call and --cmd,\\n\";\n\t\t\tcout << \"\\t\\tthen the profile is activated before executing --call or \\n\";\n\t\t\tcout << \"\\t\\t--cmd.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --show\";\n\t\t\tcout << \"\\t\\tInstruct a running instance of Twinkle to show the main window\\n\";\n\t\t\tcout << \"\\t\\tand take focus.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --hide\";\n\t\t\tcout << \"\\t\\tInstruct a running instance of Twinkle to hide in the system tray.\\n\";\n\t\t\tcout << \"\\t\\tIf no system tray is used, then Twinkle will minimize.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --help-cli [cli command]\\n\";\n\t\t\tcout << \"\\t\\tWithout a cli command this option lists all available CLI\\n\";\n\t\t\tcout << \"\\t\\tcommands. With a CLI command this option prints help on\\n\";\n\t\t\tcout << \"\\t\\tthe CLI command.\\n\";\n\t\t\tcout << endl;\n\t\t\tcout << \" --version\";\n\t\t\tcout << \"\\tGet version information.\\n\";\n\t\t\texit(0);\n\t\t} else if (strcmp(argv[i], \"--version\") == 0) {\n\t\t\t// Get version\n\t\t\tQString s = sys_config->about(false).c_str();\n            cout << s.toStdString();\n\t\t\texit(0);\n\t\t} else if (strcmp(argv[i], \"-c\") == 0) {\n\t\t\t// CLI mode\n\t\t\tcli_mode = true;\n\t\t} else if (strcmp(argv[i], \"--share\") == 0) {\n\t\t\tif (i < argc - 1 && argv[i+1][0] != '-') {\n\t\t\t\ti++;\n\t\t\t\tsys_config->set_dir_share(argv[i]);\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Directory missing for option '-share'.\\n\";\n\t\t\t\texit(0);\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"-f\") == 0) {\n\t\t\tif (i < argc - 1 && argv[i+1][0] != '-') {\n\t\t\t\twhile (i < argc -1 && argv[i+1][0] != '-') {\n\t\t\t\t\ti++;\n\t\t\t\t\t// Config file name\n\t\t\t\t\tQString config_file = argv[i];\n\t\t\t\t\tif (!config_file.endsWith(USER_FILE_EXT)) \n\t\t\t\t\t{\n\t\t\t\t\t\tconfig_file += USER_FILE_EXT;\n\t\t\t\t\t}\n                    config_files.push_back(config_file.toStdString());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Config file name missing for option '-f'.\\n\";\n\t\t\t\texit(0);\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--force\") == 0) {\n\t\t\toverride_lock_file = true;\n#if 0\n\t\t// DEPRECATED\n\t\t} else if (strcmp(argv[i], \"-i\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\t// IP address\n\t\t\t\tuser_host = argv[i];\n\t\t\t\tif (!exists_interface(user_host)) {\n\t\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\t\tcout << \"There is no interface with IP address \";\n\t\t\t\t\tcout << user_host << endl;\n\t\t\t\t\texit(0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"IP address missing for option '-i'.\\n\";\n\t\t\t\texit(0);\n\t\t\t}\n#endif\n\t\t} else if (strcmp(argv[i], \"--sip-port\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\tg_cmd_args.override_sip_port = atoi(argv[i]);\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Port missing for option '--sip-port'\\n\";\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--rtp-port\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\tg_cmd_args.override_rtp_port = atoi(argv[i]);\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Port missing for option '--rtp-port'\\n\";\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--call\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\t// SIP URI\n\t\t\t\tg_cmd_args.callto_destination = argv[i];\n\t\t\t\t\n\t\t\t\tif (g_cmd_args.callto_destination.isEmpty()) {\n\t\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\t\tcout << \"--call argument may not be empty.\\n\";\n\t\t\t\t\texit(0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"SIP URI missing for option '--call'.\\n\";\n\t\t\t\texit(0);\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--cmd\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\t// CLI command\n\t\t\t\tg_cmd_args.cli_command = argv[i];\n\t\t\t\t\n\t\t\t\tif (g_cmd_args.cli_command.isEmpty()) {\n\t\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\t\tcout << \"--cmd argument may not be empty.\\n\";\n\t\t\t\t\texit(0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"CLI command missing for option '--cmd'.\\n\";\n\t\t\t\texit(0);\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--immediate\") == 0) {\n\t\t\t// Immediate mode\n\t\t\tg_cmd_args.cmd_immediate_mode = true;\n\t\t} else if (strcmp(argv[i], \"--set-profile\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\t// Set profile\n\t\t\t\tg_cmd_args.cmd_set_profile = argv[i];\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Profile missing for option '--set-profile'.\\n\";\n\t\t\t\texit(0);\n\t\t\t}\t\n\t\t} else if (strcmp(argv[i], \"--show\") == 0) {\n\t\t\t// Show main window\n\t\t\tg_cmd_args.cmd_show = true;\n\t\t} else if (strcmp(argv[i], \"--hide\") == 0) {\n\t\t\t// Hide main window\n\t\t\tg_cmd_args.cmd_hide = true;\n\t\t} else if (strcmp(argv[i], \"--help-cli\") == 0) {\n\t\t\tstring cmd_help(\"help \");\n\t\t\tif (i < argc -1) {\n\t\t\t\ti++;\n\t\t\t\t// Help CLI\n\t\t\t\tcmd_help += argv[i];\n\t\t\t}\n\t\t\t\n\t\t\tt_phone p;\n\t\t\tt_userintf u(&p);\n\t\t\tu.exec_command(cmd_help);\n\t\t\texit(0);\n\t\t} else {\n\t\t\t// Unknown argument. Assume that it is an Qt/KDE argument.\n\t\t\tremain_argv[remain_argc++] = argv[i];\n\t\t}\n\t}\n\t\n\tif (!g_cmd_args.callto_destination.isEmpty() && !g_cmd_args.cli_command.isEmpty()) {\n\t\tcout << argv[0] << \": \";\n\t\tcout << \"--call and --cmd cannot be used at the same time.\\n\";\n\t\texit(0);\n\t}\n\t\n\treturn;\n}\n\nbool open_sip_socket(bool cli_mode) {\n\tQString sock_type;\n\t\n\t// Open socket for SIP signaling\n\ttry {\n\t\tsock_type = \"UDP\";\n\t\tsip_socket = new t_socket_udp(sys_config->get_sip_port(true));\n\t\tMEMMAN_NEW(sip_socket);\n\t\tif (sip_socket->enable_icmp()) {\n\t\t\tlog_file->write_report(\"ICMP processing enabled.\", \"::main\");\n\t\t} else {\n\t\t\tlog_file->write_report(\"ICMP processing disabled.\", \"::main\");\n\t\t}\n\t\t\n\t\tsock_type = \"TCP\";\n\t\tsip_socket_tcp = new t_socket_tcp(sys_config->get_sip_port());\n\t\tMEMMAN_NEW(sip_socket_tcp);\t\n\t} catch (int err) {\t\t\n\t\tstring msg;\n\t\tif (cli_mode) {\n\t\t\tmsg = QString(\"Failed to create a %1 socket (SIP) on port %2\")\n\t\t\t   .arg(sock_type)\n               .arg(sys_config->get_sip_port()).toStdString();\n\t\t} else {\n\t\t\tmsg = qApp->translate(\"GUI\", \"Failed to create a %1 socket (SIP) on port %2\")\n\t\t\t   .arg(sock_type)\n               .arg(sys_config->get_sip_port()).toStdString();\n\t\t}\n\t\tmsg += \"\\n\";\n\t\tmsg += get_error_str(err);\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nQApplication *create_user_interface(bool cli_mode, int argc, char **argv, QTranslator *appTranslator, QTranslator *qtTranslator) {\n\tQApplication *qa = NULL;\n\t\n\tif (cli_mode) {\n\t\t// CLI mode\n\t\tui = new t_userintf(phone);\n\t\tMEMMAN_NEW(ui);\n\t} else {\n\t\t// GUI mode\n\t\t\n#ifdef HAVE_KDE\n\t\t// Store the defualt mime source factory for the embedded icons.\n\t\t// This is created by Qt. The KApplication constructor seems to destroy\n\t\t// this default.\n\t\tQ3MimeSourceFactory *factory_qt = Q3MimeSourceFactory::takeDefaultFactory();\n\t\t\n\t\t// Initialize the KApplication\n\t\tKCmdLineArgs::init(argc, argv, \"twinkle\", PRODUCT_NAME, \"Soft phone\",\n\t\t\t\t   PRODUCT_VERSION, true);\n\t\tqa = new t_twinkle_application();\n\t\tMEMMAN_NEW(qa);\n\t\t\n\t\t// Store the KDE mime source factory\n\t\tQ3MimeSourceFactory *factory_kde = Q3MimeSourceFactory::takeDefaultFactory();\n\t\t\n\t\t// Make the Qt factory the default to make the embedded icons work.\n\t\tQ3MimeSourceFactory::setDefaultFactory(factory_qt);\n\t\t\n\t\t// Add the KDE factory\n\t\tQ3MimeSourceFactory::addFactory(factory_kde);\n#else\n\t\tstatic int tmp = argc;\n\t\tqa = new t_twinkle_application(tmp, argv);\n\t\tMEMMAN_NEW(qa);\n#endif\n\n        g_gui_state = new QSettings(QDir::home().absoluteFilePath(QString(\"%1/%2\").arg(DIR_USER).arg(\"gui_state.ini\")),\n                                    QSettings::IniFormat, qa);\n\t\t\n\t\t// Install Qt translator\n\t\t// Do not report to memman as the translator will be deleted\n\t\t// automatically when the QApplication is deleted.\n\t\tappTranslator = new QTranslator(0);\n\t\tqtTranslator = new QTranslator(0);\n\n\t\tQString langName = QLocale::system().name().left(2);\n\n\t\tqDebug() << \"Language name:\" << langName;\n\t\tappTranslator->load(QString(\"twinkle_\") + langName,\n\t\t\tQString(sys_config->get_dir_lang().c_str()));\n\t\tqa->installTranslator(appTranslator);\n\t\t\n\t\tqtTranslator->load(\"qt_\" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));\n\t\tqa->installTranslator(qtTranslator);\n\n\t\tqa->setQuitOnLastWindowClosed(false);\n\t\t\n\t\t// Create translator for translation of strings from the core\n\t\ttranslator = new t_qt_translator(qa);\n\t\tMEMMAN_NEW(translator);\n\n\t\tui = new t_gui(phone);\n\t\tMEMMAN_NEW(ui);\n\t}\n\t\n\treturn qa;\n}\n\nvoid blockSignals()\n{\n\t// Dedicated thread will catch SIGALRM, SIGINT, SIGTERM, SIGCHLD signals,\n\t// therefore all threads must block these signals. Block now, then all\n\t// created threads will inherit the signal mask.\n\t// In LinuxThreads the sigwait does not work very well, so\n\t// in LinuxThreads a signal handler is used instead.\n\n\tif (!threading_is_LinuxThreads) {\n\t\tsigset_t sigset;\n\t\tsigemptyset(&sigset);\n\t\tsigaddset(&sigset, SIGALRM);\n\t\tsigaddset(&sigset, SIGINT);\n\t\tsigaddset(&sigset, SIGTERM);\n\t\tsigaddset(&sigset, SIGCHLD);\n\t\tsigprocmask(SIG_BLOCK, &sigset, NULL);\n\t} else {\n\t\tif (!phone->set_sighandler()) {\n\t\t\tstring msg = \"Failed to register signal handler.\";\n\t\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\tsys_config->delete_lock_file();\n\t\t\texit(1);\n\t\t}\n\t}\n\n\t// Ignore SIGPIPE so read from broken sockets will not cause\n\t// the process to terminate.\n\t(void)signal(SIGPIPE, SIG_IGN);\n}\n\nint main( int argc, char ** argv )\n{\n\tstring error_msg;\n\tbool cli_mode;\n\tbool override_lock_file;\n\tlist<string> config_files;\n\t\n\t// Initialize globals\n\tend_app = false;\n\t\n\t// Determine threading implementation\n\tthreading_is_LinuxThreads = t_thread::is_LinuxThreads();\n\n\tblockSignals();\n\n\tQApplication *qa = NULL;\n\tQTranslator *appTranslator = NULL;\n\tQTranslator *qtTranslator = NULL;\n\n  // Set phone role\n  qputenv(\"PULSE_PROP_media.role\", \"phone\");\n\t\n\t// Store id of main thread\n\tthread_id_main = t_thread::self();\n\t\n\tmemman = new t_memman();\n\tMEMMAN_NEW(memman);\n\tconnection_table = new t_connection_table();\n\tMEMMAN_NEW(connection_table);\n\tevq_trans_mgr = new t_event_queue();\n\tMEMMAN_NEW(evq_trans_mgr);\n\tevq_sender = new t_event_queue();\n\tMEMMAN_NEW(evq_sender);\n\tevq_trans_layer = new t_event_queue();\n\tMEMMAN_NEW(evq_trans_layer);\n\tevq_timekeeper = new t_event_queue();\n\tMEMMAN_NEW(evq_timekeeper);\n\ttimekeeper = new t_timekeeper();\n\tMEMMAN_NEW(timekeeper);\n\ttransaction_mgr = new t_transaction_mgr();\n\tMEMMAN_NEW(transaction_mgr);\n\tphone = new t_phone();\n\tMEMMAN_NEW(phone);\n\t\n\t// Create system configuration object\n\tsys_config = new t_sys_settings();\n\tMEMMAN_NEW(sys_config);\n\t\n\t// Parse command line arguments\n\tint remain_argc = 0;\n\tchar **remain_argv = NULL;\n\tparse_main_args(argc, argv, cli_mode, override_lock_file, config_files, remain_argc, remain_argv);\n\tsys_config->set_override_sip_port(g_cmd_args.override_sip_port);\n\tsys_config->set_override_rtp_port(g_cmd_args.override_rtp_port);\n\t\n\t// Checking the environment and creating the lock is done at\n\t// this early stage to improve performance of the --call parameter.\n\t// Creation of the QApplication object for the GUI is slow.\n\t// However for errors, the user interface must be created to give\n\t// either a message box or text formatted error.\n\t\n\t// Check requirements on environment\n\t// If check fails, then display error after user interface has been\n\t// created.\n\tstring env_error_msg;\n\tbool env_check_ok = sys_config->check_environment(env_error_msg);\n\t\n\t// Create a lock file to guarantee that the application runs only once.\n\tbool already_running;\n\tbool lock_created = false;\n\tstring lock_error_msg;\t\n\tif (env_check_ok &&\n\t    !(lock_created = sys_config->create_lock_file(false, lock_error_msg, already_running))) \n\t{\n\t\tbool must_exit = false;\n\t\t\n\t\t// Show the main window of the running Twinkle process.\n\t\tif (already_running && g_cmd_args.cmd_show) {\n\t\t\tcmdsocket::cmd_show();\n\t\t\tmust_exit = true;\n\t\t}\n\t\t\n\t\t// Hide the main window of the running Twinkle process.\n\t\tif (already_running && g_cmd_args.cmd_hide) {\n\t\t\tcmdsocket::cmd_hide();\n\t\t\tmust_exit = true;\n\t\t}\n\t\t\n\t\t// Activate a profile in the running Twinkle process.\n\t\tif (already_running && !g_cmd_args.cmd_set_profile.isEmpty()) {\n            cmdsocket::cmd_cli(string(\"user \") + g_cmd_args.cmd_set_profile.toStdString(), true);\n\t\t\t// Do not exit now as this option may be used in conjunction\n\t\t\t// with --call or --cmd\n\t\t\tmust_exit = true;\n\t\t}\n\t\t\n\t\t// If Twinkle is running already and the --call parameter\n\t\t// is present, then send the call destination to the running\n\t\t// Twinkle process.\n\t\tif (already_running && !g_cmd_args.callto_destination.isEmpty()) {\n            cmdsocket::cmd_call(g_cmd_args.callto_destination.toStdString(),\n\t\t\t\t\t    g_cmd_args.cmd_immediate_mode);\n\t\t\texit(0);\n\t\t}\n\t\t\n\t\t// If the --cmd parameter is present, send the cli command\n\t\t// to the running Twinkle process\n\t\tif (already_running && !g_cmd_args.cli_command.isEmpty()) {\n            cmdsocket::cmd_cli(g_cmd_args.cli_command.toStdString(),\n\t\t\t\t\t   g_cmd_args.cmd_immediate_mode);\n\t\t\texit(0);\n\t\t}\n\t\t\n\t\t// Exit if an instruction for a running instance was given.\n\t\tif (must_exit) {\n\t\t\texit(0);\n\t\t}\n\t}\n\t\n\t// Read system configuration\n\tbool sys_config_read = sys_config->read_config(error_msg);\n\tqa = create_user_interface(cli_mode, remain_argc, remain_argv, appTranslator, qtTranslator);\n\tif (!sys_config_read) {\n\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\texit(1);\n\t}\n\n\tuser_host = AUTO_IP4_ADDRESS;\n\tlocal_hostname = get_local_hostname();\n\t\n\tif (!env_check_ok) {\n\t\t// Environment is not good\n\t\t// Call the check_environment once more to get proper translation\n\t\t// of the error message. The previous check was done before\n\t\t// the QApplication was created.\n\t\t(void)sys_config->check_environment(env_error_msg);\n\t\tui->cb_show_msg(env_error_msg, MSG_CRITICAL);\n\t\texit(1);\n\t}\n\t\n\t// Show error if lock file could not be created\n\tif (!lock_created) {\t\n\t\tstring msg;\n\t\t// Call create lock file once more to get proper translation of\n\t\t// error message.\n\t\tif (!sys_config->create_lock_file(false, msg, already_running)) {\n\t\t\tif (already_running) {\n\t\t\t\tif (!cli_mode) {\n\t\t\t\t\tmsg += \"\\n\\n\";\n\t\t\t\t\tmsg += qApp->translate(\"GUI\",\n                        \"Override lock file and start anyway?\").toStdString();\n\t\t\t\t}\n\t\t\t\tif (override_lock_file || ui->cb_ask_msg(msg, MSG_WARNING)) {\n\t\t\t\t\tsys_config->delete_lock_file();\n\t\t\t\t\tif (!sys_config->create_lock_file(true, msg, \n\t\t\t\t\t\talready_running))\n\t\t\t\t\t{\n\t\t\t\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\t\t\t\texit(1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\texit(1);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\t\texit(1);\n\t\t\t}\n\t\t}\n\t\t// If for some obscure reason the lock file could be\n\t\t// created this time, then continue.\n\t}\n\t\n\t// Create log file\n\tlog_file = new t_log();\n\tMEMMAN_NEW(log_file);\n\t\n\t// Write threading implementation to log file. May be useful for debugging.\n\tif (threading_is_LinuxThreads) {\n\t\tlog_file->write_report(\"Threading implementation is LinuxThreads.\",\n\t\t\t\"::main\", LOG_NORMAL, LOG_INFO);\n\t} else {\n\t\tlog_file->write_report(\"Threading implementation is NPTL.\",\n\t\t\t\"::main\", LOG_NORMAL, LOG_INFO);\n\t}\n\t\n\t// Check if the previous Twinkle session was stopped by a system\n\t// shutdow and now gets restored.\n\tif (qa && qa->isSessionRestored()) {\n\t\tQString msg = \"Restore session: \" + qa->sessionId();\n        log_file->write_report(msg.toStdString(), \"::main\");\n\t\t\n        if (sys_config->get_ui_session_id() == qa->sessionId().toStdString()) {\n\t\t\tconfig_files = sys_config->get_ui_session_active_profiles();\n\t\t\t// Note: the GUI state is restore in t_gui::run()\n\t\t} else {\n\t\t\tlog_file->write_header(\"::main\", LOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Cannot restore session.\\n\");\n\t\t\tlog_file->write_raw(\"Stored session id: \");\n\t\t\tlog_file->write_raw(sys_config->get_ui_session_id());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\n\t\n\t// Get default values from system configuration\n\tif (config_files.empty()) {\n\t\tlist<string> start_user_profiles = sys_config->get_start_user_profiles();\n\t\tfor (list<string>::iterator i = start_user_profiles.begin();\n\t\ti != start_user_profiles.end(); i++)\n\t\t{\n\t\t\tQString config_file = (*i).c_str();\n\t\t\tconfig_file += USER_FILE_EXT;\n            config_files.push_back(config_file.toStdString());\n\t\t}\n\t}\n\t\n\tbool profile_selected = false;\n\twhile(!profile_selected) {\n\t\t// Select user profile\n\t\tif (config_files.empty()) {\n\t\t\tif (!ui->select_user_config(config_files)) {\n\t\t\t\tsys_config->delete_lock_file();\n\t\t\t\texit(1);\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (list<string>::iterator i = config_files.begin();\n\t\ti != config_files.end(); i++)\n\t\t{\t\n\t\t\tt_user user_config;\n\t\t\t\n\t\t\t// Read user configuration\n\t\t\tif (user_config.read_config(*i, error_msg)) {\n\t\t\t\tt_user *dup_user;\n\t\t\t\tif (phone->add_phone_user(\n\t\t\t\t\t\tuser_config, &dup_user))\n\t\t\t\t{\n\t\t\t\t\tprofile_selected = true;\n\t\t\t\t} else {\n\t\t\t\t\tif (cli_mode) {\n                        error_msg = QString(\"The following profiles are both for user %1\").arg(user_config.get_name().c_str()).toStdString();\n\t\t\t\t\t} else {\n                        error_msg = qApp->translate(\"GUI\", \"The following profiles are both for user %1\").arg(user_config.get_name().c_str()).toStdString();\n\t\t\t\t\t}\n\t\t\t\t\terror_msg += \":\\n\\n\";\n\t\t\t\t\terror_msg += user_config.get_profile_name();\n\t\t\t\t\terror_msg += \"\\n\";\n\t\t\t\t\terror_msg += dup_user->get_profile_name();\n\t\t\t\t\terror_msg += \"\\n\\n\";\n\t\t\t\t\tif (cli_mode) {\n                        error_msg += QString(\"You can only run multiple profiles for different users.\").toStdString();\n\t\t\t\t\t\terror_msg += \"\\n\";\n                        error_msg += QString(\"If these are users for different domains, then enable the following option in your user profile (SIP protocol):\").toStdString();\n\t\t\t\t\t\terror_msg += \"\\n\";\n                        error_msg += QString(\"Use domain name to create a unique contact header\").toStdString();\n\t\t\t\t\t} else {\n                        error_msg += qApp->translate(\"GUI\", \"You can only run multiple profiles for different users.\").toStdString();\n\t\t\t\t\t\terror_msg += \"\\n\";\n                        error_msg += qApp->translate(\"GUI\", \"If these are users for different domains, then enable the following option in your user profile (SIP protocol)\").toStdString();\n\t\t\t\t\t\terror_msg += \":\\n\";\n                        error_msg += qApp->translate(\"GUI\", \"Use domain name to create a unique contact header\").toStdString();\n\t\t\t\t\t}\n\t\t\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t\t\t\tprofile_selected = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t\t\tprofile_selected = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (profile_selected && !open_sip_socket(cli_mode)) {\n\t\t\t// Opening SIP socket failed. Let user pick a user profile\n\t\t\t// again, so he can make changes in settings to fix the error.\n\t\t\tprofile_selected = false;\n\t\t}\n\t\t\n\t\t// In CLI mode the user cannot select another profile.\n\t\tif (!profile_selected) {\n\t\t\tif (cli_mode) {\n\t\t\t\tsys_config->delete_lock_file();\n\t\t\t\texit(1);\n\t\t\t}\n\t\t}\n\t\t\n\t\tconfig_files.clear();\n\t}\n\t\n\t// Initialize RTP port settings.\n\tphone->init_rtp_ports();\n\t\n\t// Create call history\n\tcall_history = new t_call_history();\n\tMEMMAN_NEW(call_history);\n\t\n\t// Read call history\n\tif (!call_history->load(error_msg)) {\n\t\tlog_file->write_report(error_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t}\n\t\n\t// Create local address book\n\tab_local = new t_address_book();\n\tMEMMAN_NEW(ab_local);\n\t\n\t// Read local address book\n\tif (!ab_local->load(error_msg)) {\n\t\tlog_file->write_report(error_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t\tui->cb_show_msg(error_msg, MSG_WARNING);\n\t}\n\t\n\t// Preload the address finder (Akonadi is only available in GUI mode)\n\tif (!cli_mode) {\n\t\tt_address_finder::preload();\n\t}\n\t\n\t// Create mime database\n\tmime_database = new t_mime_database();\n\tMEMMAN_NEW(mime_database);\n\tif (!mime_database->load(error_msg)) {\n\t\tlog_file->write_report(error_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t}\n\t\n\t// Discover NAT type if STUN is enabled\n\tlist<t_user *> user_list = phone->ref_users();\n\tui->cb_nat_discovery_progress_start(user_list.size());\n\tlist<string> msg_list;\n\tint progressStep = 0;\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tui->cb_nat_discovery_progress_step(progressStep);\n\t\t\n\t\tif (ui->cb_nat_discovery_cancelled()) {\n\t\t\tlog_file->write_report(\"User aborted NAT discovery.\", \"::main\");\n\t\t\tsys_config->delete_lock_file();\n\t\t\texit(1);\n\t\t}\n\t\t\n\t\tif (!phone->stun_discover_nat(*i, error_msg)) {\n\t\t\tmsg_list.push_back(error_msg);\n\t\t}\n\t\t\n\t\tprogressStep++;\n\t}\n\tui->cb_nat_discovery_finished();\n\t\n\tfor (list<string>::iterator i = msg_list.begin();\n\t     i != msg_list.end(); i++)\n\t{\n\t\tui->cb_show_msg(*i, MSG_WARNING);\n\t}\n\t\n\t// Open socket for external commands from the command line\n\tstring cmd_sock_name = sys_config->get_dir_user();\n\tcmd_sock_name += '/';\n\tcmd_sock_name += CMD_SOCKNAME;\n\tt_socket_local *sock_cmd = NULL;\n\ttry {\n\n\t\t\n\t\t// The local socket may still exist if Twinkle got killed\n\t\t// previously, so remove it if it is still there.\n\t\tunlink(cmd_sock_name.c_str());\n\t\t\n\t\tsock_cmd = new t_socket_local();\n\t\tMEMMAN_NEW(sock_cmd);\n\t\tsock_cmd->bind(cmd_sock_name);\n\t\tsock_cmd->listen(5);\n\t\t\n\t\tstring log_msg = \"Created local socket: \";\n\t\tlog_msg += cmd_sock_name;\n\t\tlog_file->write_report(log_msg, \"::main\");\n\t}\n\tcatch (int e) {\n\t\tif (sock_cmd) {\n\t\t\tMEMMAN_DELETE(sock_cmd);\n\t\t\tdelete sock_cmd;\n\t\t\tsock_cmd = NULL;\n\t\t}\n\t\tstring log_msg = \"Failed to create local socket: \";\n\t\tlog_msg += cmd_sock_name;\n\t\tlog_msg += \"\\n\";\n\t\tlog_msg += get_error_str(e);\n\t\tlog_msg += \"\\n\";\n\t\tlog_file->write_report(log_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t}\n\t\t\t\t \n\t// Create threads\n\tt_thread *thr_sender;\n\tt_thread *thr_tcp_sender;\n\tt_thread *thr_listen_udp;\n\tt_thread *thr_listen_data_tcp;\n\tt_thread *thr_listen_conn_tcp;\n\tt_thread *thr_conn_timeout_handler;\n\tt_thread *thr_timekeeper;\n\tt_thread *thr_alarm_catcher = NULL;\n\tt_thread *thr_sig_catcher = NULL;\n\tt_thread *thr_trans_mgr;\n\tt_thread *thr_phone_uas;\n\tt_thread *thr_listen_cmd = NULL;\n\t\n\ttry {\n\t\t// SIP sender thread\n\t\tthr_sender = new t_thread(sender_loop, NULL);\n\t\tMEMMAN_NEW(thr_sender);\n\t\t\n\t\t// SIP TCP sender thread\n\t\tthr_tcp_sender = new t_thread(tcp_sender_loop, NULL);\n\t\tMEMMAN_NEW(thr_tcp_sender);\n\n\t\t// UDP listener thread\n\t\tthr_listen_udp = new t_thread(listen_udp, NULL);\n\t\tMEMMAN_NEW(thr_listen_udp);\n\t\t\n\t\t// TCP data listener thread\n\t\tthr_listen_data_tcp = new t_thread(listen_for_data_tcp, NULL);\n\t\tMEMMAN_NEW(thr_listen_data_tcp);\n\t\t\n\t\t// TCP connection listener thread\n\t\tthr_listen_conn_tcp = new t_thread(listen_for_conn_requests_tcp, NULL);\n\t\tMEMMAN_NEW(thr_listen_conn_tcp);\n\t\t\n\t\t// Connection timeout handler thread\n\t\tthr_conn_timeout_handler = new t_thread(connection_timeout_main, NULL);\n\t\tMEMMAN_NEW(thr_conn_timeout_handler);\n\n\t\t// Timekeeper thread\n\t\tthr_timekeeper = new t_thread(timekeeper_main, NULL);\n\t\tMEMMAN_NEW(thr_timekeeper);\n\t\t\n\t\tif (!threading_is_LinuxThreads) {\n\t\t\t// Alarm catcher thread\n\t\t\tthr_alarm_catcher = new t_thread(timekeeper_sigwait, NULL);\n\t\t\tMEMMAN_NEW(thr_alarm_catcher);\n\n\t\t\t// Signal catcher thread\n\t\t\tthr_sig_catcher = new t_thread(phone_sigwait, NULL);\n\t\t\tMEMMAN_NEW(thr_sig_catcher);\n\t\t}\n\n\t\t// Transaction manager thread\n\t\tthr_trans_mgr = new t_thread(transaction_mgr_main, NULL);\n\t\tMEMMAN_NEW(thr_trans_mgr);\n\n\t\t// Phone thread (UAS)\n\t\tthr_phone_uas = new t_thread(phone_uas_main, NULL);\n\t\tMEMMAN_NEW(thr_phone_uas);\n\t\t\n\t\t// External command listener thread\n\t\tif (sock_cmd) {\n\t\t\tthr_listen_cmd = new t_thread(cmdsocket::listen_cmd, sock_cmd);\n\t\t\tMEMMAN_NEW(thr_listen_cmd);\n\t\t}\n\t} catch (int) {\n\t\tstring msg = \"Failed to create threads.\";\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t}\n\t\n\t// Validate sound devices\n\tif (!sys_config->exec_audio_validation(true, true, true, error_msg)) {\n\t\tui->cb_show_msg(error_msg, MSG_WARNING);\n\t}\n\n\t// Start UI event loop (CLI/QApplication/KApplication)\n\ttry {\n\t\tui->run();\n\t} catch (string e) {\n\t\tstring msg = \"Exception: \";\n\t\tmsg += e;\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t} catch (int e) {\n\t\tstring msg = \"Error code exception: \";\n\t\tmsg += e;\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t} catch (const std::exception& e) {\n\t\tstring msg = \"std::exception exception: \";\n\t\tmsg += e.what();\n\t\tmsg += \" (\";\n\t\tmsg += typeid(e).name();\n\t\tmsg += \")\";\n\t\t\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t} catch (...) {\n\t\tstring msg = \"Unknown exception\";\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t}\n\t\n\t// Application is ending\n\tend_app = true;\n\t\n\t// Terminate threads\n\t// Kill the threads getting receiving input from the outside world first,\n\t// so no new inputs come in during termination.\n\tif (thr_listen_cmd) {\n\t\tthr_listen_cmd->cancel();\n\t\tthr_listen_cmd->join();\n\t\tlog_file->write_report(\"thr_listen_cmd stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t}\n\t\n\tthr_listen_udp->cancel();\n\tthr_listen_udp->join();\n\tlog_file->write_report(\"thr_listen_udp stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tthr_listen_conn_tcp->cancel();\n\tthr_listen_conn_tcp->join();\n\tlog_file->write_report(\"thr_listen_conn_tcp stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tconnection_table->cancel_select();\n\tthr_listen_data_tcp->join();\n\tlog_file->write_report(\"thr_listen_data_tcp stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\tthr_conn_timeout_handler->join();\n\tlog_file->write_report(\"thr_conn_timeout_handler stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tthr_tcp_sender->join();\n\tlog_file->write_report(\"thr_tcp_sender stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tevq_trans_layer->push_quit();\n\tthr_phone_uas->join();\n\tlog_file->write_report(\"thr_phone_uas stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tevq_trans_mgr->push_quit();\n\tthr_trans_mgr->join();\n\t\n\tif (!threading_is_LinuxThreads) {\n\t\ttry {\n\t\t\tthr_sig_catcher->cancel();\n\t\t} catch (int) {\n\t\t\t// Thread terminated already by itself\n\t\t}\n\t\tthr_sig_catcher->join();\n\t\tlog_file->write_report(\"thr_sig_catcher stopped.\", \"::main\", \n\t\t\t\t       LOG_NORMAL, LOG_DEBUG);\n\t\t\n\t\tthr_alarm_catcher->cancel();\n\t\tthr_alarm_catcher->join();\n\t\tlog_file->write_report(\"thr_alarm_catcher stopped.\", \"::main\", \n\t\t\t\t       LOG_NORMAL, LOG_DEBUG);\n\t}\n\t\n\tevq_timekeeper->push_quit();\n\tthr_timekeeper->join();\n\tlog_file->write_report(\"thr_timekeeper stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tevq_sender->push_quit();\n\tthr_sender->join();\n\tlog_file->write_report(\"thr_sender stopped.\", \"::main\", LOG_NORMAL, LOG_DEBUG);\n\t\n\tsys_config->remove_all_tmp_files();\n\t\n\tif (thr_listen_cmd) {\n\t\tMEMMAN_DELETE(thr_listen_cmd);\n\t\tdelete thr_listen_cmd;\n\t}\n\n\tMEMMAN_DELETE(thr_phone_uas);\n\tdelete thr_phone_uas;\n\tMEMMAN_DELETE(thr_trans_mgr);\n\tdelete thr_trans_mgr;\n\tMEMMAN_DELETE(thr_timekeeper);\n\tdelete thr_timekeeper;\n\tMEMMAN_DELETE(thr_conn_timeout_handler);\n\tdelete thr_conn_timeout_handler;\n\t\n\tif (!threading_is_LinuxThreads) {\n\t\tMEMMAN_DELETE(thr_sig_catcher);\n\t\tdelete thr_sig_catcher;\n\t\tMEMMAN_DELETE(thr_alarm_catcher);\n\t\tdelete thr_alarm_catcher;\n\t}\n\t\n\tMEMMAN_DELETE(thr_listen_udp);\n\tdelete thr_listen_udp;\n\tMEMMAN_DELETE(thr_sender);\n\tdelete thr_sender;\n\tMEMMAN_DELETE(thr_tcp_sender);\n\tdelete thr_tcp_sender;\n\t\n\tMEMMAN_DELETE(thr_listen_data_tcp);\n\tdelete thr_listen_data_tcp;\n\tMEMMAN_DELETE(thr_listen_conn_tcp);\n\tdelete thr_listen_conn_tcp;\n\n\tMEMMAN_DELETE(mime_database);\n\tdelete mime_database;\n\tMEMMAN_DELETE(ab_local);\n\tdelete ab_local;\n\tMEMMAN_DELETE(call_history);\n\tdelete call_history;\n\tcall_history = NULL;\n\n\tMEMMAN_DELETE(ui);\n\tdelete ui;\n\tui = NULL;\n\t\n\tMEMMAN_DELETE(connection_table);\n\tdelete connection_table;\n\tMEMMAN_DELETE(sip_socket_tcp);\n\tdelete sip_socket_tcp;\n\tMEMMAN_DELETE(sip_socket);\n\tdelete sip_socket;\n\t\n\tif (sock_cmd) {\n\t\tMEMMAN_DELETE(sock_cmd);\n\t\tdelete sock_cmd;\n\t\tunlink(cmd_sock_name.c_str());\n\t}\n\n\tMEMMAN_DELETE(phone);\n\tdelete phone;\n\tMEMMAN_DELETE(transaction_mgr);\n\tdelete transaction_mgr;\n\tMEMMAN_DELETE(timekeeper);\n\tdelete timekeeper;\n\tMEMMAN_DELETE(evq_trans_mgr);\n\tdelete evq_trans_mgr;\n\tMEMMAN_DELETE(evq_sender);\n\tdelete evq_sender;\n\tMEMMAN_DELETE(evq_trans_layer);\n\tdelete evq_trans_layer;\n\tMEMMAN_DELETE(evq_timekeeper);\n\tdelete evq_timekeeper;\n\t\n\tif (translator) {\n\t\tMEMMAN_DELETE(translator);\n\t\tdelete translator;\n\t\ttranslator = NULL;\n\t}\n\t\n\tif (appTranslator) {\n\t\tMEMMAN_DELETE(appTranslator);\n\t\tdelete(appTranslator);\n\t}\n\t\n\tif (qa) {\n\t\tMEMMAN_DELETE(qa);\n\t\tdelete qa;\n\t}\n\n\t// Report memory leaks\n\t// Report deletion of log_file and sys_config already to get a correct\n\t// report.\n\tMEMMAN_DELETE(sys_config);\n\tMEMMAN_DELETE(log_file);\n\tMEMMAN_DELETE(memman);\n\tMEMMAN_REPORT;\n\t\n\tdelete log_file;\n\tdelete memman;\n\t\n\tsys_config->delete_lock_file();\n\tdelete sys_config;\n}\n"
  },
  {
    "path": "src/gui/messageform.cpp",
    "content": "\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifdef HAVE_KDE\n#include <kfiledialog.h>\n#include <kiconloader.h>\n#include <kmimetype.h>\n#include <krun.h>\n#include <kuserprofile.h>\n#else\n#include \"utils/mime_database.h\"\n//Added by qt3to4:\n#include <QKeyEvent>\n#include <QLabel>\n#include <QPixmap>\n#endif\n\n#include \"gui.h\"\n#include \"sockets/url.h\"\n#include <QTextDocument>\n#include \"audits/memman.h\"\n#include \"util.h\"\n#include <QPixmap>\n#include <QCursor>\n#include <QFileDialog>\n#include \"utils/file_utils.h\"\n#include <QFile>\n#include \"sendfileform.h\"\n#include <QStatusBar>\n#include <QFrame>\n#include <QTextDocument>\n#include \"messageform.h\"\n\n\nusing namespace utils;\n\n/** Maximum width for an inline image */\n#define MAX_WIDTH_IMG_INLINE 400\n\n/** Maximum height for an inline image */\n#define MAX_HEIGHT_IMG_INLINE 400\n\n#define IMG_SCALE_FACTOR(width, height) (std::min<float>( float(MAX_WIDTH_IMG_INLINE) / (width), float(MAX_HEIGHT_IMG_INLINE) / (height) ) )\n\nMessageForm::MessageForm(QWidget* parent)\n    : QMainWindow(parent)\n{\n\tsetupUi(this);\n\n\t(void)statusBar();\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nMessageForm::~MessageForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid MessageForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid MessageForm::init()\n{\n    this->setAttribute(Qt::WA_DeleteOnClose);\n\t\n\t_getAddressForm = 0;\n\t_remotePartyComplete = false;\n\t\n\t// Add label to display size of typed message\n\t_msgSizeLabel = new QLabel(this);\n\tstatusBar()->addWidget(_msgSizeLabel);\n\tshowMessageSize();\n\t\n\t// Set toolbutton icons for disabled options.\n\tsetDisabledIcon(addressToolButton, \"kontact_contacts-disabled.png\");\n\t\n\tattachmentPopupMenu = new QMenu(this);\n\tMEMMAN_NEW(attachmentPopupMenu);\n\t\n    connect(attachmentPopupMenu, SIGNAL(triggered(QAction*)),\n        this, SLOT(attachmentPopupActivated(QAction*)));\n\t\n\t_serviceMap = NULL;\n\t_saveAsDialog = NULL;\n\t\n\t_isComposingLabel = NULL;\n\t\n\t// When the user edits the message, the composition indication\n\t// will be set to active.\n\tconnect(msgLineEdit, SIGNAL(textChanged(const QString &)),\n\t\tthis, SLOT(setLocalComposingIndicationActive()));\n}\n\nvoid MessageForm::destroy()\n{\n\tif (_getAddressForm) {\n\t\tMEMMAN_DELETE(_getAddressForm);\n\t\tdelete _getAddressForm;\n\t}\n\t\n\tMEMMAN_DELETE(attachmentPopupMenu);\n\tdelete attachmentPopupMenu;\n\t\n#ifdef HAVE_KDE\n\tvector<KService::Ptr> *serviceMap = (vector<KService::Ptr> *)_serviceMap;\n\tif (serviceMap) {\n\t\tMEMMAN_DELETE(serviceMap);\n\t\tdelete serviceMap;\n\t}\n#endif\n\t\n\tif (_saveAsDialog) {\n\t\tMEMMAN_DELETE(_saveAsDialog);\n\t\tdelete _saveAsDialog;\n\t}\n\t\n\tif (_isComposingLabel) {\n\t\tMEMMAN_DELETE(_isComposingLabel);\n\t\tdelete _isComposingLabel;\n\t}\n}\n\nvoid MessageForm::closeEvent(QCloseEvent *e)\n{\n\tMEMMAN_DELETE(this); // destructive close\n\tQMainWindow::closeEvent(e);\n}\n\n\nvoid MessageForm::show()\n{\n\tif (toLineEdit->text().isEmpty()) {\n\t\tsendFileAction->setEnabled(false);\n\t\ttoLineEdit->setFocus();\n\t} else {\n\t\t// Once a message session has been created, the\n\t\t// source and destination cannot be changed anymore.\n\t\tfromComboBox->setEnabled(false);\n\t\ttoLineEdit->setEnabled(false);\n\t\taddressToolButton->setEnabled(false);\n\t\tsendFileAction->setEnabled(true);\n\t\tmsgLineEdit->setFocus();\n\t}\n\t\n\tQMainWindow::show();\n}\n\nvoid MessageForm::selectUserConfig(t_user *user_config)\n{\n\tfor (int i = 0; i < fromComboBox->count(); i++) {\n        if (fromComboBox->itemText(i) ==\n\t\t    user_config->get_profile_name().c_str())\n\t\t{\n            fromComboBox->setCurrentIndex(i);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid MessageForm::showAddressBook()\n{\n\tif (!_getAddressForm) {\n        _getAddressForm = new GetAddressForm(this);\n        _getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(_getAddressForm);\n\t}\n\t\n\tconnect(_getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\t_getAddressForm->show();\n}\n\nvoid MessageForm::selectedAddress(const QString &address)\n{\n\ttoLineEdit->setText(address);\n}\n\n/**\n  * Check if there is a valid sender and receiver. If so, then set the sender\n  * and receiver addresses in the message session object.\n  */\nbool MessageForm::updateMessageSession()\n{\n\tstring display, dest_str;\n\tt_user *from_user = phone->ref_user_profile(\n                fromComboBox->currentText().toStdString());\n\tif (!from_user) {\n\t\t// The user profile is not active anymore\n\t\tfromComboBox->setFocus();\n\t\treturn false;\n\t}\n\t\n    ui->expand_destination(from_user, toLineEdit->text().trimmed().toStdString(),\n\t\t\t       display, dest_str);\n\tt_url dest(dest_str);\n\t\n\tif (!dest.is_valid()) {\n\t\ttoLineEdit->setFocus();\n\t\ttoLineEdit->selectAll();\n\t\treturn false;\n\t}\n\t\n\t_msgSession->set_user(from_user);\n\t_msgSession->set_remote_party(t_display_url(dest, display));\n\t\n\tsetRemotePartyCaption();\n\n\treturn true;\n}\n\n/**\n  * Determine if a message can be sent, i.e. there is a valid sender and\n  * receiver. If a message can be sent, then lock the sender and receiver.\n  * @return True if message can be sent, false otherwise.\n  */\nbool MessageForm::prepareSendMessage() {\n\tif (toLineEdit->text().isEmpty()) {\n\t\t// No recipient selected.\n\t\treturn false;\n\t}\n\t\n\tif (toLineEdit->isEnabled()) {\n\t\tif (!updateMessageSession()) {\n\t\t\t// No valid sender and receiver.\n\t\t\treturn false;\n\t\t}\n\n\t\t// Once a message session has been created, the\n\t\t// source and destination cannot be changed anymore.\n\t\tfromComboBox->setEnabled(false);\n\t\ttoLineEdit->setEnabled(false);\n\t\taddressToolButton->setEnabled(false);\t\n\t\t\n\t}\n\t\n\treturn true;\n}\n\n/** Send a text message */\nvoid MessageForm::sendMessage() {\n\tif (msgLineEdit->text().isEmpty()) {\n\t\t// Nothing to send\n\t\treturn;\n\t}\n\t\n\tif (!prepareSendMessage()) {\n\t\treturn;\n\t}\n\t\n    _msgSession->send_msg(msgLineEdit->text().toStdString(), im::TXT_PLAIN);\n}\n\n/**\n  * Send a file.\n  * @param filename [in] Name of file to send.\n  * @param subject [in] Subject to set in the message.\n  */\nvoid MessageForm::sendFile(const QString &filename, const QString &subject) {\n\tif (!prepareSendMessage()) {\n\t\treturn;\n\t}\n\t\n\tt_media media(\"application/octet-stream\");\n\t\n#ifdef HAVE_KDE\n\t// Get mime type for the attachment\n\tKMimeType::Ptr pMime = KMimeType::findByURL(filename);\n\tmedia = t_media(pMime->name().ascii());\n#else\n    string mime_type = mime_database->get_mimetype(filename.toStdString());\n\tif (!mime_type.empty()) {\n\t\tmedia = t_media(mime_type);\n\t}\n#endif\n\n    _msgSession->send_file(filename.toStdString(), media, subject.toStdString());\n}\n\n/**\n  * Add a message to the converstation broswer.\n  * @param msg [in] The message to add.\n  * @param name [in] The name of the sender of the message.\n  */\nvoid MessageForm::addMessage(const im::t_msg &msg, const QString &name)\n{\n\tQString s = \"<b>\";\n\t\n\t// Timestamp and name of sender\n\tif (msg.direction == im::MSG_DIR_IN) s += \"<font color=\\\"blue\\\">\";\n\ts += time2str(msg.timestamp, \"%H:%M:%S \").c_str();\n\ts += name.toHtmlEscaped();\n\tif (msg.direction == im::MSG_DIR_IN) s += \"</font>\";\n\ts += \"</b>\";\n\t\n\t// Subject\n\tif (!msg.subject.empty()) {\n\t\ts += \"<br>\";\n\t\ts += \"<b>\";\n\t\ts += \"Subject: \";\n\t\ts += \"</b>\";\n\t\ts += msg.subject.c_str();\n\t}\n\t\n\t// Text message\n\tif (!msg.message.empty()) {\n\t\ts += \"<br>\";\n\t\tif (msg.format == im::TXT_HTML) {\n\t\t\ts += msg.message.c_str();\n\t\t} else {\n\t\t\ts += QString::fromStdString(msg.message).toHtmlEscaped();\n\t\t}\n\t}\n\t\n\t// Attachment\n\tif (msg.has_attachment) {\n\t\ts += \"<br>\";\n\t\ts += \"<a href=\\\"\";\n\t\ts += QUrl::fromLocalFile(msg.attachment_filename.c_str()).toString();\n\t\ts += \"\\\">\";\n\t\t\n\t\tbool show_attachment_inline = false;\n\t\tbool scale_image = false;\n\t\tint scaled_width = 0, scaled_height = 0;\n\t\t\n\t\tif (msg.attachment_media.type == \"image\") {\n\t\t\t// Show image inline if possible\n\t\t\tQPixmap image (msg.attachment_filename.c_str());\n\t\t\tif (!image.isNull()) {\n\t\t\t\tshow_attachment_inline = true;\n\t\t\t\tif (image.width() > MAX_WIDTH_IMG_INLINE &&\n\t\t\t\t    image.height() > MAX_HEIGHT_IMG_INLINE)\n\t\t\t\t{\n\t\t\t\t\t// Shrink image\n\t\t\t\t\tscaled_width = int(image.width() * IMG_SCALE_FACTOR(image.width(), image.height()));\n\t\t\t\t\tscaled_height = int(image.height() * IMG_SCALE_FACTOR(image.width(), image.height()));\n\t\t\t\t\tscale_image = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\ts += \"<img \";\n\t\t\n\t\tif (scale_image) {\n\t\t\ts += \"width=\";\n\t\t\ts += QString().setNum(scaled_width);\n\t\t\ts += \" height=\";\n\t\t\ts += QString().setNum(scaled_height);\n\t\t\ts += \" \";\n\t\t}\n\t\t     \n\t\ts += \"src=\\\"\";\n\t\t\n\t\tif (show_attachment_inline) {\n\t\t\ts += msg.attachment_filename.c_str();\n\t\t} else {\n\t\t\t// Show an icon representing the attachment\n#ifdef HAVE_KDE\t\t\n\t\t\tKIconLoader iconLoader;\n\t\t\tQString iconName = KMimeType::iconForURL(\n\t\t\t\t\tmsg.attachment_filename.c_str());\n\t\t\ts += iconLoader.iconPath(iconName, KIcon::Desktop);\n#else\n\t\t\t// Set icon based on main mime type\n\t\t\ts += \"mime_\";\n\t\t\ts += msg.attachment_media.type.c_str();\n\t\t\ts += \".png\";\n#endif\n\t\t}\n\t\t\n\t\ts += \"\\\">\";\n\t\ts+= \"<br>\";\n\t\t\n\t\ts += msg.attachment_save_as_name.c_str();\n\t\t\n\t\ts += \"</a> \";\n\t\t\n\t\tif (scale_image) {\n\t\t\ts += \"<br>\";\n\t\t\ts += \"<i>\";\n\t\t\ts += tr(\"image size is scaled down in preview\");\n\t\t\ts += \"</i>\";\n\t\t}\n\t\t\n\t\t// Store the association between the tempory file name of the\n\t\t// save attachment and the suggested save-as file name. When\n\t\t// the user clicks on the attachment, only the temporary file name\n\t\t// is available. Through this association the suggested file name\n\t\t// can be retrieved.\n\t\t_filenameMap[msg.attachment_filename] = msg.attachment_save_as_name;\n\t}\n\n\tconversationBrowser->append(s);\n}\n\nvoid MessageForm::displayError(const QString &errorMsg)\n{\n\tQString s = \"<font color =\\\"red\\\">\";\n\ts += \"<b>\";\n    s += tr(\"Delivery failure\");\n\ts += \": </b>\";\n\ts += errorMsg.toHtmlEscaped();\n\ts += \"</font>\";\n\t\n\tconversationBrowser->append(s);\n}\n\nvoid MessageForm::displayDeliveryNotification(const QString &notification)\n{\n\tQString s = \"<font color =\\\"darkgreen\\\">\";\n\ts += \"<b>\";\n    s += tr(\"Delivery notification\");\n\ts += \": </b>\";\n\ts += notification.toHtmlEscaped();\n\ts += \"</font>\";\n\t\n\tconversationBrowser->append(s);\n}\n\nvoid MessageForm::setRemotePartyCaption(void) {\n\tif (!_msgSession) return;\n\tt_user *user = _msgSession->get_user();\n\tt_display_url remote_party = _msgSession->get_remote_party();\n    setWindowTitle(ui->format_sip_address(user,\n\t\t\tremote_party.display, remote_party.url).c_str());\n}\n\nvoid MessageForm::showAttachmentPopupMenu(const QUrl &attachment) {\n#ifdef HAVE_KDE\n\tvector<KService::Ptr> *serviceMap = (vector<KService::Ptr> *)_serviceMap;\n\t\n\tif (serviceMap) {\n\t\tMEMMAN_DELETE(serviceMap);\n\t\tdelete serviceMap;\n\t}\n\tserviceMap = new vector<KService::Ptr>;\n\tMEMMAN_NEW(serviceMap);\n\t_serviceMap = (void *)serviceMap;\n#endif\n\t\n\tint id = 0; // Identity of popup menu item\n\t\n\t// Store attachment. When the user selects an attachment we still\n\t// know which attachment was clicked.\n\tclickedAttachment = attachment.toLocalFile();\n\t\n\tattachmentPopupMenu->clear();\n\t\n\tQIcon saveIcon(QPixmap(\":/icons/images/save_as.png\"));\n    attachmentPopupMenu->addAction(saveIcon, \"Save as...\")->setData(id++);\n\t\n#ifdef HAVE_KDE\n\t// Get mime type for the attachment\n\tKMimeType::Ptr pMime = KMimeType::findByURL(attachment);\n\t\n\t// Get applications that can open the mime type\n\tKServiceTypeProfile::OfferList services = KServiceTypeProfile::offers(\n\t\t\tpMime->name(), \"Application\");\n\t\n\tKServiceTypeProfile::OfferList::ConstIterator it;\n\tfor (it = services.begin(); it != services.end(); ++it) {\n\t\tKService::Ptr service = (*it).service();\n\t\tserviceMap->push_back(service);\n\t\tQString menuText = tr(\"Open with %1...\").arg(service->name());\n\t\tQPixmap pixmap = service->pixmap(KIcon::Small);\n\t\tQIcon iconSet;\n\t\ticonSet.setPixmap(pixmap, QIcon::Small);\n\t\tattachmentPopupMenu->insertItem(iconSet, menuText, id++);\n\t}\n\t\n\tQIcon openIcon(QPixmap(\":/icons/images/fileopen.png\"));\n\tattachmentPopupMenu->insertItem(openIcon, tr(\"Open with...\"), id++);\n#endif\n\t\n\tattachmentPopupMenu->popup(QCursor::pos(), 0);\n}\n\nvoid MessageForm::attachmentPopupActivated(QAction* act) {\n    int id = act->data().toInt();\n\n#ifdef HAVE_KDE\n\tvector<KService::Ptr> *serviceMap = (vector<KService::Ptr> *)_serviceMap;\n\tassert(serviceMap);\n#endif\n\t\n\tif (id == 0) {\n#ifdef HAVE_KDE\t\t\n\t\tKFileDialog *d = new KFileDialog(QString::null, QString::null, this, 0, true);\n\t\tMEMMAN_NEW(d);\n\t\td->setOperationMode(KFileDialog::Saving);\n\t\t\n\t\tconnect(d, SIGNAL(okClicked()), this,\n\t\t\tSLOT(saveAttachment()));\n#else\n\t\tQFileDialog *d = new QFileDialog(this);\n\t\tMEMMAN_NEW(d);\n\t\t\n\t\tconnect(d, SIGNAL(fileSelected(const QString &)), this,\n\t\t\tSLOT(saveAttachment()));\n#endif\n        d->selectFile(QString::fromStdString(_filenameMap[clickedAttachment.toStdString()]));\n        d->setWindowTitle(tr(\"Save attachment as...\"));\n\t\t\n\t\tif (_saveAsDialog) {\n\t\t\tMEMMAN_DELETE(_saveAsDialog);\n\t\t\tdelete _saveAsDialog;\n\t\t}\n\t\t_saveAsDialog = d;\n\t\t\n\t\td->show();\n#ifdef HAVE_KDE\n\t} else if (id > serviceMap->size()) {\n\t\tKURL::List urls;\n\t\turls << clickedAttachment;\n\t\tKRun::displayOpenWithDialog(urls, false);\n\t} else {\n\t\tKURL::List urls;\n\t\turls << clickedAttachment;\n\t\tKRun::run(*serviceMap->at(id-1), urls);\n#endif\n\t}\n}\n\nvoid MessageForm::saveAttachment() {\n#ifdef HAVE_KDE\n\tKFileDialog *d = dynamic_cast<KFileDialog *>(_saveAsDialog);\n#else\n\tQFileDialog *d = dynamic_cast<QFileDialog *>(_saveAsDialog);\n#endif\n    QStringList files = d->selectedFiles();\n    QString filename;\n\n    if (!files.empty())\n        filename = files[0];\n\t\n\tif (QFile::exists(filename)) {\n\t\tbool overwrite = ((t_gui *)ui)->cb_ask_msg(this, \n                  tr(\"File already exists. Do you want to overwrite this file?\").toStdString(),\n\t\t\t\t  MSG_WARNING);\n\t\t\n\t\tif (!overwrite) return;\n\t}\n\t\n    if (!filecopy(clickedAttachment.toStdString(), filename.toStdString())) {\n        ((t_gui *)ui)->cb_show_msg(this, tr(\"Failed to save attachment.\").toStdString(),\n\t\t\t\t\t   MSG_CRITICAL);\n\t}\n}\n\n/** Choose a file to send */\nvoid MessageForm::chooseFileToSend()\n{\n\t// Indicate that a message is being composed.\n\tsetLocalComposingIndicationActive();\n\t\n\tSendFileForm *form = new SendFileForm(this);\n\tMEMMAN_NEW(form);\n\t// Form will auto destruct itself on close.\n\t\n\tconnect(form, SIGNAL(selected(const QString &, const QString &)),\n\t\tthis, SLOT(sendFile(const QString &, const QString &)));\n\t\n\tform->show();\n}\n\n/** \n * Show an is-composing indication in the status bar.\n * @param name [in] The name of the sender of the message.\n */\nvoid MessageForm::setComposingIndication(const QString &name)\n{\n\n\tif (!_isComposingLabel) {\n\t\t_isComposingLabel = new QLabel(NULL);\n\t\tMEMMAN_NEW(_isComposingLabel);\n\t\t\n\t\t_isComposingLabel->setText(tr(\"%1 is typing a message.\").arg(name));\n\t\t_isComposingLabel->setFrameStyle(QFrame::NoFrame | QFrame::Plain);\n\t\tstatusBar()->addWidget(_isComposingLabel);\n\t}\n}\n\n/** Clear an is-composing indication from the status bar. */\nvoid MessageForm::clearComposingIndication()\n{\n\tif (_isComposingLabel) {\n\t\tstatusBar()->removeWidget(_isComposingLabel);\n        statusBar()->clearMessage();\n\t\n\t\tMEMMAN_DELETE(_isComposingLabel);\n\t\tdelete _isComposingLabel;\n\t\t_isComposingLabel = NULL;\n\t}\n}\n\n/** Set the local composition indication to active. */\nvoid MessageForm::setLocalComposingIndicationActive()\n{\n\t_msgSession->set_local_composing_state(im::COMPOSING_STATE_ACTIVE);\n}\n\nvoid MessageForm::keyPressEvent(QKeyEvent *e)\n{\n\tswitch (e->key()) {\n\tcase Qt::Key_Return:\n\tcase Qt::Key_Enter:\n\t\tif (sendPushButton->isEnabled()) {\n\t\t\tsendPushButton->animateClick();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\te->ignore();\n\t}\n}\n\nvoid MessageForm::toAddressChanged(const QString &address)\n{\n\tsendFileAction->setEnabled(!address.isEmpty());\n}\n\n/** Show the size of the typed message */\nvoid MessageForm::showMessageSize()\n{\n\tunsigned int len = msgLineEdit->text().length();\n\t\n\tQString s(tr(\"Size\"));\n\ts += \": \";\n\ts += QString().setNum(len);\n\t\n\t_msgSizeLabel->setText(s);\n}\n"
  },
  {
    "path": "src/gui/messageform.h",
    "content": "#ifndef MESSAGEFORM_H\n#define MESSAGEFORM_H\n#include \"getaddressform.h\"\n#include \"im/msg_session.h\"\n#include \"phone.h\"\n#include <QLabel>\n#include <QtCore/QStringRef>\n#include \"user.h\"\n#include \"ui_messageform.h\"\n#include <QMainWindow>\n#include <QMenu>\n\nclass t_phone;\nextern t_phone *phone;\n\nclass MessageForm : public QMainWindow, public Ui::MessageForm\n{\n\tQ_OBJECT\n\npublic:\n    MessageForm(QWidget* parent = 0);\n\t~MessageForm();\n\n\tvirtual bool updateMessageSession();\n\tvirtual bool prepareSendMessage();\n\npublic slots:\n\tvirtual void closeEvent( QCloseEvent * e );\n\tvirtual void show();\n\tvirtual void selectUserConfig( t_user * user_config );\n\tvirtual void showAddressBook();\n\tvirtual void selectedAddress( const QString & address );\n\tvirtual void sendMessage();\n\tvirtual void sendFile( const QString & filename, const QString & subject );\n\tvirtual void addMessage( const im::t_msg & msg, const QString & name );\n\tvirtual void displayError( const QString & errorMsg );\n\tvirtual void displayDeliveryNotification( const QString & notification );\n\tvirtual void setRemotePartyCaption( void );\n\tvirtual void showAttachmentPopupMenu( const QUrl & attachment );\n    virtual void attachmentPopupActivated( QAction* action );\n\tvirtual void saveAttachment();\n\tvirtual void chooseFileToSend();\n\tvirtual void setComposingIndication( const QString & name );\n\tvirtual void clearComposingIndication();\n\tvirtual void setLocalComposingIndicationActive();\n\tvirtual void keyPressEvent( QKeyEvent * e );\n\tvirtual void toAddressChanged( const QString & address );\n\tvirtual void showMessageSize();\n\nprotected:\n\tim::t_msg_session *_msgSession;\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tQDialog *_saveAsDialog;\n\tmap<string, string> _filenameMap;\n\tbool _remotePartyComplete;\n\tGetAddressForm *_getAddressForm;\n\tQMenu *attachmentPopupMenu;\n\tQString clickedAttachment;\n\tvoid *_serviceMap;\n\tQLabel *_isComposingLabel;\n\tQLabel *_msgSizeLabel;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/messageform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>MessageForm</class>\n  <widget class=\"QMainWindow\" name=\"MessageForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>578</width>\n        <height>356</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Instant message</string>\n    </property>\n    <widget class=\"QWidget\">\n      <layout class=\"QVBoxLayout\">\n        <item>\n          <layout class=\"QGridLayout\">\n            <item row=\"1\" column=\"0\">\n              <widget class=\"QLabel\" name=\"toTextLabel\">\n                <property name=\"text\">\n                  <string>&amp;To:</string>\n                </property>\n                <property name=\"buddy\" stdset=\"0\">\n                  <cstring>toLineEdit</cstring>\n                </property>\n                <property name=\"wordWrap\">\n                  <bool>false</bool>\n                </property>\n              </widget>\n            </item>\n            <item row=\"0\" column=\"1\">\n              <widget class=\"QComboBox\" name=\"fromComboBox\">\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>The user that will send the message.</string>\n                </property>\n              </widget>\n            </item>\n            <item row=\"1\" column=\"1\">\n              <layout class=\"QHBoxLayout\">\n                <item>\n                  <widget class=\"QLineEdit\" name=\"toLineEdit\">\n                    <property name=\"whatsThis\" stdset=\"0\">\n                      <string>The address of the user that you want to send a message. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</string>\n                    </property>\n                  </widget>\n                </item>\n                <item>\n                  <widget class=\"QToolButton\" name=\"addressToolButton\">\n                    <property name=\"focusPolicy\">\n                      <enum>Qt::TabFocus</enum>\n                    </property>\n                    <property name=\"text\">\n                      <string/>\n                    </property>\n                    <property name=\"shortcut\">\n                      <string>F10</string>\n                    </property>\n                    <property name=\"icon\">\n                      <iconset>:/icons/images/kontact_contacts.png</iconset>\n                    </property>\n                    <property name=\"toolTip\" stdset=\"0\">\n                      <string>Address book</string>\n                    </property>\n                    <property name=\"whatsThis\" stdset=\"0\">\n                      <string>Select an address from the address book.</string>\n                    </property>\n                  </widget>\n                </item>\n              </layout>\n            </item>\n            <item row=\"0\" column=\"0\">\n              <widget class=\"QLabel\" name=\"profileTextLabel\">\n                <property name=\"text\">\n                  <string>&amp;User profile:</string>\n                </property>\n                <property name=\"buddy\" stdset=\"0\">\n                  <cstring>fromComboBox</cstring>\n                </property>\n                <property name=\"wordWrap\">\n                  <bool>false</bool>\n                </property>\n              </widget>\n            </item>\n          </layout>\n        </item>\n        <item>\n          <widget class=\"QGroupBox\" name=\"conversationGroupBox\">\n            <property name=\"title\">\n              <string>Conversation</string>\n            </property>\n            <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\">\n                <widget class=\"QTextBrowser\" name=\"conversationBrowser\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy vsizetype=\"Expanding\" hsizetype=\"Expanding\" />\n                 </property>\n                <property name=\"openLinks\">\n                  <bool>false</bool>\n                </property>\n                </widget>\n              </item>\n            </layout>\n          </widget>\n        </item>\n        <item>\n          <layout class=\"QHBoxLayout\">\n            <item>\n              <widget class=\"QLineEdit\" name=\"msgLineEdit\">\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>Type your message here and then press &quot;send&quot; to send it.</string>\n                </property>\n              </widget>\n            </item>\n            <item>\n              <widget class=\"QPushButton\" name=\"sendPushButton\">\n                <property name=\"text\">\n                  <string>&amp;Send</string>\n                </property>\n                <property name=\"shortcut\">\n                  <string>Alt+S</string>\n                </property>\n                <property name=\"autoDefault\">\n                  <bool>false</bool>\n                </property>\n                <property name=\"default\">\n                  <bool>true</bool>\n                </property>\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>Send the message.</string>\n                </property>\n              </widget>\n            </item>\n          </layout>\n        </item>\n      </layout>\n    </widget>\n    <widget class=\"QToolBar\" name=\"Toolbar\">\n      <addaction name=\"sendFileAction\"/>\n    </widget>\n    <action name=\"sendFileAction\">\n      <property name=\"icon\">\n        <iconset>:/icons/images/attach.png</iconset>\n      </property>\n      <property name=\"iconText\">\n        <string>Send file...</string>\n      </property>\n      <property name=\"toolTip\">\n        <string>Send file</string>\n      </property>\n    </action>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <includes>\n    <include location=\"local\">ui_getaddressform.h</include>\n    <include location=\"local\">qstring.h</include>\n    <include location=\"local\">user.h</include>\n    <include location=\"local\">im/msg_session.h</include>\n    <include location=\"local\">phone.h</include>\n    <include location=\"local\">qlabel.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>sendPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>MessageForm</receiver>\n      <slot>sendMessage()</slot>\n    </connection>\n    <connection>\n      <sender>addressToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>MessageForm</receiver>\n      <slot>showAddressBook()</slot>\n    </connection>\n    <connection>\n      <sender>conversationBrowser</sender>\n      <signal>anchorClicked(QUrl)</signal>\n      <receiver>MessageForm</receiver>\n      <slot>showAttachmentPopupMenu(QUrl)</slot>\n    </connection>\n    <connection>\n      <sender>sendFileAction</sender>\n      <signal>triggered()</signal>\n      <receiver>MessageForm</receiver>\n      <slot>chooseFileToSend()</slot>\n    </connection>\n    <connection>\n      <sender>toLineEdit</sender>\n      <signal>textChanged(QString)</signal>\n      <receiver>MessageForm</receiver>\n      <slot>toAddressChanged(QString)</slot>\n    </connection>\n    <connection>\n      <sender>msgLineEdit</sender>\n      <signal>textChanged(QString)</signal>\n      <receiver>MessageForm</receiver>\n      <slot>showMessageSize()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/messageformview.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"messageformview.h\"\n\n#include \"gui.h\"\n\n#include \"audits/memman.h\"\n\nMessageFormView::MessageFormView(QWidget *parent, im::t_msg_session *s) :\n\t\tMessageForm(parent),\n\t\tt_observer()\n{\n\t_msgSession = s;\n\t_msgSession->attach(this);\n\t_destructing = false;\n\n\tconnect(this, SIGNAL(update_signal()), this, SLOT(update_slot()));\n}\n\nMessageFormView::~MessageFormView()\n{\n\t_destructing = true;\n\tif (_msgSession) {\n\t\t((t_gui *)ui)->removeMessageSession(_msgSession);\n\t\tMEMMAN_DELETE(_msgSession);\n\t\tdelete _msgSession;\n\t}\n}\n\nvoid MessageFormView::updatePartyInfo(void)\n{\n\tt_user *user_config = _msgSession->get_user();\n\tselectUserConfig(user_config);\n\tt_display_url to_url = _msgSession->get_remote_party();\n\t\n\tif (to_url.is_valid()) {\n\t\ttoLineEdit->setText(ui->format_sip_address(user_config, to_url.display, to_url.url).c_str());\n\t} else {\n\t\ttoLineEdit->clear();\n\t}\n}\n\nvoid MessageFormView::update_slot(void) {\n\t// Called directly from core, so lock GUI\n\tui->lock();\n\t\n\tupdatePartyInfo();\n\tsetRemotePartyCaption();\n\t\n\tt_user *user_config = _msgSession->get_user();\n\tt_display_url to_url = _msgSession->get_remote_party();\n\t\n\t// Update msgLineEdit field based on msg-in-flight indication\n\tif (!_msgSession->is_msg_in_flight() && !msgLineEdit->isEnabled()) {\n\t\tmsgLineEdit->clear();\n\t\t\n\t\t// When the user edits the message, the composition indication\n\t\t// will be set to active.\n\t\tconnect(msgLineEdit, SIGNAL(textChanged(const QString &)),\t\t\n\t\t\tthis, SLOT(setLocalComposingIndicationActive()));\n\t\t\n\t\t// Enable msgLineEdit first, otherwise the setFocus does not work\n\t\tmsgLineEdit->setEnabled(true);\n\t\t\n\t\tmsgLineEdit->setFocus();\n\t} else if (_msgSession->is_msg_in_flight() && msgLineEdit->isEnabled()) {\n\t\t// Disable the triggering of the composition indication while a message\n\t\t// is being sent.\n\t\tdisconnect(msgLineEdit, SIGNAL(textChanged(const QString &)),\n\t\t\t   this, SLOT(setLocalComposingIndicationActive()));\n\t\tmsgLineEdit->setText(tr(\"sending message\"));\n\t}\n\t\n\t// Enable/disable msgLineEdit here to be robust, such that msgLineEdit\n\t// does not stay disabled forever.\n\tmsgLineEdit->setEnabled(!_msgSession->is_msg_in_flight());\n\t\n\tsendFileAction->setEnabled(!_msgSession->is_msg_in_flight());\n\t\n\t// Display error\n\tif (_msgSession->error_received()) {\n\t\tstring error_msg = _msgSession->take_error();\n\t\tdisplayError(error_msg.c_str());\n\t}\n\t\n\t// Display delivery notification\n\tif (_msgSession->delivery_notification_received()) {\n\t\tstring notification = _msgSession->take_delivery_notification();\n\t\tdisplayDeliveryNotification(notification.c_str());\n\t}\n\t\n\t// Display message composing indication\n\tif (_msgSession->get_remote_composing_state() == im::COMPOSING_STATE_ACTIVE) {\n\t\tQString name = to_url.display.c_str();\n\t\tif (name.isEmpty()) {\n\t\t\tname = to_url.url.get_user().c_str();\n\t\t}\n\t\t\n\t\tsetComposingIndication(name);\n\t} else {\n\t\tclearComposingIndication();\n\t}\n\t\n\t// Display message\n\tif (_msgSession->is_new_message_added()) {\n\t\tim::t_msg m;\n\t\ttry {\n\t\t\tm = _msgSession->get_last_message();\n\t\t} catch (empty_list_exception) {\n\t\t\tui->unlock();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tQString name;\n\t\tif (m.direction == im::MSG_DIR_IN) {\n\t\t\tname = to_url.display.c_str();\n\t\t\tif (name.isEmpty()) {\n\t\t\t\tname = to_url.url.get_user().c_str();\n\t\t\t}\n\t\t} else {\n\t\t\tname = user_config->get_display(false).c_str();\n\t\t\tif (name.isEmpty()) {\n\t\t\t\tname = user_config->get_name().c_str();\n\t\t\t}\n\t\t}\n\t\t\n\t\taddMessage(m, name);\n\t}\n\t\n\tui->unlock();\n}\n\nvoid MessageFormView::update(void) {\n\temit update_signal();\n}\n\nvoid MessageFormView::subject_destroyed()\n{\n\t_msgSession = NULL;\n\t\n\tif (!_destructing) close();\n}\n\nvoid MessageFormView::show()\n{\n\t((t_gui *)ui)->fill_user_combo(fromComboBox);\n\tupdatePartyInfo();\n\tMessageForm::show();\n}\n"
  },
  {
    "path": "src/gui/messageformview.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _MESSAGEFORMVIEW_H\n#define _MESSAGEFORMVIEW_H\n\n#include \"messageform.h\"\n\n#include \"im/msg_session.h\"\n#include \"patterns/observer.h\"\n\nclass MessageFormView : public MessageForm, patterns::t_observer\n{\t\n\tQ_OBJECT\nprivate:\n\tbool _destructing; /**< Indicates if object is being destructed. */\npublic:\n\tMessageFormView(QWidget *parent, im::t_msg_session *s);\n\tvirtual ~MessageFormView();\n\tvirtual void updatePartyInfo(void);\n\t\n\t/** Update the message form with the latest message session state. */\n\tvirtual void update(void);\n\t\n\tvirtual void subject_destroyed(void);\n\tvirtual void show(void);\n\nsignals:\n\tvoid update_signal();\n\nprivate slots:\n\tvoid update_slot();\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/mphoneform.cpp",
    "content": "/****************************************************************************\n** ui.h extension file, included from the uic-generated form implementation.\n**\n** If you wish to add, delete or rename functions or slots use\n** Qt Designer which will update this file, pres:erving your code. Ceate an\n** init() function in place of a constructor, and a destroy() function in\n** place of a destructor.\n*****************************************************************************/\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n#include \"mphoneform.h\"\n#include \"twinkle_config.h\"\n#include <QWhatsThis>\n#include <QLabel>\n#include <QPixmap>\n#include <QMouseEvent>\n#include <QCloseEvent>\n#include <QKeyEvent>\n#include <QtDebug>\n#include <QEvent>\n#include <QFrame>\n#include \"../audits/memman.h\"\n#include \"user.h\"\n#include <QTextEdit>\n#include <QCheckBox>\n#include <QApplication>\n#include <QDesktopWidget>\n#include \"gui.h\"\n#include <QPixmap>\n#include <QIcon>\n#include <QMessageBox>\n#include \"audits/memman.h\"\n#include \"line.h\"\n#include \"stun/stun_transaction.h\"\n#include \"log.h\"\n#include <QProgressDialog>\n#include \"util.h\"\n#include <QTimer>\n#include <QCursor>\n#include <QRegularExpression>\n#include <QValidator>\n#include <QSettings>\n#include <QRegularExpressionValidator>\n#include \"buddyform.h\"\n#include \"diamondcardprofileform.h\"\n#include \"osd.h\"\n#include \"incoming_call_popup.h\"\n\nextern QSettings*      g_gui_state;\n\n// Time (s) that the conversation timer of a line should stay visible after\n// a call has ended\n#define HIDE_LINE_TIMER_AFTER\t5\n\nMphoneForm::MphoneForm(QWidget* parent)\n    : QMainWindow(parent)\n{\n\tsetupUi(this);\n\tinit();\n}\n\nMphoneForm::~MphoneForm()\n{\n\tdestroy();\n}\n\nvoid MphoneForm::init()\n{\n\t// Forms\n\tdtmfForm = 0;\n\tinviteForm = 0;\n\tredirectForm = 0;\n\ttransferForm = 0;\n\ttermCapForm = 0;\n\tsrvRedirectForm = 0;\n\tuserProfileForm = 0;\n\tsysSettingsForm = 0;\n\tlogViewForm = 0;\n\thistoryForm = 0;\n\tselectUserForm = 0;\n\tselectProfileForm = 0;\n\tgetAddressForm = 0;\n\tsysTray = 0;\n\t\n\t// Popup menu for a single buddy\n\tQIcon inviteIcon(QPixmap(\":/icons/images/invite.png\"));\n\tQIcon messageIcon(QPixmap(\":/icons/images/message.png\"));\n\tQIcon editIcon(QPixmap(\":/icons/images/edit16.png\"));\n\tQIcon deleteIcon(QPixmap(\":/icons/images/editdelete.png\"));\n    buddyPopupMenu = new QMenu(this);\n\tMEMMAN_NEW(buddyPopupMenu);\n    buddyPopupMenu->addAction(inviteIcon, tr(\"&Call...\"), this, SLOT(doCallBuddy()));\n    buddyPopupMenu->addAction(messageIcon, tr(\"Instant &message...\"), this, SLOT(doMessageBuddy()));\n    buddyPopupMenu->addAction(editIcon, tr(\"&Edit...\"), this, SLOT(doEditBuddy()));\n    buddyPopupMenu->addAction(deleteIcon, tr(\"&Delete\"), this, SLOT(doDeleteBuddy()));\n\t\n\t// Popup menu for a buddy list (click on profile name)\n\tQIcon changeAvailabilityIcon(QPixmap(\":/icons/images/presence_online.png\"));\n\tQIcon addIcon(QPixmap(\":/icons/images/buddy.png\"));\n    buddyListPopupMenu = new QMenu(this);\n\tMEMMAN_NEW(buddyListPopupMenu);\n    changeAvailabilityPopupMenu = buddyListPopupMenu->addMenu(changeAvailabilityIcon, tr(\"&Change availability\"));\n\n    // Change availibility sub popup menu\n    QIcon availOnlineIcon(QPixmap(\":/icons/images/presence_online.png\"));\n    QIcon availOfflineIcon(QPixmap(\":/icons/images/presence_offline.png\"));\n    changeAvailabilityPopupMenu->addAction(availOfflineIcon, tr(\"O&ffline\"), this,\n                        SLOT(doAvailabilityOffline()));\n    changeAvailabilityPopupMenu->addAction(availOnlineIcon, tr(\"&Online\"), this,\n                        SLOT(doAvailabilityOnline()));\n\n    buddyListPopupMenu->addAction(addIcon, tr(\"&Add buddy...\"), this, SLOT(doAddBuddy()));\n\t\n\t// ToDo: Tool tip for buddy list\n\t\n\t// Line timers\n\tlineTimer1 = 0;\n\tlineTimer2 = 0;\n\ttimer1TextLabel->hide();\n\ttimer2TextLabel->hide();\n\t\n\t// Timer to hide the conversation timer after a conversation has ended.\n\thideLineTimer1 = new QTimer(this);\n\tMEMMAN_NEW(hideLineTimer1);\n\thideLineTimer2 = new QTimer(this);\n\tMEMMAN_NEW(hideLineTimer2);\n\tconnect(hideLineTimer1, SIGNAL(timeout()), timer1TextLabel, SLOT(hide()));\n\tconnect(hideLineTimer2, SIGNAL(timeout()), timer2TextLabel, SLOT(hide()));\n\t\n\t// Attach the MWI flash slot to the MWI flash timer\n\tconnect(&tmrFlashMWI, SIGNAL(timeout()), this, SLOT(flashMWI()));\n\t\n\t// Add \"Main Toolbar\" entry to the View menu\n\tView->insertAction(nullptr, callToolbar->toggleViewAction());\n\n\t// Set toolbar icons for disabled options.\n\tsetDisabledIcon(callInvite, \"invite-disabled.png\");\n\tsetDisabledIcon(callAnswer, \"answer-disabled.png\");\n\tsetDisabledIcon(callBye, \"bye-disabled.png\");\n\tsetDisabledIcon(callReject, \"reject-disabled.png\");\n\tsetDisabledIcon(callRedirect, \"redirect-disabled.png\");\n\tsetDisabledIcon(callTransfer, \"transfer-disabled.png\");\n\tsetDisabledIcon(callHold, \"hold-disabled.png\");\n\tsetDisabledIcon(callConference, \"conf-disabled.png\");\n\tsetDisabledIcon(callMute, \"mute-disabled.png\");\n\tsetDisabledIcon(callDTMF, \"dtmf-disabled.png\");\n\tsetDisabledIcon(callRedial, \"redial-disabled.png\");\n\t\n\t// Set tool button icons for disabled options\n\tsetDisabledIcon(addressToolButton, \"kontact_contacts-disabled.png\");\n\t\n\t// Some text labels on the main window are implemented as QLineEdit\n\t// objects as these do not automatically resize when a text set with setText\n\t// does not fit. The background of a QLineEdit is static however, it does not\n\t// automatically take a background color passed by the -bg parameter.\n\t// Set the background color of these QLineEdit objects here.\n    //from1Label->setPaletteBackgroundColor(paletteBackgroundColor());\n    //to1Label->setPaletteBackgroundColor(paletteBackgroundColor());\n    //subject1Label->setPaletteBackgroundColor(paletteBackgroundColor());\n    //from2Label->setPaletteBackgroundColor(paletteBackgroundColor());\n    //to2Label->setPaletteBackgroundColor(paletteBackgroundColor());\n    //subject2Label->setPaletteBackgroundColor(paletteBackgroundColor());\n\n\ttry {\n\t\tosdWindow = new OSD(this);\n\t\tconnect(osdWindow, SIGNAL(hangupClicked()), this, SLOT(phoneBye()));\n\t\tconnect(osdWindow, SIGNAL(muteClicked()), this, SLOT(osdMuteClicked()));\n\t} catch (const std::exception &e) {\n\t\tosdWindow = nullptr;\n\t\tqWarning() << e.what();\n\t}\n\n\ttry {\n\t\tincomingCallPopup = new IncomingCallPopup(this);\n\t\tconnect(incomingCallPopup, SIGNAL(answerClicked()), this, SLOT(phoneAnswer()));\n\t\tconnect(incomingCallPopup, SIGNAL(rejectClicked()), this, SLOT(phoneReject()));\n\t} catch (const std::exception &e) {\n\t\tincomingCallPopup = nullptr;\n\t\tqWarning() << e.what();\n\t}\n\t\n\t// A QComboBox accepts a new line through copy/paste.\n\tQRegularExpression rxNoNewLine(\"[^\\\\n\\\\r]*\");\n\tcallComboBox->setValidator(new QRegularExpressionValidator(rxNoNewLine, this));\n\t\n\tif (sys_config->get_gui_use_systray()) {\n\t\t// Create system tray icon\n\t\tsysTray = new QSystemTrayIcon(this);\n\n\t\tsysTray->setIcon(\n\t\t\t\tQPixmap(\":/icons/images/sys_idle_dis.png\"));\n\t\tsysTray->setToolTip(PRODUCT_NAME);\n\t\t\n\t\t// Add items to the system tray menu\n\t\tQMenu *menu;\n\n\t\tmenu = new QMenu(this);\n\t\tsysTray->setContextMenu(menu);\n\n\t\tconnect(sysTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),\n\t\t\t\tthis, SLOT(sysTrayIconClicked(QSystemTrayIcon::ActivationReason)));\n\t\tconnect(menu, &QMenu::aboutToShow, this, &MphoneForm::updateTrayIconMenu);\n\n\t\t// Toggle window visibility\n\t\tmenu->addAction(toggleWindowAction);\n\n\t\tmenu->addSeparator();\n\t\t\n\t\t// Call menu\n        menu->addAction(callInvite);\n        menu->addAction(callAnswer);\n        menu->addAction(callBye);\n        menu->addAction(callReject);\n        menu->addAction(callRedirect);\n        menu->addAction(callTransfer);\n        menu->addAction(callHold);\n        menu->addAction(callConference);\n        menu->addAction(callMute);\n        menu->addAction(callDTMF);\n        menu->addAction(callRedial);\n\t\t\n        menu->addSeparator();\n\t\t\n\t\t// Messaging\n        menu->addAction(actionSendMsg);\n\t\t\n        menu->addSeparator();\n\t\t\n\t\t// Line activation\n        menu->addActions(actgrActivateLine->actions());\n\t\t\n        menu->addSeparator();\n\t\t\n\t\t// Service menu\n        menu->addAction(serviceDnd);\n        menu->addAction(serviceRedirection);\n        menu->addAction(serviceAutoAnswer);\n        menu->addAction(servicesVoice_mailAction);\n\t\t\n        menu->addSeparator();\n\t\t\n\t\t// View menu\n        menu->addAction(viewCall_HistoryAction);\n\t\t\n        menu->addSeparator();\n\t\t\n#ifdef WITH_DIAMONDCARD\n\t\t// Diamondcard menu\n\t\tmenu->addMenu(Diamondcard);\n        menu->addSeparator();\n#endif\n\t\t\n        menu->addAction(fileExitAction);\n\t\t\n\t\tsysTray->show();\n\t}\n\n#ifndef WITH_DIAMONDCARD\n\tDiamondcard->menuAction()->setVisible(false);\n#endif\n\n\trestoreState(g_gui_state->value(\"mainwindow/state\").toByteArray());\n\trestoreGeometry(g_gui_state->value(\"mainwindow/geometry\").toByteArray());\n\tlayoutWidgetSplitter->restoreState(g_gui_state->value(\"mainwindow/mainsplitter\").toByteArray());\n}\n\nvoid MphoneForm::destroy()\n{\n\tg_gui_state->setValue(\"mainwindow/state\", saveState());\n\tg_gui_state->setValue(\"mainwindow/geometry\", saveGeometry());\n\tg_gui_state->setValue(\"mainwindow/mainsplitter\", layoutWidgetSplitter->saveState());\n\n\tif (dtmfForm) {\n\t\tMEMMAN_DELETE(dtmfForm);\n\t\tdelete dtmfForm;\n\t}\n\tif (inviteForm) {\n\t\tMEMMAN_DELETE(inviteForm);\n\t\tdelete inviteForm;\n\t}\n\tif (redirectForm) {\n\t\tMEMMAN_DELETE(redirectForm);\n\t\tdelete redirectForm;\n\t}\n\tif (termCapForm) {\n\t\tMEMMAN_DELETE(termCapForm);\n\t\tdelete termCapForm;\n\t}\n\tif (srvRedirectForm) {\n\t\tMEMMAN_DELETE(srvRedirectForm);\n\t\tdelete srvRedirectForm;\n\t}\n\tif (userProfileForm) {\n\t\tMEMMAN_DELETE(userProfileForm);\n\t\tdelete userProfileForm;\n\t}\n\tif (transferForm) {\n\t\tMEMMAN_DELETE(transferForm);\n\t\tdelete transferForm;\n\t}\n\tif (sysSettingsForm) {\n\t\tMEMMAN_DELETE(sysSettingsForm);\n\t\tdelete sysSettingsForm;\n\t}\n\tif (logViewForm) {\n        if (logViewForm->isVisible()) logViewForm->close();\n\t\tMEMMAN_DELETE(logViewForm);\n\t\tdelete logViewForm;\n\t}\n\tif (historyForm) {\n        if (historyForm->isVisible()) historyForm->close();\n\t\tMEMMAN_DELETE(historyForm);\n\t\tdelete historyForm;\n\t}\n\tif (selectUserForm) {\n\t\tMEMMAN_DELETE(selectUserForm);\n\t\tdelete selectUserForm;\n\t}\n\tif (selectProfileForm) {\n\t\tMEMMAN_DELETE(selectProfileForm);\n\t\tdelete selectProfileForm;\n\t}\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n\tif (sysTray) {\n\t\tMEMMAN_DELETE(sysTray);\n\t\tdelete sysTray;\n\t}\n\t\n\tif (lineTimer1) {\n\t\tMEMMAN_DELETE(lineTimer1);\n\t\tdelete lineTimer1;\n\t}\n\tif (lineTimer2) {\n\t\tMEMMAN_DELETE(lineTimer2);\n\t\tdelete lineTimer2;\n\t}\n\tMEMMAN_DELETE(hideLineTimer1);\n\tdelete hideLineTimer1;\n\tMEMMAN_DELETE(hideLineTimer2);\n\tdelete hideLineTimer2;\n\tMEMMAN_DELETE(buddyPopupMenu);\n\tdelete buddyPopupMenu;\n\tMEMMAN_DELETE(buddyListPopupMenu);\n\tdelete buddyListPopupMenu;\n}\n\nQString MphoneForm::lineSubstate2str( int line) {\n\tQString reason;\n\t\n\tt_call_info call_info = phone->get_call_info(line);\n\t\n\tswitch(phone->get_line_substate(line)) {\n\tcase LSSUB_IDLE:\t\n\t\treturn tr(\"idle\");\n\tcase LSSUB_SEIZED:\n\t\treturn tr(\"dialing\");\n\tcase LSSUB_OUTGOING_PROGRESS:\n\t\treason = call_info.last_provisional_reason.c_str();\n\t\tif (reason == \"\") {\n\t\t\treturn tr(\"attempting call, please wait\");\n\t\t}\n\t\treturn reason;\n\tcase LSSUB_INCOMING_PROGRESS:\n\t\treturn QString(\"<font color=red>\") + tr(\"incoming call\") + \"</font>\";\n\tcase LSSUB_ANSWERING:\n\t\treturn tr(\"establishing call, please wait\");\n\tcase LSSUB_ESTABLISHED:\n\t\tif (phone->has_line_media(line)) {\n\t\t\treturn tr(\"established\");\n\t\t} else {\n\t\t\treturn tr(\"established (waiting for media)\");\n\t\t}\n\t\tbreak;\n\tcase LSSUB_RELEASING:\n\t\treturn tr(\"releasing call, please wait\");\n\tdefault:\n\t\treturn tr(\"unknown state\");\n\t}\n}\n\nvoid MphoneForm::closeEvent( QCloseEvent *e )\n{\n\tif (sysTray && sys_config->get_gui_hide_on_close()) {\n\t\thide();\n\t} else {\n\t\tfileExit();\n\t}\n}\n\nvoid MphoneForm::fileExit()\n{\n\thide();\n\tqApp->quit();\n}\n\n// Append a string to the display window\nvoid MphoneForm::display( const QString &s )\n{\n\tdisplayContents.push_back(s);\n\tif (displayContents.size() > 100) {\n\t\tdisplayContents.pop_front();\n\t}\n\t\n\tdisplayTextEdit->setText(displayContents.join(\"\\n\"));\n\t\n\t// Set cursor position at the end of text\n    QTextCursor cursor;\n    cursor = displayTextEdit->textCursor();\n    cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);\n    displayTextEdit->setTextCursor(cursor);\n}\n\n// Print message header on display\nvoid MphoneForm::displayHeader()\n{\n\tdisplay(\"\");\n\tdisplay(current_time2str(\"%a %H:%M:%S\").c_str());\n}\n\n// Update the conversation timer\nvoid MphoneForm::showLineTimer(int line)\n{\n\tstruct timeval t;\n\tgettimeofday(&t, NULL);\n\t\n\tQLabel *timerLabel;\n\t\n\tif (line == 0) {\n\t\ttimerLabel = timer1TextLabel;\n\t} else {\n\t\ttimerLabel = timer2TextLabel;\n\t}\n\t\n\t// Calculate duration of call\n\tt_call_record cr = phone->get_call_hist(line);\n\tunsigned long duration = t.tv_sec - cr.time_answer;\n\n\ttimerLabel->setText(timer2str(duration).c_str());\n\tupdateOSD();\n}\n\nvoid MphoneForm::showLineTimer1()\n{\n\tshowLineTimer(0);\n}\n\nvoid MphoneForm::showLineTimer2()\n{\n\tshowLineTimer(1);\n}\n\n// Update visibility of the conversation timer for a line\n// Initialize the timer for a new established call.\nvoid MphoneForm::updateLineTimer(int line)\n{\n\tQLabel *timerLabel;\n\tQTimer **timer;\n\tQTimer *hideLineTimer;\n\t\n\tif (line == 0) {\n\t\ttimerLabel = timer1TextLabel;\n\t\ttimer = &lineTimer1;\n\t\thideLineTimer = hideLineTimer1;\n\t} else {\n\t\ttimerLabel = timer2TextLabel;\n\t\ttimer = &lineTimer2;\n\t\thideLineTimer = hideLineTimer2;\n\t}\n\t\n\tt_line_substate line_substate = phone->get_line_substate(line);\n\t\n\t// Stop hide timer if necessary\n\tswitch(line_substate) {\n\tcase LSSUB_IDLE:\n\tcase LSSUB_RELEASING:\n\t\t// Timer can be shown as long as line is idle or releasing.\n\t\tbreak;\n\tdefault:\n\t\t// The timer showing the call duration should only stay\n\t\t// for a few seconds as long as the line is idle or being\n\t\t// released.\n\t\t// If a new call arrives on the line, the hide timer should\n\t\t// be stopped, otherwise the timer of the new call will\n\t\t// automatically disappear.\n\t\tif (hideLineTimer->isActive()) {\n\t\t\thideLineTimer->stop();\n\t\t\tif (*timer == NULL) timerLabel->hide();\n\t\t}\n\t\tbreak;\n\t}\n\t\n\tswitch(line_substate) {\n\tcase LSSUB_ESTABLISHED:\n\t\t// Initialize and show call duration timer\n\t\tif (*timer == NULL) {\n\t\t\ttimerLabel->setText(timer2str(0).c_str());\n\t\t\ttimerLabel->show();\n\t\t\t*timer = new QTimer(this);\n\t\t\tMEMMAN_NEW(*timer);\n\t\t\t\n\t\t\tif (line == 0) {\n\t\t\t\tconnect(*timer, SIGNAL(timeout()), this, \n\t\t\t\t\tSLOT(showLineTimer1()));\n\t\t\t} else {\n\t\t\t\tconnect(*timer, SIGNAL(timeout()), this, \n\t\t\t\t\tSLOT(showLineTimer2()));\n\t\t\t}\n\t\t\t\n\t\t\t// Update timer every 1s\n            (*timer)->setSingleShot(false);\n            (*timer)->start(1000);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Hide call duration timer\n\t\tif (*timer != NULL) {\n\t\t\t// Hide the timer after a few seconds\n            hideLineTimer->setSingleShot(true);\n            hideLineTimer->start(HIDE_LINE_TIMER_AFTER * 1000);\n\t\t\t(*timer)->stop();\n\t\t\tMEMMAN_DELETE(*timer);\n\t\t\t*timer = NULL;\n\t\t}\n\t\tbreak;\n\t}\n}\n\nvoid MphoneForm::updateLineEncryptionState(int line)\n{\n\tQLabel *cryptLabel, *sasLabel;\n\tif (line == 0) {\n\t\tcryptLabel = crypt1Label;\n\t\tsasLabel = line1SasLabel;\n\t} else {\n\t\tcryptLabel = crypt2Label;\n\t\tsasLabel = line2SasLabel;\n\t}\n\t\n\tt_audio_session *as = phone->get_line(line)->get_audio_session();\n\tif (as && phone->is_line_encrypted(line)) {\n\t\tstring zrtp_sas = as->get_zrtp_sas();\n\t\tbool zrtp_sas_confirmed = as->get_zrtp_sas_confirmed();\n\t\tstring srtp_cipher_mode = as->get_srtp_cipher_mode();\n\t\t\n        cryptLabel->setToolTip(QString());\n\t\tQString toolTip = tr(\"Voice is encrypted\") + \" (\";\n\t\ttoolTip.append(srtp_cipher_mode.c_str()).append(\")\");\n\t\t\n\t\tif (!zrtp_sas.empty()) {\n\t\t\t// Set tool tip on encryption icon\n\t\t\ttoolTip.append(\"\\nSAS = \");\n\t\t\ttoolTip.append(zrtp_sas.c_str());\n\t\t\t\n\t\t\t// Show SAS\n\t\t\tsasLabel->setText(zrtp_sas.c_str());\n\t\t\tsasLabel->show();\n\t\t} else {\n\t\t\tsasLabel->hide();\n\t\t}\n\t\t\t\n\t\tif (!zrtp_sas_confirmed) {\n\t\t\ttoolTip.append(\"\\n\").append(tr(\"Click to confirm SAS.\"));\n            cryptLabel->setFrameStyle(QFrame::Panel | QFrame::Raised);\n\t\t\tcryptLabel->setPixmap(\n\t\t\t\tQPixmap(\":/icons/images/encrypted.png\"));\n\t\t} else {\n\t\t\ttoolTip.append(\"\\n\").append(tr(\"Click to clear SAS verification.\"));\n            cryptLabel->setFrameStyle(QFrame::NoFrame);\n\t\t\tcryptLabel->setPixmap(\n\t\t\t\tQPixmap(\":/icons/images/encrypted_verified.png\"));\n\t\t}\n\n        cryptLabel->setToolTip(toolTip);\n\t\tcryptLabel->show();\n\t} else {\n\t\tcryptLabel->hide();\n\t\tsasLabel->hide();\n\t}\n}\n\nvoid MphoneForm::updateLineStatus(int line)\n{\n\tQString state;\n\tbool on_hold; // indicates if a line is put on-hold\n\tbool in_conference; // indicates if a line is in a conference\n\tbool is_muted; // indicates is a line is muted\n\tt_refer_state refer_state; // indicates if a call transfer is in progress\n\tbool is_transfer_consult; // indicates if the call is a consultation\n\tbool to_be_transferred; // indicates if the line is to be transferred after consultation\n\tt_call_info call_info;\n\tunsigned short dummy;\n\t\n\tQLabel *statLabel, *holdLabel, *muteLabel, *confLabel, *referLabel, *statusTextLabel;\n\t\n\tif (line == 0) {\n\t\tstatLabel = line1StatLabel;\n\t\tholdLabel = line1HoldLabel;\n\t\tmuteLabel = line1MuteLabel;\n\t\tconfLabel = line1ConfLabel;\n\t\treferLabel = line1ReferLabel;\n\t\tstatusTextLabel = status1TextLabel;\n\t} else {\n\t\tstatLabel = line2StatLabel;\n\t\tholdLabel = line2HoldLabel;\n\t\tmuteLabel = line2MuteLabel;\n\t\tconfLabel = line2ConfLabel;\n\t\treferLabel = line2ReferLabel;\n\t\tstatusTextLabel = status2TextLabel;\n\t}\n\t\n\tstate = lineSubstate2str(line);\n\ton_hold = phone->is_line_on_hold(line);\n\tif (on_hold) {\n\t\tholdLabel->show();\n\t} else {\n\t\tholdLabel->hide();\n\t}\n\tin_conference = phone->part_of_3way(line);\n\tif (in_conference) {\n\t\tconfLabel->show();\n\t} else {\n\t\tconfLabel->hide();\n\t}\n\tis_muted = phone->is_line_muted(line);\n\tif (is_muted) {\n\t\tmuteLabel->show();\n\t} else {\n\t\tmuteLabel->hide();\n\t}\n\trefer_state = phone->get_line_refer_state(line);\n\tis_transfer_consult = phone->is_line_transfer_consult(line, dummy);\n\tto_be_transferred = phone->line_to_be_transferred(line, dummy);\n\tif (refer_state != REFST_NULL || is_transfer_consult || to_be_transferred) {\n\t\tQString toolTip;\n        referLabel->setToolTip(QString());\n\t\treferLabel->show();\n\t\tif (is_transfer_consult) {\n\t\t\treferLabel->setPixmap(\n\t\t\t\tQPixmap(\":/icons/images/consult-xfer.png\"));\n\t\t\ttoolTip = tr(\"Transfer consultation\");\n\t\t} else {\n\t\t\treferLabel->setPixmap(\n\t\t\t\tQPixmap(\":/icons/images/cf.png\"));\n\t\t\ttoolTip = tr(\"Transferring call\");\n\t\t}\n        referLabel->setToolTip(toolTip);\n\t} else {\n\t\treferLabel->hide();\n\t}\n\t\n\tstatusTextLabel->setText(state);\n\t\n\tt_line_substate line_substate;\n\n\tline_substate = phone->get_line_substate(line);\n\tswitch (line_substate) {\n\tcase LSSUB_IDLE:\n\t\t((t_gui *)ui)->clearLineFields(line);\n\t\tstatLabel->hide();\n\t\tbreak;\n\tcase LSSUB_SEIZED:\n\tcase LSSUB_OUTGOING_PROGRESS:\n\t\tstatLabel->setPixmap(QPixmap(\":/icons/images/stat_outgoing.png\"));\n\t\tstatLabel->show();\n\t\tbreak;\n\tcase LSSUB_INCOMING_PROGRESS:\n\t\tstatLabel->setPixmap(QPixmap(\":/icons/images/stat_ringing.png\"));\n\t\tstatLabel->show();\n\n\t\tbreak;\n\tcase LSSUB_ANSWERING:\n\t\tstatLabel->setPixmap(QPixmap(\":/icons/images/gear.png\"));\n\t\tstatLabel->show();\n\t\tbreak;\n\tcase LSSUB_ESTABLISHED:\n\t\tif (phone->has_line_media(line)) {\n\t\t\tstatLabel->setPixmap(QPixmap(\n\t\t\t\t\t\":/icons/images/stat_established.png\"));\n\t\t} else {\n\t\t\tstatLabel->setPixmap(QPixmap(\n\t\t\t\t\t\":/icons/images/stat_established_nomedia.png\"));\n\t\t}\n\t\tstatLabel->show();\n\t\tbreak;\n\tcase LSSUB_RELEASING:\n\t\tstatLabel->setPixmap(QPixmap(\":/icons/images/gear.png\"));\n\t\tstatLabel->show();\n\t\tbreak;\n\tdefault:\n\t\tstatLabel->hide();\n\t\tbreak;\n\t}\n\t\n\tupdateLineEncryptionState(line);\n\tupdateLineTimer(line);\n}\n\n// Update line state and enable/disable buttons depending on state\nvoid MphoneForm::updateState()\n{\n\tQString state;\n\tint line, other_line;\n\tbool on_hold; // indicates if a line is put on-hold\n\tbool in_conference; // indicates if a line is in a conference\n\tbool is_muted; // indicates is a line is muted\n\tt_refer_state refer_state; // indicates if a call transfer is in progress\n\tbool is_transfer_consult; // indicates if the call is a consultation\n\tbool to_be_transferred; // indicates if the line is to be transferred after consultation\n\tbool has_media; // indicates if a media stream is present\n\tt_call_info call_info;\n\tunsigned short dummy;\n\t\n\t// Update status of line 1\n\tupdateLineStatus(0);\n\t\n\t// Update status of line 2\n\tupdateLineStatus(1);\n\t\n\t// Disable/enable controls depending on the active line state\n\tt_line_substate line_substate;\n\tline = phone->get_active_line();\n\tline_substate = phone->get_line_substate(line);\n\ton_hold = phone->is_line_on_hold(line);\n\tin_conference = phone->part_of_3way(line);\n\tis_muted = phone->is_line_muted(line);\n\trefer_state = phone->get_line_refer_state(line);\n\tis_transfer_consult = phone->is_line_transfer_consult(line, dummy);\n\tto_be_transferred = phone->line_to_be_transferred(line, dummy);\n\thas_media = phone->has_line_media(line);\n\tother_line = (line == 0 ? 1 : 0);\n\tcall_info = phone->get_call_info(line);\n\tt_user *user_config = phone->get_line_user(line);\n\t\n\t// The active line may change when one of the parties in a conference\n\t// releases the call. If this happens, then update the state of the\n\t// line radio buttons.\n    if (line == 0 && line2RadioButton->isChecked())\n\t{\n\t\tline1RadioButton->setChecked(true);\n    } else if (line == 1 && line1RadioButton->isChecked())\n\t{\n\t\tline2RadioButton->setChecked(true);\n\t}\n\t\n\t// Same logic for the activate line menu items\n    if (line == 0 && actionLine2->isChecked())\n\t{\n        actionLine1->setChecked(true);\n    } else if (line == 1 && actionLine1->isChecked())\n\t{\n        actionLine2->setChecked(true);\n\t}\n\n\tupdateOSD();\n\t\n\tbool showIncomingCallPopup = false;\n\n\tswitch(line_substate) {\n\t\tcase LSSUB_IDLE:\n\t\t\tenableCallOptions(true);\n\t\t\tcallAnswer->setEnabled(false);\n\t\t\tcallBye->setEnabled(false);\n\t\t\tcallReject->setEnabled(false);\n\t\t\tcallRedirect->setEnabled(false);\n\t\t\tcallTransfer->setEnabled(false);\n\t\t\tcallHold->setEnabled(false);\n\t\t\tcallConference->setEnabled(false);\n\t\t\tcallMute->setEnabled(false);\n\t\t\tcallDTMF->setEnabled(false);\n\t\t\tcallRedial->setEnabled(ui->can_redial());\n\t\t\tbreak;\n\t\tcase LSSUB_OUTGOING_PROGRESS:\n\t\t\tenableCallOptions(false);\n\t\t\tcallAnswer->setEnabled(false);\n\t\t\tcallBye->setEnabled(true);\n\t\t\tcallReject->setEnabled(false);\n\t\t\tcallRedirect->setEnabled(false);\n\n\t\t\tif (is_transfer_consult && user_config->get_allow_transfer_consultation_inprog()) {\n\t\t\t\tcallTransfer->setEnabled(true);\n\t\t\t} else {\n\t\t\t\tcallTransfer->setEnabled(false);\n\t\t\t}\n\n\t\t\tcallHold->setEnabled(false);\n\t\t\tcallConference->setEnabled(false);\n\t\t\tcallMute->setEnabled(false);\n\t\t\tcallDTMF->setEnabled(call_info.dtmf_supported);\n\t\t\tcallRedial->setEnabled(false);\n\t\t\tbreak;\n\t\tcase LSSUB_INCOMING_PROGRESS:\n\t\t{\n\t\t\tenableCallOptions(false);\n\t\t\tcallAnswer->setEnabled(true);\n\t\t\tcallBye->setEnabled(false);\n\t\t\tcallReject->setEnabled(true);\n\t\t\tcallRedirect->setEnabled(true);\n\t\t\tcallTransfer->setEnabled(false);\n\t\t\tcallHold->setEnabled(false);\n\t\t\tcallConference->setEnabled(false);\n\t\t\tcallMute->setEnabled(false);\n\t\t\tcallDTMF->setEnabled(call_info.dtmf_supported);\n\t\t\tcallRedial->setEnabled(false);\n\n\t\t\tstd::string name;\n\t\t\tt_call_record cr = phone->get_call_hist(line);\n\t\t\t// t_user *user_config = phone->get_line_user(line);\n\n\t\t\t// name = ui->format_sip_address(user_config, cr.from_display, cr.from_uri);\n\t\t\tname = cr.from_display;\n\t\t\tif (name.empty())\n\t\t\t\tname = cr.from_uri.encode_no_params_hdrs(false);\n\n\t\t\tif (incomingCallPopup)\n\t\t\t\tincomingCallPopup->setCallerName(QString::fromStdString(name));\n\t\t\tif (sys_config->get_gui_show_incoming_popup())\n\t\t\t\tshowIncomingCallPopup = true;\n\n\t\t\tbreak;\n\t\t}\n\t\tcase LSSUB_ESTABLISHED:\n\t\t\tenableCallOptions(false);\n\t\t\tcallInvite->setEnabled(false);\n\t\t\tcallAnswer->setEnabled(false);\n\t\t\tcallBye->setEnabled(true);\n\t\t\tcallReject->setEnabled(false);\n\t\t\tcallRedirect->setEnabled(false);\n\n\t\t\tif (in_conference) {\n\t\t\t\tcallTransfer->setEnabled(false);\n\t\t\t\tcallHold->setEnabled(false);\n\t\t\t\tcallConference->setEnabled(false);\n\t\t\t\tcallDTMF->setEnabled(false);\n\t\t\t} else {\n\t\t\t\tcallTransfer->setEnabled(has_media &&\n\t\t\t\t\t\t\t call_info.refer_supported &&\n\t\t\t\t\t\t\t refer_state == REFST_NULL &&\n\t\t\t\t\t\t\t !to_be_transferred);\n\t\t\t\tcallHold->setEnabled(has_media);\n\t\t\t\tcallDTMF->setEnabled(call_info.dtmf_supported);\n\n\t\t\t\tif (phone->get_line_substate(other_line) ==\n\t\t\t\t\tLSSUB_ESTABLISHED)\n\t\t\t\t{\n\t\t\t\t\t// If one of the lines is transferring a call, then a\n\t\t\t\t\t// conference cannot be setup.\n\t\t\t\t\tif (refer_state != REFST_NULL ||\n\t\t\t\t\t\tphone->get_line_refer_state(other_line) != REFST_NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tcallConference->setEnabled(false);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcallConference->setEnabled(has_media);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcallConference->setEnabled(false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcallMute->setEnabled(true);\n\t\t\tcallRedial->setEnabled(false);\n\t\t\tbreak;\n\t\tcase LSSUB_SEIZED:\n\t\tcase LSSUB_ANSWERING:\n\t\tcase LSSUB_RELEASING:\n\t\t\t// During dialing, answering and call release no other actions are\n\t\t\t// possible\n\t\t\tenableCallOptions(false);\n\t\t\tcallAnswer->setEnabled(false);\n\t\t\tcallBye->setEnabled(false);\n\t\t\tcallReject->setEnabled(false);\n\t\t\tcallRedirect->setEnabled(false);\n\t\t\tcallTransfer->setEnabled(false);\n\t\t\tcallHold->setEnabled(false);\n\t\t\tcallConference->setEnabled(false);\n\t\t\tcallMute->setEnabled(false);\n\t\t\tcallDTMF->setEnabled(false);\n\t\t\tcallRedial->setEnabled(false);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableCallOptions(true);\n\t\t\tcallAnswer->setEnabled(true);\n\t\t\tcallBye->setEnabled(true);\n\t\t\tcallReject->setEnabled(true);\n\t\t\tcallRedirect->setEnabled(true);\n\t\t\tcallTransfer->setEnabled(true);\n\t\t\tcallHold->setEnabled(true);\n\t\t\tcallConference->setEnabled(false);\n\t\t\tcallMute->setEnabled(true);\n\t\t\tcallDTMF->setEnabled(true);\n\t\t\tcallRedial->setEnabled(ui->can_redial());\n\t}\n\n\tif (incomingCallPopup)\n\t\tincomingCallPopup->setVisible(showIncomingCallPopup);\n\t\n\t// Set hold action in correct state\n    callHold->setChecked(on_hold);\n\t\n\t// Set mute action in correct state\n    callMute->setChecked(is_muted);\n\t\n\t// Set transfer action in correct state\n    callTransfer->setChecked(is_transfer_consult);\n\t\n\t// Hide redirect form if it is still visible, but not applicable anymore\n\tif (!callRedirect->isEnabled() && redirectForm && \n\t    redirectForm->isVisible()) \n\t{\n\t\tredirectForm->hide();\n\t}\n\t\n\t// Hide transfer form if it is still visible, but not applicable anymore\n\tif (!callTransfer->isEnabled() && transferForm && \n\t    transferForm->isVisible()) \n\t{\n\t\ttransferForm->hide();\n\t}\n\t\n\t// Hide DTMF form if it is still visible, but not applicable anymore\n\tif (!callDTMF->isEnabled() && dtmfForm && \n\t    dtmfForm->isVisible()) \n\t{\n\t\tdtmfForm->hide();\n\t}\n\t\n\t// Set last called address in the redial tool tip\n\tt_url last_url;\n\tstring last_display;\n\tstring last_subject;\n\tt_user *last_user;\n\tbool hide_user;\n\tif (callRedial->isEnabled() && \n\t    ui->get_last_call_info(last_url, last_display, last_subject, &last_user, hide_user))\n\t{\n\t\tQString s = \"<b>\";\n\t\ts += tr(\"Repeat last call\");\n\t\ts += \"</b><br>\";\n\t\ts += \"<table>\";\n\t\t\n\t\ts += \"<tr><td>\";\n\t\ts += tr(\"User:\").append(\"</td><td>\");\n\t\ts += last_user->get_profile_name().c_str();\n\t\ts.append(\"</td></tr><tr><td>\").append(tr(\"Call:\")).append(\"</td><td>\");\n\t\ts += ui->format_sip_address(last_user,\n\t\t\t\t\tlast_display, last_url).c_str();\n\t\ts += \"</td></tr>\";\n\n\t\tif (!last_subject.empty()) {\n\t\t\ts.append(\"<tr><td>\").append(tr(\"Subject:\")).append(\"</td><td>\");\n\t\t\ts += last_subject.c_str();\n\t\t\ts += \"</td></tr>\";\n\t\t}\n\t\t\n\t\tif (hide_user) {\n\t\t\ts.append(\"<tr><td colspan=2>\").append(tr(\"Hide identity\"));\n\t\t\ts += \"</td></tr>\";\n\t\t}\n\t\t\n\t\ts += \"</table>\";\n\t\tcallRedial->setToolTip(s);\n\t} else {\n\t\tcallRedial->setToolTip(tr(\"Repeat last call\"));\n\t}\n\tcallRedial->setStatusTip(tr(\"Repeat last call\"));\n\t\n\tupdateSysTrayStatus();\n}\n\n// Update registration status\nvoid MphoneForm::updateRegStatus()\n{\n\tsize_t num_registered = 0;\n\tsize_t num_failed = 0;\n\tQString toolTip = \"<b>\";\n\ttoolTip.append(tr(\"Registration status:\"));\n\ttoolTip.append(\"</b><br>\");\n\ttoolTip.append(\"<table>\");\n\t\n\t// Count number of successful and failed registrations.\n\t// Determine tool tip showing registration details for all users.\n\tlist<t_user *>user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\ttoolTip.append(\"<tr><td>\");\n\t\ttoolTip.append((*i)->get_profile_name().c_str());\n\t\ttoolTip.append(\"</td><td>\");\n\t\tif (phone->get_is_registered(*i)) {\n\t\t\tnum_registered++;\n\t\t\ttoolTip.append(tr(\"Registered\"));\n\t\t} else if (phone->get_last_reg_failed(*i)) {\n\t\t\tnum_failed++;\n\t\t\ttoolTip.append(tr(\"Failed\"));\n\t\t} else {\n\t\t\ttoolTip.append(tr(\"Not registered\").replace(' ', \"&nbsp;\"));\n\t\t}\n\t\ttoolTip.append(\"</td></tr>\");\n\t}\n\ttoolTip.append(\"</table><br>\");\n\ttoolTip.append(\"<i>\");\n\ttoolTip.append(tr(\"Click to show registrations.\").replace(' ', \"&nbsp;\"));\n\ttoolTip.append(\"</i>\");\n\t\n\t// Set registration status\n\tif (num_registered == user_list.size()) {\n\t\t// All users are registered\n\t\tstatRegLabel->setPixmap(QPixmap(\":/icons/images/twinkle16.png\"));\n\t} else if (num_failed == user_list.size()) {\n\t\t// All users failed to register\n\t\tstatRegLabel->setPixmap(QPixmap(\":/icons/images/reg_failed.png\"));\n\t} else if (num_registered > 0) {\n\t\t// Some users are registered\n\t\tstatRegLabel->setPixmap(QPixmap(\n\t\t\t\t\":/icons/images/twinkle16.png\"));\n\t} else if (num_failed > 0) {\n\t\t// Some users failed, none are registered\n\t\tstatRegLabel->setPixmap(QPixmap(\":/icons/images/reg_failed.png\"));\n\t} else {\n\t\t// No users are registered, no users failed\n\t\tstatRegLabel->setPixmap(QPixmap(\":/icons/images/twinkle16-disabled.png\"));\n\t}\n\t\n\t// Set tool tip with detailed info.\n    statRegLabel->setToolTip(QString());\n\t\n\tif (num_registered > 0 || num_failed > 0) {\n        statRegLabel->setToolTip(toolTip);\n\t} else {\n        statRegLabel->setToolTip(tr(\"No users are registered.\"));\n\t}\n\t\n\tupdateSysTrayStatus();\n}\n\n// Create a status message based on the number of waiting messages.\n// On return, msg_waiting will indicate if the MWI indicator should show\n// waiting messages.\nQString MphoneForm::getMWIStatus(const t_mwi &mwi, bool &msg_waiting) const\n{\n\tQString status;\n\tmsg_waiting = false;\n\tt_msg_summary summary = mwi.get_voice_msg_summary();\n\t\t\n\tif (summary.newmsgs > 0 && summary.oldmsgs > 0) {\n\t\tif (summary.oldmsgs == 1) {\n\t\t\tstatus = tr(\"%1 new, 1 old message\").\n\t\t\t\t arg(summary.newmsgs);\n\t\t} else {\n\t\t\tstatus = tr(\"%1 new, %2 old messages\").\n\t\t\t\t arg(summary.newmsgs).\n\t\t\t\t arg(summary.oldmsgs);\n\t\t}\n\t\tmsg_waiting = true;\n\t} else if (summary.newmsgs > 0 && summary.oldmsgs == 0) {\n\t\tif (summary.newmsgs == 1) {\n\t\t\tstatus = tr(\"1 new message\");\n\t\t} else {\n\t\t\tstatus = tr(\"%1 new messages\").\n\t\t\t\t arg(summary.newmsgs);\n\t\t}\n\t\tmsg_waiting = true;\n\t} else if (summary.oldmsgs > 0) {\n\t\tif (summary.oldmsgs == 1) {\n\t\t\tstatus = tr(\"1 old message\");\n\t\t} else {\n\t\t\tstatus = tr(\"%1 old messages\").\n\t\t\t\t arg(summary.oldmsgs);\n\t\t}\n\t} else {\n\t\tif (mwi.get_msg_waiting()) {\n\t\t\tstatus = tr(\"Messages waiting\");\n\t\t\tmsg_waiting = true;\n\t\t} else {\n\t\t\tstatus = tr(\"No messages\");\n\t\t}\n\t}\n\n\treturn status.replace(' ', \"&nbsp;\");\n}\n\n// Flash the MWI icon\nvoid MphoneForm::flashMWI()\n{\n\tif (mwiFlashStatus) {\n\t\tmwiFlashStatus = false;\n\t\tstatMWILabel->setPixmap(QPixmap(\n\t\t\t\t\":/icons/images/mwi_none16.png\"));\n\t} else {\n\t\tmwiFlashStatus = true;\n\t\tstatMWILabel->setPixmap(QPixmap(\n\t\t\t\t\":/icons/images/mwi_new16.png\"));\n\t}\n}\n\n// Update MWI\nvoid MphoneForm::updateMwi()\n{\n\tbool mwi_known = false;\n\tbool mwi_new_msgs = false;\n\tbool mwi_failure = false;\n\n\t// Determine tool tip\n\tQString toolTip = tr(\"<b>Voice mail status:</b>\").append(\"\\n\");\n\ttoolTip.append(\"<br><table>\");\n\tlist<t_user *>user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\ttoolTip.append(\"<tr><td>\");\n\t\ttoolTip.append((*i)->get_profile_name().c_str());\n\t\tt_mwi mwi = phone->get_mwi(*i);\n\t\ttoolTip.append(\"</td><td>\");\n\t\tif (phone->is_mwi_subscribed(*i)) {\n\t\t\tif (mwi.get_status() == t_mwi::MWI_KNOWN) {\n\t\t\t\tbool new_msgs;\n\t\t\t\tQString status = getMWIStatus(mwi, new_msgs);\n\t\t\t\ttoolTip.append(status);\n\t\t\t\tmwi_known = true;\n\t\t\t\tmwi_new_msgs |= new_msgs;\n\t\t\t} else if (mwi.get_status() == t_mwi::MWI_FAILED) {\n\t\t\t\ttoolTip.append(tr(\"Failure\"));\n\t\t\t\tmwi_failure = true;\n\t\t\t} else {\n\t\t\t\ttoolTip.append(tr(\"Unknown\"));\n\t\t\t}\n\t\t} else {\n\t\t\tif ((*i)->get_mwi_solicited()) {\n\t\t\t\tif (mwi.get_status() == t_mwi::MWI_FAILED) {\n\t\t\t\t\ttoolTip.append(tr(\"Failure\"));\n\t\t\t\t\tmwi_failure = true;\n\t\t\t\t} else {\n\t\t\t\t\ttoolTip.append(tr(\"Unknown\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Unsolicited MWI\t\t\t\t\n\t\t\t\tif (mwi.get_status() == t_mwi::MWI_KNOWN) {\n\t\t\t\t\tbool new_msgs;\n\t\t\t\t\tQString status = getMWIStatus(mwi, new_msgs);\n\t\t\t\t\ttoolTip.append(status);\n\t\t\t\t\tmwi_known = true;\n\t\t\t\t\tmwi_new_msgs |= new_msgs;\n\t\t\t\t} else {\n\t\t\t\t\ttoolTip.append(tr(\"Unknown\"));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttoolTip.append(\"</td></tr>\");\n\t}\n\t\n\ttoolTip.append(\"</table><br>\");\n\ttoolTip.append(\"<i>\");\n\ttoolTip.append(tr(\"Click to access voice mail.\").replace(' ', \"&nbsp;\"));\n\ttoolTip.append(\"</i>\");\n\t\n\t// Set MWI icon\n\tif (mwi_new_msgs) {\n\t\tstatMWILabel->setPixmap(QPixmap(\n\t\t\t\":/icons/images/mwi_new16.png\"));\n\t\tmwiFlashStatus = true;\n\t\t\n\t\t// Start the flash MWI timer to flash the indicator\n\t\ttmrFlashMWI.start(1000);\n\t} else if (mwi_failure) {\n\t\ttmrFlashMWI.stop();\n\t\tstatMWILabel->setPixmap(QPixmap(\n\t\t\t\":/icons/images/mwi_failure16.png\"));\n\t} else if (mwi_known) {\n\t\ttmrFlashMWI.stop();\n\t\tstatMWILabel->setPixmap(QPixmap(\n\t\t\t\":/icons/images/mwi_none16.png\"));\n\t} else {\n\t\ttmrFlashMWI.stop();\n\t\tstatMWILabel->setPixmap(QPixmap(\n\t\t\t\":/icons/images/mwi_none16_dis.png\"));\n\t}\n\t\n\t// Set tool tip\n    statMWILabel->setToolTip(toolTip);\n\t\n\tupdateSysTrayStatus();\n}\n\n// Update active services status\nvoid MphoneForm::updateServicesStatus()\n{\t\n\tsize_t num_dnd = 0;\n\tsize_t num_cf = 0;\n\tsize_t num_auto_answer = 0;\n\tQString tipDnd = \"<b>\";\n\ttipDnd += tr(\"Do not disturb active for:\").replace(' ', \"&nbsp;\");\n\ttipDnd += \"</b><br>\\n<table>\";\n\tQString tipCf = \"<b>\";\n\ttipCf += tr(\"Redirection active for:\").replace(' ', \"&nbsp;\");\n\ttipCf +=  \"</b><br>\\n<table>\";\n\tQString tipAa = \"<b>\";\n\ttipAa += tr(\"Auto answer active for:\").replace(' ', \"&nbsp;\");\n\ttipAa += \"</b><br>\\n<table>\";\n\t\n\t// Calculate number of services active.\n\t// Determine tool tips with detailed service status for all users.\n\tlist<t_user *>user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tif (phone->ref_service(*i)->is_dnd_active()) {\n\t\t\tnum_dnd++;\n\t\t\ttipDnd.append(\"<tr><td>\");\n\t\t\ttipDnd.append((*i)->get_profile_name().c_str());\n\t\t\ttipDnd.append(\"</td></tr>\");\n\t\t}\n\t\tif (phone->ref_service(*i)->is_cf_active()) {\n\t\t\tnum_cf++;\n\t\t\ttipCf.append(\"<tr><td>\");\n\t\t\ttipCf.append((*i)->get_profile_name().c_str());\n\t\t\ttipCf.append(\"</td></tr>\");\n\t\t}\n\t\tif (phone->ref_service(*i)->is_auto_answer_active()) {\n\t\t\tnum_auto_answer++;\n\t\t\ttipAa.append(\"<tr><td>\");\n\t\t\ttipAa.append((*i)->get_profile_name().c_str());\n\t\t\ttipAa.append(\"</td></tr>\");\n\t\t}\n\t}\n\t\n\tQString footer = \"<i>\";\n\tfooter += tr(\"Click to activate/deactivate\").replace(' ', \"&nbsp;\");\n\tfooter += \"</i>\";\n\t\n\ttipDnd.append(\"</table><br>\");\n\ttipDnd.append(footer);\n\ttipCf.append(\"</table><br>\");\n\ttipCf.append(footer);\n\ttipAa.append(\"</table><br>\");\n\ttipAa.append(footer);\n\t\n\t// Set service status\n\tif (num_dnd == user_list.size()) {\n\t\t// All users enabled dnd\n\t\tstatDndLabel->setPixmap(QPixmap(\":/icons/images/cancel.png\"));\n\t} else if (num_dnd > 0) {\n\t\t// Some users enabled dnd\n\t\tstatDndLabel->setPixmap(QPixmap(\":/icons/images/cancel.png\"));\n\t} else {\n\t\t// No users enabeld dnd\n\t\tstatDndLabel->setPixmap(QPixmap(\":/icons/images/cancel-disabled.png\"));\n\t}\n\t\n\tif (num_cf == user_list.size()) {\n\t\t// All users enabled redirecton\n\t\tstatCfLabel->setPixmap(QPixmap(\":/icons/images/cf.png\"));\n\t} else if (num_cf > 0) {\n\t\t// Some users enabled redirection\n\t\tstatCfLabel->setPixmap(QPixmap(\":/icons/images/cf.png\"));\n\t} else {\n\t\t// No users enabled redirection\n\t\tstatCfLabel->setPixmap(QPixmap(\":/icons/images/cf-disabled.png\"));\n\t}\n\t\n\tif (num_auto_answer == user_list.size()) {\n\t\t// All users enabled auto answer\n\t\tstatAaLabel->setPixmap(QPixmap(\":/icons/images/auto_answer.png\"));\n\t} else if (num_auto_answer > 0) {\n\t\t// Some users enabled auto answer\n\t\tstatAaLabel->setPixmap(QPixmap(\n\t\t\t\t\":/icons/images/auto_answer.png\"));\n\t} else {\n\t\t// No users enabeld auto answer\n\t\tstatAaLabel->setPixmap(QPixmap(\n\t\t\t\t\":/icons/images/auto_answer-disabled.png\"));\n\t}\n\t\n\t// Set tool tip with detailed info for multiple users.\n    statDndLabel->setToolTip(QString());\n    statCfLabel->setToolTip(QString());\n    statAaLabel->setToolTip(QString());\n\n\tQString clickToActivate(\"<i>\");\n\tclickToActivate += tr(\"Click to activate\").replace(' ', \"&nbsp;\");\n\tclickToActivate += \"</i>\";\n\tif (num_dnd > 0) {\n        statDndLabel->setToolTip(tipDnd);\n\t} else {\n\t\tQString status(\"<p>\");\n\t\tstatus += tr(\"Do not disturb is not active.\").replace(' ', \"&nbsp;\");\n\t\tstatus += \"</p>\";\n\t\tstatus += clickToActivate;\n        statDndLabel->setToolTip(status);\n\t}\t\t\n\t\n\tif (num_cf > 0) {\n        statCfLabel->setToolTip(tipCf);\n\t} else {\n\t\tQString status(\"<p>\");\n\t\tstatus += tr(\"Redirection is not active.\").replace(' ', \"&nbsp;\");\n\t\tstatus += \"</p>\";\n\t\tstatus += clickToActivate;\n        statCfLabel->setToolTip(status);\n\t}\n\t\n\tif (num_auto_answer > 0) {\n        statAaLabel->setToolTip(tipAa);\n\t} else {\n\t\tQString status(\"<p>\");\n\t\tstatus += tr(\"Auto answer is not active.\").replace(' ', \"&nbsp;\");\n\t\tstatus += \"</p>\";\n\t\tstatus += clickToActivate;\n        statAaLabel->setToolTip(status);\n\t}\n\t\n\tupdateSysTrayStatus();\n}\n\nvoid MphoneForm::updateMissedCallStatus(int num_missed_calls)\n{\t\n\tQString clickDetails(\"<i>\");\n\tclickDetails += tr(\"Click to see call history for details.\").replace(' ', \"&nbsp;\");\n\tclickDetails += \"</i>\";\n\tif (num_missed_calls == 0) {\n\t\tstatMissedLabel->setPixmap(QPixmap(\":/icons/images/missed-disabled.png\"));\n\t\tQString status(\"<p>\");\n\t\tstatus += tr(\"You have no missed calls.\").replace(' ', \"&nbsp;\");\n\t\tstatus += \"</p>\";\n\t\tstatus += clickDetails;\n        statMissedLabel->setToolTip(status);\n\t} else {\n\t\tstatMissedLabel->setPixmap(\n\t\t\tQPixmap(\":/icons/images/missed.png\"));\n\t\t\n\t\tQString tip(\"<p>\");\n\t\tif (num_missed_calls == 1) {\n\t\t\ttip += tr(\"You missed 1 call.\").replace(' ', \"&nbsp;\");\n\t\t} else {\n\t\t\ttip += tr(\"You missed %1 calls.\").arg(num_missed_calls).\n\t\t\t       replace(' ', \"&nbsp;\");\n\t\t}\n\t\ttip += \"</p>\";\n\t\ttip += clickDetails;\n        statMissedLabel->setToolTip(tip);\n\t}\n\t\n\tupdateSysTrayStatus();\n}\n\n// Update system tray status\nvoid MphoneForm::updateSysTrayStatus()\n{\n\tQString icon_name;\n\tbool cf_active = false;\n\tbool dnd_active = false;\n\tbool auto_answer_active = false;\n\tbool multi_services = false;\n\tint num_services;\n\tbool msg_waiting = false;\n\t\n\tif (!sysTray) return;\n\t\n\t// Get status of active line\n\tint line = phone->get_active_line();\n\tt_line_substate line_substate = phone->get_line_substate(line);\n\t\n\tlist<t_user *> user_list = phone->ref_users();\n\t\n\tswitch(line_substate) {\n\tcase LSSUB_IDLE:\n\tcase LSSUB_SEIZED:\n\t\t// Determine MWI and service status\n\t\tuser_list = phone->ref_users();\n\t\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\t\tt_mwi mwi = phone->get_mwi(*i);\n\t\t\tif (mwi.get_status() == t_mwi::MWI_KNOWN &&\n\t\t\t    mwi.get_msg_waiting() &&\n\t\t\t    mwi.get_voice_msg_summary().newmsgs > 0)\n\t\t\t{\n\t\t\t\tmsg_waiting = true;\n\t\t\t} else if (phone->ref_service(*i)->multiple_services_active()) {\n\t\t\t\tmulti_services = true;\n\t\t\t} else {\n\t\t\t\tif (phone->ref_service(*i)->is_dnd_active())  {\n\t\t\t\t\tdnd_active = true;\n\t\t\t\t}\n\t\t\t\tif (phone->ref_service(*i)->is_cf_active()) {\n\t\t\t\t\tcf_active = true;\n\t\t\t\t}\n\t\t\t\tif (phone->ref_service(*i)->is_auto_answer_active()) {\n\t\t\t\t\tauto_answer_active = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// If there are messages waiting, then show MWI icon\n\t\tif (msg_waiting) {\n\t\t\ticon_name = \"sys_mwi\";\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\t// If there are missed calls, then show the missed call icon\n\t\tif (call_history->get_num_missed_calls() > 0) {\n\t\t\ticon_name = \"sys_missed\";\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\t// If a service is active, then show the service icon\n\t\tnum_services = (dnd_active ? 1 : 0) + (cf_active ? 1 : 0) + \n\t\t\t       (auto_answer_active ? 1 : 0);\n\t\t\n\t\tif (multi_services || num_services > 1) {\n\t\t\ticon_name = \"sys_services\";\n\t\t} else if (dnd_active) {\n\t\t\ticon_name = \"sys_dnd\";\n\t\t} else if (cf_active) {\n\t\t\ticon_name = \"sys_redir\";\n\t\t} else if (auto_answer_active) {\n\t\t\ticon_name = \"sys_auto_ans\";\n\t\t} else {\n\t\t\t// No service is active, show the idle icon\n\t\t\tif (icon_name.isEmpty()) icon_name = \"sys_idle\";\n\t\t}\n\n\t\tbreak;\n\tcase LSSUB_ESTABLISHED:\n\t\tif (phone->is_line_on_hold(line)) {\n\t\t\ticon_name = \"sys_hold\";\n\t\t} else if (phone->is_line_muted(line)) {\n\t\t\ticon_name = \"sys_mute\";\n\t\t} else if (phone->is_line_encrypted(line)) {\n\t\t\tt_audio_session *as = phone->get_line(line)->get_audio_session();\n\t\t\tif (as && as->get_zrtp_sas_confirmed()) {\n\t\t\t\ticon_name = \"sys_encrypted_verified\";\n\t\t\t} else {\n\t\t\t\ticon_name = \"sys_encrypted\";\n\t\t\t}\n\t\t} else {\n\t\t\ticon_name = \"sys_busy_estab\";\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Line is in a busy transient state\n\t\ticon_name = \"sys_busy_trans\";\n\t}\n\t\n\t// Based on the registration status use the active or disabled version\n\t// of the icon.\n\tbool registered = false;\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tif (phone->get_is_registered(*i)) {\n\t\t\tregistered = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tif (registered) {\n\t\ticon_name += \".png\";\n\t} else {\n\t\ticon_name += \"_dis.png\";\n\t}\n\t\n\tsysTray->setIcon(QPixmap(\":/icons/images/\" + icon_name));\n}\n\n// Update menu status based on the number of active users\nvoid MphoneForm::updateMenuStatus()\n{\n\t// Some menu options should be toggle actions when there is only\n\t// 1 user active, but they should be normal actions when there are\n\t// multiple users.\n\tdisconnect(serviceDnd, 0, 0, 0);\n\tdisconnect(serviceAutoAnswer, 0, 0, 0);\n\tif (phone->ref_users().size() == 1) {\n\t\tt_service *srv = phone->ref_service(phone->ref_users().front());\n\t\t\n        serviceDnd->setCheckable(true);\n        serviceDnd->setChecked(srv->is_dnd_active());\n\t\tconnect(serviceDnd, SIGNAL(toggled(bool)),\n\t\t\tthis, SLOT(srvDnd(bool)));\n\t\t\n        serviceAutoAnswer->setCheckable(true);\n        serviceAutoAnswer->setChecked(srv->is_auto_answer_active());\n\t\tconnect(serviceAutoAnswer, SIGNAL(toggled(bool)),\n\t\t\tthis, SLOT(srvAutoAnswer(bool)));\n\t} else {\n        serviceDnd->setChecked(false);\n        serviceDnd->setCheckable(false);\n\t\tconnect(serviceDnd, SIGNAL(triggered()),\n\t\t\tthis, SLOT(srvDnd()));\n\t\t\n        serviceAutoAnswer->setChecked(false);\n        serviceAutoAnswer->setCheckable(false);\n\t\tconnect(serviceAutoAnswer, SIGNAL(triggered()),\n\t\t\tthis, SLOT(srvAutoAnswer()));\n\t}\n\t\n#ifdef WITH_DIAMONDCARD\n\tupdateDiamondcardMenu();\n#endif\n}\n\nvoid MphoneForm::updateDiamondcardMenu()\n{\n\t// If one Diamondcard user is active, then create actions in the Diamondcard\n\t// main menu for recharging, call history, etc. These actions will show the\n\t// Diamondcard web page.\n\t// If multiple Diamondcard users are active then create a submenu of each\n\t// Diamondcard action. In each submenu create an item for each user.\n\t// When a user item is clicked, the web page for the action and that user is\n\t// shown.\n\tlist<t_user *> diamondcard_users = diamondcard_get_users(phone);\n\t\n\t// Menu item identifiers\n    static QAction* rechargeId = nullptr;\n    static QAction* balanceHistoryId = nullptr;\n    static QAction* callHistoryId = nullptr;\n    static QAction* adminCenterId = nullptr;\n\t\n\t// Sub menu's\n    static QMenu *rechargeMenu = NULL;\n    static QMenu *balanceHistoryMenu = NULL;\n    static QMenu *callHistoryMenu = NULL;\n    static QMenu *adminCenterMenu = NULL;\n\t\n\t// Clear old menu\n\tremoveDiamondcardAction(rechargeId);\n\tremoveDiamondcardAction(balanceHistoryId);\n\tremoveDiamondcardAction(callHistoryId);\n\tremoveDiamondcardAction(adminCenterId);\n\tremoveDiamondcardMenu(rechargeMenu);\n\tremoveDiamondcardMenu(balanceHistoryMenu);\n\tremoveDiamondcardMenu(callHistoryMenu);\n\tremoveDiamondcardMenu(adminCenterMenu);\n\t\n\tif (diamondcard_users.size() <= 1)\n\t{\n        rechargeId = Diamondcard->addAction(tr(\"Recharge...\"), this, SLOT(DiamondcardRecharge()));\n        rechargeId->setData(0);\n        balanceHistoryId = Diamondcard->addAction(tr(\"Balance history...\"), this, SLOT(DiamondcardBalanceHistory()));\n        balanceHistoryId->setData(0);\n        callHistoryId = Diamondcard->addAction(tr(\"Call history...\"), this, SLOT(DiamondcardCallHistory()));\n        callHistoryId->setData(0);\n        adminCenterId = Diamondcard->addAction(tr(\"Admin center...\"), this, SLOT(DiamondcardAdminCenter()));\n        adminCenterId->setData(0);\n\t\t\n\t\t// Disable actions as there is no active Diamondcard users.\n\t\tif (diamondcard_users.empty()) {\n            rechargeId->setEnabled(false);\n            balanceHistoryId->setEnabled(false);\n            callHistoryId->setEnabled(false);\n            adminCenterId->setEnabled(false);\n\t\t}\n\t}\n\telse\n\t{\n        // Add the Diamondcard popup menus to the main Diamondcard menu.\n        rechargeMenu = Diamondcard->addMenu(tr(\"Recharge\"));\n        balanceHistoryMenu = Diamondcard->addMenu(tr(\"Balance history\"));\n        callHistoryMenu = Diamondcard->addMenu(tr(\"Call history\"));\n        adminCenterMenu = Diamondcard->addMenu(tr(\"Admin center\"));\n\n\n\t\t// No MEMMAN registration as the popup menu may be automatically\n\t\t// deleted by Qt on application close down. This would show up as\n\t\t// a memory leak in MEMMAN.\n\t\t\n\t\t// Insert a menu item for each Diamondcard user.\n\t\tint idx = 0;\n\t\tfor (list<t_user *>::const_iterator it = diamondcard_users.begin(); \n\t\t       it != diamondcard_users.end(); ++it)\n\t\t{\n            QAction* menuId;\n\t\t\tt_user *user = *it;\n\t\t\t\n\t\t\t// Set the index in the user list as parameter to the menu item.\n\t\t\t// When the menu item gets clicked, then the receiver of the signal\n\t\t\t// received this parameter and can use it as an index in the user list\n\t\t\t// to find the user.\n\t\t\t\n            menuId = rechargeMenu->addAction(user->get_profile_name().c_str(), this,\n                                  SLOT(DiamondcardRecharge()));\n            menuId->setData(idx);\n            menuId = balanceHistoryMenu->addAction(user->get_profile_name().c_str(), this,\n                                  SLOT(DiamondcardBalanceHistory()));\n            menuId->setData(idx);\n            menuId = callHistoryMenu->addAction(user->get_profile_name().c_str(), this,\n                                  SLOT(DiamondcardCallHistory()));\n            menuId->setData(idx);\n            menuId = adminCenterMenu->addAction(user->get_profile_name().c_str(), this,\n                                  SLOT(DiamondcardAdminCenter()));\n            menuId->setData(idx);\n\t\t\t\n\t\t\t++idx;\n\t\t}\n\t\t\n\n\t}\n}\n\nvoid MphoneForm::removeDiamondcardAction(QAction*& act)\n{\n    if (act != nullptr) {\n        Diamondcard->removeAction(act);\n        act = nullptr;\n\t}\n}\n\nvoid MphoneForm::removeDiamondcardMenu(QMenu* &menu)\n{\n\tif (menu) {\n\t\tdelete menu;\n\t\tmenu = NULL;\n\t}\n}\n\nvoid MphoneForm::phoneRegister()\n{\n\tt_gui *gui = (t_gui *)ui;\n\tlist<t_user *> user_list = phone->ref_users();\n\t\n\tif (user_list.size() > 1) {\n\t\tif (selectUserForm) {\n\t\t\tMEMMAN_DELETE(selectUserForm);\n\t\t\tdelete (selectUserForm);\n\t\t}\n\t\t\n        selectUserForm = new SelectUserForm(this);\n        selectUserForm->setModal(true);\n\t\tMEMMAN_NEW(selectUserForm);\n\t\t\n\t\tconnect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, \n\t\t\tSLOT(do_phoneRegister(list<t_user *>)));\n\t\tselectUserForm->show(SELECT_REGISTER);\n\t} else {\n\t\tgui->action_register(user_list);\n\t}\n}\n\nvoid MphoneForm::do_phoneRegister(list<t_user *> user_list)\n{\n\t((t_gui *)ui)->action_register(user_list);\n}\n\nvoid MphoneForm::phoneDeregister()\n{\n\tt_gui *gui = (t_gui *)ui;\n\tlist<t_user *> user_list = phone->ref_users();\n\t\n\tif (user_list.size() > 1) {\n\t\tif (selectUserForm) {\n\t\t\tMEMMAN_DELETE(selectUserForm);\n\t\t\tdelete (selectUserForm);\n\t\t}\n\t\t\n        selectUserForm = new SelectUserForm(this);\n        selectUserForm->setModal(true);\n\t\tMEMMAN_NEW(selectUserForm);\n\t\t\n\t\tconnect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, \n\t\t\tSLOT(do_phoneDeregister(list<t_user *>)));\n\t\tselectUserForm->show(SELECT_DEREGISTER);\n\t} else {\n\t\tgui->action_deregister(user_list, false);\n\t}\n}\n\nvoid MphoneForm::do_phoneDeregister(list<t_user *> user_list)\n{\n\t((t_gui *)ui)->action_deregister(user_list, false);\n}\n\nvoid MphoneForm::phoneDeregisterAll()\n{\n\tt_gui *gui = (t_gui *)ui;\n\tlist<t_user *> user_list = phone->ref_users();\n\t\n\tif (user_list.size() > 1) {\n\t\tif (selectUserForm) {\n\t\t\tMEMMAN_DELETE(selectUserForm);\n\t\t\tdelete (selectUserForm);\n\t\t}\n\t\t\n        selectUserForm = new SelectUserForm(this);\n        selectUserForm->setModal(true);\n\t\tMEMMAN_NEW(selectUserForm);\n\t\n\t\tconnect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, \n\t\t\tSLOT(do_phoneDeregisterAll(list<t_user *>)));\n\t\tselectUserForm->show(SELECT_DEREGISTER_ALL);\n\t} else {\n\t\tgui->action_deregister(user_list, true);\n\t}\n}\n\nvoid MphoneForm::do_phoneDeregisterAll(list<t_user *> user_list)\n{\n\t((t_gui *)ui)->action_deregister(user_list, true);\n}\n\nvoid MphoneForm::phoneShowRegistrations()\n{\n\tlist<t_user *> user_list = phone->ref_users();\n\t((t_gui *)ui)->action_show_registrations(user_list);\n}\n\n\n// Show the semi-modal invite window\nvoid MphoneForm::phoneInvite(t_user * user_config, \n\t\tconst QString &dest, const QString &subject, bool anonymous)\n{\n\t// Seize the line, so no incoming call can take the line\n\tif (!((t_gui *)ui)->action_seize()) return;\n\t\n\tif (inviteForm) {\n\t\tinviteForm->clear();\n\t} else {\n        inviteForm = new InviteForm(this);\n        inviteForm->setModal(true);\n\t\tMEMMAN_NEW(inviteForm);\n\t\t\n\t\t// Initialize the destination history list\n\t\tfor (int i = callComboBox->count() - 1; i >= 0; i--) {\n            inviteForm->addToInviteComboBox(callComboBox->itemText(i));\n\t\t}\n\t\t\n\t\tconnect(inviteForm, \n\t\t\tSIGNAL(destination(t_user *, const QString &, const t_url &, \n\t\t\t\t\t   const QString &, bool)),\n\t\t\tthis, \n\t\t\tSLOT(do_phoneInvite(t_user *, const QString &, \n\t\t\t\t    const t_url &, const QString &, bool)));\n\t\t\n\t\tconnect(inviteForm, SIGNAL(raw_destination(const QString &)), \n\t\t\tthis, SLOT(addToCallComboBox(const QString &)));\n\t}\n\t\n\tinviteForm->show(user_config, dest, subject, anonymous);\n\tupdateState();\n}\n\nvoid MphoneForm::phoneInvite(const QString &dest, const QString &subject, bool anonymous)\n{\n    t_user *user = phone->ref_user_profile(userComboBox->currentText().toStdString());\n\tif (!user) {\n\t\tlog_file->write_report(\"Cannot find user profile.\",\n\t\t\t       \"MphoneForm::phoneInvite\", \n\t\t\t       LOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n\tphoneInvite(user, dest, subject, anonymous);\n}\n\nvoid MphoneForm::phoneInvite()\n{\n    t_user *user = phone->ref_user_profile(userComboBox->currentText().toStdString());\n\tif (!user) {\n\t\tlog_file->write_report(\"Cannot find user profile.\",\n\t\t\t       \"MphoneForm::phoneInvite\", \n\t\t\t       LOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n\tphoneInvite(user, \"\", \"\", false);\n}\n\n// Execute the invite action. This slot is connected to the destination\n// signal of the invite window.\nvoid MphoneForm::do_phoneInvite(t_user *user_config, const QString &display, \n\t\t\tconst t_url &destination, const QString &subject,\n\t\t\tbool anonymous)\n{\n    ((t_gui *)ui)->action_invite(user_config, destination, display.toStdString(), subject.toStdString(),\n\t\t\t\t     anonymous);\n\tupdateState();\n}\n\n// Redial last call\nvoid MphoneForm::phoneRedial(void)\n{\n\tt_url url;\n\tstring display, subject;\n\tt_user *user_config;\n\tbool hide_user;\n\t\n\tif (!ui->get_last_call_info(url, display, subject, &user_config, hide_user)) return;\n\t((t_gui *)ui)->action_invite(user_config, url, display, subject, hide_user);\n\tupdateState();\n}\n\n\nvoid MphoneForm::phoneAnswer()\n{\n\t((t_gui *)ui)->action_answer();\n\tupdateState();\n}\n\n// A call can be answered from the systray popup. The user may have\n// switched lines, the systray popup answer button should answer the\n// correct line.\nvoid MphoneForm::phoneAnswerFromSystrayPopup()\n{\n#ifdef HAVE_KDE\n\tunsigned short line = ((t_gui *)ui)->get_line_sys_tray_popup();\n\tunsigned short active_line = phone->get_active_line();\n\t\n\tif (line != active_line) {\n\t\t((t_gui *)ui)->action_activate_line(line);\n\t}\n\t\n\t((t_gui *)ui)->action_answer();\n\tupdateState();\n#endif\n}\n\nvoid MphoneForm::phoneBye()\n{\n\t((t_gui *)ui)->action_bye();\n\tupdateState();\n}\n\n\nvoid MphoneForm::phoneReject()\n{\n\t((t_gui *)ui)->action_reject();\n\tupdateState();\n}\n\n// A call can be rejected from the systray popup. The user may have\n// switched lines, the systray popup reject button should answer the\n// correct line.\nvoid MphoneForm::phoneRejectFromSystrayPopup()\n{\n#ifdef HAVE_KDE\n\tunsigned short line = ((t_gui *)ui)->get_line_sys_tray_popup();\n\t((t_gui *)ui)->action_reject(line);\n\tupdateState();\n#endif\n}\n\n\n// Show the semi-modal redirect form\nvoid MphoneForm::phoneRedirect(const list<string> &contacts)\n{\n\tint active_line = phone->get_active_line();\n\tt_user *user_config = phone->get_line_user(active_line);\n\t\n\tif (redirectForm) {\n\t\tMEMMAN_DELETE(redirectForm);\n\t\tdelete (redirectForm);\n\t}\n\t\n    redirectForm = new RedirectForm(this);\n    redirectForm->setModal(true);\n\tMEMMAN_NEW(redirectForm);\n\tconnect(redirectForm, SIGNAL(destinations(const list<t_display_url> &)),\n\t\tthis, SLOT(do_phoneRedirect(const list<t_display_url> &)));\n\t\n\tredirectForm->show(user_config, contacts);\n}\n\nvoid MphoneForm::phoneRedirect()\n{\n\tconst list<string> l;\n\tphoneRedirect(l);\n}\n\n// Execute the redirect action.\nvoid MphoneForm::do_phoneRedirect(const list<t_display_url> &destinations)\n{\n\t((t_gui *)ui)->action_redirect(destinations);\n\tupdateState();\n}\n\n// Show the semi-modal call transfer window\nvoid MphoneForm::phoneTransfer(const string &dest, t_transfer_type transfer_type)\n{\n\tint active_line = phone->get_active_line();\n\tt_user *user_config = phone->get_line_user(active_line);\n\t\n\t// Hold the call if setting in user profile indicates call hold\n\tif (user_config->get_referrer_hold()) {\n\t\tphoneHold(true);\n\t}\n\t\n\tif (transferForm) {\n\t\tMEMMAN_DELETE(transferForm);\n\t\tdelete transferForm;\n\t}\n\t\n    transferForm = new TransferForm(this);\n    transferForm->setModal(true);\n\tMEMMAN_NEW(transferForm);\n\tconnect(transferForm, SIGNAL(destination(const t_display_url &, t_transfer_type)),\n\t\tthis, SLOT(do_phoneTransfer(const t_display_url &, t_transfer_type)));\n\t\n\tif (dest.empty() && transfer_type == TRANSFER_BASIC) {\n\t\t// Let form pick a default transfer type based on the current\n\t\t// call status.\n\t\ttransferForm->show(user_config);\n\t} else {\n\t\t// Set passed destination and transfer type in form\n\t\ttransferForm->show(user_config, dest, transfer_type);\n\t}\n\tupdateState();\n}\n\nvoid MphoneForm::phoneTransfer()\n{\n\tunsigned short active_line = phone->get_active_line();\n\tunsigned short dummy;\n\t\n\tif (phone->is_line_transfer_consult(active_line, dummy)) {\n\t\tdo_phoneTransferLine();\n\t} else {\n\t\tphoneTransfer(\"\", TRANSFER_BASIC);\n\t}\n}\n\n// Execute the transfer action. This slot is connected to the destination\n// signal of the transfer window.\nvoid MphoneForm::do_phoneTransfer(const t_display_url &destination, \n\t\t\t\t  t_transfer_type transfer_type)\n{\n\tunsigned short active_line;\n\tunsigned short other_line;\n\t\t\n\tswitch (transfer_type) {\n\tcase TRANSFER_BASIC:\n\t\t((t_gui *)ui)->action_refer(destination.url, destination.display);\n\t\tbreak;\n\tcase TRANSFER_CONSULT:\n\t\t((t_gui *)ui)->action_setup_consultation_call(\n\t\t\t\tdestination.url, destination.display);\n\t\tbreak;\n\tcase TRANSFER_OTHER_LINE:\n\t\tactive_line = phone->get_active_line();\n\t\tother_line = (active_line == 0 ? 1 : 0);\n\t\t\n\t\tif (phone->get_line_substate(other_line) == LSSUB_ESTABLISHED) {\n\t\t\t((t_gui *)ui)->action_refer(active_line, other_line);\n\t\t} else {\n\t\t\t// The other line was released while the user was entering\n\t\t\t// the refer-target.\n\t\t\tt_user *user_config = phone->get_line_user(active_line);\n\t\t\tif (user_config->get_referrer_hold()) {\n\t\t\t\tphoneHold(false);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\tupdateState();\n}\n\n// Transfer the remote party on the held line to the remote party on the\n// active line.\nvoid MphoneForm::do_phoneTransferLine()\n{\n\tunsigned short active_line = phone->get_active_line();\n\tunsigned short line_to_be_transferred;\n\t\n\tif (!phone->is_line_transfer_consult(active_line, line_to_be_transferred)) {\n\t\t// Somehow the line is not a consultation call.\n\t\tupdateState();\n\t\treturn;\n\t}\n\t\n\t((t_gui *)ui)->action_refer(line_to_be_transferred, active_line);\n\tupdateState();\n}\n\nvoid MphoneForm::phoneHold(bool on)\n{\n\tif (on) {\n\t\t((t_gui *)ui)->action_hold();\n\t} else {\n\t\t((t_gui *)ui)->action_retrieve();\n\t}\n\t\n\tupdateState();\n}\n\nvoid MphoneForm::phoneConference()\n{\n\t((t_gui *)ui)->action_conference();\n\tupdateState();\n}\n\nvoid MphoneForm::phoneMute(bool on)\n{\n\t((t_gui *)ui)->action_mute(on);\n\tupdateState();\n}\n\nvoid MphoneForm::phoneTermCap(const QString &dest)\n{\n\t// In-dialog OPTIONS request\n\tint line = phone->get_active_line();\n\tif (phone->get_line_substate(line) == LSSUB_ESTABLISHED) {\n\t\t((t_gui *)ui)->action_options();\n\t\treturn;\n\t}\n\t\n\t// Out-of-dialog OPTIONS request\n\tif (termCapForm) {\n\t\tMEMMAN_DELETE(termCapForm);\n\t\tdelete (termCapForm);\n\t}\n\t\n    termCapForm = new TermCapForm(this);\n    termCapForm->setModal(true);\n\tMEMMAN_NEW(termCapForm);\n\tconnect(termCapForm, SIGNAL(destination(t_user *, const t_url &)),\n\t\tthis, SLOT(do_phoneTermCap(t_user *, const t_url &)));\n\t\n    t_user *user = phone->ref_user_profile(userComboBox->currentText().toStdString());\n\tif (!user) {\n\t\tlog_file->write_report(\"Cannot find user profile.\",\n\t\t\t       \"MphoneForm::phoneTermcap\", \n\t\t\t       LOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n\ttermCapForm->show(user, dest);\n}\n\nvoid MphoneForm::phoneTermCap()\n{\n\tphoneTermCap(\"\");\n}\n\nvoid MphoneForm::do_phoneTermCap(t_user *user_config, const t_url &destination)\n{\n\t((t_gui *)ui)->action_options(user_config, destination);\n}\n\nvoid MphoneForm::phoneDTMF()\n{\n\tif (!dtmfForm) {\n\t\tdtmfForm = new DtmfForm(this);\n\t\tMEMMAN_NEW(dtmfForm);\n\t\tconnect(dtmfForm, SIGNAL(digits(const QString &)),\n\t\t\tthis, SLOT(sendDTMF(const QString &)));\n\t}\n\t\n\tdtmfForm->show();\n}\n\nvoid MphoneForm::sendDTMF(const QString &digits)\n{\n    ((t_gui *)ui)->action_dtmf(digits.toStdString());\n}\n\nvoid MphoneForm::startMessageSession(void)\n{\n    t_user *user = phone->ref_user_profile(userComboBox->currentText().toStdString());\n\tif (!user) {\n\t\tlog_file->write_report(\"Cannot find user profile.\",\n\t\t\t       \"MphoneForm::startMessageSession\", \n\t\t\t       LOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\tim::t_msg_session *session = new im::t_msg_session(user);\n\tMEMMAN_NEW(session);\n\t((t_gui  *)ui)->addMessageSession(session);\n\tMessageFormView *messageFormView = new MessageFormView(NULL, session);\n\tMEMMAN_NEW(messageFormView);\n\tmessageFormView->show();\t\n}\n\nvoid MphoneForm::startMessageSession(t_buddy *buddy)\n{\n\tt_user *user_config = buddy->get_user_profile();\n\tt_url dest_url(ui->expand_destination(user_config, buddy->get_sip_address()));\n\tif (!dest_url.is_valid()) return;\n\tstring display = buddy->get_name();\n\t\n\t// Find an existing session\n\tim::t_msg_session *session = ((t_gui *)ui)->getMessageSession(user_config, dest_url, display);\n\tif (!session) {\n\t\t// There is no session yet, create one.\n\t\tsession = new im::t_msg_session(user_config, t_display_url(dest_url, display));\n\t\tMEMMAN_NEW(session);\n\t\t((t_gui *)ui)->addMessageSession(session);\n\t\tMessageFormView *view = new MessageFormView(NULL, session);\n\t\tMEMMAN_NEW(view);\n\t\tview->show();\n\t}\n}\n\nvoid MphoneForm::phoneConfirmZrtpSas(int line)\n{\n\t((t_gui *)ui)->action_confirm_zrtp_sas(line);\n\tupdateState();\n}\n\nvoid MphoneForm::phoneConfirmZrtpSas()\n{\n\t((t_gui *)ui)->action_confirm_zrtp_sas();\n\tupdateState();\n}\n\nvoid MphoneForm::phoneResetZrtpSasConfirmation(int line)\n{\n\t((t_gui *)ui)->action_reset_zrtp_sas_confirmation(line);\n\tupdateState();\t\n}\n\nvoid MphoneForm::phoneResetZrtpSasConfirmation()\n{\n\t((t_gui *)ui)->action_reset_zrtp_sas_confirmation();\n\tupdateState();\t\n}\n\nvoid MphoneForm::phoneEnableZrtp(bool on)\n{\n\tif (on) {\n\t\t((t_gui *)ui)->action_enable_zrtp();\n\t} else {\n\t\t((t_gui *)ui)->action_zrtp_request_go_clear();\n\t}\n\t\n\tupdateState();\n}\n\nvoid MphoneForm::phoneZrtpGoClearOk(unsigned short line)\n{\n\t((t_gui *)ui)->action_zrtp_go_clear_ok(line);\n\tupdateState();\n}\n\n// Radio button for line 1 changed state\nvoid MphoneForm::line1rbChangedState( bool on )\n{\n\t// If the radio button is switched off, then return, the toggle\n\t// on the other line will handle the action\n\tif (!on) return;\n\t\n\t((t_gui *)ui)->action_activate_line(0);\n}\n\nvoid MphoneForm::line2rbChangedState( bool on )\n{\n\t// If the radio button is switched off, then return, the toggle\n\t// on the other line will handle the action\n\tif (!on) return;\n\t\n\t((t_gui *)ui)->action_activate_line(1);\n}\n\nvoid MphoneForm::actionLine1Toggled( bool on)\n{\n\tif (!on) return;\n\t((t_gui *)ui)->action_activate_line(0);\n}\n\nvoid MphoneForm::actionLine2Toggled( bool on)\n{\n\tif (!on) return;\n\t((t_gui *)ui)->action_activate_line(1);\n}\n\n// Enable/disable dnd when there is 1 user active\nvoid MphoneForm::srvDnd( bool on ) \n{\n\t((t_gui *)ui)->srv_dnd(phone->ref_users(), on);\n\tupdateServicesStatus();\n}\n\n// Enable/disable dnd when there are multiple users active\nvoid MphoneForm::srvDnd()\n{\n\tif (selectUserForm) {\n\t\tMEMMAN_DELETE(selectUserForm);\n\t\tdelete (selectUserForm);\n\t}\n\t\t\n    selectUserForm = new SelectUserForm(this);\n    selectUserForm->setModal(true);\n\tMEMMAN_NEW(selectUserForm);\n\t\t\n\tconnect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, \n\t\t\tSLOT(do_srvDnd_enable(list<t_user *>)));\n\tconnect(selectUserForm, SIGNAL(not_selected(list<t_user *>)), this, \n\t\t\tSLOT(do_srvDnd_disable(list<t_user *>)));\n\t\t\t\n\tselectUserForm->show(SELECT_DND);\n}\n\nvoid MphoneForm::do_srvDnd_enable(list<t_user *> user_list) {\n\t((t_gui *)ui)->srv_dnd(user_list, true);\n\tupdateServicesStatus();\n}\n\nvoid MphoneForm::do_srvDnd_disable(list<t_user *> user_list) {\n\t((t_gui *)ui)->srv_dnd(user_list, false);\n\tupdateServicesStatus();\n}\n\n// Enable/disable auto answer when there is 1 user active\nvoid MphoneForm::srvAutoAnswer( bool on ) \n{\n\t((t_gui *)ui)->srv_auto_answer(phone->ref_users(), on);\n\tupdateServicesStatus();\n}\n\n// Enable/disable auto answer when there are multiple users active\nvoid MphoneForm::srvAutoAnswer()\n{\n\tif (selectUserForm) {\n\t\tMEMMAN_DELETE(selectUserForm);\n\t\tdelete (selectUserForm);\n\t}\n\t\t\n    selectUserForm = new SelectUserForm(this);\n    selectUserForm->setModal(true);\n\tMEMMAN_NEW(selectUserForm);\n\t\t\n\tconnect(selectUserForm, SIGNAL(selection(list<t_user *>)), this, \n\t\t\tSLOT(do_srvAutoAnswer_enable(list<t_user *>)));\n\tconnect(selectUserForm, SIGNAL(not_selected(list<t_user *>)), this, \n\t\t\tSLOT(do_srvAutoAnswer_disable(list<t_user *>)));\n\t\t\t\n\tselectUserForm->show(SELECT_AUTO_ANSWER);\n}\n\nvoid MphoneForm::do_srvAutoAnswer_enable(list<t_user *> user_list) {\n\t((t_gui *)ui)->srv_auto_answer(user_list, true);\n\tupdateServicesStatus();\n}\n\nvoid MphoneForm::do_srvAutoAnswer_disable(list<t_user *> user_list) {\n\t((t_gui *)ui)->srv_auto_answer(user_list, false);\n\tupdateServicesStatus();\n}\n\nvoid MphoneForm::srvRedirect()\n{\n\tif (!srvRedirectForm) {\n        srvRedirectForm = new SrvRedirectForm(this);\n        srvRedirectForm->setModal(true);\n\n\t\tMEMMAN_NEW(srvRedirectForm);\n\t\tconnect(srvRedirectForm, \n\t\t\tSIGNAL(destinations(t_user *,\n\t\t\t\t\t    const list<t_display_url> &,\n\t\t\t\t\t    const list<t_display_url> &,\n\t\t\t\t\t    const list<t_display_url> &)),\n\t\t\tthis, \n\t\t\tSLOT(do_srvRedirect(t_user *,\n\t\t\t\t\t    const list<t_display_url> &,\n\t\t\t\t\t    const list<t_display_url> &,\n\t\t\t\t\t    const list<t_display_url> &)));\n\t}\n\t\n\tsrvRedirectForm->show();\n}\n\nvoid MphoneForm::do_srvRedirect(t_user *user_config,\n\t\t\t\tconst list<t_display_url> &always, \n\t\t\t\tconst list<t_display_url> &busy,\n\t\t\t\tconst list<t_display_url> &noanswer)\n{\n\t// Redirection always\n\tif (always.empty()) {\n\t\t((t_gui *)ui)->srv_disable_cf(user_config, CF_ALWAYS);\n\t} else {\n\t\t((t_gui *)ui)->srv_enable_cf(user_config, CF_ALWAYS, always);\n\t}\n\t\n\t// Redirection busy\n\tif (busy.empty()) {\n\t\t((t_gui *)ui)->srv_disable_cf(user_config, CF_BUSY);\n\t} else {\n\t\t((t_gui *)ui)->srv_enable_cf(user_config, CF_BUSY, busy);\n\t}\n\t\n\t// Redirection no answer\n\tif (noanswer.empty()) {\n\t\t((t_gui *)ui)->srv_disable_cf(user_config, CF_NOANSWER);\n\t} else {\n\t\t((t_gui *)ui)->srv_enable_cf(user_config, CF_NOANSWER, noanswer);\n\t}\n\t\n\tupdateServicesStatus();\n}\n\n\nvoid MphoneForm::about()\n{\n\tQString s = sys_config->about(true).c_str();\n\t\n\tQMessageBox mbAbout(QMessageBox::Information,\n\t\t\tPRODUCT_NAME, s.replace(' ', \"&nbsp;\"),\n\t\t\tQMessageBox::Ok);\n\tmbAbout.setIconPixmap(QPixmap(\":/icons/images/twinkle48.png\"));\n\tmbAbout.exec();\n}\n\nvoid MphoneForm::aboutQt()\n{\n\tQMessageBox::aboutQt(this, PRODUCT_NAME);\n}\n\nvoid MphoneForm::manual()\n{\n\t((t_gui *)ui)->open_url_in_browser(\"https://mfnboer.home.xs4all.nl/twinkle/\");\n}\n\nvoid MphoneForm::editUserProfile()\n{\n\tif (!userProfileForm) {\n        userProfileForm = new UserProfileForm(this);\n        userProfileForm->setModal(true);\n\t\tMEMMAN_NEW(userProfileForm);\n\t\n\t\tconnect(userProfileForm,\n\t\t\tSIGNAL(authCredentialsChanged(t_user *, const string&)),\n\t\t\tthis,\n\t\t\tSLOT(updateAuthCache(t_user *, const string&)));\n\t\t\n\t\tconnect(userProfileForm,\n\t\t\tSIGNAL(stunServerChanged(t_user *)),\n\t\t\tthis,\n\t\t\tSLOT(updateStunSettings(t_user *)));\n\t\t\n\t\t// MWI settings change triggers an unsubscribe\n\t\tconnect(userProfileForm,\n\t\t\tSIGNAL(mwiChangeUnsubscribe(t_user *)),\n\t\t\tthis,\n\t\t\tSLOT(unsubscribeMWI(t_user *)));\n\t\t\n\t\t// MWI settings change triggers a subscribe\n\t\tconnect(userProfileForm,\n\t\t\tSIGNAL(mwiChangeSubscribe(t_user *)),\n\t\t\tthis,\n\t\t\tSLOT(subscribeMWI(t_user *)));\n\t}\n\t\n\tuserProfileForm->show(phone->ref_users(), \n\t\t\t      userComboBox->currentText());\n}\n\nvoid MphoneForm::editSysSettings()\n{\n\tif (!sysSettingsForm) {\n        sysSettingsForm = new SysSettingsForm(this);\n        sysSettingsForm->setModal(true);\n\t\tMEMMAN_NEW(sysSettingsForm);\n\t\tconnect(sysSettingsForm, SIGNAL(inhibitIdleSessionChanged()),\n\t\t\t(t_gui *)ui, SLOT(updateInhibitIdleSession()));\n\t\tconnect(sysSettingsForm, SIGNAL(sipUdpPortChanged()),\n\t\t\tthis, SLOT(updateSipUdpPort()));\n\t\tconnect(sysSettingsForm, SIGNAL(rtpPortChanged()),\n\t\t\tthis, SLOT(updateRtpPorts()));\n\t}\n\t\n\tsysSettingsForm->show();\n}\n\nvoid MphoneForm::selectProfile()\n{\n\tif (!selectProfileForm) {\n        selectProfileForm = new SelectProfileForm(this);\n        selectProfileForm->setModal(true);\n\t\tMEMMAN_NEW(selectProfileForm);\n\t\tconnect(selectProfileForm, SIGNAL(selection(const list<string> &)),\n\t\t\tthis, SLOT(newUsers(const list<string> &)));\n\t\tconnect(selectProfileForm, SIGNAL(profileRenamed()),\n\t\t\tthis, SLOT(updateUserComboBox()));\n\t\tconnect(selectProfileForm, SIGNAL(profileRenamed()),\n\t\t\tthis, SLOT(populateBuddyList()));\n\t}\n\t\n\tselectProfileForm->showForm(this);\n}\n\n// A new set of users has been selected.\n// Remove users from the current user set that are not in the selection.\n// Add users from the selection that are not in the current set of users.\nvoid MphoneForm::newUsers(const list<string> &profiles)\n{\n\tstring error_msg;\n\t\n\t// NOTE: First users must be removed. It could be that a\n\t// user profile of an active was renamed. In this case, the user \n\t// with the old profile name is first removed and then added again.\n\t\n\tlist<t_user *> user_list = phone->ref_users();\n\t\n\t// Remove current users that are not selected anymore.\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n\t\tif (std::find(profiles.begin(), profiles.end(), \n\t\t\t      (*i)->get_filename().c_str()) == profiles.end())\n\t\t{\n\t\t\t// User is not selected anymore.\n\t\t\t// Unsubscribe MWI\n\t\t\tif (phone->is_mwi_subscribed(*i)) {\n\t\t\t\tphone->pub_unsubscribe_mwi(*i);\n\t\t\t}\n\t\t\t\n\t\t\t// Unpublish presence of user\n\t\t\tphone->pub_unpublish_presence(*i);\n\t\t\t\n\t\t\t// Unsubscribe presence\n\t\t\tphone->pub_unsubscribe_presence(*i);\n\t\t\t\n\t\t\t// Deregister user\n\t\t\tif (phone->get_is_registered(*i)) {\n\t\t\t\tphone->pub_registration(*i, REG_DEREGISTER);\n\t\t\t}\n\t\t\t\n\t\t\tlog_file->write_header(\"MphoneForm::newUsers\");\n\t\t\tlog_file->write_raw(\"Stop user profile: \");\n\t\t\tlog_file->write_raw((*i)->get_profile_name());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\tphone->remove_phone_user(*(*i));\n\t\t}\n\t}\n\t\n\t// Determine which users to add\n\tlist<string> add_profile_list;\n\tfor (list<string>::const_iterator i = profiles.begin(); i != profiles.end(); i++) {\n\t\tQString profile = (*i).c_str();\n\t\t// Strip off the .cfg extension\n\t\tprofile.truncate(profile.length() - 4);\n\t\t\n        if (!phone->ref_user_profile(profile.toStdString())) {\n\t\t\tadd_profile_list.push_back(*i);\n\t\t}\n\t}\n\t\n\t// Add new phone users\n    QProgressDialog progress(tr(\"Starting user profiles...\"), \"Abort\", 0, add_profile_list.size(), this);\n    progress.setModal(true);\n    progress.setWindowTitle(PRODUCT_NAME);\n\tprogress.setMinimumDuration(200);\n\tint progressStep = 0;\n\tfor (list<string>::iterator i = add_profile_list.begin(); i != add_profile_list.end(); i++) {\n        progress.setValue(progressStep);\n\t\tqApp->processEvents();\n\t\t\n        if (progress.wasCanceled()) {\n\t\t\tlog_file->write_report(\"User aborted startup of new users.\", \n\t\t\t\t\t       \"MphoneForm::newUsers\");\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tt_user user_config;\n\t\t\n\t\t// Read user configuration\n\t\tif (user_config.read_config(*i, error_msg)) {\n\t\t\tt_user *dup_user;\n\t\t\t\n\t\t\tlog_file->write_header(\"MphoneForm::newUsers\");\n\t\t\tlog_file->write_raw(\"Run user profile: \");\n\t\t\tlog_file->write_raw(user_config.get_profile_name());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tif (phone->add_phone_user(user_config, &dup_user))\n\t\t\t{\n\t\t\t\t// NAT discovery\n\t\t\t\tif (!phone->stun_discover_nat(&user_config, error_msg)) \n\t\t\t\t{\n\t\t\t\t\t// Warn user that the STUN settings will not work.\n\t\t\t\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, \n\t\t\t\t\t\t\tMSG_WARNING);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Register at startup\n\t\t\t\tif (user_config.get_register_at_startup()) {\n\t\t\t\t\tphone->pub_registration(&user_config,\n\t\t\t\t\t\tREG_REGISTER,\n\t\t\t\t\t\tDUR_REGISTRATION(&user_config));\n\t\t\t\t} else {\n\t\t\t\t\t// No registration needed, initialize extensions now.\n\t\t\t\t\tphone->init_extensions(&user_config);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Extension initialization will be done after \n\t\t\t\t// registration succeeded.\n\t\t\t} else {\n                error_msg = tr(\"The following profiles are both for user %1\").arg(QString::fromStdString(user_config.get_name())).toStdString();\n\t\t\t\terror_msg += '@';\n\t\t\t\terror_msg += user_config.get_domain();\n\t\t\t\terror_msg += \":\\n\\n\";\n\t\t\t\terror_msg += user_config.get_profile_name();\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += dup_user->get_profile_name();\n\t\t\t\terror_msg += \"\\n\\n\";\n                error_msg += tr(\"You can only run multiple profiles for different users.\").toStdString();\n\t\t\t\t\n\t\t\t\tlog_file->write_report(error_msg,\n\t\t\t\t\t\"MphoneForm::newUsers\", \n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tui->cb_display_msg(error_msg, MSG_WARNING);\n\t\t\t}\n\t\t} else {\n\t\t\tlog_file->write_report(error_msg,\n\t\t\t\t\t\"MphoneForm::newUsers\", \n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tui->cb_display_msg(error_msg, MSG_CRITICAL);\n\t\t}\n\t\t\n\t\tprogressStep++;\n\t}\n    progress.setValue(add_profile_list.size());\n\t\n\tpopulateBuddyList();\n\tupdateUserComboBox();\n\tupdateRegStatus();\n\tupdateMwi();\n\tupdateServicesStatus();\n\tupdateSysTrayStatus();\n\tupdateMenuStatus();\n\tupdateState();\n\t\n\tcall_history->clear_num_missed_calls();\n}\n\nvoid MphoneForm::updateUserComboBox()\n{\n\tQString current_user;\n\t\n\tif (userComboBox->count() == 0) {\n\t\t// The last used profile\n\t\tcurrent_user = sys_config->get_last_used_profile().c_str();\n\t} else {\n\t\t// Keep the current active profile\n\t\tcurrent_user = userComboBox->currentText();\n\t}\n\t\n\t((t_gui *)ui)->fill_user_combo(userComboBox);\n\t\n\t// If previous selected user is still active, make it the current user\n\tfor (int i = 0; i < userComboBox->count(); i++) {\n        if (userComboBox->itemText(i) == current_user) {\n            userComboBox->setCurrentIndex(i);\n\t\t}\n\t}\n}\n\nvoid MphoneForm::updateSipUdpPort()\n{\n\t((t_gui *)ui)->cb_show_msg(sysSettingsForm,\n\t\t\ttr(\"You have changed the SIP UDP port. This setting will only become \"\\\n            \"active when you restart Twinkle.\").toStdString(),\n\t\t\tMSG_INFO);\n}\n\nvoid MphoneForm::updateRtpPorts()\n{\n\tphone->init_rtp_ports();\n}\n\nvoid MphoneForm::updateStunSettings(t_user *user_config)\n{\n\tstring s;\n\tif (!phone->stun_discover_nat(user_config, s)) {\n\t\t// Warn user that the STUN settings will not work.\n\t\t((t_gui *)ui)->cb_show_msg(this, s, MSG_WARNING);\n\t}\n\t\n\tif (!user_config->get_use_stun()) {\n\t\t// Disable STUN\n\t\tphone->disable_stun(user_config);\n\t}\n\t\n\t// Synchronize the sending of NAT keep alives with the user profile settings.\n\tphone->sync_nat_keepalive(user_config);\n}\n\nvoid MphoneForm::updateAuthCache(t_user *user_config, const string &realm)\n{\n\tphone->remove_cached_credentials(user_config, realm);\n}\n\nvoid MphoneForm::unsubscribeMWI(t_user *user_config)\n{\n\tphone->pub_unsubscribe_mwi(user_config);\n}\n\nvoid MphoneForm::subscribeMWI(t_user *user_config)\n{\n\tphone->pub_subscribe_mwi(user_config);\n}\n\nvoid MphoneForm::viewLog()\n{\n\tif (!logViewForm) {\n\t\tlogViewForm = new LogViewForm(NULL);\n\t\tMEMMAN_NEW(logViewForm);\n\t}\n\t\n\tlogViewForm->show();\n}\n\nvoid MphoneForm::updateLog(bool log_zapped)\n{\n\tif (logViewForm) logViewForm->update(log_zapped);\n}\n\nvoid MphoneForm::viewHistory()\n{\n\tif (!historyForm) {\n\t\thistoryForm = new HistoryForm(NULL);\n\t\tMEMMAN_NEW(historyForm);\n\t}\n\t\n\tconnect(historyForm, \n\t\tSIGNAL(call(t_user *, const QString &, const QString &, bool)), this,  \n\t\tSLOT(phoneInvite(t_user *, const QString &, const QString &, bool)));\n\t\n\thistoryForm->show();\n}\n\nvoid MphoneForm::updateCallHistory()\n{\n\tif (historyForm) historyForm->update();\n}\n\nQSystemTrayIcon *MphoneForm::getSysTray()\n{\n\treturn sysTray;\n}\n\n// Execute call directly from the main window (press call button)\nvoid MphoneForm::quickCall()\n{\n\tstring display, dest_str;\n\t\n\tt_user *from_user = phone->ref_user_profile(\n                userComboBox->currentText().toStdString());\n\tif (!from_user) {\n\t\tlog_file->write_report(\"Cannot find user profile.\",\n\t\t\t       \"MphoneForm::quickCall\", \n\t\t\t       LOG_NORMAL, LOG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\tui->expand_destination(from_user, \n                   callComboBox->currentText().trimmed().toStdString(),\n\t\t\t       display, dest_str);\n\tt_url dest(dest_str);\n\t\n\tif (dest.is_valid()) {\n\t\tQString destination = callComboBox->currentText();\n\t\taddToCallComboBox(destination);\n\t\tif (inviteForm) inviteForm->addToInviteComboBox(destination);\n\t\tcallComboBox->setFocus();\n\t\tdo_phoneInvite(from_user, display.c_str(), dest, \"\", false);\n\t}\n}\n\n// Add a destination to the list of callComboBox\nvoid MphoneForm::addToCallComboBox(const QString &destination)\n{\n\t// Remove duplicate entries\n\tfor (int i = callComboBox->count() - 1; i >= 0; i--) {\n        if (callComboBox->itemText(i) == destination) {\n\t\t\tcallComboBox->removeItem(i);\n\t\t}\n\t}\n\t\n\t// Add entry\n    callComboBox->insertItem(0, destination);\n    callComboBox->setCurrentIndex(0);\n\t\n\t// Remove last entry is list exceeds maximum size\n\tif (callComboBox->count() > SIZE_REDIAL_LIST) {\n\t\tcallComboBox->removeItem(callComboBox->count() - 1);\n\t}\n\t\n\t// Clearing the edit line must be done here as this function is\n\t// also called when a call is made through the inviteForm.\n\t// The insertItem puts the text also in the edit field. So it must\n\t// be cleared here.\n    callComboBox->clearEditText();\n}\n\nvoid MphoneForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid MphoneForm::selectedAddress(const QString &address)\n{\n\tcallComboBox->setEditText(address);\n}\n\n// Enable/disable the various call widgets\nvoid MphoneForm::enableCallOptions(bool enable)\n{\n\t// Enable/disable widgets\n\tcallInvite->setEnabled(enable);\n\tcallPushButton->setEnabled(enable);\n\tcallComboBox->setEnabled(enable);\n\taddressToolButton->setEnabled(enable);\n\t\n\t// Set focus on callComboBox\n\tif (enable) {\n\t\tcallComboBox->setFocus();\n\t}\n}\n\nvoid MphoneForm::keyPressEvent(QKeyEvent *e)\n{\n\tif (callPushButton->isEnabled()) {\n\t\t// Quick dial\n\t\tswitch (e->key()) {\n\t\tcase Qt::Key_Return:\n\t\tcase Qt::Key_Enter:\n\t\t\tquickCall();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\te->ignore();\n\t\t}\n\t} else if (callDTMF->isEnabled()) {\n\t\t// DTMF keys\n\t\tswitch (e->key()) {\n\t\tcase Qt::Key_1:\n\t\t\tsendDTMF(\"1\");\n\t\t\tbreak;\n\t\tcase Qt::Key_2:\n\t\tcase Qt::Key_A:\n\t\tcase Qt::Key_B:\n\t\tcase Qt::Key_C:\n\t\t\tsendDTMF(\"2\");\n\t\t\tbreak;\n\t\tcase Qt::Key_3:\n\t\tcase Qt::Key_D:\n\t\tcase Qt::Key_E:\n\t\tcase Qt::Key_F:\n\t\t\tsendDTMF(\"3\");\n\t\t\tbreak;\n\t\tcase Qt::Key_4:\n\t\tcase Qt::Key_G:\n\t\tcase Qt::Key_H:\n\t\tcase Qt::Key_I:\n\t\t\tsendDTMF(\"4\");\n\t\t\tbreak;\n\t\tcase Qt::Key_5:\n\t\tcase Qt::Key_J:\n\t\tcase Qt::Key_K:\n\t\tcase Qt::Key_L:\n\t\t\tsendDTMF(\"5\");\n\t\t\tbreak;\n\t\tcase Qt::Key_6:\n\t\tcase Qt::Key_M:\n\t\tcase Qt::Key_N:\n\t\tcase Qt::Key_O:\n\t\t\tsendDTMF(\"6\");\n\t\t\tbreak;\n\t\tcase Qt::Key_7:\n\t\tcase Qt::Key_P:\n\t\tcase Qt::Key_Q:\n\t\tcase Qt::Key_R:\n\t\tcase Qt::Key_S:\n\t\t\tsendDTMF(\"7\");\n\t\t\tbreak;\n\t\tcase Qt::Key_8:\n\t\tcase Qt::Key_T:\n\t\tcase Qt::Key_U:\n\t\tcase Qt::Key_V:\n\t\t\tsendDTMF(\"8\");\n\t\t\tbreak;\n\t\tcase Qt::Key_9:\n\t\tcase Qt::Key_W:\n\t\tcase Qt::Key_X:\n\t\tcase Qt::Key_Y:\n\t\tcase Qt::Key_Z:\n\t\t\tsendDTMF(\"9\");\n\t\t\tbreak;\n\t\tcase Qt::Key_0:\n\t\tcase Qt::Key_Space:\n\t\t\tsendDTMF(\"0\");\n\t\t\tbreak;\n\t\tcase Qt::Key_Asterisk:\n\t\t\tsendDTMF(\"*\");\n\t\t\tbreak;\n\t\tcase Qt::Key_NumberSign:\n\t\t\tsendDTMF(\"#\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\te->ignore();\n\t\t}\n\t} else {\n\t\te->ignore();\n\t}\n}\n\n// QLabels do not have mouse click events. I want the status labels\n// to be clickable however. Explicitly check here if a status label has\n// been clicked.\nvoid MphoneForm::mouseReleaseEvent(QMouseEvent *e)\n{\n\tif (e->button() == Qt::LeftButton && e->type() == QEvent::MouseButtonRelease) {\n\t\tprocessLeftMouseButtonRelease(e);\n\t} else if (e->button() == Qt::RightButton && e->type() == QEvent::MouseButtonRelease) {\n\t\tprocessRightMouseButtonRelease(e);\n\t} else {\n\t\te->ignore();\n\t}\n}\n\nvoid MphoneForm::processLeftMouseButtonRelease(QMouseEvent *e)\n{\n    if (statAaLabel->testAttribute(Qt::WA_UnderMouse)) {\n\t\tif (phone->ref_users().size() == 1) {\n            bool enable = !serviceAutoAnswer->isChecked();\n\t\t\tsrvAutoAnswer(enable);\n            serviceAutoAnswer->setChecked(enable);\n\t\t} else {\n\t\t\tsrvAutoAnswer();\n\t\t}\n    } else if (statDndLabel->testAttribute(Qt::WA_UnderMouse)) {\n\t\tif (phone->ref_users().size() == 1) {\n            bool enable = !serviceDnd->isChecked();\n\t\t\tsrvDnd(enable);\n            serviceDnd->setChecked(enable);\n\t\t} else {\n\t\t\tsrvDnd();\n\t\t}\n    } else if (statCfLabel->testAttribute(Qt::WA_UnderMouse)) {\n\t\tsrvRedirect();\n    } else if (statMWILabel->testAttribute(Qt::WA_UnderMouse)) {\n\t\tpopupMenuVoiceMail(e->globalPos());\n    } else if (statMissedLabel->testAttribute(Qt::WA_UnderMouse)) {\n\t\t// Open the history form, when the user clicks on the \n\t\t// missed calls indication.\n\t\tviewHistory();\n    } else if (statRegLabel->testAttribute(Qt::WA_UnderMouse)) {\n\t\t// Fetch registration status\n\t\tphoneShowRegistrations();\n    } else if (crypt1Label->testAttribute(Qt::WA_UnderMouse)) {\n\t\tprocessCryptLabelClick(0);\n    } else if (crypt2Label->testAttribute(Qt::WA_UnderMouse)) {\n\t\tprocessCryptLabelClick(1);\n\t} else {\n\t\te->ignore();\n\t}\n}\n\nvoid MphoneForm::processRightMouseButtonRelease(QMouseEvent *e)\n{\n\te->ignore();\n}\n\nvoid MphoneForm::processCryptLabelClick(int line) \n{\n\tt_audio_session *as = phone->get_line(line)->get_audio_session();\n\tif (!as) return;\n\t\n\tif (as->get_zrtp_sas_confirmed()) {\n\t\tphoneResetZrtpSasConfirmation(line);\n\t} else {\n\t\tphoneConfirmZrtpSas(line);\n\t}\n}\n\n// Show popup menu to access voice mail\nvoid MphoneForm::popupMenuVoiceMail(const QPoint &pos)\n{\n    QMenu menu(this);\n\tQIcon vmIcon(QPixmap(\":/icons/images/mwi_none16.png\"));\n    vmIcon.addPixmap(QPixmap(\":/icons/images/mwi_none16_dis.png\"), QIcon::Disabled);\n\t\n\tlist<t_user *>user_list = phone->ref_users();\n    map<QAction*, t_user *> vm;\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); ++i) {\n\t\tQString address = (*i)->get_mwi_vm_address().c_str();\n\t\tQString entry\t\t= (*i)->get_profile_name().c_str();\n\t\tentry += \" - \";\t\t\n\t\tif (address.isEmpty()) {\n\t\t\tentry += tr(\"not provisioned\");\n\t\t} else {\n\t\t\tentry += address;\n\t\t}\t\t\n\t\t\n        QAction* act = menu.addAction(vmIcon, entry);\n\t\tif (address.isEmpty()) {\n            act->setEnabled(false);\n\t\t}\n\t\t\n        vm.insert(make_pair(act, *i));\n\t}\n\t\n    QAction* selected;\n\t\n\t// If multiple profiles are active, then show the popup menu.\n\t// If one profile is active, then call voice mail immediately.\n\tif (user_list.size() > 1) {\n\t\tselected = menu.exec(pos);\n        if (selected == nullptr) return;\n\t} else {\n\t\tif (vm.begin()->second->get_mwi_vm_address().empty()) {\n\t\t\tui->cb_show_msg(\n\t\t\t\ttr(\"You must provision your voice mail address in your \"\n                   \"user profile, before you can access it.\").toStdString(),\n\t\t\t\tMSG_INFO);\n\t\t\treturn;\n\t\t}\n\t\tselected = vm.begin()->first;\n\t}\n\t\n\t// Call can only be made if line is idle\n\tint line = phone->get_active_line();\n\tif (phone->get_line_state(line) == LS_BUSY) {\n        ui->cb_show_msg(tr(\"The line is busy. Cannot access voice mail.\").toStdString(),\n\t\t\t\tMSG_WARNING);\n\t\treturn;\n\t}\n\t\n\tt_user *selectedUser = vm[selected];\n\tstring display, dest_str;\n\tui->expand_destination(selectedUser, \n\t\t       selectedUser->get_mwi_vm_address(), \n\t\t       display, dest_str);\n\tt_url dest(dest_str);\n\t\n\tif (dest.is_valid()) {\n\t\tQString destination = selectedUser->get_mwi_vm_address().c_str();\n\t\taddToCallComboBox(destination);\n\t\tif (inviteForm) inviteForm->addToInviteComboBox(destination);\n\t\tcallComboBox->setFocus();\n\t\tdo_phoneInvite(selectedUser, display.c_str(), dest, \"\", false);\n\t} else {\n\t\tQString msg(tr(\"The voice mail address %1 is an invalid address. \"\n\t\t\t       \"Please provision a valid address in your user profile.\"));\n        ui->cb_show_msg(msg.arg(selectedUser->get_mwi_vm_address().c_str()).toStdString(),\n\t\t\t\tMSG_CRITICAL);\n\t}\n}\n\nvoid MphoneForm::popupMenuVoiceMail(void)\n{\n\tpopupMenuVoiceMail(QCursor::pos());\n}\n\nvoid MphoneForm::showDisplay(bool on)\n{\n\tif (on) {\n\t\tdisplayGroupBox->show();\n\t} else {\n\t\tint hDisplay = displayGroupBox->height();\n\t\tdisplayGroupBox->hide();\n\t\t\n\t\tif (hDisplay < minimumHeight()) {\n\t\t\tsetMinimumHeight(minimumHeight() - hDisplay);\n\t\t}\n\t\tresize(width(), minimumHeight());\n\t}\n\t\n\tviewDisplay = on;\n    viewDisplayAction->setChecked(on);\n}\n\nvoid MphoneForm::showBuddyList(bool on)\n{\n\tif (on) {\n\t\tbuddyListView->show();\n\t} else {\n\t\tbuddyListView->hide();\n\t}\n\t\n\tviewBuddyList = on;\n    viewBuddyListAction->setChecked(on);\n}\n\nvoid MphoneForm::showCompactLineStatus(bool on)\n{\n\tif (on) {\n\t\tint hLabels = fromhead1Label->height() +\n\t\t\t      tohead1Label->height() +\n\t\t\t      subjecthead1Label->height() +\n\t\t\t      fromhead2Label->height() +\n\t\t\t      tohead2Label->height() +\n\t\t\t      subjecthead2Label->height();\n\t\t\n\t\tfromhead1Label->hide();\n\t\ttohead1Label->hide();\n\t\tsubjecthead1Label->hide();\n\t\tfrom1Label->hide();\n\t\tto1Label->hide();\n\t\tsubject1Label->hide();\n\t\tphoto1Label->hide();\n\t\tfromhead2Label->hide();\n\t\ttohead2Label->hide();\n\t\tsubjecthead2Label->hide();\n\t\tfrom2Label->hide();\n\t\tto2Label->hide();\n\t\tsubject2Label->hide();\n\t\tphoto2Label->hide();\n\t\t\n\t\tif (hLabels < minimumHeight()) {\n\t\t\tsetMinimumHeight(minimumHeight() - hLabels);\n\t\t}\n\t\tresize(width(), minimumHeight());\n\t} else {\n\t\tfromhead1Label->show();\n\t\ttohead1Label->show();\n\t\tsubjecthead1Label->show();\n\t\tfrom1Label->show();\n\t\tto1Label->show();\n\t\tsubject1Label->show();\n\t\tfromhead2Label->show();\n\t\ttohead2Label->show();\n\t\tsubjecthead2Label->show();\n\t\tfrom2Label->show();\n\t\tto2Label->show();\n\t\tsubject2Label->show();\n\t}\n\t\n\tviewCompactLineStatus = on;\n\t//viewCompactLineStatusAction->setOn(on);\n}\n\nbool MphoneForm::getViewDisplay()\n{\n\treturn viewDisplay;\n}\n\nbool MphoneForm::getViewBuddyList()\n{\n\treturn viewBuddyList;\n}\n\nbool MphoneForm::getViewCompactLineStatus()\n{\n\treturn viewCompactLineStatus;\n}\n\nvoid MphoneForm::populateBuddyList()\n{\n\tbuddyListView->clear();\n\n\tlist<t_user *> user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); ++i) {\n\t\tt_presence_epa *epa = phone->ref_presence_epa(*i);\n\t\tif (!epa) continue;\n\t\t\n\t\tBLViewUserItem *profileItem = new BLViewUserItem(buddyListView, epa);\n\t\tt_buddy_list *buddy_list = phone->ref_buddy_list(*i);\n\t\t\n\t\tlist<t_buddy> *buddies = buddy_list->get_records();\n\t\tfor (list<t_buddy>::iterator bit = buddies->begin(); bit != buddies->end(); ++bit) {\n\t\t\tQString name = bit->get_name().c_str();\n\t\t\tnew BuddyListViewItem(profileItem, &(*bit));\n\t\t}\n\t\t\n        // profileItem->setOpen(true);\n\t}\n    buddyListView->expandAll();\n}\n\nvoid MphoneForm::showBuddyListPopupMenu(const QPoint &pos)\n{\n    QTreeWidgetItem* item = buddyListView->currentItem();\n\tif (!item) return;\n\t\n\tBuddyListViewItem *buddyItem = dynamic_cast<BuddyListViewItem *>(item);\n\tif (buddyItem) {\n        buddyPopupMenu->popup(buddyListView->mapToGlobal(pos));\n\t} else {\n        buddyListPopupMenu->popup(buddyListView->mapToGlobal(pos));\n\t}\n}\n\nvoid MphoneForm::doCallBuddy()\n{\n    QTreeWidgetItem *qitem = buddyListView->currentItem();\n\tBuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_buddy *buddy = item->get_buddy();\n\tt_user *user_config = buddy->get_user_profile();\n\t\n\tphoneInvite(user_config, buddy->get_sip_address().c_str(), \"\", false);\n}\n\nvoid MphoneForm::doMessageBuddy(QTreeWidgetItem *qitem)\n{\t\n\tBuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_buddy *buddy = item->get_buddy();\n\t\n\tstartMessageSession(buddy);\n}\n\nvoid MphoneForm::doMessageBuddy()\n{\n    QTreeWidgetItem *item = buddyListView->currentItem();\n\tdoMessageBuddy(item);\n}\n\nvoid MphoneForm::doEditBuddy()\n{\n    QTreeWidgetItem *qitem = buddyListView->currentItem();\n\tBuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_buddy *buddy = item->get_buddy();\n\t\n    BuddyForm *form = new BuddyForm(this);\n    form->setModal(true);\n    form->setAttribute(Qt::WA_DeleteOnClose, true);\n\t// Do not call MEMMAN as this form will be deleted automatically.\t\n\tform->showEdit(*buddy);\n}\n\nvoid MphoneForm::doDeleteBuddy()\n{\n    QTreeWidgetItem *qitem = buddyListView->currentItem();\n\tBuddyListViewItem *item = dynamic_cast<BuddyListViewItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_buddy *buddy = item->get_buddy();\n\tt_buddy_list *buddy_list = buddy->get_buddy_list();\n\t\n\t// Delete the list item before deleting the buddy as\n\t// deleting the item will detach the item from the buddy.\n\tdelete item;\n\t\t\n\tif (buddy->is_presence_terminated()) {\n\t\tbuddy_list->del_buddy(*buddy);\n\t} else {\n\t\tbuddy->unsubscribe_presence(true);\n\t}\n\t\t\n\tstring err_msg;\n\tif (!buddy_list->save(err_msg)) {\n\t\tQString msg = tr(\"Failed to save buddy list: %1\").arg(err_msg.c_str());\n        ((t_gui *)ui)->cb_show_msg(this, msg.toStdString(), MSG_CRITICAL);\n\t}\n}\n\nvoid MphoneForm::doAddBuddy()\n{\n    QTreeWidgetItem *qitem = buddyListView->currentItem();\n\tBLViewUserItem *item = dynamic_cast<BLViewUserItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_phone_user *pu = item->get_presence_epa()->get_phone_user();\n\tif (!pu) return;\n\tt_buddy_list *buddy_list = pu->get_buddy_list();\n\tif (!buddy_list) return;\n\t\n    BuddyForm *form = new BuddyForm(this);\n    form->setModal(true);\n    form->setAttribute( Qt::WA_DeleteOnClose, true );\n\t// Do not call MEMMAN as this form will be deleted automatically.\n\tform->showNew(*buddy_list, item);\n}\n\nvoid MphoneForm::doAvailabilityOffline()\n{\n    QTreeWidgetItem *qitem = buddyListView->currentItem();\n\tBLViewUserItem *item = dynamic_cast<BLViewUserItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_phone_user *pu = item->get_presence_epa()->get_phone_user();\n\tif (!pu) return;\n\t\n\tpu->publish_presence(t_presence_state::ST_BASIC_CLOSED);\n}\n\nvoid MphoneForm::doAvailabilityOnline()\n{\n    QTreeWidgetItem *qitem = buddyListView->currentItem();\n\tBLViewUserItem *item = dynamic_cast<BLViewUserItem *>(qitem);\n\tif (!item) return;\n\t\n\tt_phone_user *pu = item->get_presence_epa()->get_phone_user();\n\tif (!pu) return;\n\t\n\tpu->publish_presence(t_presence_state::ST_BASIC_OPEN);\n}\n\nvoid MphoneForm::DiamondcardSignUp()\n{\n    DiamondcardProfileForm *f = new DiamondcardProfileForm(this);\n\t\n    f->setModal(true);\n    f->setAttribute( Qt::WA_DeleteOnClose, true );\n\tconnect(f, SIGNAL(newDiamondcardProfile(const QString&)),\n\t\t\tthis, SLOT(newDiamondcardUser(const QString &)));\n\t\n\tf->show(NULL);\n}\n\nvoid MphoneForm::newDiamondcardUser(const QString &filename)\n{\n\tlist<string> profileFilenames;\n\tlist<t_user *> users = phone->ref_users();\n\t\n\tfor (list<t_user *>::const_iterator it = users.begin(); it != users.end(); ++it) {\n\t\tt_user *user = *it;\n\t\tprofileFilenames.push_back(user->get_filename());\n\t}\n\t\n    profileFilenames.push_back(filename.toStdString());\n\tnewUsers(profileFilenames);\n}\n\nvoid MphoneForm::DiamondcardAction(t_dc_action action, int userIdx)\n{\n\tlist<t_user *> diamondcard_users = diamondcard_get_users(phone);\n\tvector<t_user *> v(diamondcard_users.begin(), diamondcard_users.end());\n\t\n\tif (userIdx < 0 || (unsigned int)userIdx >= v.size()) return;\n\t\n\tt_user *user = v[userIdx];\n\tQString url(diamondcard_url(action, user->get_name(), user->get_auth_pass()).c_str());\n\t((t_gui *)ui)->open_url_in_browser(url);\n}\n\nvoid MphoneForm::DiamondcardRecharge()\n{\n    DiamondcardAction(DC_ACT_RECHARGE, static_cast<QAction*>(sender())->data().toInt());\n}\n\nvoid MphoneForm::DiamondcardBalanceHistory()\n{\n    DiamondcardAction(DC_ACT_BALANCE_HISTORY, static_cast<QAction*>(sender())->data().toInt());\n}\n\nvoid MphoneForm::DiamondcardCallHistory()\n{\n    DiamondcardAction(DC_ACT_CALL_HISTORY, static_cast<QAction*>(sender())->data().toInt());\n}\n\nvoid MphoneForm::DiamondcardAdminCenter()\n{\n    DiamondcardAction(DC_ACT_ADMIN_CENTER, static_cast<QAction*>(sender())->data().toInt());\n}\n\nvoid MphoneForm::whatsThis()\n{\n    QWhatsThis::enterWhatsThisMode();\n}\n\nvoid MphoneForm::sysTrayIconClicked(QSystemTrayIcon::ActivationReason reason)\n{\n\tif (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick)\n\t\ttoggleWindow();\n}\n\nvoid MphoneForm::toggleWindow()\n{\n\tsetVisible(!isVisible());\n}\n\nvoid MphoneForm::updateTrayIconMenu()\n{\n\tif (isVisible())\n\t\ttoggleWindowAction->setText(tr(\"Hide window\"));\n\telse\n\t\ttoggleWindowAction->setText(tr(\"Show window\"));\n}\n\nbool MphoneForm::event(QEvent * event)\n{\n\tif (event->type() == QEvent::WindowActivate || event->type() == QEvent::WindowDeactivate)\n\t\tif (osdWindow)\n\t\t\tosdWindow->setVisible(shouldDisplayOSD());\n\n\treturn QMainWindow::event(event);\n}\n\nbool MphoneForm::shouldDisplayOSD()\n{\n\tif (QApplication::activeWindow() == this)\n\t\treturn false;\n\n\tt_line_substate ss;\n\n\tss = phone->get_line_substate(phone->get_active_line());\n\n\tswitch (ss)\n\t{\n\tcase LSSUB_ANSWERING:\n\tcase LSSUB_ESTABLISHED:\n\tcase LSSUB_OUTGOING_PROGRESS:\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\n\tif (!sys_config->get_gui_show_call_osd())\n\t\treturn false;\n\n\treturn true;\n}\n\nvoid MphoneForm::updateOSD()\n{\n\tt_line_substate ss;\n\tint line;\n\tbool osdDisplayed;\n\tstruct timeval t;\n\tunsigned long duration;\n\tt_user *user_config;\n\tt_call_record cr;\n\n\tosdDisplayed = shouldDisplayOSD();\n\tif (osdWindow)\n\t\tosdWindow->setVisible(osdDisplayed);\n\n\tline = phone->get_active_line();\n\tss = phone->get_line_substate(line);\n\tuser_config = phone->get_line_user(line);\n\n\tcr = phone->get_call_hist(line);\n\n\tif (ss == LSSUB_ESTABLISHED)\n\t{\n\t\t// Calculate duration of call\n\t\tgettimeofday(&t, nullptr);\n\t\tduration = t.tv_sec - cr.time_answer;\n\n\t\tif (osdWindow) {\n\t\t\tosdWindow->setTime(QString::fromStdString(timer2str(duration)));\n\t\t\tosdWindow->setMuted(phone->is_line_muted(line));\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (osdWindow)\n\t\t\tosdWindow->setTime(lineSubstate2str(line));\n\t}\n\n\tif (ss != LSSUB_IDLE && user_config != nullptr)\n\t{\n\t\tstd::string address;\n\n\t\taddress = (cr.direction == t_call_record::DIR_IN ?\n\t\t\t ui->format_sip_address(user_config,\n\t\t\t  cr.from_display, cr.from_uri) :\n\t\t\t ui->format_sip_address(user_config,\n\t\t\t  cr.to_display, cr.to_uri));\n\n\t\tif (osdWindow)\n\t\t\tosdWindow->setCaller(QString::fromStdString(address));\n\t}\n}\n\nvoid MphoneForm::osdMuteClicked()\n{\n\t((t_gui *)ui)->action_mute(!phone->is_line_muted(phone->get_active_line()));\n\tupdateState();\n}\n"
  },
  {
    "path": "src/gui/mphoneform.h",
    "content": "#ifndef MPHONEFORM_UI_H\n#define MPHONEFORM_UI_H\n#include <QMainWindow>\n#include \"ui_mphoneform.h\"\n#include \"phone.h\"\n#include \"dtmfform.h\"\n#include \"inviteform.h\"\n#include \"redirectform.h\"\n#include \"termcapform.h\"\n#include \"srvredirectform.h\"\n#include \"userprofileform.h\"\n#include \"transferform.h\"\n#include \"syssettingsform.h\"\n#include \"logviewform.h\"\n#include \"historyform.h\"\n#include \"selectuserform.h\"\n#include \"selectprofileform.h\"\n#include <QEvent>\n#include <QMenu>\n#include <QSystemTrayIcon>\n#include \"im/msg_session.h\"\n#include \"messageformview.h\"\n#include \"buddylistview.h\"\n#include \"diamondcard.h\"\n\nclass t_phone;\nextern t_phone *phone;\n\nclass OSD;\nclass IncomingCallPopup;\n\nclass MphoneForm : public QMainWindow, public Ui::MphoneForm\n{\nQ_OBJECT\npublic:\n    MphoneForm(QWidget* parent = 0);\n\t~MphoneForm();\n\npublic:\n\tQString getMWIStatus( const t_mwi & mwi, bool & msg_waiting ) const;\n\tQSystemTrayIcon * getSysTray();\n\tbool getViewDisplay();\n\tbool getViewBuddyList();\n\tbool getViewCompactLineStatus();\nprotected:\n\tvirtual void closeEvent( QCloseEvent * e ) override;\n\tvirtual bool event(QEvent * event) override;\npublic slots:\n\tvoid fileExit();\n\tvoid display( const QString & s );\n\tvoid displayHeader();\n\tvoid showLineTimer( int line );\n\tvoid showLineTimer1();\n\tvoid showLineTimer2();\n\tvoid updateLineTimer( int line );\n\tvoid updateLineEncryptionState( int line );\n\tvoid updateLineStatus( int line );\n\tvoid updateState();\n\tvoid updateRegStatus();\n\tvoid flashMWI();\n\tvoid updateMwi();\n\tvoid updateServicesStatus();\n\tvoid updateMissedCallStatus( int num_missed_calls );\n\tvoid updateSysTrayStatus();\n\tvoid updateMenuStatus();\n\tvoid updateDiamondcardMenu();\n    void removeDiamondcardAction( QAction* & id );\n    void removeDiamondcardMenu( QMenu * & menu );\n\tvoid phoneRegister();\n\tvoid do_phoneRegister( list<t_user *> user_list );\n\tvoid phoneDeregister();\n\tvoid do_phoneDeregister( list<t_user *> user_list );\n\tvoid phoneDeregisterAll();\n\tvoid do_phoneDeregisterAll( list<t_user *> user_list );\n\tvoid phoneShowRegistrations();\n\tvoid phoneInvite( t_user * user_config, const QString & dest, const QString & subject, bool anonymous );\n\tvoid phoneInvite( const QString & dest, const QString & subject, bool anonymous );\n\tvoid phoneInvite();\n\tvoid do_phoneInvite( t_user * user_config, const QString & display, const t_url & destination, const QString & subject, bool anonymous );\n\tvoid phoneRedial( void );\n\tvoid phoneAnswer();\n\tvoid phoneAnswerFromSystrayPopup();\n\tvoid phoneBye();\n\tvoid phoneReject();\n\tvoid phoneRejectFromSystrayPopup();\n\tvoid phoneRedirect( const list<string> & contacts );\n\tvoid phoneRedirect();\n\tvoid do_phoneRedirect( const list<t_display_url> & destinations );\n\tvoid phoneTransfer( const string & dest, t_transfer_type transfer_type );\n\tvoid phoneTransfer();\n\tvoid do_phoneTransfer( const t_display_url & destination, t_transfer_type transfer_type );\n\tvoid do_phoneTransferLine();\n\tvoid phoneHold( bool on );\n\tvoid phoneConference();\n\tvoid phoneMute( bool on );\n\tvoid phoneTermCap( const QString & dest );\n\tvoid phoneTermCap();\n\tvoid do_phoneTermCap( t_user * user_config, const t_url & destination );\n\tvoid phoneDTMF();\n\tvoid sendDTMF( const QString & digits );\n\tvoid startMessageSession( void );\n\tvoid startMessageSession( t_buddy * buddy );\n\tvoid phoneConfirmZrtpSas( int line );\n\tvoid phoneConfirmZrtpSas();\n\tvoid phoneResetZrtpSasConfirmation( int line );\n\tvoid phoneResetZrtpSasConfirmation();\n\tvoid phoneEnableZrtp( bool on );\n\tvoid phoneZrtpGoClearOk( unsigned short line );\n\tvoid line1rbChangedState( bool on );\n\tvoid line2rbChangedState( bool on );\n\tvoid actionLine1Toggled( bool on );\n\tvoid actionLine2Toggled( bool on );\n\tvoid srvDnd( bool on );\n\tvoid srvDnd();\n\tvoid do_srvDnd_enable( list<t_user *> user_list );\n\tvoid do_srvDnd_disable( list<t_user *> user_list );\n\tvoid srvAutoAnswer( bool on );\n\tvoid srvAutoAnswer();\n\tvoid do_srvAutoAnswer_enable( list<t_user *> user_list );\n\tvoid do_srvAutoAnswer_disable( list<t_user *> user_list );\n\tvoid srvRedirect();\n\tvoid do_srvRedirect( t_user * user_config, const list<t_display_url> & always, const list<t_display_url> & busy, const list<t_display_url> & noanswer );\n\tvoid about();\n\tvoid aboutQt();\n\tvoid manual();\n\tvoid editUserProfile();\n\tvoid editSysSettings();\n\tvoid selectProfile();\n\tvoid newUsers( const list<string> & profiles );\n\tvoid updateUserComboBox();\n\tvoid updateSipUdpPort();\n\tvoid updateRtpPorts();\n\tvoid updateStunSettings( t_user * user_config );\n\tvoid updateAuthCache( t_user * user_config, const string & realm );\n\tvoid unsubscribeMWI( t_user * user_config );\n\tvoid subscribeMWI( t_user * user_config );\n\tvoid viewLog();\n\tvoid updateLog( bool log_zapped );\n\tvoid viewHistory();\n\tvoid updateCallHistory();\n\tvoid quickCall();\n\tvoid addToCallComboBox( const QString & destination );\n\tvoid showAddressBook();\n\tvoid selectedAddress( const QString & address );\n\tvoid enableCallOptions( bool enable );\n\tvirtual void keyPressEvent( QKeyEvent * e ) override;\n\tvirtual void mouseReleaseEvent( QMouseEvent * e ) override;\n\tvoid processLeftMouseButtonRelease( QMouseEvent * e );\n\tvoid processRightMouseButtonRelease( QMouseEvent * e );\n\tvoid processCryptLabelClick( int line );\n\tvoid popupMenuVoiceMail( const QPoint & pos );\n\tvoid popupMenuVoiceMail( void );\n\tvoid showDisplay( bool on );\n\tvoid showBuddyList( bool on );\n\tvoid showCompactLineStatus( bool on );\n\tvoid populateBuddyList();\n    void showBuddyListPopupMenu( const QPoint & pos );\n\tvoid doCallBuddy();\n    void doMessageBuddy( QTreeWidgetItem * qitem );\n\tvoid doMessageBuddy();\n\tvoid doEditBuddy();\n\tvoid doDeleteBuddy();\n\tvoid doAddBuddy();\n\tvoid doAvailabilityOffline();\n\tvoid doAvailabilityOnline();\n\tvoid DiamondcardSignUp();\n\tvoid newDiamondcardUser( const QString & filename );\n\tvoid DiamondcardAction( t_dc_action action, int userIdx );\n    void DiamondcardRecharge();\n    void DiamondcardBalanceHistory();\n    void DiamondcardCallHistory();\n    void DiamondcardAdminCenter();\n    void whatsThis();\n\tvoid sysTrayIconClicked(QSystemTrayIcon::ActivationReason);\n\tvoid toggleWindow();\n\tvoid updateTrayIconMenu();\n\n\tvoid osdMuteClicked();\n\nprivate:\n\tvoid init();\n\tvoid destroy();\n\tbool shouldDisplayOSD();\n\tvoid updateOSD();\n\tQString lineSubstate2str( int line );\n\nprivate:\n\tQTimer tmrFlashMWI;\n\tGetAddressForm *getAddressForm;\n\tSelectProfileForm *selectProfileForm;\n\tSelectUserForm *selectUserForm;\n\tHistoryForm *historyForm;\n\tTransferForm *transferForm;\n\tUserProfileForm *userProfileForm;\n\tSrvRedirectForm *srvRedirectForm;\n\tTermCapForm *termCapForm;\n\tRedirectForm *redirectForm;\n\tInviteForm *inviteForm;\n\tDtmfForm *dtmfForm;\n\tSysSettingsForm *sysSettingsForm;\n\tQStringList displayContents;\n\tLogViewForm *logViewForm;\n\tQSystemTrayIcon *sysTray;\n\tQTimer *lineTimer1;\n\tQTimer *lineTimer2;\n\tQTimer *hideLineTimer1;\n\tQTimer *hideLineTimer2;\n\tbool viewDisplay;\n\tbool viewCompactLineStatus;\n\tbool mwiFlashStatus;\n    QMenu *buddyPopupMenu;\n    QMenu *buddyListPopupMenu;\n    QMenu *changeAvailabilityPopupMenu;\n\tbool viewBuddyList;\n\tOSD\t*osdWindow;\n\tIncomingCallPopup *incomingCallPopup;\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/mphoneform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MphoneForm</class>\n <widget class=\"QMainWindow\" name=\"MphoneForm\">\n  <property name=\"enabled\">\n   <bool>true</bool>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>733</width>\n    <height>693</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>500</width>\n    <height>693</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset resource=\"icons.qrc\">\n    <normaloff>:/icons/images/twinkle48.png</normaloff>:/icons/images/twinkle48.png</iconset>\n  </property>\n  <property name=\"rightJustification\" stdset=\"0\">\n   <bool>false</bool>\n  </property>\n  <property name=\"usesBigPixmaps\" stdset=\"0\">\n   <bool>false</bool>\n  </property>\n  <property name=\"usesTextLabel\" stdset=\"0\">\n   <bool>true</bool>\n  </property>\n  <widget class=\"QWidget\" name=\"layoutWidget\">\n   <layout class=\"QGridLayout\">\n    <item row=\"0\" column=\"0\">\n     <widget class=\"QSplitter\" name=\"layoutWidgetSplitter\">\n      <property name=\"orientation\">\n       <enum>Qt::Horizontal</enum>\n      </property>\n      <widget class=\"QTreeWidget\" name=\"buddyListView\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n         <horstretch>150</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>0</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"contextMenuPolicy\">\n        <enum>Qt::CustomContextMenu</enum>\n       </property>\n       <property name=\"whatsThis\">\n        <string>You can create a separate buddy list for each user profile. You can only see availability of your buddies and publish your own availability if your provider offers a presence server.</string>\n       </property>\n       <property name=\"rootIsDecorated\">\n        <bool>true</bool>\n       </property>\n       <column>\n        <property name=\"text\">\n         <string>Buddy list</string>\n        </property>\n       </column>\n      </widget>\n      <widget class=\"QWidget\">\n       <layout class=\"QVBoxLayout\">\n        <item>\n         <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"callTextLabel\">\n            <property name=\"text\">\n             <string comment=\"Label in front of combobox to enter address\">&amp;Call:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>callComboBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QComboBox\" name=\"callComboBox\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"whatsThis\">\n               <string>The address that you want to call. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</string>\n              </property>\n              <property name=\"editable\">\n               <bool>true</bool>\n              </property>\n              <property name=\"maxCount\">\n               <number>10</number>\n              </property>\n              <property name=\"insertPolicy\">\n               <enum>QComboBox::NoInsert</enum>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QToolButton\" name=\"addressToolButton\">\n              <property name=\"focusPolicy\">\n               <enum>Qt::TabFocus</enum>\n              </property>\n              <property name=\"toolTip\">\n               <string>Address book</string>\n              </property>\n              <property name=\"whatsThis\">\n               <string>Select an address from the address book.</string>\n              </property>\n              <property name=\"text\">\n               <string/>\n              </property>\n              <property name=\"icon\">\n               <iconset resource=\"icons.qrc\">\n                <normaloff>:/icons/images/kontact_contacts.png</normaloff>:/icons/images/kontact_contacts.png</iconset>\n              </property>\n              <property name=\"shortcut\">\n               <string>F10</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"callPushButton\">\n              <property name=\"whatsThis\">\n               <string>Dial the address.</string>\n              </property>\n              <property name=\"text\">\n               <string>Dial</string>\n              </property>\n              <property name=\"default\">\n               <bool>true</bool>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"userLabel\">\n            <property name=\"text\">\n             <string>&amp;User:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>userComboBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QComboBox\" name=\"userComboBox\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"whatsThis\">\n               <string>The user that will make the call.</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QHBoxLayout\">\n              <item>\n               <widget class=\"QLabel\" name=\"statAaLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Auto answer indication.</string>\n                </property>\n                <property name=\"pixmap\">\n                 <pixmap resource=\"icons.qrc\">:/icons/images/auto_answer.png</pixmap>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"statCfLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Call redirect indication.</string>\n                </property>\n                <property name=\"pixmap\">\n                 <pixmap resource=\"icons.qrc\">:/icons/images/cf.png</pixmap>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"statDndLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Do not disturb indication.</string>\n                </property>\n                <property name=\"pixmap\">\n                 <pixmap resource=\"icons.qrc\">:/icons/images/cancel.png</pixmap>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"statMWILabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Message waiting indication.</string>\n                </property>\n                <property name=\"pixmap\">\n                 <pixmap resource=\"icons.qrc\">:/icons/images/mwi_none16.png</pixmap>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"statMissedLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Missed call indication.</string>\n                </property>\n                <property name=\"pixmap\">\n                 <pixmap resource=\"icons.qrc\">:/icons/images/missed.png</pixmap>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"statRegLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Registration status.</string>\n                </property>\n                <property name=\"pixmap\">\n                 <pixmap resource=\"icons.qrc\">:/icons/images/twinkle16.png</pixmap>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <widget class=\"QGroupBox\" name=\"displayGroupBox\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"title\">\n           <string>Display</string>\n          </property>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QTextEdit\" name=\"displayTextEdit\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"MinimumExpanding\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n             <property name=\"text\" stdset=\"0\">\n              <string/>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QGroupBox\" name=\"lineButtonGroup\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"title\">\n           <string>Line status</string>\n          </property>\n          <property name=\"exclusive\" stdset=\"0\">\n           <bool>true</bool>\n          </property>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <layout class=\"QHBoxLayout\">\n             <item>\n              <layout class=\"QVBoxLayout\">\n               <item>\n                <widget class=\"QRadioButton\" name=\"line1RadioButton\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"whatsThis\">\n                  <string>Click to switch to line 1.</string>\n                 </property>\n                 <property name=\"text\">\n                  <string>Line &amp;1:</string>\n                 </property>\n                 <property name=\"shortcut\">\n                  <string>Alt+1</string>\n                 </property>\n                 <property name=\"checked\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"fromhead1Label\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>From:</string>\n                 </property>\n                 <property name=\"wordWrap\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"indent\">\n                  <number>21</number>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"tohead1Label\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>To:</string>\n                 </property>\n                 <property name=\"wordWrap\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"indent\">\n                  <number>21</number>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"subjecthead1Label\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>Subject:</string>\n                 </property>\n                 <property name=\"wordWrap\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"indent\">\n                  <number>21</number>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <layout class=\"QVBoxLayout\">\n               <item>\n                <layout class=\"QHBoxLayout\">\n                 <item>\n                  <widget class=\"QLabel\" name=\"line1StatLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"status1TextLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"text\">\n                    <string comment=\"No need to translate\">idle</string>\n                   </property>\n                   <property name=\"textFormat\">\n                    <enum>Qt::RichText</enum>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line1HoldLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line1MuteLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line1ConfLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line1ReferLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"crypt1Label\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line1SasLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"whatsThis\">\n                    <string>Short authentication string</string>\n                   </property>\n                   <property name=\"text\">\n                    <string comment=\"No need to translate\">sas</string>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"codec1TextLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"whatsThis\">\n                    <string>Audio codec</string>\n                   </property>\n                   <property name=\"text\">\n                    <string comment=\"No need to translate\">g711a/g711a</string>\n                   </property>\n                   <property name=\"alignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"timer1TextLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"font\">\n                    <font>\n                     <family>Courier New</family>\n                    </font>\n                   </property>\n                   <property name=\"whatsThis\">\n                    <string>Call duration</string>\n                   </property>\n                   <property name=\"text\">\n                    <string>0:00:00</string>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                </layout>\n               </item>\n               <item>\n                <widget class=\"QLineEdit\" name=\"from1Label\">\n                 <property name=\"focusPolicy\">\n                  <enum>Qt::NoFocus</enum>\n                 </property>\n                 <property name=\"text\">\n                  <string comment=\"No need to translate\">sip:from</string>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLineEdit\" name=\"to1Label\">\n                 <property name=\"focusPolicy\">\n                  <enum>Qt::NoFocus</enum>\n                 </property>\n                 <property name=\"text\">\n                  <string comment=\"No need to translate\">sip:to</string>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLineEdit\" name=\"subject1Label\">\n                 <property name=\"focusPolicy\">\n                  <enum>Qt::NoFocus</enum>\n                 </property>\n                 <property name=\"text\">\n                  <string comment=\"No need to translate\">subject</string>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"photo1Label\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>70</width>\n                 <height>98</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>70</width>\n                 <height>98</height>\n                </size>\n               </property>\n               <property name=\"frameShape\">\n                <enum>QFrame::StyledPanel</enum>\n               </property>\n               <property name=\"text\">\n                <string comment=\"No need to translate\">photo</string>\n               </property>\n               <property name=\"wordWrap\">\n                <bool>false</bool>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\">\n             <item>\n              <layout class=\"QVBoxLayout\">\n               <item>\n                <widget class=\"QRadioButton\" name=\"line2RadioButton\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"whatsThis\">\n                  <string>Click to switch to line 2.</string>\n                 </property>\n                 <property name=\"text\">\n                  <string>Line &amp;2:</string>\n                 </property>\n                 <property name=\"shortcut\">\n                  <string>Alt+2</string>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"fromhead2Label\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>From:</string>\n                 </property>\n                 <property name=\"wordWrap\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"indent\">\n                  <number>21</number>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"tohead2Label\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>To:</string>\n                 </property>\n                 <property name=\"wordWrap\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"indent\">\n                  <number>21</number>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"subjecthead2Label\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>Subject:</string>\n                 </property>\n                 <property name=\"wordWrap\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"indent\">\n                  <number>21</number>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <layout class=\"QVBoxLayout\">\n               <item>\n                <layout class=\"QHBoxLayout\">\n                 <item>\n                  <widget class=\"QLabel\" name=\"line2StatLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"status2TextLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"text\">\n                    <string comment=\"No need to translate\">idle</string>\n                   </property>\n                   <property name=\"textFormat\">\n                    <enum>Qt::RichText</enum>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line2HoldLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line2MuteLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line2ConfLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line2ReferLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"crypt2Label\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"line2SasLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"whatsThis\">\n                    <string>Short authentication string</string>\n                   </property>\n                   <property name=\"text\">\n                    <string comment=\"No need to translate\">sas</string>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"codec2TextLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"whatsThis\">\n                    <string>Audio codec</string>\n                   </property>\n                   <property name=\"text\">\n                    <string comment=\"No need to translate\">g711a/g711a</string>\n                   </property>\n                   <property name=\"alignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item>\n                  <widget class=\"QLabel\" name=\"timer2TextLabel\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"font\">\n                    <font>\n                     <family>Courier New</family>\n                    </font>\n                   </property>\n                   <property name=\"whatsThis\">\n                    <string>Call duration</string>\n                   </property>\n                   <property name=\"text\">\n                    <string>0:00:00</string>\n                   </property>\n                   <property name=\"wordWrap\">\n                    <bool>false</bool>\n                   </property>\n                  </widget>\n                 </item>\n                </layout>\n               </item>\n               <item>\n                <widget class=\"QLineEdit\" name=\"from2Label\">\n                 <property name=\"focusPolicy\">\n                  <enum>Qt::NoFocus</enum>\n                 </property>\n                 <property name=\"text\">\n                  <string comment=\"No need to translate\">sip:from</string>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLineEdit\" name=\"to2Label\">\n                 <property name=\"focusPolicy\">\n                  <enum>Qt::NoFocus</enum>\n                 </property>\n                 <property name=\"text\">\n                  <string comment=\"No need to translate\">sip:to</string>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLineEdit\" name=\"subject2Label\">\n                 <property name=\"focusPolicy\">\n                  <enum>Qt::NoFocus</enum>\n                 </property>\n                 <property name=\"text\">\n                  <string comment=\"No need to translate\">subject</string>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"photo2Label\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>70</width>\n                 <height>98</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>70</width>\n                 <height>98</height>\n                </size>\n               </property>\n               <property name=\"frameShape\">\n                <enum>QFrame::StyledPanel</enum>\n               </property>\n               <property name=\"text\">\n                <string comment=\"No need to translate\">photo</string>\n               </property>\n               <property name=\"wordWrap\">\n                <bool>false</bool>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QToolBar\" name=\"callToolbar\">\n   <property name=\"windowTitle\">\n    <string>Main Toolbar</string>\n   </property>\n   <property name=\"toolButtonStyle\">\n    <enum>Qt::ToolButtonTextUnderIcon</enum>\n   </property>\n   <property name=\"resizeEnabled\" stdset=\"0\">\n    <bool>false</bool>\n   </property>\n   <property name=\"movingEnabled\" stdset=\"0\">\n    <bool>false</bool>\n   </property>\n   <property name=\"horizontallyStretchable\" stdset=\"0\">\n    <bool>false</bool>\n   </property>\n   <property name=\"verticallyStretchable\" stdset=\"0\">\n    <bool>false</bool>\n   </property>\n   <property name=\"label\" stdset=\"0\">\n    <string/>\n   </property>\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n   <addaction name=\"callInvite\"/>\n   <addaction name=\"callAnswer\"/>\n   <addaction name=\"callBye\"/>\n   <addaction name=\"callReject\"/>\n   <addaction name=\"callRedirect\"/>\n   <addaction name=\"callTransfer\"/>\n   <addaction name=\"callHold\"/>\n   <addaction name=\"callConference\"/>\n   <addaction name=\"callMute\"/>\n   <addaction name=\"callDTMF\"/>\n   <addaction name=\"callRedial\"/>\n   <addaction name=\"actionSendMsg\"/>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menubar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>733</width>\n     <height>17</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"fileMenu\">\n    <property name=\"title\">\n     <string>&amp;File</string>\n    </property>\n    <addaction name=\"fileChangeUserAction\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"fileExitAction\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"Edit\">\n    <property name=\"title\">\n     <string>&amp;Edit</string>\n    </property>\n    <addaction name=\"editUserProfileAction\"/>\n    <addaction name=\"editSysSettingsAction\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"Call\">\n    <property name=\"title\">\n     <string>C&amp;all</string>\n    </property>\n    <widget class=\"QMenu\" name=\"activateLineMenu\">\n     <property name=\"title\">\n      <string>Activate line</string>\n     </property>\n     <addaction name=\"actionLine1\"/>\n     <addaction name=\"actionLine2\"/>\n    </widget>\n    <addaction name=\"callInvite\"/>\n    <addaction name=\"callAnswer\"/>\n    <addaction name=\"callBye\"/>\n    <addaction name=\"callReject\"/>\n    <addaction name=\"callRedirect\"/>\n    <addaction name=\"callTransfer\"/>\n    <addaction name=\"callHold\"/>\n    <addaction name=\"callConference\"/>\n    <addaction name=\"callMute\"/>\n    <addaction name=\"callDTMF\"/>\n    <addaction name=\"callRedial\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"callTermCap\"/>\n    <addaction name=\"activateLineMenu\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"Message\">\n    <property name=\"title\">\n     <string>&amp;Message</string>\n    </property>\n    <addaction name=\"actionSendMsg\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"registrationMenu\">\n    <property name=\"title\">\n     <string>&amp;Registration</string>\n    </property>\n    <addaction name=\"regRegister\"/>\n    <addaction name=\"regDeregister\"/>\n    <addaction name=\"regDeregisterAll\"/>\n    <addaction name=\"regShow\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"servicesMenu\">\n    <property name=\"title\">\n     <string>&amp;Services</string>\n    </property>\n    <addaction name=\"serviceDnd\"/>\n    <addaction name=\"serviceRedirection\"/>\n    <addaction name=\"serviceAutoAnswer\"/>\n    <addaction name=\"servicesVoice_mailAction\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"View\">\n    <property name=\"title\">\n     <string>&amp;View</string>\n    </property>\n    <addaction name=\"viewCall_HistoryAction\"/>\n    <addaction name=\"viewLogAction\"/>\n    <addaction name=\"viewDisplayAction\"/>\n    <addaction name=\"viewBuddyListAction\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"Diamondcard\">\n    <property name=\"title\">\n     <string>Diamondcard</string>\n    </property>\n    <addaction name=\"diamondcardSign_upAction\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"helpMenu\">\n    <property name=\"title\">\n     <string>&amp;Help</string>\n    </property>\n    <addaction name=\"helpWhats_ThisAction\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"helpManualAction\"/>\n    <addaction name=\"helpAboutAction\"/>\n    <addaction name=\"helpAboutQtAction\"/>\n   </widget>\n   <addaction name=\"fileMenu\"/>\n   <addaction name=\"Edit\"/>\n   <addaction name=\"Call\"/>\n   <addaction name=\"Message\"/>\n   <addaction name=\"registrationMenu\"/>\n   <addaction name=\"servicesMenu\"/>\n   <addaction name=\"View\"/>\n   <addaction name=\"Diamondcard\"/>\n   <addaction name=\"helpMenu\"/>\n   <addaction name=\"separator\"/>\n  </widget>\n  <action name=\"fileExitAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/exit.png</normaloff>:/icons/images/exit.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Quit</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Quit</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Q</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>fileExitAction</cstring>\n   </property>\n  </action>\n  <action name=\"helpAboutAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/twinkle16.png</normaloff>:/icons/images/twinkle16.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;About Twinkle</string>\n   </property>\n   <property name=\"iconText\">\n    <string>About Twinkle</string>\n   </property>\n   <property name=\"shortcut\">\n    <string/>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>helpAboutAction</cstring>\n   </property>\n  </action>\n  <action name=\"callInvite\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/invite.png</normaloff>:/icons/images/invite.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Call...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Call</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Call someone</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F5</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callInvite</cstring>\n   </property>\n  </action>\n  <action name=\"callAnswer\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/answer.png</normaloff>:/icons/images/answer.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Answer</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Answer</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Answer incoming call</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F6</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callAnswer</cstring>\n   </property>\n  </action>\n  <action name=\"callBye\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/bye.png</normaloff>:/icons/images/bye.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Bye</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Bye</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Release call</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Esc</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callBye</cstring>\n   </property>\n  </action>\n  <action name=\"callReject\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/reject.png</normaloff>:/icons/images/reject.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Reject</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Reject</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Reject incoming call</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F8</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callReject</cstring>\n   </property>\n  </action>\n  <action name=\"callHold\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/hold.png</normaloff>:/icons/images/hold.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Hold</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Hold</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Put a call on hold, or retrieve a held call</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callHold</cstring>\n   </property>\n  </action>\n  <action name=\"callRedirect\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/redirect.png</normaloff>:/icons/images/redirect.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>R&amp;edirect...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Redirect</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Redirect incoming call without answering</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callRedirect</cstring>\n   </property>\n  </action>\n  <action name=\"callDTMF\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/dtmf.png</normaloff>:/icons/images/dtmf.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Dtmf...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Dtmf</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Open keypad to enter digits for voice menu's</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callDTMF</cstring>\n   </property>\n  </action>\n  <action name=\"regRegister\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/twinkle16.png</normaloff>:/icons/images/twinkle16.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Register</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Register</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>regRegister</cstring>\n   </property>\n  </action>\n  <action name=\"regDeregister\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/twinkle16-disabled.png</normaloff>:/icons/images/twinkle16-disabled.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Deregister</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Deregister</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Deregister this device</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>regDeregister</cstring>\n   </property>\n  </action>\n  <action name=\"regShow\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/reg-query.png</normaloff>:/icons/images/reg-query.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Show registrations</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Show registrations</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>regShow</cstring>\n   </property>\n  </action>\n  <action name=\"callTermCap\">\n   <property name=\"text\">\n    <string>&amp;Terminal capabilities...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Terminal capabilities</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Request terminal capabilities from someone</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callTermCap</cstring>\n   </property>\n  </action>\n  <action name=\"serviceDnd\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/cancel.png</normaloff>:/icons/images/cancel.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Do not disturb</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Do not disturb</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>serviceDnd</cstring>\n   </property>\n  </action>\n  <action name=\"serviceRedirection\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/cf.png</normaloff>:/icons/images/cf.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Call &amp;redirection...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Call redirection</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>serviceRedirection</cstring>\n   </property>\n  </action>\n  <action name=\"callRedial\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/redial.png</normaloff>:/icons/images/redial.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Redial</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Redial</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Repeat last call</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F12</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callRedial</cstring>\n   </property>\n  </action>\n  <action name=\"helpAboutQtAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/qt-logo.png</normaloff>:/icons/images/qt-logo.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>About &amp;Qt</string>\n   </property>\n   <property name=\"iconText\">\n    <string>About Qt</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>helpAboutQtAction</cstring>\n   </property>\n  </action>\n  <action name=\"editUserProfileAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/penguin-small.png</normaloff>:/icons/images/penguin-small.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;User profile...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>User profile</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>editUserProfileAction</cstring>\n   </property>\n  </action>\n  <action name=\"callConference\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/conference.png</normaloff>:/icons/images/conference.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Conference</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Conf</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Join two calls in a 3-way conference</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callConference</cstring>\n   </property>\n  </action>\n  <action name=\"callMute\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/mute.png</normaloff>:/icons/images/mute.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Mute</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Mute</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Mute a call</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callMute</cstring>\n   </property>\n  </action>\n  <action name=\"callTransfer\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/transfer.png</normaloff>:/icons/images/transfer.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Trans&amp;fer...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Xfer</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Transfer call</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>callTransfer</cstring>\n   </property>\n  </action>\n  <action name=\"editSysSettingsAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/settings.png</normaloff>:/icons/images/settings.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;System settings...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>System settings</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>editSysSettingsAction</cstring>\n   </property>\n  </action>\n  <action name=\"registrationAction\">\n   <property name=\"text\">\n    <string/>\n   </property>\n   <property name=\"iconText\">\n    <string/>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>registrationAction</cstring>\n   </property>\n  </action>\n  <action name=\"regDeregisterAll\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/twinkle16-disabled.png</normaloff>:/icons/images/twinkle16-disabled.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Deregister &amp;all</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Deregister all</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Deregister all your registered devices</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>regDeregisterAll</cstring>\n   </property>\n  </action>\n  <action name=\"serviceAutoAnswer\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/auto_answer.png</normaloff>:/icons/images/auto_answer.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Auto answer</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Auto answer</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>serviceAutoAnswer</cstring>\n   </property>\n  </action>\n  <action name=\"viewLogAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/log_small.png</normaloff>:/icons/images/log_small.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Log...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Log</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>viewLogAction</cstring>\n   </property>\n  </action>\n  <action name=\"viewCall_HistoryAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/missed.png</normaloff>:/icons/images/missed.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Call &amp;history...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Call history</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F9</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>viewCall_HistoryAction</cstring>\n   </property>\n  </action>\n  <action name=\"fileChangeUserAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/penguin-small.png</normaloff>:/icons/images/penguin-small.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Change user ...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Change user ...</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Activate or de-activate users</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>fileChangeUserAction</cstring>\n   </property>\n  </action>\n  <action name=\"helpWhats_ThisAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/contexthelp.png</normaloff>:/icons/images/contexthelp.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>What's &amp;This?</string>\n   </property>\n   <property name=\"iconText\">\n    <string>What's This?</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Shift+F1</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>helpWhats_ThisAction</cstring>\n   </property>\n  </action>\n  <action name=\"viewDisplayAction\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Display</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Display</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>viewDisplayAction</cstring>\n   </property>\n  </action>\n  <action name=\"servicesVoice_mailAction\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/mwi_none16.png</normaloff>:/icons/images/mwi_none16.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Voice mail</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Voice mail</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Access voice mail</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F11</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>servicesVoice_mailAction</cstring>\n   </property>\n  </action>\n  <action name=\"actionSendMsg\">\n   <property name=\"icon\">\n    <iconset resource=\"icons.qrc\">\n     <normaloff>:/icons/images/message.png</normaloff>:/icons/images/message.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Instant &amp;message...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Msg</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Instant message</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>actionSendMsg</cstring>\n   </property>\n  </action>\n  <action name=\"viewBuddyListAction\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Buddy list</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Buddy list</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Buddy list</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>viewBuddyListAction</cstring>\n   </property>\n  </action>\n  <action name=\"helpManualAction\">\n   <property name=\"text\">\n    <string>&amp;Manual</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Manual</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>helpManualAction</cstring>\n   </property>\n  </action>\n  <action name=\"diamondcardSign_upAction\">\n   <property name=\"text\">\n    <string>&amp;Sign up...</string>\n   </property>\n   <property name=\"iconText\">\n    <string>Sign up</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Sign up</string>\n   </property>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>diamondcardSign_upAction</cstring>\n   </property>\n  </action>\n  <actiongroup name=\"actgrActivateLine\">\n   <action name=\"actionLine1\">\n    <property name=\"checkable\">\n     <bool>true</bool>\n    </property>\n    <property name=\"checked\">\n     <bool>true</bool>\n    </property>\n    <property name=\"iconText\">\n     <string>Line 1</string>\n    </property>\n    <property name=\"name\" stdset=\"0\">\n     <cstring>actionLine1</cstring>\n    </property>\n   </action>\n   <action name=\"actionLine2\">\n    <property name=\"checkable\">\n     <bool>true</bool>\n    </property>\n    <property name=\"iconText\">\n     <string>Line 2</string>\n    </property>\n    <property name=\"name\" stdset=\"0\">\n     <cstring>actionLine2</cstring>\n    </property>\n   </action>\n   <property name=\"name\" stdset=\"0\">\n    <cstring>actgrActivateLine</cstring>\n   </property>\n  </actiongroup>\n  <action name=\"toggleWindowAction\">\n   <property name=\"text\">\n    <string>Toggle window visibility</string>\n   </property>\n  </action>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>callComboBox</tabstop>\n  <tabstop>addressToolButton</tabstop>\n  <tabstop>callPushButton</tabstop>\n  <tabstop>displayTextEdit</tabstop>\n  <tabstop>line1RadioButton</tabstop>\n  <tabstop>line2RadioButton</tabstop>\n  <tabstop>userComboBox</tabstop>\n  <tabstop>from1Label</tabstop>\n  <tabstop>subject1Label</tabstop>\n  <tabstop>to1Label</tabstop>\n  <tabstop>subject2Label</tabstop>\n  <tabstop>from2Label</tabstop>\n  <tabstop>to2Label</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">phone.h</include>\n  <include location=\"local\">ui_dtmfform.h</include>\n  <include location=\"local\">ui_inviteform.h</include>\n  <include location=\"local\">ui_redirectform.h</include>\n  <include location=\"local\">ui_termcapform.h</include>\n  <include location=\"local\">ui_srvredirectform.h</include>\n  <include location=\"local\">ui_userprofileform.h</include>\n  <include location=\"local\">ui_transferform.h</include>\n  <include location=\"local\">ui_syssettingsform.h</include>\n  <include location=\"local\">ui_logviewform.h</include>\n  <include location=\"local\">ui_historyform.h</include>\n  <include location=\"local\">ui_selectuserform.h</include>\n  <include location=\"local\">ui_selectprofileform.h</include>\n  <include location=\"local\">qevent.h</include>\n  <include location=\"local\">im/msg_session.h</include>\n  <include location=\"local\">messageformview.h</include>\n  <include location=\"local\">buddylistview.h</include>\n  <include location=\"local\">diamondcard.h</include>\n </includes>\n <resources>\n  <include location=\"icons.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>helpWhats_ThisAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>whatsThis()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>addressToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>showAddressBook()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>634</x>\n     <y>127</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>quickCall()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>664</x>\n     <y>127</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>fileChangeUserAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>selectProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>viewCall_HistoryAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>viewHistory()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>viewLogAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>viewLog()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>regDeregisterAll</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneDeregisterAll()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>editSysSettingsAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>editSysSettings()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callMute</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneMute(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callConference</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneConference()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>editUserProfileAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>editUserProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>helpAboutQtAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>aboutQt()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>helpAboutAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>about()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callRedial</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneRedial()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>serviceRedirection</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>srvRedirect()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>regShow</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneShowRegistrations()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>regDeregister</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneDeregister()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>regRegister</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneRegister()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callHold</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneHold(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callReject</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneReject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callRedirect</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneRedirect()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callInvite</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneInvite()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callDTMF</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneDTMF()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callBye</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneBye()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callAnswer</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneAnswer()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callTermCap</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneTermCap()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>line2RadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>line2rbChangedState(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>309</x>\n     <y>597</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>line1RadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>line1rbChangedState(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>309</x>\n     <y>491</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>fileExitAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>fileExit()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>actionLine1</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>actionLine1Toggled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>actionLine2</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>actionLine2Toggled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>viewDisplayAction</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>showDisplay(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>callTransfer</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>phoneTransfer()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>servicesVoice_mailAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>popupMenuVoiceMail()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>actionSendMsg</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>startMessageSession()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>viewBuddyListAction</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>showBuddyList(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>helpManualAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>manual()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>diamondcardSign_upAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>DiamondcardSignUp()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buddyListView</sender>\n   <signal>itemDoubleClicked(QTreeWidgetItem*,int)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>doMessageBuddy(QTreeWidgetItem*)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>130</x>\n     <y>206</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>3</x>\n     <y>195</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buddyListView</sender>\n   <signal>customContextMenuRequested(QPoint)</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>showBuddyListPopupMenu(QPoint)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>98</x>\n     <y>285</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>3</x>\n     <y>301</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>toggleWindowAction</sender>\n   <signal>triggered()</signal>\n   <receiver>MphoneForm</receiver>\n   <slot>toggleWindow()</slot>\n  </connection>\n </connections>\n <slots>\n  <slot>doMessageBuddy(QTreeWidgetItem*)</slot>\n  <slot>showBuddyListPopupMenu(QPoint)</slot>\n </slots>\n</ui>\n"
  },
  {
    "path": "src/gui/numberconversionform.cpp",
    "content": "#include \"numberconversionform.h\"\n\n#include <QRegularExpressionValidator>\n#include \"gui.h\"\n\n/*\n *  Constructs a NumberConversionForm which is a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nNumberConversionForm::NumberConversionForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nNumberConversionForm::~NumberConversionForm()\n{\n    // no need to delete child widgets, Qt does it all for us\n}\n\nvoid NumberConversionForm::init()\n{\n\tQRegularExpression rxNoAtSign(\"[^@]*\");\n\n\texprLineEdit->setValidator(new QRegularExpressionValidator(rxNoAtSign, this));\n\treplaceLineEdit->setValidator(new QRegularExpressionValidator(rxNoAtSign, this));\n}\n\nint NumberConversionForm::exec(QString &expr, QString &replace)\n{\n\texprLineEdit->setText(expr);\n\treplaceLineEdit->setText(replace);\n\tint retval = QDialog::exec();\n\n\tif (retval == QDialog::Accepted) {\n\t\texpr = exprLineEdit->text();\n\t\treplace = replaceLineEdit->text();\n\t}\n\n\treturn retval;\n}\n\nvoid NumberConversionForm::validate()\n{\n\tQString expr = exprLineEdit->text();\n\tQString replace = replaceLineEdit->text();\n\n\tif (expr.isEmpty()) {\n\t\t((t_gui *)ui)->cb_show_msg(this,\n            tr(\"Match expression may not be empty.\").toStdString(), MSG_CRITICAL);\n\t\texprLineEdit->setFocus();\n\t\texprLineEdit->selectAll();\n\t\treturn;\n\t}\n\n\tif (replace.isEmpty()) {\n\t\t((t_gui *)ui)->cb_show_msg(this,\n            tr(\"Replace value may not be empty.\").toStdString(), MSG_CRITICAL);\n\t\treplaceLineEdit->setFocus();\n\t\treplaceLineEdit->selectAll();\n\t\treturn;\n\t}\n\n\ttry {\n        std::regex re(expr.toStdString());\n    } catch (std::regex_error) {\n\t\t((t_gui *)ui)->cb_show_msg(this,\n            tr(\"Invalid regular expression.\").toStdString(), MSG_CRITICAL);\n\t\texprLineEdit->setFocus();\n\t\treturn;\n\t}\n\n\taccept();\n}\n"
  },
  {
    "path": "src/gui/numberconversionform.h",
    "content": "#ifndef NUMBERCONVERSIONFORM_H\n#define NUMBERCONVERSIONFORM_H\n\n#include <QDialog>\n#include \"ui_numberconversionform.h\"\n\nclass NumberConversionForm : public QDialog, private Ui::NumberConversionForm\n{\n    Q_OBJECT\n\npublic:\n    NumberConversionForm(QWidget* parent = 0);\n    ~NumberConversionForm();\n\n    int exec(QString& expr, QString& replace);\n\npublic slots:\n    void validate();\n\nprivate:\n    void init();\n};\n\n#endif // NUMBERCONVERSIONFORM_H\n"
  },
  {
    "path": "src/gui/numberconversionform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>NumberConversionForm</class>\n  <widget class=\"QDialog\" name=\"NumberConversionForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>436</width>\n        <height>122</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Number conversion</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"exprTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Match expression:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>exprLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"replaceTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Replace:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>replaceLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"replaceLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Perl style format string for the replacement number.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"exprLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Perl style regular expression matching the number format you want to modify.</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>20</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>71</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+C</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <tabstops>\n    <tabstop>exprLineEdit</tabstop>\n    <tabstop>replaceLineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>NumberConversionForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>NumberConversionForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/osd.cpp",
    "content": "#include \"osd.h\"\n#include <QtDebug>\n\n#include <QDesktopWidget>\n#include <QSettings>\n#include <QApplication>\n#include <QQuickView>\n#include <QQuickItem>\n#include <QQmlContext>\n\nextern QSettings* g_gui_state;\n\nOSD::OSD(QObject* parent)\n\t: QObject(parent)\n{\n\tm_view = new QQuickView;\n\tm_view->setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::ToolTip);\n\n\tm_view->rootContext()->setContextProperty(\"viewerWidget\", m_view);\n\tm_view->setSource(QUrl(\"qrc:/qml/osd.qml\"));\n\n\tif (!m_view->rootObject()) {\n\t\tthrow std::runtime_error(\"Could not load osd.qml\");\n\t}\n\n\tpositionWindow();\n\n\tQObject* buttonHangup;\n\tbuttonHangup = m_view->rootObject()->findChild<QObject*>(\"hangup\");\n\n\tconnect(buttonHangup, SIGNAL(clicked()), this, SLOT(onHangupClicked()));\n\n\tm_caller = m_view->rootObject()->findChild<QQuickItem*>(\"callerName\");\n\tm_time = m_view->rootObject()->findChild<QQuickItem*>(\"callTime\");\n\tm_mute = m_view->rootObject()->findChild<QQuickItem*>(\"mute\");\n\n\tconnect(m_mute, SIGNAL(clicked()), this, SLOT(onMuteClicked()));\n\n\tconnect(m_view->rootObject(), SIGNAL(moved()), this, SLOT(saveState()));\n}\n\nOSD::~OSD()\n{\n\tdelete m_view;\n}\n\nvoid OSD::positionWindow()\n{\n\tQDesktopWidget* desktop = QApplication::desktop();\n\tint x, y;\n\tint defaultX, defaultY;\n\n\tdefaultX = desktop->width() - this->width() - 10;\n\tdefaultY = 10;\n\n\tx = g_gui_state->value(\"osd/x\", defaultX).toInt();\n\ty = g_gui_state->value(\"osd/y\", defaultY).toInt();\n\n\t// Reset position if off screen\n\tif (x > desktop->width() || x < 0)\n\t\tx = defaultX;\n\tif (y > desktop->height() || y < 0)\n\t\ty = defaultY;\n\n\tm_view->setPosition(x, y);\n}\n\nvoid OSD::saveState()\n{\n\tQPoint pos = m_view->position();\n\tg_gui_state->setValue(\"osd/x\", pos.x());\n\tg_gui_state->setValue(\"osd/y\", pos.y());\n}\n\nvoid OSD::onHangupClicked()\n{\n\temit hangupClicked();\n}\n\nvoid OSD::onMuteClicked()\n{\n\temit muteClicked();\n}\n\nvoid OSD::setMuted(bool muted)\n{\n\tQString path;\n\n\tif (muted)\n\t\tpath = \"qrc:/icons/images/osd_mic_off.png\";\n\telse\n\t\tpath = \"qrc:/icons/images/osd_mic_on.png\";\n\n\tm_mute->setProperty(\"image\", path);\n}\n\nvoid OSD::setCaller(const QString& text)\n{\n\tm_caller->setProperty(\"text\", text);\n}\n\nvoid OSD::setTime(const QString& timeText)\n{\n\tm_time->setProperty(\"text\", timeText);\n}\n\nvoid OSD::move(int x, int y)\n{\n\tm_view->setPosition(x, y);\n}\n\nvoid OSD::show()\n{\n\tm_view->show();\n}\n\nvoid OSD::hide()\n{\n\tm_view->hide();\n}\n\nint OSD::width() const\n{\n\treturn m_view->width();\n}\n\nint OSD::height() const\n{\n\treturn m_view->height();\n}\n"
  },
  {
    "path": "src/gui/osd.h",
    "content": "#ifndef OSD_H\n#define OSD_H\n#include <QObject>\n#include <QString>\n\n// Must use forward declaration, otherwise build fails\n// due to double QMetaTypeID<QAction*> definition (wtf).\n// Hence I also cannot inherit from OSD_VIEWCLASS...\nclass QQuickView;\nclass QQuickItem;\n\nclass OSD : public QObject\n{\n\tQ_OBJECT\npublic:\n\tOSD(QObject* parent = 0);\n\t~OSD();\n\n\tvoid setCaller(const QString& text);\n\tvoid setTime(const QString& timeText);\n\tvoid setMuted(bool muted);\n\n\tvoid move(int x, int y);\n\tvoid show();\n\tvoid hide();\n\tint width() const;\n\tint height() const;\n\tvoid setVisible(bool v) { if (v) show(); else hide(); }\n\nprivate:\n\tvoid positionWindow();\npublic slots:\n\tvoid onHangupClicked();\n\tvoid onMuteClicked();\n\tvoid saveState();\n\nsignals:\n\tvoid hangupClicked();\n\tvoid muteClicked();\n\nprivate:\n\tQQuickView* m_view;\n\tQQuickItem* m_caller;\n\tQQuickItem* m_time;\n\tQQuickItem* m_mute;\n};\n\n#endif // OSD_H\n"
  },
  {
    "path": "src/gui/qml/ImageButton.qml",
    "content": "import QtQuick 2.0\n\nRectangle {\n\n    property alias image: img.source\n    signal clicked\n\n    color: \"transparent\"\n    z: 2\n\n    MouseArea {\n        id: mouseArea\n        anchors.fill: parent\n        onClicked: parent.clicked()\n    }\n\n    Image {\n        id: img\n        smooth: true\n        anchors.fill: parent\n    }\n\n    states: State {\n        name: \"pressed\"; when: mouseArea.pressed\n        PropertyChanges { target: img; anchors.topMargin: 2; anchors.bottomMargin: -2 }\n    }\n}\n\n"
  },
  {
    "path": "src/gui/qml/TextImageButton.qml",
    "content": "import QtQuick 2.0\n\nRectangle {\n    id: backgroundRect\n    width: 150\n    height: 30\n    radius: 0\n\n    property alias image: img.source\n    property alias text: text.text\n    property alias color: backgroundRect.color\n    signal clicked\n\n    color: \"red\"\n    // Pre-compute this to avoid binding color to itself\n    property color darkerColor\n    Component.onCompleted: {\n\t    darkerColor = Qt.darker(color)\n    }\n\n    z: 2\n\n    Image {\n        id: img\n        width: height\n        anchors.top: parent.top\n        anchors.topMargin: 2\n        anchors.bottom: parent.bottom\n        anchors.bottomMargin: 2\n        anchors.left: parent.left\n        anchors.leftMargin: 5\n        smooth: true\n        source: \"qrc:/qtquickplugin/images/template_image.png\"\n    }\n\n    MouseArea {\n        id: mouseArea\n        anchors.fill: parent\n        onClicked: parent.clicked()\n    }\n\n    Text {\n        id: text\n        text: \"Button text\"\n        font.bold: true\n        horizontalAlignment: Text.AlignHCenter\n        verticalAlignment: Text.AlignVCenter\n        anchors.right: parent.right\n        anchors.rightMargin: 0\n        anchors.left: img.right\n        anchors.leftMargin: 5\n        anchors.top: parent.top\n        anchors.topMargin: 0\n        anchors.bottom: parent.bottom\n        anchors.bottomMargin: 0\n        color: \"white\"\n        font.pixelSize: 12\n    }\n\n    states: State {\n        name: \"pressed\"; when: mouseArea.pressed\n        PropertyChanges { target: backgroundRect; color: darkerColor }\n    }\n}\n\n"
  },
  {
    "path": "src/gui/qml/incoming_call.qml",
    "content": "import QtQuick 2.0\n\nRectangle {\n    id: rectanglePopup\n    width: 400\n    height: 70\n    color: \"black\"\n\n    signal moved\n\n    Image {\n        id: image1\n        anchors.bottom: parent.bottom\n        anchors.bottomMargin: 5\n        anchors.top: parent.top\n        anchors.topMargin: 5\n        anchors.left: parent.left\n        anchors.leftMargin: 5\n        source: \"qrc:/icons/images/twinkle48.png\"\n        width: height\n    }\n\n    Text {\n        id: callerText\n        objectName: \"callerText\"\n        height: 22\n        color: \"#ffffff\"\n        text: \"... calling\"\n        anchors.top: parent.top\n        anchors.topMargin: 8\n        anchors.left: image1.right\n        anchors.leftMargin: 9\n        anchors.right: parent.right\n        anchors.rightMargin: 10\n        font.pixelSize: 19\n    }\n\n    TextImageButton {\n        id: buttonAnswer\n        objectName: \"buttonAnswer\"\n        x: 74\n        y: 36\n        width: 120\n        height: 26\n        color: \"#00aa00\"\n        radius: 7\n        text: qsTr(\"Answer\")\n        image: \"qrc:/icons/images/popup_incoming_answer.png\"\n    }\n\n    TextImageButton {\n        id: buttonReject\n        objectName: \"buttonReject\"\n        y: 36\n        width: 120\n        height: 26\n        radius: 7\n        text: qsTr(\"Reject\")\n        anchors.left: buttonAnswer.right\n        anchors.leftMargin: 15\n        image: \"qrc:/icons/images/popup_incoming_reject.png\"\n    }\n\n    MouseArea {\n        anchors.fill: parent\n        property real lastMouseX: 0\n        property real lastMouseY: 0\n        onPressed: {\n            lastMouseX = mouseX\n            lastMouseY = mouseY\n        }\n        onMouseXChanged: viewerWidget.x += (mouseX - lastMouseX)\n        onMouseYChanged: viewerWidget.y += (mouseY - lastMouseY)\n    }\n}\n\n"
  },
  {
    "path": "src/gui/qml/osd.qml",
    "content": "import QtQuick 2.0\n\nRectangle {\n    id: rectangleOsd\n    width: 310\n    height: 55\n    color: \"black\"\n\n    signal moved\n\n    Image {\n        id: image1\n        anchors.bottom: parent.bottom\n        anchors.bottomMargin: 5\n        anchors.top: parent.top\n        anchors.topMargin: 5\n        anchors.left: parent.left\n        anchors.leftMargin: 5\n        source: \"qrc:/icons/images/twinkle48.png\"\n        width: height\n    }\n\n    ImageButton {\n        id: hangup\n        objectName: \"hangup\"\n        x: 262\n        anchors.bottom: parent.bottom\n        anchors.bottomMargin: 15\n        anchors.top: parent.top\n        anchors.topMargin: 15\n        anchors.right: parent.right\n        anchors.rightMargin: 10\n        width: height\n        image: \"qrc:/icons/images/osd_hangup.png\"\n    }\n\n    ImageButton {\n        id: mute\n        objectName: \"mute\"\n        x: 222\n        width: height\n        image: \"qrc:/icons/images/osd_mic_on.png\"\n        anchors.bottomMargin: 15\n        anchors.topMargin: 15\n        anchors.top: parent.top\n        anchors.bottom: parent.bottom\n        anchors.rightMargin: 14\n        anchors.right: hangup.left\n    }\n\n    Text {\n        id: callerName\n        objectName: \"callerName\"\n        x: 56\n        y: 5\n        width: 158\n        height: 21\n        text: \"Caller name\"\n        clip: true\n        verticalAlignment: Text.AlignVCenter\n        font.bold: true\n        font.pixelSize: 12\n        color: \"white\"\n    }\n\n    Text {\n        id: callTime\n        objectName: \"callTime\"\n        x: 56\n        y: 27\n        width: 158\n        height: 20\n        text: \"Time\"\n        clip: true\n        verticalAlignment: Text.AlignVCenter\n        font.pixelSize: 12\n        color: \"white\"\n    }\n\n    MouseArea {\n        anchors.fill: parent\n        property real lastMouseX: 0\n        property real lastMouseY: 0\n        onPressed: {\n            lastMouseX = mouseX\n            lastMouseY = mouseY\n        }\n        onMouseXChanged: viewerWidget.x += (mouseX - lastMouseX)\n        onMouseYChanged: viewerWidget.y += (mouseY - lastMouseY)\n    }\n}\n\n"
  },
  {
    "path": "src/gui/qml/qml.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/qml\">\n        <file>ImageButton.qml</file>\n        <file>osd.qml</file>\n        <file>TextImageButton.qml</file>\n        <file>incoming_call.qml</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/gui/qt_translator.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _QT_TRANSLATOR_H\n#define _QT_TRANSLATOR_H\n\n#include <qapplication.h>\n#include \"translator.h\"\n\n// This class provides the translation service from Qt to the\n// core of Twinkle.\nclass t_qt_translator : public t_translator {\npublic:\n\tt_qt_translator(QApplication *qa) : _qa(qa) {};\n\t\n\tvirtual string translate(const string &s) {\n        return _qa->translate(\"TwinkleCore\", s.c_str()).toStdString();\n\t};\n\t\n\tvirtual string translate2(const string &context, const string &s) {\n        return _qa->translate(context.c_str(), s.c_str()).toStdString();\n\t};\n\nprivate:\n\tQApplication *_qa;\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/redirectform.cpp",
    "content": "#include \"redirectform.h\"\n//Added by qt3to4:\n#include <QPixmap>\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"audits/memman.h\"\n#include \"gui.h\"\n\nRedirectForm::RedirectForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\tinit();\n}\n\nRedirectForm::~RedirectForm()\n{\n\tdestroy();\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid RedirectForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\nvoid RedirectForm::init()\n{\n\t// Keeps track of which address book tool button is clicked.\n\tnrAddressBook = 0;\n\t\n\tgetAddressForm = 0;\n\t\n\t// Set toolbutton icons for disabled options.\n\tQIcon i;\n    i = address1ToolButton->icon();\n    i.addPixmap(QPixmap(\":/icons/images/kontact_contacts-disabled.png\"),\n            QIcon::Disabled);\n    address1ToolButton->setIcon(i);\n    address2ToolButton->setIcon(i);\n    address3ToolButton->setIcon(i);\n}\n\nvoid RedirectForm::destroy()\n{\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n}\n\nvoid RedirectForm::show(t_user *user, const list<string> &contacts)\n{\n\tuser_config = user;\n\t\n\tint num = 0;\n\tfor (list<string>::const_iterator i = contacts.begin();\n\ti != contacts.end(); i++, num++)\n\t{\n\t\tif (num == 0) contact1LineEdit->setText(i->c_str());\n\t\tif (num == 1) contact2LineEdit->setText(i->c_str());\n\t\tif (num == 2) contact3LineEdit->setText(i->c_str());\n\t}\n\t\n\tQDialog::show();\n}\n\nvoid RedirectForm::validate()\n{\n\tt_display_url destination;\n\tlist<t_display_url> dest_list;\n\t\n\t// 1st choice destination\n    ui->expand_destination(user_config, contact1LineEdit->text().trimmed().toStdString(),\n\t\t\t       destination);\n\tif (destination.is_valid()) {\n\t\tdest_list.push_back(destination);\n\t} else {\n\t\tcontact1LineEdit->selectAll();\n\t\treturn;\n\t}\n\t\n\t// 2nd choice destination\n\tif (!contact2LineEdit->text().isEmpty()) {\n\t\tui->expand_destination(user_config,\n            contact2LineEdit->text().trimmed().toStdString(), destination);\n\t\tif (destination.is_valid()) {\n\t\t\tdest_list.push_back(destination);\n\t\t} else {\n\t\t\tcontact2LineEdit->selectAll();\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// 3rd choice destination\n\tif (!contact3LineEdit->text().isEmpty()) {\n\t\tui->expand_destination(user_config,\n            contact3LineEdit->text().trimmed().toStdString(), destination);\n\t\tif (destination.is_valid()) {\n\t\t\tdest_list.push_back(destination);\n\t\t} else {\n\t\t\tcontact3LineEdit->selectAll();\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\temit destinations(dest_list);\n\taccept();\n}\n\nvoid RedirectForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid RedirectForm::showAddressBook1()\n{\n\tnrAddressBook = 1;\n\tshowAddressBook();\n}\n\nvoid RedirectForm::showAddressBook2()\n{\n\tnrAddressBook = 2;\n\tshowAddressBook();\n}\n\nvoid RedirectForm::showAddressBook3()\n{\n\tnrAddressBook = 3;\n\tshowAddressBook();\n}\n\nvoid RedirectForm::selectedAddress(const QString &address)\n{\n\tswitch(nrAddressBook) {\n\tcase 1:\n\t\tcontact1LineEdit->setText(address);\n\t\tbreak;\n\tcase 2:\n\t\tcontact2LineEdit->setText(address);\n\t\tbreak;\n\tcase 3:\n\t\tcontact3LineEdit->setText(address);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "src/gui/redirectform.h",
    "content": "#ifndef REDIRECTFORM_UI_H\n#define REDIRECTFORM_UI_H\n#include \"ui_redirectform.h\"\n#include <QVariant>\n#include <QAction>\n#include <QApplication>\n#include <QButtonGroup>\n#include <QDialog>\n#include <QGridLayout>\n#include <QHBoxLayout>\n#include <QHeaderView>\n#include <QLabel>\n#include <QLineEdit>\n#include <QPushButton>\n#include <QSpacerItem>\n#include <QToolButton>\n#include <QVBoxLayout>\n#include <list>\n#include \"getaddressform.h\"\n#include \"sockets/url.h\"\n#include \"user.h\"\n\n\nclass RedirectForm : public QDialog, public Ui::RedirectForm\n{\n\tQ_OBJECT\n\npublic:\n    RedirectForm(QWidget* parent = 0);\n\t~RedirectForm();\n\npublic slots:\n\tvirtual void show( t_user * user, const list<string> & contacts );\n\tvirtual void validate();\n\tvirtual void showAddressBook();\n\tvirtual void showAddressBook1();\n\tvirtual void showAddressBook2();\n\tvirtual void showAddressBook3();\n\tvirtual void selectedAddress( const QString & address );\n\nsignals:\n\tvoid destinations(const list<t_display_url> &);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tGetAddressForm *getAddressForm;\n\tint nrAddressBook;\n\tt_user *user_config;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/redirectform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>RedirectForm</class>\n  <widget class=\"QDialog\" name=\"RedirectForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>588</width>\n        <height>190</height>\n      </rect>\n    </property>\n    <property name=\"sizePolicy\">\n      <sizepolicy>\n        <hsizetype>5</hsizetype>\n        <vsizetype>5</vsizetype>\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n      </sizepolicy>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Redirect</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <widget class=\"QGroupBox\" name=\"redirectGroupBox\">\n          <property name=\"title\">\n            <string>Redirect incoming call to</string>\n          </property>\n          <layout class=\"QGridLayout\">\n            <item row=\"2\" column=\"1\">\n              <widget class=\"QLineEdit\" name=\"contact3LineEdit\">\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                </property>\n              </widget>\n            </item>\n            <item row=\"2\" column=\"0\">\n              <widget class=\"QLabel\" name=\"contact3TextLabel\">\n                <property name=\"text\">\n                  <string>&amp;3rd choice destination:</string>\n                </property>\n                <property name=\"buddy\" stdset=\"0\">\n                  <cstring>contact3LineEdit</cstring>\n                </property>\n                <property name=\"wordWrap\">\n                  <bool>false</bool>\n                </property>\n              </widget>\n            </item>\n            <item row=\"1\" column=\"0\">\n              <widget class=\"QLabel\" name=\"contact2TextLabel\">\n                <property name=\"text\">\n                  <string>&amp;2nd choice destination:</string>\n                </property>\n                <property name=\"buddy\" stdset=\"0\">\n                  <cstring>contact2LineEdit</cstring>\n                </property>\n                <property name=\"wordWrap\">\n                  <bool>false</bool>\n                </property>\n              </widget>\n            </item>\n            <item row=\"1\" column=\"1\">\n              <widget class=\"QLineEdit\" name=\"contact2LineEdit\">\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                </property>\n              </widget>\n            </item>\n            <item row=\"0\" column=\"1\">\n              <widget class=\"QLineEdit\" name=\"contact1LineEdit\">\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                </property>\n              </widget>\n            </item>\n            <item row=\"0\" column=\"0\">\n              <widget class=\"QLabel\" name=\"contact1TextLabel\">\n                <property name=\"text\">\n                  <string>&amp;1st choice destination:</string>\n                </property>\n                <property name=\"buddy\" stdset=\"0\">\n                  <cstring>contact1LineEdit</cstring>\n                </property>\n                <property name=\"wordWrap\">\n                  <bool>false</bool>\n                </property>\n              </widget>\n            </item>\n            <item row=\"0\" column=\"2\">\n              <widget class=\"QToolButton\" name=\"address1ToolButton\">\n                <property name=\"focusPolicy\">\n                  <enum>Qt::TabFocus</enum>\n                </property>\n                <property name=\"text\">\n                  <string/>\n                </property>\n                <property name=\"shortcut\">\n                  <string>F10</string>\n                </property>\n                <property name=\"icon\">\n                  <iconset>:/icons/images/kontact_contacts.png</iconset>\n                </property>\n                <property name=\"toolTip\" stdset=\"0\">\n                  <string>Address book</string>\n                </property>\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>Select an address from the address book.</string>\n                </property>\n              </widget>\n            </item>\n            <item row=\"2\" column=\"2\">\n              <widget class=\"QToolButton\" name=\"address3ToolButton\">\n                <property name=\"focusPolicy\">\n                  <enum>Qt::TabFocus</enum>\n                </property>\n                <property name=\"text\">\n                  <string/>\n                </property>\n                <property name=\"shortcut\">\n                  <string>F12</string>\n                </property>\n                <property name=\"icon\">\n                  <iconset>:/icons/images/kontact_contacts.png</iconset>\n                </property>\n                <property name=\"toolTip\" stdset=\"0\">\n                  <string>Address book</string>\n                </property>\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>Select an address from the address book.</string>\n                </property>\n              </widget>\n            </item>\n            <item row=\"1\" column=\"2\">\n              <widget class=\"QToolButton\" name=\"address2ToolButton\">\n                <property name=\"focusPolicy\">\n                  <enum>Qt::TabFocus</enum>\n                </property>\n                <property name=\"text\">\n                  <string/>\n                </property>\n                <property name=\"shortcut\">\n                  <string>F11</string>\n                </property>\n                <property name=\"icon\">\n                  <iconset>:/icons/images/kontact_contacts.png</iconset>\n                </property>\n                <property name=\"toolTip\" stdset=\"0\">\n                  <string>Address book</string>\n                </property>\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>Select an address from the address book.</string>\n                </property>\n              </widget>\n            </item>\n          </layout>\n        </widget>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>361</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>contact1LineEdit</tabstop>\n    <tabstop>contact2LineEdit</tabstop>\n    <tabstop>contact3LineEdit</tabstop>\n    <tabstop>address1ToolButton</tabstop>\n    <tabstop>address2ToolButton</tabstop>\n    <tabstop>address3ToolButton</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"global\">list</include>\n    <include location=\"local\">sockets/url.h</include>\n    <include location=\"local\">ui_getaddressform.h</include>\n    <include location=\"local\">user.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>RedirectForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>RedirectForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>address1ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>RedirectForm</receiver>\n      <slot>showAddressBook1()</slot>\n    </connection>\n    <connection>\n      <sender>address2ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>RedirectForm</receiver>\n      <slot>showAddressBook2()</slot>\n    </connection>\n    <connection>\n      <sender>address3ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>RedirectForm</receiver>\n      <slot>showAddressBook3()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/selectnicform.cpp",
    "content": "//Added by qt3to4:\n#include <QPixmap>\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"gui.h\"\n#include <QMessageBox>\n#include \"sys_settings.h\"\n#include \"selectnicform.h\"\n/*\n *  Constructs a SelectNicForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nSelectNicForm::SelectNicForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nSelectNicForm::~SelectNicForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid SelectNicForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid SelectNicForm::init()\n{\n\tidxDefault = -1;\n}\n\nvoid SelectNicForm::setAsDefault(bool setIp)\n{\n#if 0\n\t// DEPRECATED\n\t// Only show the information when the default button is\n\t// pressed for the first time.\n\tif (idxDefault == -1) {\n\t\tQMessageBox::information(this, PRODUCT_NAME, tr(\n\t\t\t\"If you want to remove or \"\n\t\t\t\"change the default at a later time, you can do that \"\n\t\t\t\"via the system settings.\"));\n\t}\n\t\n\t// Store current index as the changeItem method also changes\n\t// the current index as a side effect.\n\tint idxNewDefault = nicListBox->currentItem();\n\t\n\t// Restore pixmap of the old default\n\tif (idxDefault != -1) {\n\t\tnicListBox->changeItem(\n\t\t\tQPixmap(\":/icons/images/kcmpci16.png\"),\n\t\t\tnicListBox->text(idxDefault),\n\t\t\tidxDefault);\n\t}\n\t\n\t// Set pixmap of the default\n\tidxDefault = idxNewDefault;\n\tnicListBox->changeItem(\n\t\tQPixmap(\":/icons/images/twinkle16.png\"),\n\t\tnicListBox->text(idxDefault),\n\t\tidxDefault);\t\n\t\n\t// Write default to system settings\n\tint pos = nicListBox->currentText().findRev(':');\n\tif (setIp) {\n\t\tsys_config->set_start_user_host(nicListBox->currentText().mid(pos + 1).ascii());\n\t\tsys_config->set_start_user_nic(\"\");\n\t} else {\n\t\tsys_config->set_start_user_nic(nicListBox->currentText().left(pos).ascii());\n\t\tsys_config->set_start_user_host(\"\");\n\t}\n\tstring error_msg;\n\tif (!sys_config->write_config(error_msg)) {\n\t\t// Failed to write config file\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_CRITICAL);\n\t}\n#endif\n}\n\nvoid SelectNicForm::setAsDefaultIp()\n{\n\tsetAsDefault(true);\n}\n\nvoid SelectNicForm::setAsDefaultNic()\n{\n\tsetAsDefault(false);\n}\n"
  },
  {
    "path": "src/gui/selectnicform.h",
    "content": "#ifndef SELECTNICFORM_H\n#define SELECTNICFORM_H\n#include <QDialog>\n#include \"ui_selectnicform.h\"\n\nclass SelectNicForm : public QDialog, public Ui::SelectNicForm\n{\n\tQ_OBJECT\n\npublic:\n    SelectNicForm(QWidget* parent = 0);\n\t~SelectNicForm();\n\npublic slots:\n\tvirtual void setAsDefault( bool setIp );\n\tvirtual void setAsDefaultIp();\n\tvirtual void setAsDefaultNic();\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tint idxDefault;\n\n\tvoid init();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/selectnicform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>SelectNicForm</class>\n  <widget class=\"QDialog\" name=\"SelectNicForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>482</width>\n        <height>144</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Select NIC</string>\n    </property>\n    <layout class=\"QGridLayout\">\n      <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"nicIconTextLabel\">\n          <property name=\"text\">\n            <string/>\n          </property>\n          <property name=\"pixmap\">\n            <pixmap>:/icons/images/kcmpci.png</pixmap>\n          </property>\n          <property name=\"wordWrap\">\n            <bool>false</bool>\n          </property>\n        </widget>\n      </item>\n      <item row=\"1\" column=\"0\" rowspan=\"2\" colspan=\"1\">\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>41</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item row=\"0\" column=\"1\">\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <widget class=\"QLabel\" name=\"selectTextLabel\">\n              <property name=\"text\">\n                <string>Select the network interface/IP address that you want to use:</string>\n              </property>\n              <property name=\"alignment\">\n                <set>Qt::AlignTop</set>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QListWidget\" name=\"nicListBox\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>You have multiple IP addresses. Here you must select which IP address should be used. This IP address will be used inside the SIP messages.</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item row=\"1\" column=\"1\">\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item row=\"2\" column=\"1\">\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>40</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"defaultIpPushButton\">\n              <property name=\"text\">\n                <string>Set as default &amp;IP</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+I</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Make the selected IP address the default IP address. The next time you start Twinkle, this IP address will be automatically selected.</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"defaultNicPushButton\">\n              <property name=\"text\">\n                <string>Set as default &amp;NIC</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+N</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Make the selected network interface the default interface. The next time you start Twinkle, this interface will be automatically selected.</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <connections>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SelectNicForm</receiver>\n      <slot>accept()</slot>\n    </connection>\n    <connection>\n      <sender>nicListBox</sender>\n      <signal>selected(QString)</signal>\n      <receiver>SelectNicForm</receiver>\n      <slot>accept()</slot>\n    </connection>\n    <connection>\n      <sender>defaultIpPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SelectNicForm</receiver>\n      <slot>setAsDefaultIp()</slot>\n    </connection>\n    <connection>\n      <sender>defaultNicPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SelectNicForm</receiver>\n      <slot>setAsDefaultNic()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/selectprofileform.cpp",
    "content": "//Added by qt3to4:\n#include <QPixmap>\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <QDir>\n#include \"user.h\"\n#include <QStringList>\n#include <QMessageBox>\n#include \"protocol.h\"\n#include \"gui.h\"\n#include \"userprofileform.h\"\n#include \"getprofilenameform.h\"\n#include \"audits/memman.h\"\n#include \"wizardform.h\"\n#include \"syssettingsform.h\"\n#include <QListWidgetItem>\n#include \"service.h\"\n#include \"presence/buddy.h\"\n#include \"diamondcardprofileform.h\"\n#include \"selectprofileform.h\"\n/*\n *  Constructs a SelectProfileForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nSelectProfileForm::SelectProfileForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nSelectProfileForm::~SelectProfileForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid SelectProfileForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid SelectProfileForm::init()\n{\n\tuser_config = 0;\n\n#ifndef WITH_DIAMONDCARD\n\tdiamondcardPushButton->hide();\n#endif\n}\n\nvoid SelectProfileForm::destroy()\n{\n\tif (user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\n}\n\n// The exec() method is called at startup\nint SelectProfileForm::execForm()\n{\n\tmainWindow = 0;\n\tprofileListView->clear();\n\tdefaultSet = false; // no default has been set\n\t\n\t// Get list of all profiles\n\tQStringList profiles;\n\tQString error;\n\tif (!SelectProfileForm::getUserProfiles(profiles, error)) {\n\t\tQMessageBox::critical(this, PRODUCT_NAME, error);\n\t\treturn QDialog::Rejected;\n\t}\n\t\n\t// If there are no profiles then the user has to create one\n\tif (profiles.isEmpty()) {\n\t\tQMessageBox::information(this, PRODUCT_NAME, tr(\n\t\t\t\"<html>\"\\\n\t\t\t\"Before you can use Twinkle, you must create a user \"\\\n\t\t\t\"profile.<br>Click OK to create a profile.</html>\"));\n\t\t\n\t\tQMessageBox mb(QMessageBox::Question, PRODUCT_NAME, tr(\n\t\t\t\"<html>\"\\\n\t\t\t\"You can use the profile editor to create a profile. \"\\\n\t\t\t\"With the profile editor you can change many settings \"\\\n\t\t\t\"to tune the SIP protocol, RTP and many other things.<br><br>\"\\\n\t\t\t\"Alternatively you can use the wizard to quickly setup a \"\\\n\t\t\t\"user profile. The wizard asks you only a few essential \"\\\n\t\t\t\"settings. If you create a user profile with the wizard you \"\\\n\t\t\t\"can still edit the full profile with the profile editor at a later \"\\\n            \"time.<br><br>\")\n#ifdef WITH_DIAMONDCARD\n            + tr (\"You can create a Diamondcard account to make worldwide \"\\\n            \"calls to regular and cell phones and send SMS messages.<br><br>\")\n#endif\n            + tr(\"Choose what method you wish to use.</html>\"),\n\t\t\tQMessageBox::NoButton, this);\n\t\tQPushButton *wizardButton = mb.addButton(\n\t\t\t\ttr(\"&Wizard\"),\n\t\t\t\tQMessageBox::AcceptRole);\n\t\tQPushButton *editorButton = mb.addButton(\n\t\t\t\ttr(\"&Profile editor\"),\n\t\t\t\tQMessageBox::AcceptRole);\n#ifdef WITH_DIAMONDCARD\n\t\tQPushButton *diamondCardButton = mb.addButton(\n\t\t\t\ttr(\"&Diamondcard\"),\n\t\t\t\tQMessageBox::AcceptRole);\n#endif\n\t\tmb.exec();\n\t\t\n\t\tif (mb.clickedButton() == wizardButton) {\n\t\t\twizardProfile(true);\n\t\t} else if (mb.clickedButton() == editorButton) {\n\t\t\tnewProfile(true);\n#ifdef WITH_DIAMONDCARD\n\t\t} else if (mb.clickedButton() == diamondCardButton) {\n\t\t\tdiamondcardProfile(true);\n#endif\n\t\t} else {\n\t\t\treturn QDialog::Rejected;\n\t\t}\n\t\t\n        if (profileListView->count() == 0) {\n\t\t\t// No profile has been created.\n\t\t\treturn QDialog::Rejected;\n\t\t}\n\t\t\n\t\t// Select the created profile\n        QListWidgetItem* item = profileListView->currentItem();\n\t\tQString profile = item->text();\n\t\tprofile.append(USER_FILE_EXT);\n\t\tselectedProfiles.clear();\n        selectedProfiles.push_back(profile.toStdString());\n\t\t\n\t\tQMessageBox::information(this, PRODUCT_NAME, tr(\n\t\t\t\"<html>\"\\\n\t\t\t\"Next you may adjust the system settings. \"\\\n\t\t\t\"You can change these settings always at a later time.\"\\\n\t\t\t\"<br><br>\"\\\n\t\t\t\"Click OK to view and adjust the system settings.</html>\"));\n\t\t\n        SysSettingsForm f(this);\n        f.setModal(true);\n\t\tf.exec();\n\t\t\n\t\treturn QDialog::Accepted;\n\t}\n\t\n\tfillProfileListView(profiles);\n\tsysPushButton->show();\n\trunPushButton->setFocus();\n\t\n\t// Show the modal dialog\n\treturn QDialog::exec();\n}\n\n// The showForm() method is called from File menu when Twinkle is running.\n// The execForm() method cannot be used as it will block the Qt event loop.\n// NOTE: the method show() is not re-implemented as Qt calls this method\n//   from exec() internally.\nvoid SelectProfileForm::showForm(QWidget *_mainWindow)\n{\n\tmainWindow = _mainWindow;\n\tprofileListView->clear();\n\tdefaultSet = false;\n\t\n\t// Get list of all profiles\n\tQStringList profiles;\n\tQString error;\n\tif (!SelectProfileForm::getUserProfiles(profiles, error)) {\n\t\tQMessageBox::critical(this, PRODUCT_NAME, error);\n\t\treturn;\n\t}\n\t\n\t// Initialize profile list view\n\tfillProfileListView(profiles);\n\n    for (int i = 0; i < profileListView->count(); i++)\n    {\n        QListWidgetItem* item = profileListView->item(i);\n        QString profile = item->text();\n\t\t\n\t\t// Set pixmap of default profile\n\t\tlist<string> l = sys_config->get_start_user_profiles();\n        if (std::find(l.begin(),  l.end(), profile.toStdString()) != l.end())\n\t\t{\n            item->setData(Qt::DecorationRole, QPixmap(\":/icons/images/twinkle16.png\"));\n\t\t\tdefaultSet = true;\n\t\t}\n\t\t\n\t\t// Tick check box of active profile\n        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);\n\n        if (phone->ref_user_profile(profile.toStdString())) {\n            item->setCheckState(Qt::Checked);\n\t\t}\n\t}\t\n\t\n\tsysPushButton->hide();\n\trunPushButton->setText(\"&OK\");\n\trunPushButton->setFocus();\n\tQDialog::show();\n}\n\nvoid SelectProfileForm::runProfile()\n{\n\tselectedProfiles.clear();\n\n    for (int i = 0; i < profileListView->count(); i++)\n    {\n        QListWidgetItem* item = profileListView->item(i);\n\n        if (item->checkState() == Qt::Checked)\n        {\n            QString profile = item->text();\n            profile.append(USER_FILE_EXT);\n            selectedProfiles.push_back(profile.toStdString());\n        }\n\t}\n\t\n\tif (selectedProfiles.empty()) {\n\t\tQMessageBox::warning(this, PRODUCT_NAME, tr(\n\t\t\t\t\"You did not select any user profile to run.\\n\"\\\n\t\t\t\t\"Please select a profile.\"));\n\t\treturn;\n\t}\n\t\n\t// This signal will be caught when Twinkle is running.\n\t// At startup the selectedProfiles attribute is read.\n\temit selection(selectedProfiles);\n\t\n\taccept();\n}\n\nvoid SelectProfileForm::editProfile()\n{\n    QListWidgetItem *item = profileListView->currentItem();\n\tQString profile = item->text();\n\t\n\t// If the profile to edit is currently active, then edit the in-memory\n\t// user profile owned by the t_phone_user object\n\tif (mainWindow) {\n        t_user *active_user = phone->ref_user_profile(profile.toStdString());\n\t\tif (active_user) {\n\t\t\tlist<t_user *> user_list;\n\t\t\tuser_list.push_back(active_user);\n            UserProfileForm *f = new UserProfileForm(this);\n            f->setAttribute(Qt::WA_DeleteOnClose);\n            f->setModal(true);\n\t\t\t\n\t\t\tconnect(f, SIGNAL(authCredentialsChanged(t_user *, const string&)),\n\t\t\t\tmainWindow, \n\t\t\t\tSLOT(updateAuthCache(t_user *, const string&)));\n\t\t\t\n\t\t\tconnect(f, SIGNAL(stunServerChanged(t_user *)),\n\t\t\t\tmainWindow, SLOT(updateStunSettings(t_user *)));\n\t\t\n\t\t\tf->show(user_list, \"\");\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// Edit the user profile from disk.\n\tprofile.append(USER_FILE_EXT);\n\t\n\t// Read selected config file\n\tstring error_msg;\n\t\n\tif (user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\n\tuser_config = new t_user();\n\tMEMMAN_NEW(user_config);\n\t\n    if (!user_config->read_config(profile.toStdString(), error_msg)) {\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_WARNING);\n\t\treturn;\n\t}\n\t\n\t// Show the edit user profile form (modal dialog)\n\tlist<t_user *> user_list;\n\tuser_list.push_back(user_config);\n    UserProfileForm *f = new UserProfileForm(this);\n    f->setAttribute(Qt::WA_DeleteOnClose);\n    f->setModal(true);\n\tf->show(user_list, \"\");\n}\n\nvoid SelectProfileForm::newProfile()\n{\n\tnewProfile(false);\n}\n\nvoid SelectProfileForm::newProfile(bool exec_mode)\n{\n\t// Ask user for a profile name\n    GetProfileNameForm getProfileNameForm(this);\n    getProfileNameForm.setModal(true);\n\tif (!getProfileNameForm.execNewName()) return;\n\t\n\t// Create file name\n\tQString profile = getProfileNameForm.getProfileName();\n\tQString filename = profile;\n\tfilename.append(USER_FILE_EXT);\n\t\n\t// Create a new user config\n\tif (user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\n\tuser_config = new t_user();\n\tMEMMAN_NEW(user_config);\n    user_config->set_config(filename.toStdString());\n\t\n\t// Show the edit user profile form (modal dialog)\n\tlist<t_user *> user_list;\n\tuser_list.push_back(user_config);\n    UserProfileForm *f = new UserProfileForm(this);\n    f->setAttribute(Qt::WA_DeleteOnClose);\n    f->setModal(true);\n\tconnect(f, SIGNAL(success()), this, SLOT(newProfileCreated()));\n\t\n\tif (exec_mode) {\n\t\tf->exec(user_list, \"\");\n\t} else {\n\t\tf->show(user_list, \"\");\n\t}\n}\n\nvoid SelectProfileForm::newProfileCreated()\n{\n\t// New profile created\n\t// Add the new profile to the profile list box\n    QListWidgetItem* item = new QListWidgetItem(QPixmap(\":/icons/images/penguin-small.png\"),\n                                                QString::fromStdString(user_config->get_profile_name()),\n                                                profileListView);\n\n    item->setFlags(item->flags() | Qt::ItemIsUserCheckable);\n    item->setCheckState(Qt::Unchecked);\n\t\t\n\t// Make the new profile the selected profile\n\t// Do not change this without changing the exec method.\n\t// When there are no profiles, the exec methods relies on the\n\t// fact that afer creation of the profile it is selected.\n    profileListView->setCurrentItem(item);\n\t\t\n\t// Enable buttons that act on a profile\n\teditPushButton->setEnabled(true);\n\tdeletePushButton->setEnabled(true);\n\trenamePushButton->setEnabled(true);\n\tdefaultPushButton->setEnabled(true);\n\trunPushButton->setEnabled(true);\n}\n\nvoid SelectProfileForm::deleteProfile()\n{\n    QListWidgetItem *item = profileListView->currentItem();\n\tQString profile = item->text();\n\tQString msg = tr(\"Are you sure you want to delete profile '%1'?\").arg(profile);\n\tint button = QMessageBox::warning(this, tr(\"Delete profile\"), msg,\n\t\t\tQMessageBox::Yes | QMessageBox::No);\n\tif (button == QMessageBox::Yes) {\n\t\t// Delete file\n\t\tQDir d = QDir::home();\n\t\td.cd(USER_DIR);\n\t\tQString filename = profile;\n\t\tfilename.append(USER_FILE_EXT);\n\t\tQString fullname = d.filePath(filename);\n\t\tif (!QFile::remove(fullname)) {\n\t\t\t// Failed to delete file\n\t\t\tQMessageBox::critical(this, PRODUCT_NAME,\n\t\t\t\ttr(\"Failed to delete profile.\"));\n\t\t} else {\n\t\t\t// Delete possible backup of the profile\n\t\t\tQString backupname = fullname;\n\t\t\tbackupname.append(\"~\");\n\t\t\t(void)QFile::remove(backupname);\n\t\t\t\n\t\t\t// Delete service files\n\t\t\tfilename = profile;\n\t\t\tfilename.append(SVC_FILE_EXT);\n\t\t\tfullname = d.filePath(filename);\n\t\t\t(void)QFile::remove(fullname);\n\t\t\tfullname.append(\"~\");\n\t\t\t(void)QFile::remove(fullname);\n\t\t\t\n\t\t\t// Delete profile from list of default profiles in\n\t\t\t// system settings\n\t\t\tlist<string> l = sys_config->get_start_user_profiles();\n            if (std::find(l.begin(), l.end(), profile.toStdString()) != l.end()) {\n                l.remove(profile.toStdString());\n\t\t\t\tsys_config->set_start_user_profiles(l);\n\t\t\t\t\n\t\t\t\tstring error_msg;\n\t\t\t\tif (!sys_config->write_config(error_msg)) {\n\t\t\t\t\t// Failed to write config file\n\t\t\t\t\t((t_gui *)ui)->cb_show_msg(this, \n\t\t\t\t\t\terror_msg, MSG_CRITICAL);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// Delete profile from profile list box\n            QListWidgetItem *item = profileListView->\n\t\t\t\t\t       currentItem();\n\t\t\tdelete item;\n            if (profileListView->count() == 0) {\n\t\t\t\t// There are no profiles anymore\n\t\t\t\t// Disable buttons that act on a profile\n\t\t\t\teditPushButton->setEnabled(false);\n\t\t\t\tdeletePushButton->setEnabled(false);\n\t\t\t\trenamePushButton->setEnabled(false);\n\t\t\t\tdefaultPushButton->setEnabled(false);\n\t\t\t\trunPushButton->setEnabled(false);\n\t\t\t} else {\n                profileListView->setCurrentItem(profileListView->item(0));\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid SelectProfileForm::renameProfile()\n{\n    QListWidgetItem *item = profileListView->currentItem();\n\tQString oldProfile = item->text();\n\t\n\t// Ask user for a new profile name\n    GetProfileNameForm getProfileNameForm(this);\n    getProfileNameForm.setModal(true);\n\tif (!getProfileNameForm.execRename(oldProfile)) return;\n\t\n\t// Create file name for the new profile\n\tQString newProfile = getProfileNameForm.getProfileName();\n\tQString newFilename = newProfile;\n\tnewFilename.append(USER_FILE_EXT);\n\t\n\t// Create file name for the old profile\n\tQString oldFilename = oldProfile;\n\toldFilename.append(USER_FILE_EXT);\n\t\n\t// Rename the file\n\tQDir d = QDir::home();\n\td.cd(USER_DIR);\n\tif (!d.rename(oldFilename, newFilename)) {\n\t\t// Failed to delete file\n\t\tQMessageBox::critical(this, PRODUCT_NAME, \n\t\t\t\t      tr(\"Failed to rename profile.\"));\n\t} else {\n\t\t// If there is a backup of the profile, rename it too.\n\t\tQString oldBackupFilename = oldFilename;\n\t\toldBackupFilename.append(\"~\");\n\t\tQString oldBackupFullname = d.filePath(oldBackupFilename);\n\t\tif (QFile::exists(oldBackupFullname)) {\n\t\t\tQString newBackupFilename = newFilename;\n\t\t\tnewBackupFilename.append(\"~\");\n\t\t\td.rename(oldBackupFilename, newBackupFilename);\n\t\t}\n\t\t\n\t\t// Rename service files\n\t\toldFilename = oldProfile;\n\t\toldFilename.append(SVC_FILE_EXT);\n\t\tQString oldFullname = d.filePath(oldFilename);\n\t\tif (QFile::exists(oldFullname)) {\n\t\t\tnewFilename = newProfile;\n\t\t\tnewFilename.append(SVC_FILE_EXT);\n\t\t\td.rename(oldFilename, newFilename);\n\t\t}\n\t\t\n\t\t// Rename service backup file\n\t\toldFilename.append(\"~\");\n\t\toldFullname = d.filePath(oldFilename);\n\t\tif (QFile::exists(oldFullname)) {\n\t\t\tnewFilename.append(\"~\");\n\t\t\td.rename(oldFilename, newFilename);\n\t\t}\n\t\t\n\t\t// Rename buddy list file\n\t\toldFilename = oldProfile;\n\t\toldFilename.append(BUDDY_FILE_EXT);\n\t\toldFullname = d.filePath(oldFilename);\n\t\tif (QFile::exists(oldFullname)) {\n\t\t\tnewFilename = newProfile;\n\t\t\tnewFilename.append(BUDDY_FILE_EXT);\n\t\t\td.rename(oldFilename, newFilename);\n\t\t}\n\t\t\n\t\t// Rename profile in list of default profiles in\n\t\t// system settings\n\t\tlist<string> l = sys_config->get_start_user_profiles();\n        if (std::find(l.begin(), l.end(), oldProfile.toStdString()) != l.end())\n\t\t{\n            std::replace(l.begin(), l.end(), oldProfile.toStdString(), newProfile.toStdString());\n\t\t\tsys_config->set_start_user_profiles(l);\n\t\t\t\t\n\t\t\tstring error_msg;\n\t\t\tif (!sys_config->write_config(error_msg)) {\n\t\t\t\t// Failed to write config file\n\t\t\t\t((t_gui *)ui)->cb_show_msg(this, \n\t\t\t\t\t\terror_msg, MSG_CRITICAL);\n\t\t\t}\n\t\t}\n\t\t\n\t\temit profileRenamed();\n\t\t\n\t\t// Change profile name in the list box\n        QListWidgetItem *item = profileListView->currentItem();\n        item->setText(newProfile);\n\t}\n}\n\nvoid SelectProfileForm::setAsDefault()\n{\n\t// Only show the information when the default button is\n\t// pressed for the first time.\n\tif (!defaultSet) {\n\t\tQMessageBox::information(this, PRODUCT_NAME, tr(\n\t\t\t\"<p>\"\n\t\t\t\"If you want to remove or \"\n\t\t\t\"change the default at a later time, you can do that \"\n\t\t\t\"via the system settings.\"\n\t\t\t\"</p>\"));\n\t}\n\t\n\tdefaultSet = true;\n\t\n\t// Restore all pixmaps\n    // Set pixmap of the default profiles.\n    // Set default profiles in system settings.\n    list<string> l;\n    for (int i = 0; i < profileListView->count(); i++) {\n        QListWidgetItem* item = profileListView->item(i);\n\n        if (item->checkState() == Qt::Checked)\n        {\n            item->setData(Qt::DecorationRole, QPixmap(\":/icons/images/twinkle16.png\"));\n            l.push_back(item->text().toStdString());\n        }\n        else\n        {\n            item->setData(Qt::DecorationRole, QPixmap(\":/icons/images/penguin-small.png\"));\n        }\n\t}\n\n\tsys_config->set_start_user_profiles(l);\n\t\n\t// Write default to system settings\n\tstring error_msg;\n\tif (!sys_config->write_config(error_msg)) {\n\t\t// Failed to write config file\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_CRITICAL);\n\t}\n}\n\nvoid SelectProfileForm::wizardProfile()\n{\n\twizardProfile(false);\n}\n\nvoid SelectProfileForm::wizardProfile(bool exec_mode)\n{\n\t// Ask user for a profile name\n    GetProfileNameForm getProfileNameForm(this);\n    getProfileNameForm.setModal(true);\n\tif (!getProfileNameForm.execNewName()) return;\n\t\n\t// Create file name\n\tQString profile = getProfileNameForm.getProfileName();\n\tQString filename = profile;\n\tfilename.append(USER_FILE_EXT);\n\t\n\t// Create a new user config\n\tif (user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\t\n\tuser_config = new t_user();\n\tMEMMAN_NEW(user_config);\n    user_config->set_config(filename.toStdString());\n\t\n\t// Show the wizard form (modal dialog)\n    WizardForm *f = new WizardForm(this);\n    f->setAttribute(Qt::WA_DeleteOnClose);\n    f->setModal(true);\n\tconnect(f, SIGNAL(success()), this, SLOT(newProfileCreated()));\n\t\n\tif (exec_mode) {\n\t\tf->exec(user_config);\n\t} else {\n\t\tf->show(user_config);\n\t}\n}\n\nvoid SelectProfileForm::diamondcardProfile()\n{\n\tdiamondcardProfile(false);\n}\n\nvoid SelectProfileForm::diamondcardProfile(bool exec_mode)\n{\n\t// Create a new user config\n\tif (user_config) {\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\t\n\tuser_config = new t_user();\n\tMEMMAN_NEW(user_config);\n\t\n\t// Show the diamondcard profile form (modal dialog)\n    DiamondcardProfileForm *f = new DiamondcardProfileForm(this);\n    f->setAttribute(Qt::WA_DeleteOnClose);\n    f->setModal(true);\n\n\tconnect(f, SIGNAL(success()), this, SLOT(newProfileCreated()));\n\t\n\tif (exec_mode) {\n\t\tf->exec(user_config);\n\t} else {\n\t\tf->show(user_config);\n\t}\n}\n\n\nvoid SelectProfileForm::sysSettings()\n{\n    SysSettingsForm *f = new SysSettingsForm(this);\n    f->setAttribute(Qt::WA_DeleteOnClose);\n    f->setModal(true);\n\tf->show();\n}\n\n// Get a list of all profiles. Returns false if there is an error.\nbool SelectProfileForm::getUserProfiles(QStringList &profiles, QString &error)\n{\n\t// Find the .twinkle directory in HOME\n\tQDir d = QDir::home();\n\tif (!d.cd(USER_DIR)) {\n\t\terror = tr(\"Cannot find .twinkle directory in your home directory.\");\n\t\treturn false;\n\t}\n\t\n\t// Select all config files\n    QString filterName = \"*\";\n\tfilterName.append(USER_FILE_EXT);\n\td.setFilter(QDir::Files);\n    d.setNameFilters(QStringList(filterName));\n\td.setSorting(QDir::Name | QDir::IgnoreCase);\n\tprofiles = d.entryList();\n\t\n\treturn true;\n}\n\nvoid SelectProfileForm::fillProfileListView(const QStringList &profiles)\n{\n\t// Put the profiles in the profile list view\n\tfor (QStringList::ConstIterator i = profiles.begin(); i != profiles.end(); i++) {\n\t\t// Strip off the user file extension\n\t\tQString profile = *i;\n\t\tprofile.truncate(profile.length() - strlen(USER_FILE_EXT));\n\n        QListWidgetItem* item = new QListWidgetItem(QPixmap(\":/icons/images/penguin-small.png\"), profile, profileListView);\n        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);\n        item->setCheckState(Qt::Unchecked);\n\t}\n\t\n\t// Highlight the first profile\n    profileListView->setCurrentItem(profileListView->item(0));\n}\n\nvoid SelectProfileForm::toggleItem(QModelIndex index)\n{\n    QListWidgetItem* item = profileListView->item(index.row());\n    Qt::CheckState state = item->checkState();\n    if (state == Qt::Checked)\n        item->setCheckState(Qt::Unchecked);\n    else\n        item->setCheckState(Qt::Checked);\n}\n\n"
  },
  {
    "path": "src/gui/selectprofileform.h",
    "content": "#ifndef SELECTPROFILEFORM_H\n#define SELECTPROFILEFORM_H\nclass t_phone;\nextern t_phone *phone;\n\n#include <list>\n#include <string>\n#include \"phone.h\"\n#include <QDialog>\n#include \"ui_selectprofileform.h\"\n\nclass SelectProfileForm : public QDialog, public Ui::SelectProfileForm\n{\n\tQ_OBJECT\n\npublic:\n    SelectProfileForm(QWidget* parent = 0);\n\t~SelectProfileForm();\n\n\tstd::list<std::string> selectedProfiles;\n\n\tvirtual int execForm();\n\tstatic bool getUserProfiles( QStringList & profiles, QString & error );\n\npublic slots:\n\tvirtual void showForm( QWidget * _mainWindow );\n\tvirtual void runProfile();\n\tvirtual void editProfile();\n\tvirtual void newProfile();\n\tvirtual void newProfile( bool exec_mode );\n\tvirtual void newProfileCreated();\n\tvirtual void deleteProfile();\n\tvirtual void renameProfile();\n\tvirtual void setAsDefault();\n\tvirtual void wizardProfile();\n\tvirtual void wizardProfile( bool exec_mode );\n\tvirtual void diamondcardProfile();\n\tvirtual void diamondcardProfile( bool exec_mode );\n\tvirtual void sysSettings();\n\tvirtual void fillProfileListView( const QStringList & profiles );\n    virtual void toggleItem( QModelIndex item );\n\nsignals:\n\tvoid selection(const list<string> &);\n\tvoid profileRenamed();\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tbool defaultSet;\n\tt_user *user_config;\n\tQWidget *mainWindow;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/selectprofileform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SelectProfileForm</class>\n <widget class=\"QDialog\" name=\"SelectProfileForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>499</width>\n    <height>511</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Select user profile</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QLabel\" name=\"selectTextLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string>Select user profile(s) to run:</string>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignVCenter</set>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"1\" rowspan=\"2\">\n    <layout class=\"QVBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Vertical</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"newProfileGroupBox\">\n       <property name=\"title\">\n        <string>Create profile</string>\n       </property>\n       <layout class=\"QVBoxLayout\">\n        <item>\n         <widget class=\"QPushButton\" name=\"newPushButton\">\n          <property name=\"whatsThis\">\n           <string>Create a new profile with the profile editor.</string>\n          </property>\n          <property name=\"text\">\n           <string>Ed&amp;itor</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+I</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"wizardPushButton\">\n          <property name=\"whatsThis\">\n           <string>Create a new profile with the wizard.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Wizard</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+W</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"diamondcardPushButton\">\n          <property name=\"whatsThis\">\n           <string>Create a profile for a Diamondcard account. With a Diamondcard account you can make worldwide calls to regular and cell phones and send SMS messages.</string>\n          </property>\n          <property name=\"text\">\n           <string>Dia&amp;mondcard</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+M</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"modifyProfileGroupBox\">\n       <property name=\"title\">\n        <string>Modify profile</string>\n       </property>\n       <layout class=\"QVBoxLayout\">\n        <item>\n         <widget class=\"QPushButton\" name=\"editPushButton\">\n          <property name=\"whatsThis\">\n           <string>Edit the highlighted profile.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Edit</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+E</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"deletePushButton\">\n          <property name=\"whatsThis\">\n           <string>Delete the highlighted profile.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Delete</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+D</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"renamePushButton\">\n          <property name=\"whatsThis\">\n           <string>Rename the highlighted profile.</string>\n          </property>\n          <property name=\"text\">\n           <string>Ren&amp;ame</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+A</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"startupProfileGroupBox\">\n       <property name=\"title\">\n        <string>Startup profile</string>\n       </property>\n       <layout class=\"QVBoxLayout\">\n        <item>\n         <widget class=\"QPushButton\" name=\"defaultPushButton\">\n          <property name=\"whatsThis\">\n           <string>Make the selected profiles the default profiles. The next time you start Twinkle, these profiles will be automatically run.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Set as default</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+S</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"runPushButton\">\n          <property name=\"whatsThis\">\n           <string>Run Twinkle with the selected profiles.</string>\n          </property>\n          <property name=\"text\">\n           <string>&amp;Run</string>\n          </property>\n          <property name=\"shortcut\">\n           <string>Alt+R</string>\n          </property>\n          <property name=\"default\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"sysPushButton\">\n       <property name=\"whatsThis\">\n        <string>Edit the system settings.</string>\n       </property>\n       <property name=\"text\">\n        <string>S&amp;ystem settings</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+Y</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelPushButton\">\n       <property name=\"text\">\n        <string>&amp;Cancel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QListWidget\" name=\"profileListView\">\n     <property name=\"whatsThis\">\n      <string>Tick the check boxes of the user profiles that you want to run and press run.</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>profileListView</tabstop>\n  <tabstop>newPushButton</tabstop>\n  <tabstop>wizardPushButton</tabstop>\n  <tabstop>diamondcardPushButton</tabstop>\n  <tabstop>editPushButton</tabstop>\n  <tabstop>deletePushButton</tabstop>\n  <tabstop>renamePushButton</tabstop>\n  <tabstop>defaultPushButton</tabstop>\n  <tabstop>runPushButton</tabstop>\n  <tabstop>sysPushButton</tabstop>\n  <tabstop>cancelPushButton</tabstop>\n </tabstops>\n <includes>\n  <include location=\"global\">list</include>\n  <include location=\"global\">string</include>\n  <include location=\"local\">phone.h</include>\n </includes>\n <resources/>\n <connections>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>392</x>\n     <y>501</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>runPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>runProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>435</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>editPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>editProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>289</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>newPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>newProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>172</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>deletePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>deleteProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>318</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>renamePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>renameProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>347</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>wizardPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>wizardProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>201</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>defaultPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>setAsDefault()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>406</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>sysPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>sysSettings()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>392</x>\n     <y>472</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>diamondcardPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>diamondcardProfile()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>400</x>\n     <y>230</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>profileListView</sender>\n   <signal>doubleClicked(QModelIndex)</signal>\n   <receiver>SelectProfileForm</receiver>\n   <slot>toggleItem(QModelIndex)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>228</x>\n     <y>131</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>433</x>\n     <y>27</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n <slots>\n  <slot>toggleItem(QModelIndex)</slot>\n </slots>\n</ui>\n"
  },
  {
    "path": "src/gui/selectuserform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <QVariant>\n#include <cassert>\n#include <QImage>\n#include <QPixmap>\n\n#include <QListWidget>\n#include \"selectuserform.h\"\n/*\n *  Constructs a SelectUserForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nSelectUserForm::SelectUserForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nSelectUserForm::~SelectUserForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid SelectUserForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid SelectUserForm::init()\n{\n}\n\nvoid SelectUserForm::show(t_select_purpose purpose)\n{\n\tQString title, msg_purpose;\n\t\n\t// Set dialog caption and purpose\n\ttitle = PRODUCT_NAME;\n\ttitle += \" - \";\n\tswitch (purpose) {\n\tcase SELECT_REGISTER:\n\t\ttitle.append(tr(\"Register\"));\n\t\tmsg_purpose = tr(\"Select users that you want to register.\");\n\t\tbreak;\n\tcase SELECT_DEREGISTER:\n\t\ttitle.append(tr(\"Deregister\"));\n\t\tmsg_purpose = tr(\"Select users that you want to deregister.\");\n\t\tbreak;\n\tcase SELECT_DEREGISTER_ALL:\n\t\ttitle.append(tr(\"Deregister all devices\"));\n\t\tmsg_purpose = tr(\"Select users for which you want to deregister all devices.\");\n\t\tbreak;\n\tcase SELECT_DND:\n\t\ttitle.append(tr(\"Do not disturb\"));\n\t\tmsg_purpose = tr(\"Select users for which you want to enable 'do not disturb'.\");\n\t\tbreak;\n\tcase SELECT_AUTO_ANSWER:\n\t\ttitle.append(tr(\"Auto answer\"));\n\t\tmsg_purpose = tr(\"Select users for which you want to enable 'auto answer'.\");\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n    setWindowTitle(title);\n\tpurposeTextLabel->setText(msg_purpose);\n\t\n\t// Fill list view\n\tlist<t_user *> user_list = phone->ref_users();\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {\n        QListWidgetItem* item = new QListWidgetItem(QString::fromStdString((*i)->get_profile_name()), userListView);\n\n        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);\n        item->setCheckState(Qt::Unchecked);\n\t\t\n\t\tswitch (purpose) {\n\t\tcase SELECT_DND:\n            if (phone->ref_service(*i)->is_dnd_active())\n                item->setCheckState(Qt::Checked);\n\t\t\tbreak;\n\t\tcase SELECT_AUTO_ANSWER:\n            if (phone->ref_service(*i)->is_auto_answer_active())\n                item->setCheckState(Qt::Checked);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tQDialog::show();\n}\n\nvoid SelectUserForm::validate()\n{\n\tlist<t_user *> selected_list, not_selected_list;\n\t\n    for (int i = 0; i < userListView->count(); i++)\n    {\n        QListWidgetItem *item = userListView->item(i);\n        if (item->checkState() == Qt::Checked) {\n\t\t\tselected_list.push_back(phone->\n                ref_user_profile(item->text().toStdString()));\n\t\t} else {\n\t\t\tnot_selected_list.push_back(phone->\n                ref_user_profile(item->text().toStdString()));\n\t\t}\n\t}\n\t\n\temit (selection(selected_list));\n\temit (not_selected(not_selected_list));\n\taccept();\n}\n\nvoid SelectUserForm::selectAll()\n{\n    for (int i = 0; i < userListView->count(); i++)\n    {\n        QListWidgetItem *item = userListView->item(i);\n        item->setCheckState(Qt::Checked);\n    }\n}\n\nvoid SelectUserForm::clearAll()\n{\n    for (int i = 0; i < userListView->count(); i++)\n    {\n        QListWidgetItem *item = userListView->item(i);\n        item->setCheckState(Qt::Unchecked);\n    }\n}\n\nvoid SelectUserForm::toggle(QModelIndex index)\n{\n    QListWidgetItem *item = userListView->item(index.row());\n    Qt::CheckState state = item->checkState();\n    item->setCheckState((state == Qt::Checked) ? Qt::Unchecked : Qt::Checked);\n}\n"
  },
  {
    "path": "src/gui/selectuserform.h",
    "content": "#ifndef SELECTUSERFORM_H\n#define SELECTUSERFORM_H\nclass t_phone;\nextern t_phone *phone;\n\n#include \"gui.h\"\n#include \"phone.h\"\n#include \"user.h\"\n#include \"ui_selectuserform.h\"\n\nclass SelectUserForm : public QDialog, public Ui::SelectUserForm\n{\n\tQ_OBJECT\n\npublic:\n    SelectUserForm(QWidget* parent = 0);\n\t~SelectUserForm();\n\npublic slots:\n\tvirtual void show( t_select_purpose purpose );\n\tvirtual void validate();\n\tvirtual void selectAll();\n\tvirtual void clearAll();\n    virtual void toggle( QModelIndex item );\n\nsignals:\n\tvoid selection(list<t_user *>);\n\tvoid not_selected(list<t_user*>);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tvoid init();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/selectuserform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SelectUserForm</class>\n <widget class=\"QDialog\" name=\"SelectUserForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>582</width>\n    <height>226</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Select user</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"1\" column=\"4\">\n    <widget class=\"QPushButton\" name=\"cancelPushButton\">\n     <property name=\"text\">\n      <string>&amp;Cancel</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+C</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QPushButton\" name=\"selectPushButton\">\n     <property name=\"text\">\n      <string>&amp;Select all</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+S</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"2\">\n    <spacer>\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>41</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item row=\"1\" column=\"3\">\n    <widget class=\"QPushButton\" name=\"okPushButton\">\n     <property name=\"text\">\n      <string>&amp;OK</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+O</string>\n     </property>\n     <property name=\"default\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"1\">\n    <widget class=\"QPushButton\" name=\"clearPushButton\">\n     <property name=\"text\">\n      <string>C&amp;lear all</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+L</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"0\" colspan=\"5\">\n    <layout class=\"QVBoxLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"purposeTextLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string comment=\"No need to translate\">purpose</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QListWidget\" name=\"userListView\">\n       <property name=\"selectionMode\">\n        <enum>QAbstractItemView::NoSelection</enum>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>userListView</tabstop>\n  <tabstop>selectPushButton</tabstop>\n  <tabstop>clearPushButton</tabstop>\n  <tabstop>okPushButton</tabstop>\n  <tabstop>cancelPushButton</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">user.h</include>\n  <include location=\"local\">phone.h</include>\n  <include location=\"local\">gui.h</include>\n </includes>\n <resources/>\n <connections>\n  <connection>\n   <sender>okPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectUserForm</receiver>\n   <slot>validate()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectUserForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>selectPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectUserForm</receiver>\n   <slot>selectAll()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>clearPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SelectUserForm</receiver>\n   <slot>clearAll()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>userListView</sender>\n   <signal>doubleClicked(QModelIndex)</signal>\n   <receiver>SelectUserForm</receiver>\n   <slot>toggle(QModelIndex)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/sendfileform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifdef HAVE_KDE\n#include <kfiledialog.h>\n#endif\n\n#include \"audits/memman.h\"\n#include \"gui.h\"\n#include <QFile>\n#include <QFileDialog>\n#include \"sendfileform.h\"\n/*\n *  Constructs a SendFileForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nSendFileForm::SendFileForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nSendFileForm::~SendFileForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid SendFileForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid SendFileForm::init()\n{\n    setAttribute(Qt::WA_DeleteOnClose);\n\t_chooseFileDialog = NULL;\n}\n\nvoid SendFileForm::destroy()\n{\n\t// Auto destruct window\n\tMEMMAN_DELETE(this);\n\t\n\tif (_chooseFileDialog) {\n\t\tMEMMAN_DELETE(_chooseFileDialog);\n\t\tdelete _chooseFileDialog;\n\t}\n}\n\n/** Signal the selected information to an observer. */\nvoid SendFileForm::signalSelectedInfo() \n{\n\tif (!QFile::exists(fileLineEdit->text())) {\n        ((t_gui *)ui)->cb_show_msg(this,  tr(\"File does not exist.\").toStdString(), MSG_WARNING);\n\t\treturn;\n\t}\n\t\n\temit selected(fileLineEdit->text(), subjectLineEdit->text());\n\taccept();\n}\n\n/** Choose a file from a file dialog. */\nvoid SendFileForm::chooseFile()\n{\n#ifdef HAVE_KDE\n\tKFileDialog *d = new KFileDialog(QString::null, QString::null, this, 0, true);\n\tMEMMAN_NEW(d);\n\t\n\td->setOperationMode(KFileDialog::Other);\n\tconnect(d, SIGNAL(okClicked()), this, SLOT(setFilename()));\n#else\n\tQFileDialog *d = new QFileDialog(this);\n\tMEMMAN_NEW(d);\n\t\n\tconnect(d, SIGNAL(fileSelected(const QString &)), this, SLOT(setFilename()));\n#endif\n    d->setWindowTitle(tr(\"Send file...\"));\n\t\n\tif (_chooseFileDialog) {\n\t\tMEMMAN_DELETE(_chooseFileDialog);\n\t\tdelete _chooseFileDialog;\n\t}\n\t_chooseFileDialog = d;\n\t\n\td->show();\n}\n\n/**\n * Set the filename value.\n * @param filename [in] The value to set.\n */\nvoid SendFileForm::setFilename()\n{\n\tQString filename;\n#ifdef HAVE_KDE\n\tKFileDialog *d = dynamic_cast<KFileDialog *>(_chooseFileDialog);\n\tfilename = d->selectedFile();\n#else\n\tQFileDialog *d = dynamic_cast<QFileDialog *>(_chooseFileDialog);\n    QStringList files = d->selectedFiles();\n\n    if (!files.empty())\n        filename = files[0];\n#endif\n\t\n\tfileLineEdit->setText(filename);\n}\n"
  },
  {
    "path": "src/gui/sendfileform.h",
    "content": "#ifndef SENDFILEFORM_H\n#define SENDFILEFORM_H\n#include \"ui_sendfileform.h\"\n\nclass SendFileForm : public QDialog, public Ui::SendFileForm\n{\n\tQ_OBJECT\n\npublic:\n    SendFileForm(QWidget* parent = 0);\n\t~SendFileForm();\n\npublic slots:\n\tvirtual void signalSelectedInfo();\n\tvirtual void chooseFile();\n\tvirtual void setFilename();\n\nsignals:\n\tvoid selected(const QString &filename, const QString &subject);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tQDialog *_chooseFileDialog;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/sendfileform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>SendFileForm</class>\n  <widget class=\"QDialog\" name=\"SendFileForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>461</width>\n        <height>127</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Send File</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"2\">\n            <widget class=\"QToolButton\" name=\"fileToolButton\">\n              <property name=\"focusPolicy\">\n                <enum>Qt::TabFocus</enum>\n              </property>\n              <property name=\"text\">\n                <string/>\n              </property>\n              <property name=\"icon\">\n                <iconset>:/icons/images/fileopen.png</iconset>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Select file to send.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"2\">\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>28</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Minimum</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"fileTextLabel\">\n              <property name=\"text\">\n                <string>&amp;File:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>fileLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"subjectTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Subject:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>subjectLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"fileLineEdit\"/>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"subjectLineEdit\"/>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>141</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+C</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>subjectLineEdit</tabstop>\n    <tabstop>fileLineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">qstring.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SendFileForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SendFileForm</receiver>\n      <slot>signalSelectedInfo()</slot>\n    </connection>\n    <connection>\n      <sender>fileToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SendFileForm</receiver>\n      <slot>chooseFile()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/srvredirectform.cpp",
    "content": "//Added by qt3to4:\n#include <QPixmap>\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"phone.h\"\n#include <QGroupBox>\n#include <QCheckBox>\n#include \"gui.h\"\n#include \"audits/memman.h\"\n#include \"srvredirectform.h\"\n\n/*\n *  Constructs a SrvRedirectForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nSrvRedirectForm::SrvRedirectForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nSrvRedirectForm::~SrvRedirectForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid SrvRedirectForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\nvoid SrvRedirectForm::init()\n{\n\tcfAlwaysGroupBox->setEnabled(false);\n\tcfBusyGroupBox->setEnabled(false);\n\tcfNoanswerGroupBox->setEnabled(false);\n\t\n\t// Keeps track of which address book tool button is clicked.\n\tnrAddressBook = 0;\n\t\n\tgetAddressForm = 0;\n\t\n\t// Set toolbutton icons for disabled options.\n\tQIcon i;\n    i = addrAlways1ToolButton->icon();\n    i.addPixmap(QPixmap(\":/icons/images/kontact_contacts-disabled.png\"), QIcon::Disabled);\n    addrAlways1ToolButton->setIcon(i);\n    addrAlways2ToolButton->setIcon(i);\n    addrAlways3ToolButton->setIcon(i);\n    addrBusy1ToolButton->setIcon(i);\n    addrBusy2ToolButton->setIcon(i);\n    addrBusy3ToolButton->setIcon(i);\n    addrNoanswer1ToolButton->setIcon(i);\n    addrNoanswer2ToolButton->setIcon(i);\n    addrNoanswer3ToolButton->setIcon(i);\n}\n\nvoid SrvRedirectForm::destroy()\n{\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n}\n\nvoid SrvRedirectForm::show()\n{\n\tcurrent_user_idx = -1;\n\t((t_gui *)ui)->fill_user_combo(userComboBox);\n\tuserComboBox->setEnabled(userComboBox->count() > 1);\n\tcurrent_user = phone->ref_users().front();\n\tcurrent_user_idx = 0;\n\tpopulate();\n\t\n\tQDialog::show();\n}\n\nvoid SrvRedirectForm::populate()\n{\n\tt_service *srv = phone->ref_service(current_user);\n\tbool cf_active;\n\tlist<t_display_url> dest_list;\n\tint field;\n\t\n\t// Call forwarding unconditional\n\tcf_active = srv->get_cf_active(CF_ALWAYS, dest_list);\n\tcfAlwaysDst1LineEdit->clear();\n\tcfAlwaysDst2LineEdit->clear();\n\tcfAlwaysDst3LineEdit->clear();\n\tcfAlwaysCheckBox->setChecked(cf_active);\n\tif (cf_active) {\n\t\tfield = 1;\n\t\tfor (list<t_display_url>::iterator i = dest_list.begin(); i != dest_list.end(); i++) {\n\t\t\tif (field == 1) cfAlwaysDst1LineEdit->setText(i->encode().c_str());\n\t\t\tif (field == 2) cfAlwaysDst2LineEdit->setText(i->encode().c_str());\n\t\t\tif (field == 3) cfAlwaysDst3LineEdit->setText(i->encode().c_str());\n\t\t\tfield++;\n\t\t}\n\t}\n\t\n\t// Call forwarding busy\n\tcf_active = srv->get_cf_active(CF_BUSY, dest_list);\n\tcfBusyDst1LineEdit->clear();\n\tcfBusyDst2LineEdit->clear();\n\tcfBusyDst3LineEdit->clear();\n\tcfBusyCheckBox->setChecked(cf_active);\n\tif (cf_active) {\n\t\tfield = 1;\n\t\tfor (list<t_display_url>::iterator i = dest_list.begin(); i != dest_list.end(); i++) {\n\t\t\tif (field == 1) cfBusyDst1LineEdit->setText(i->encode().c_str());\n\t\t\tif (field == 2) cfBusyDst2LineEdit->setText(i->encode().c_str());\n\t\t\tif (field == 3) cfBusyDst3LineEdit->setText(i->encode().c_str());\n\t\t\tfield++;\n\t\t}\n\t}\n\t\n\t// Call forwarding no answer\n\tcf_active = srv->get_cf_active(CF_NOANSWER, dest_list);\n\tcfNoanswerDst1LineEdit->clear();\n\tcfNoanswerDst2LineEdit->clear();\n\tcfNoanswerDst3LineEdit->clear();\n\tcfNoanswerCheckBox->setChecked(cf_active);\n\tif (cf_active) {\n\t\tfield = 1;\n\t\tfor (list<t_display_url>::iterator i = dest_list.begin(); i != dest_list.end(); i++) {\n\t\t\tif (field == 1) cfNoanswerDst1LineEdit->setText(i->encode().c_str());\n\t\t\tif (field == 2) cfNoanswerDst2LineEdit->setText(i->encode().c_str());\n\t\t\tif (field == 3) cfNoanswerDst3LineEdit->setText(i->encode().c_str());\n\t\t\tfield++;\n\t\t}\n\t}\n}\n\t\t\nvoid SrvRedirectForm::validate()\n{\n\tif (validateValues()) {\n\t\taccept();\n\t} else {\n\t\t((t_gui *)ui)->cb_show_msg(this,\n            tr(\"You have entered an invalid destination.\").toStdString(),\n\t\t\tMSG_WARNING);\n\t}\n}\n\nbool SrvRedirectForm::validateValues()\n{\n\tlist<t_display_url> cfDestAlways, cfDestBusy, cfDestNoanswer;\n\tbool valid = false;\n\t\n\t// Redirect unconditional\n\tvalid = validate(cfAlwaysCheckBox->isChecked(), \n\t\t cfAlwaysDst1LineEdit, cfAlwaysDst2LineEdit, cfAlwaysDst3LineEdit,\n\t\t cfDestAlways);\n\tif (!valid) {\n        cfTabWidget->setCurrentIndex(0);\n\t\treturn false;\n\t}\n\t\n\t// Redirect busy\n\tvalid = validate(cfBusyCheckBox->isChecked(), \n\t\t cfBusyDst1LineEdit, cfBusyDst2LineEdit, cfBusyDst3LineEdit,\n\t\t cfDestBusy);\n\tif (!valid) {\n        cfTabWidget->setCurrentIndex(1);\n\t\treturn false;\n\t}\n\t\n\t// Redirect no answer\n\tvalid = validate(cfNoanswerCheckBox->isChecked(), \n\t\t cfNoanswerDst1LineEdit, cfNoanswerDst2LineEdit, \n\t\t cfNoanswerDst3LineEdit,\n\t\t cfDestNoanswer);\n\tif (!valid) {\n        cfTabWidget->setCurrentIndex(2);\n\t\treturn false;\n\t}\n\t\n\temit destinations(current_user, cfDestAlways, cfDestBusy, cfDestNoanswer);\t\n\treturn true;\n}\n\n\n// Validate 3 destinations if cf_active is true.\n// Returns true when all destinations are valid (first must be set, others may be empty)\n// dest_list containst the encoded destinations when valid.\n// If cf_active is false then the 3 destinations will be cleared.\nbool SrvRedirectForm::validate(bool cf_active,\n\t\t\t       QLineEdit *dst1, QLineEdit *dst2, QLineEdit *dst3,\n\t\t\t       list<t_display_url> &dest_list)\n{\n\tt_display_url destination;\n\t\n\tdest_list.clear();\n\t\n\tif (!cf_active) {\n\t\tdst1->clear();\n\t\tdst2->clear();\n\t\tdst3->clear();\n\t\treturn true;\n\t}\n\t\n\t// 1st choice destination\n    ui->expand_destination(current_user, dst1->text().trimmed().toStdString(), destination);\n\tif (destination.is_valid()) {\n\t\tdest_list.push_back(destination);\n\t} else {\n\t\tdst1->selectAll();\n\t\treturn false;\n\t}\n\t\n\t// 2nd choice destination\n\tif (!dst2->text().isEmpty()) {\n\t\tui->expand_destination(current_user, \n                       dst2->text().trimmed().toStdString(), destination);\n\t\tif (destination.is_valid()) {\n\t\t\tdest_list.push_back(destination);\n\t\t} else {\n\t\t\tdst2->selectAll();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// 3rd choice destination\n\tif (!dst3->text().isEmpty()) {\n\t\tui->expand_destination(current_user,\n                       dst3->text().trimmed().toStdString(), destination);\n\t\tif (destination.is_valid()) {\n\t\t\tdest_list.push_back(destination);\n\t\t} else {\n\t\t\tdst3->selectAll();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\treturn true;\n}\n\nvoid SrvRedirectForm::toggleAlways(bool on)\n{\n\tif (on) {\n\t\tcfAlwaysGroupBox->setEnabled(true);\n\t} else {\n\t\tcfAlwaysGroupBox->setEnabled(false);\n\t}\n}\n\nvoid SrvRedirectForm::toggleBusy(bool on)\n{\n\tif (on) {\n\t\tcfBusyGroupBox->setEnabled(true);\n\t} else {\n\t\tcfBusyGroupBox->setEnabled(false);\n\t}\n}\n\nvoid SrvRedirectForm::toggleNoanswer(bool on)\n{\n\tif (on) {\n\t\tcfNoanswerGroupBox->setEnabled(true);\n\t} else {\n\t\tcfNoanswerGroupBox->setEnabled(false);\n\t}\n}\n\nvoid SrvRedirectForm::changedUser(const QString &user_profile)\n{\n\tif (current_user_idx == -1) {\n\t\t// Initializing combo box\n\t\treturn;\n\t}\n\t\n    t_user *new_user = phone->ref_user_profile(user_profile.toStdString());\n\tif (!new_user) {\n        userComboBox->setCurrentIndex(current_user_idx);\n\t\treturn;\n\t}\n\t\n\tif (!validateValues()) {\n        userComboBox->setCurrentIndex(current_user_idx);\n\t\t((t_gui *)ui)->cb_show_msg(this,\n            tr(\"You have entered an invalid destination.\").toStdString(),\n\t\t\tMSG_WARNING);\n\t\treturn;\n\t}\n\t\n\t// Change current user\n    current_user_idx = userComboBox->currentIndex();\n\tcurrent_user = new_user;\n\tpopulate();\n}\n\nvoid SrvRedirectForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid SrvRedirectForm::showAddressBook1()\n{\n\tnrAddressBook = 1;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook2()\n{\n\tnrAddressBook = 2;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook3()\n{\n\tnrAddressBook = 3;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook4()\n{\n\tnrAddressBook = 4;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook5()\n{\n\tnrAddressBook = 5;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook6()\n{\n\tnrAddressBook = 6;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook7()\n{\n\tnrAddressBook = 7;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook8()\n{\n\tnrAddressBook = 8;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::showAddressBook9()\n{\n\tnrAddressBook = 9;\n\tshowAddressBook();\n}\n\nvoid SrvRedirectForm::selectedAddress(const QString &address)\n{\n\tswitch(nrAddressBook) {\n\tcase 1:\n\t\tcfAlwaysDst1LineEdit->setText(address);\n\t\tbreak;\n\tcase 2:\n\t\tcfAlwaysDst2LineEdit->setText(address);\n\t\tbreak;\n\tcase 3:\n\t\tcfAlwaysDst3LineEdit->setText(address);\n\t\tbreak;\n\tcase 4:\n\t\tcfBusyDst1LineEdit->setText(address);\n\t\tbreak;\n\tcase 5:\n\t\tcfBusyDst2LineEdit->setText(address);\n\t\tbreak;\n\tcase 6:\n\t\tcfBusyDst3LineEdit->setText(address);\n\t\tbreak;\n\tcase 7:\n\t\tcfNoanswerDst1LineEdit->setText(address);\n\t\tbreak;\n\tcase 8:\n\t\tcfNoanswerDst2LineEdit->setText(address);\n\t\tbreak;\n\tcase 9:\n\t\tcfNoanswerDst3LineEdit->setText(address);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "src/gui/srvredirectform.h",
    "content": "#ifndef SRVREDIRECTFORM_H\n#define SRVREDIRECTFORM_H\n#include <list>\n#include \"getaddressform.h\"\n#include \"phone.h\"\n#include <QLineEdit>\n#include \"sockets/url.h\"\n#include \"user.h\"\n#include \"ui_srvredirectform.h\"\n\nclass t_phone;\nextern t_phone *phone;\n\nclass SrvRedirectForm : public QDialog, public Ui::SrvRedirectForm\n{\n\tQ_OBJECT\n\npublic:\n    SrvRedirectForm(QWidget* parent = 0);\n\t~SrvRedirectForm();\n\n\tvirtual bool validateValues();\n\tvirtual bool validate( bool cf_active, QLineEdit * dst1, QLineEdit * dst2, QLineEdit * dst3, list<t_display_url> & dest_list );\n\npublic slots:\n\tvirtual void show();\n\tvirtual void populate();\n\tvirtual void validate();\n\tvirtual void toggleAlways( bool on );\n\tvirtual void toggleBusy( bool on );\n\tvirtual void toggleNoanswer( bool on );\n\tvirtual void changedUser( const QString & user_display_uri );\n\tvirtual void showAddressBook();\n\tvirtual void showAddressBook1();\n\tvirtual void showAddressBook2();\n\tvirtual void showAddressBook3();\n\tvirtual void showAddressBook4();\n\tvirtual void showAddressBook5();\n\tvirtual void showAddressBook6();\n\tvirtual void showAddressBook7();\n\tvirtual void showAddressBook8();\n\tvirtual void showAddressBook9();\n\tvirtual void selectedAddress( const QString & address );\n\nsignals:\n\tvoid destinations(t_user *, const list<t_display_url> &always, const list<t_display_url> &busy, const list<t_display_url> &noanswer);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tint nrAddressBook;\n\tGetAddressForm *getAddressForm;\n\tt_user *current_user;\n\tint current_user_idx;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/srvredirectform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>SrvRedirectForm</class>\n  <widget class=\"QDialog\" name=\"SrvRedirectForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>648</width>\n        <height>315</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Call Redirection</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <widget class=\"QLabel\" name=\"userTextLabel\">\n              <property name=\"text\">\n                <string>User:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>userComboBox</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QComboBox\" name=\"userComboBox\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>7</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <widget class=\"QTabWidget\" name=\"cfTabWidget\">\n          <property name=\"enabled\">\n            <bool>true</bool>\n          </property>\n          <property name=\"whatsThis\" stdset=\"0\">\n            <string>There are 3 redirect services:&lt;p&gt;\n&lt;b&gt;Unconditional:&lt;/b&gt; redirect all calls\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Busy:&lt;/b&gt; redirect a call if both lines are busy\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;No answer:&lt;/b&gt; redirect a call when the no-answer timer expires\n&lt;/p&gt;</string>\n          </property>\n          <widget class=\"QWidget\">\n            <attribute name=\"title\">\n              <string>&amp;Unconditional</string>\n            </attribute>\n            <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\">\n                <widget class=\"QCheckBox\" name=\"cfAlwaysCheckBox\">\n                  <property name=\"text\">\n                    <string>&amp;Redirect all calls</string>\n                  </property>\n                  <property name=\"shortcut\">\n                    <string>Alt+R</string>\n                  </property>\n                  <property name=\"whatsThis\" stdset=\"0\">\n                    <string>Activate the unconditional redirection service.</string>\n                  </property>\n                </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n                <widget class=\"QGroupBox\" name=\"cfAlwaysGroupBox\">\n                  <property name=\"enabled\">\n                    <bool>true</bool>\n                  </property>\n                  <property name=\"title\">\n                    <string>Redirect to</string>\n                  </property>\n                  <layout class=\"QGridLayout\">\n                    <item row=\"0\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfAlwaysDst1LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfAlwaysDst2LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfAlwaysDst3TextLabel\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"text\">\n                          <string>&amp;3rd choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst3LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfAlwaysDst3LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfAlwaysDst2TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;2nd choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst2LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfAlwaysDst1TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;1st choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst1LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrAlways1ToolButton\">\n                        <property name=\"focusPolicy\">\n                          <enum>Qt::TabFocus</enum>\n                        </property>\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"shortcut\">\n                          <string>F10</string>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrAlways2ToolButton\">\n                        <property name=\"focusPolicy\">\n                          <enum>Qt::TabFocus</enum>\n                        </property>\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"shortcut\">\n                          <string>F11</string>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrAlways3ToolButton\">\n                        <property name=\"focusPolicy\">\n                          <enum>Qt::TabFocus</enum>\n                        </property>\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"shortcut\">\n                          <string>F12</string>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                  </layout>\n                </widget>\n              </item>\n            </layout>\n          </widget>\n          <widget class=\"QWidget\">\n            <attribute name=\"title\">\n              <string>&amp;Busy</string>\n            </attribute>\n            <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\">\n                <widget class=\"QCheckBox\" name=\"cfBusyCheckBox\">\n                  <property name=\"text\">\n                    <string>&amp;Redirect calls when I am busy</string>\n                  </property>\n                  <property name=\"shortcut\">\n                    <string>Alt+R</string>\n                  </property>\n                  <property name=\"whatsThis\" stdset=\"0\">\n                    <string>Activate the redirection when busy service.</string>\n                  </property>\n                </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n                <widget class=\"QGroupBox\" name=\"cfBusyGroupBox\">\n                  <property name=\"enabled\">\n                    <bool>true</bool>\n                  </property>\n                  <property name=\"title\">\n                    <string>Redirect to</string>\n                  </property>\n                  <layout class=\"QGridLayout\">\n                    <item row=\"1\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfBusyDst2LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfBusyDst3TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;3rd choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst3LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfBusyDst3LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfBusyDst2TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;2nd choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst2LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfBusyDst1TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;1st choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst1LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfBusyDst1LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrBusy1ToolButton\">\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrBusy2ToolButton\">\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrBusy3ToolButton\">\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                  </layout>\n                </widget>\n              </item>\n            </layout>\n          </widget>\n          <widget class=\"QWidget\">\n            <attribute name=\"title\">\n              <string>&amp;No answer</string>\n            </attribute>\n            <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\">\n                <widget class=\"QCheckBox\" name=\"cfNoanswerCheckBox\">\n                  <property name=\"text\">\n                    <string>&amp;Redirect calls when I do not answer</string>\n                  </property>\n                  <property name=\"shortcut\">\n                    <string>Alt+R</string>\n                  </property>\n                  <property name=\"whatsThis\" stdset=\"0\">\n                    <string>Activate the redirection on no answer service.</string>\n                  </property>\n                </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n                <widget class=\"QGroupBox\" name=\"cfNoanswerGroupBox\">\n                  <property name=\"enabled\">\n                    <bool>true</bool>\n                  </property>\n                  <property name=\"title\">\n                    <string>Redirect to</string>\n                  </property>\n                  <layout class=\"QGridLayout\">\n                    <item row=\"1\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfNoanswerDst2LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfNoanswerDst3TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;3rd choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst3LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfNoanswerDst2TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;2nd choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst2LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"0\">\n                      <widget class=\"QLabel\" name=\"cfNoanswerDst1TextLabel\">\n                        <property name=\"text\">\n                          <string>&amp;1st choice destination:</string>\n                        </property>\n                        <property name=\"buddy\" stdset=\"0\">\n                          <cstring>cfAlwaysDst1LineEdit</cstring>\n                        </property>\n                        <property name=\"wordWrap\">\n                          <bool>false</bool>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfNoanswerDst1LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"1\">\n                      <widget class=\"QLineEdit\" name=\"cfNoanswerDst3LineEdit\">\n                        <property name=\"enabled\">\n                          <bool>true</bool>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>You can specify up to 3 destinations to which you want to redirect the call. If the first destination does not answer the call, the second destination will be tried and so on.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"0\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrNoanswer1ToolButton\">\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"1\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrNoanswer2ToolButton\">\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                    <item row=\"2\" column=\"2\">\n                      <widget class=\"QToolButton\" name=\"addrNoanswer3ToolButton\">\n                        <property name=\"text\">\n                          <string/>\n                        </property>\n                        <property name=\"icon\">\n                          <iconset>:/icons/images/kontact_contacts.png</iconset>\n                        </property>\n                        <property name=\"toolTip\" stdset=\"0\">\n                          <string>Address book</string>\n                        </property>\n                        <property name=\"whatsThis\" stdset=\"0\">\n                          <string>Select an address from the address book.</string>\n                        </property>\n                      </widget>\n                    </item>\n                  </layout>\n                </widget>\n              </item>\n            </layout>\n          </widget>\n        </widget>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>261</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Accept and save all changes.</string>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+C</string>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Undo your changes and close the window.</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>cfTabWidget</tabstop>\n    <tabstop>cfAlwaysCheckBox</tabstop>\n    <tabstop>cfAlwaysDst1LineEdit</tabstop>\n    <tabstop>cfAlwaysDst2LineEdit</tabstop>\n    <tabstop>cfAlwaysDst3LineEdit</tabstop>\n    <tabstop>cfBusyCheckBox</tabstop>\n    <tabstop>cfBusyDst1LineEdit</tabstop>\n    <tabstop>cfBusyDst2LineEdit</tabstop>\n    <tabstop>cfBusyDst3LineEdit</tabstop>\n    <tabstop>cfNoanswerCheckBox</tabstop>\n    <tabstop>cfNoanswerDst1LineEdit</tabstop>\n    <tabstop>cfNoanswerDst2LineEdit</tabstop>\n    <tabstop>cfNoanswerDst3LineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">sockets/url.h</include>\n    <include location=\"global\">list</include>\n    <include location=\"local\">qlineedit.h</include>\n    <include location=\"local\">ui_getaddressform.h</include>\n    <include location=\"local\">user.h</include>\n    <include location=\"local\">phone.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>cfAlwaysCheckBox</sender>\n      <signal>toggled(bool)</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>toggleAlways(bool)</slot>\n    </connection>\n    <connection>\n      <sender>cfBusyCheckBox</sender>\n      <signal>toggled(bool)</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>toggleBusy(bool)</slot>\n    </connection>\n    <connection>\n      <sender>cfNoanswerCheckBox</sender>\n      <signal>toggled(bool)</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>toggleNoanswer(bool)</slot>\n    </connection>\n    <connection>\n      <sender>addrAlways1ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook1()</slot>\n    </connection>\n    <connection>\n      <sender>addrAlways2ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook2()</slot>\n    </connection>\n    <connection>\n      <sender>addrAlways3ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook3()</slot>\n    </connection>\n    <connection>\n      <sender>addrBusy1ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook4()</slot>\n    </connection>\n    <connection>\n      <sender>addrBusy2ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook5()</slot>\n    </connection>\n    <connection>\n      <sender>addrBusy3ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook6()</slot>\n    </connection>\n    <connection>\n      <sender>addrNoanswer1ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook7()</slot>\n    </connection>\n    <connection>\n      <sender>addrNoanswer2ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook8()</slot>\n    </connection>\n    <connection>\n      <sender>addrNoanswer3ToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>showAddressBook9()</slot>\n    </connection>\n    <connection>\n      <sender>userComboBox</sender>\n      <signal>activated(QString)</signal>\n      <receiver>SrvRedirectForm</receiver>\n      <slot>changedUser(QString)</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/syssettingsform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"twinkle_config.h\"\n\n#include <QPixmap>\n#include <QComboBox>\n#include \"gui.h\"\n#include \"sockets/interfaces.h\"\n#include \"selectprofileform.h\"\n#include <QStringList>\n#include \"audits/memman.h\"\n#include <QSpinBox>\n#include <QFileDialog>\n#include <QFileInfo>\n#include \"twinkle_config.h\"\n#include <QRegularExpression>\n#include <QValidator>\n#include <QRegularExpressionValidator>\n#include \"syssettingsform.h\"\n/*\n *  Constructs a SysSettingsForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nSysSettingsForm::SysSettingsForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nSysSettingsForm::~SysSettingsForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid SysSettingsForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n// Indices of categories in the category list box\n#define idxCatGeneral\t0\n#define idxCatAudio\t1\n#define idxCatRingtones\t2\n#define idxCatAddressBook\t3\n#define idxCatNetwork\t4\n#define idxCatLog\t\t5\n\nvoid SysSettingsForm::init()\n{\n\t// Set toolbutton icons for disabled options.\n\tQIcon i;\n    i = openRingtoneToolButton->icon();\n    i.addPixmap(QPixmap(\":/icons/images/fileopen-disabled.png\"),\n            QIcon::Disabled);\n    openRingtoneToolButton->setIcon(i);\n    i = openRingbackToolButton->icon();\n    i.addPixmap(QPixmap(\":/icons/images/fileopen-disabled.png\"),\n            QIcon::Disabled);\n    openRingbackToolButton->setIcon(i);\n\t\n\tQRegularExpression rxNumber(\"[0-9]+\");\n\tmaxUdpSizeLineEdit->setValidator(new QRegularExpressionValidator(rxNumber, this));\n\tmaxTcpSizeLineEdit->setValidator(new QRegularExpressionValidator(rxNumber, this));\n}\n\nvoid SysSettingsForm::showCategory( int index )\n{\n\tif (index == idxCatGeneral) {\n        settingsWidgetStack->setCurrentWidget(pageGeneral);\n\t} else if (index == idxCatAudio) {\n        settingsWidgetStack->setCurrentWidget(pageAudio);\n\t} else if (index == idxCatRingtones) {\n        settingsWidgetStack->setCurrentWidget(pageRingtones);\n\t} else if (index == idxCatAddressBook) {\n        settingsWidgetStack->setCurrentWidget(pageAddressBook);\n\t} else if (index == idxCatNetwork) {\n        settingsWidgetStack->setCurrentWidget(pageNetwork);\n\t} else if (index == idxCatLog) {\n        settingsWidgetStack->setCurrentWidget(pageLog);\n\t}\n}\n\nstring SysSettingsForm::comboItem2audio_dev(QString item, QLineEdit *qleOther, bool playback)\n{\n\tif (item == QString(\"ALSA: \") + DEV_OTHER) {\n\t\tif (qleOther->text().isEmpty()) return \"\";\n        return (QString(PFX_ALSA) + qleOther->text()).toStdString();\n\t}\n\t\n\tif (item == QString(\"OSS: \") + DEV_OTHER) {\n\t\tif (qleOther->text().isEmpty()) return \"\";\n        return (QString(PFX_OSS) + qleOther->text()).toStdString();\n\t}\n\t\n\tlist<t_audio_device> &list_audio_dev = (playback ?\n\t\t\tlist_audio_playback_dev : list_audio_capture_dev);\n\t\n\tfor (list<t_audio_device>::iterator i = list_audio_dev.begin(); \n\ti != list_audio_dev.end(); i++)\n\t{\n        if (i->get_description() == item.toStdString()) {\n\t\t\treturn i->get_settings_value();\n\t\t}\n\t}\n\t\n\treturn \"\";\n}\n\nvoid SysSettingsForm::populateComboBox(QComboBox *cb, const QString &s)\n{\n\tfor (int i = 0; i < cb->count(); i++) {\n        if (cb->itemText(i) == s) {\n            cb->setCurrentIndex(i);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid SysSettingsForm::populate()\n{\n\tQString msg;\n\tint idx;\n\t\n\t// Select the Audio category\n    categoryListBox->setCurrentRow(idxCatGeneral);\n    settingsWidgetStack->setCurrentWidget(pageGeneral);\n\t\n\t// Set focus on first field\n\tcategoryListBox->setFocus();\n\t\n\t// Audio settings\n\tlist_audio_playback_dev = sys_config->get_audio_devices(true);\n\tlist_audio_capture_dev = sys_config->get_audio_devices(false);\n\tringtoneComboBox->clear();\n\tspeakerComboBox->clear();\n\tmicComboBox->clear();\n\tbool devRingtoneFound = false;\n\tbool devSpeakerFound = false;\n\tbool devMicFound = false;\n\t\n\t// Playback devices\n\tidx = 0;\n\tfor (list<t_audio_device>::iterator i = list_audio_playback_dev.begin(); \n\ti != list_audio_playback_dev.end(); i++, idx++) {\n\t\tstring item = i->get_description();\n        ringtoneComboBox->addItem(QString(item.c_str()));\n        speakerComboBox->addItem(QString(item.c_str()));\n\t\t\n\t\t// Select audio device\n\t\tif (sys_config->get_dev_ringtone().device == i->device) {\n            ringtoneComboBox->setCurrentIndex(idx);\n\t\t\totherRingtoneLineEdit->clear();\n\t\t\tdevRingtoneFound = true;\n\t\t}\n\t\tif (sys_config->get_dev_speaker().device == i->device) {\n            speakerComboBox->setCurrentIndex(idx);\n\t\t\totherSpeakerLineEdit->clear();\n\t\t\tdevSpeakerFound = true;\n\t\t}\n\t\t\n\t\t// Determine index for other non-standard device\n\t\tif (i->device == DEV_OTHER) {\n\t\t\tif (i->type == t_audio_device::ALSA) {\n\t\t\t\tidxOtherPlaybackDevAlsa = idx;\n\t\t\t} else {\n\t\t\t\tidxOtherPlaybackDevOss = idx;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Check for non-standard audio devices\n\tif (!devRingtoneFound) {\n\t\tt_audio_device dev = sys_config->get_dev_ringtone();\n\t\totherRingtoneLineEdit->setText(dev.device.c_str());\n        ringtoneComboBox->setCurrentIndex(\n\t\t\t(dev.type == t_audio_device::ALSA ? idxOtherPlaybackDevAlsa : idxOtherPlaybackDevOss));\n\t}\n\tif (!devSpeakerFound) {\n\t\tt_audio_device dev = sys_config->get_dev_speaker();\n\t\totherSpeakerLineEdit->setText(dev.device.c_str());\n        speakerComboBox->setCurrentIndex(\n\t\t\t(dev.type == t_audio_device::ALSA ? idxOtherPlaybackDevAlsa : idxOtherPlaybackDevOss));\n\t}\n\t\n\t// Capture device\n\tidx = 0;\n\tfor (list<t_audio_device>::iterator i = list_audio_capture_dev.begin(); \n\ti != list_audio_capture_dev.end(); i++, idx++) {\n\t\tstring item = i->get_description();\n        micComboBox->addItem(QString(item.c_str()));\n\t\t\n\t\t// Select audio device\n\t\tif (sys_config->get_dev_mic().device == i->device) {\n            micComboBox->setCurrentIndex(idx);\n\t\t\totherMicLineEdit->clear();\n\t\t\tdevMicFound = true;\n\t\t}\n\t\t\n\t\t// Determine index for other non-standard device\n\t\tif (i->device == DEV_OTHER) {\n\t\t\tif (i->type == t_audio_device::ALSA) {\n\t\t\t\tidxOtherCaptureDevAlsa = idx;\n\t\t\t} else {\n\t\t\t\tidxOtherCaptureDevOss = idx;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Check for non-standard audio devices\n\tif (!devMicFound) {\n\t\tt_audio_device dev = sys_config->get_dev_mic();\n\t\totherMicLineEdit->setText(dev.device.c_str());\n        micComboBox->setCurrentIndex(\n\t\t\t(dev.type == t_audio_device::ALSA ? idxOtherCaptureDevAlsa : idxOtherCaptureDevOss));\n\t}\n\t\n\t// Enable/disable line edit for non-standard device\n    devRingtoneSelected(ringtoneComboBox->currentIndex());\n    devSpeakerSelected(speakerComboBox->currentIndex());\n    devMicSelected(micComboBox->currentIndex());\n\t\n\tvalidateAudioCheckBox->setChecked(sys_config->get_validate_audio_dev());\n\t\n\tpopulateComboBox(ossFragmentComboBox, \n\t\t\t QString::number(sys_config->get_oss_fragment_size()));\n\tpopulateComboBox(alsaPlayPeriodComboBox,\n\t\t\t QString::number(sys_config->get_alsa_play_period_size()));\n\tpopulateComboBox(alsaCapturePeriodComboBox,\n\t\t\tQString::number(sys_config->get_alsa_capture_period_size()));\n\t\n\t// Log settings\n\tlogMaxSizeSpinBox->setValue(sys_config->get_log_max_size());\n\tlogDebugCheckBox->setChecked(sys_config->get_log_show_debug());\n\tlogSipCheckBox->setChecked(sys_config->get_log_show_sip());\n\tlogStunCheckBox->setChecked(sys_config->get_log_show_stun());\n\tlogMemoryCheckBox->setChecked(sys_config->get_log_show_memory());\n\t\n\t// General settings\n\tguiUseSystrayCheckBox->setChecked(sys_config->get_gui_use_systray());\n\tguiHideCheckBox->setChecked(sys_config->get_gui_hide_on_close());\n\tguiHideCheckBox->setEnabled(sys_config->get_gui_use_systray());\n\n\t// Inhibit idle session\n\tinhibitIdleSessionCheckBox->setChecked(sys_config->get_inhibit_idle_session());\n#ifdef HAVE_DBUS\n\tinhibitIdleSessionCheckBox->setEnabled(true);\n#else\n\tinhibitIdleSessionCheckBox->setEnabled(false);\n#endif\n\t\n\t// Call history\n\thistSizeSpinBox->setValue(sys_config->get_ch_max_size());\n\t\n\t// Show popup on incoming call\n\tincomingPopupCheckBox->setChecked(sys_config->get_gui_show_incoming_popup());\n\n\t// Auto show on incoming call\n\tautoShowCheckBox->setChecked(sys_config->get_gui_auto_show_incoming());\n\tautoShowTimeoutSpinBox->setValue(sys_config->get_gui_auto_show_timeout());\n\t\n\t// Services\n\tcallWaitingCheckBox->setChecked(sys_config->get_call_waiting());\n\thangupBothCheckBox->setChecked(sys_config->get_hangup_both_3way());\n\t\n\t// Startup settings\n\tstartHiddenCheckBox->setChecked(sys_config->get_start_hidden());\n\n\tosdCheckBox->setChecked(sys_config->get_gui_show_call_osd());\n\t\n\tQStringList profiles;\n\tif (!SelectProfileForm::getUserProfiles(profiles, msg)) {\n        ((t_gui *)ui)->cb_show_msg(this, msg.toStdString(), MSG_CRITICAL);\n\t}\n\tprofileListView->clear();\n\tfor (QStringList::Iterator i = profiles.begin(); i != profiles.end(); i++) {\n\t\t// Strip off the .cfg suffix\n\t\tQString profile = *i;\n\t\tprofile.truncate(profile.length() - 4);\n        QListWidgetItem* item = new QListWidgetItem(profile, profileListView);\n        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);\n        item->setCheckState(Qt::Unchecked);\n        item->setData(Qt::DecorationRole, QPixmap(\":/icons/images/penguin-small.png\"));\n\t\t\n\t\tlist<string> l = sys_config->get_start_user_profiles();\n        if (std::find(l.begin(), l.end(), profile.toStdString()) != l.end())\n\t\t{\n            item->setCheckState(Qt::Checked);\n\t\t}\n\t}\n\t\n\t// Web browser command\n\tbrowserLineEdit->setText(sys_config->get_gui_browser_cmd().c_str());\n\t\n\t// Network settings\n\tsipUdpPortSpinBox->setValue(sys_config->get_config_sip_port());\n\trtpPortSpinBox->setValue(sys_config->get_rtp_port());\n\t\n\tmaxUdpSizeLineEdit->setText(QString::number(sys_config->get_sip_max_udp_size()));\n\tmaxTcpSizeLineEdit->setText(QString::number(sys_config->get_sip_max_tcp_size()));\n\t\n\t// Ring tone settings\n\tplayRingtoneCheckBox->setChecked(sys_config->get_play_ringtone());\n\tdefaultRingtoneRadioButton->setChecked(sys_config->get_ringtone_file().empty());\n\tcustomRingtoneRadioButton->setChecked(!sys_config->get_ringtone_file().empty());\n\tringtoneLineEdit->setText(sys_config->get_ringtone_file().c_str());\n\tdefaultRingtoneRadioButton->setEnabled(sys_config->get_play_ringtone());\n\tcustomRingtoneRadioButton->setEnabled(sys_config->get_play_ringtone());\n\tringtoneLineEdit->setEnabled(!sys_config->get_ringtone_file().empty());\n\topenRingtoneToolButton->setEnabled(!sys_config->get_ringtone_file().empty());\n\t\n\tplayRingbackCheckBox->setChecked(sys_config->get_play_ringback());\n\tdefaultRingbackRadioButton->setChecked(sys_config->get_ringback_file().empty());\n\tcustomRingbackRadioButton->setChecked(!sys_config->get_ringback_file().empty());\n\tringbackLineEdit->setText(sys_config->get_ringback_file().c_str());\n\tdefaultRingbackRadioButton->setEnabled(sys_config->get_play_ringback());\n\tcustomRingbackRadioButton->setEnabled(sys_config->get_play_ringback());\n\tringbackLineEdit->setEnabled(!sys_config->get_ringback_file().empty());\n\topenRingbackToolButton->setEnabled(!sys_config->get_ringback_file().empty());\n\t\n\t// Address book settings\n\tabLookupNameCheckBox->setChecked(sys_config->get_ab_lookup_name());\n\tabOverrideDisplayCheckBox->setChecked(sys_config->get_ab_override_display());\n\tabOverrideDisplayCheckBox->setEnabled(sys_config->get_ab_lookup_name());\n\tabLookupPhotoCheckBox->setChecked(sys_config->get_ab_lookup_photo());\n}\n\nvoid SysSettingsForm::validate()\n{\n\tbool conversion_ok = false;\n\tunsigned short sip_max_udp_size = maxUdpSizeLineEdit->text().toUShort(&conversion_ok);\n\tif (!conversion_ok) sip_max_udp_size = sys_config->get_sip_max_udp_size();\n\n\tunsigned long sip_max_tcp_size = maxTcpSizeLineEdit->text().toULong(&conversion_ok);\n\tif (!conversion_ok) sip_max_tcp_size = sys_config->get_sip_max_tcp_size();\n\t\n\t// Audio\n\tstring dev;\n\tdev = comboItem2audio_dev(ringtoneComboBox->currentText(), otherRingtoneLineEdit, true);\n\tif (dev != \"\") sys_config->set_dev_ringtone(sys_config->audio_device(dev));\n\tdev = comboItem2audio_dev(speakerComboBox->currentText(), otherSpeakerLineEdit, true);\n\tif (dev != \"\") sys_config->set_dev_speaker(sys_config->audio_device(dev));\n\tdev = comboItem2audio_dev(micComboBox->currentText(), otherMicLineEdit, false);\n\tif (dev != \"\") sys_config->set_dev_mic(sys_config->audio_device(dev));\n\t\n\tsys_config->set_validate_audio_dev(validateAudioCheckBox->isChecked());\n\t\n\tsys_config->set_oss_fragment_size(\n\t\t\tossFragmentComboBox->currentText().toInt());\n\tsys_config->set_alsa_play_period_size(\n\t\t\talsaPlayPeriodComboBox->currentText().toInt());\n\tsys_config->set_alsa_capture_period_size(\n\t\t\talsaCapturePeriodComboBox->currentText().toInt());\n\t\n\t// Log\n\tsys_config->set_log_max_size(logMaxSizeSpinBox->value());\n\tsys_config->set_log_show_debug(logDebugCheckBox->isChecked());\n\tsys_config->set_log_show_sip(logSipCheckBox->isChecked());\n\tsys_config->set_log_show_stun(logStunCheckBox->isChecked());\n\tsys_config->set_log_show_memory(logMemoryCheckBox->isChecked());\n\t\n\t// General\n\tsys_config->set_gui_use_systray(guiUseSystrayCheckBox->isChecked());\n\tsys_config->set_gui_hide_on_close(guiHideCheckBox->isChecked());\n\tsys_config->set_gui_show_call_osd(osdCheckBox->isChecked());\n\n\t// Inhibit idle session\n\tif (sys_config->get_inhibit_idle_session() != inhibitIdleSessionCheckBox->isChecked()) {\n\t\tsys_config->set_inhibit_idle_session(inhibitIdleSessionCheckBox->isChecked());\n\t\t// Changing this setting while busy requires special handling\n\t\temit inhibitIdleSessionChanged();\n\t}\n\t\n\t// Show popup on incoming call\n\tsys_config->set_gui_show_incoming_popup(incomingPopupCheckBox->isChecked());\n\n\t// Auto show on incoming call\n\tsys_config->set_gui_auto_show_incoming(autoShowCheckBox->isChecked());\n\tsys_config->set_gui_auto_show_timeout(autoShowTimeoutSpinBox->value());\n\t\n\t// Call history\n\tsys_config->set_ch_max_size(histSizeSpinBox->value());\n\t\n\t// Services\n\tsys_config->set_call_waiting(callWaitingCheckBox->isChecked());\n\tsys_config->set_hangup_both_3way(hangupBothCheckBox->isChecked());\n\n\t// Startup\n\tsys_config->set_start_hidden(startHiddenCheckBox->isChecked() &&\n\t\t\t\t   guiUseSystrayCheckBox->isChecked());\n\t\n\tlist<string> start_user_profiles;\n    for (int i = 0; i < profileListView->count(); i++)\n    {\n        QListWidgetItem *item = profileListView->item(i);\n\t\tif (item->checkState() == Qt::Checked)\n\t\t\tstart_user_profiles.push_back(item->text().toStdString());\n\t}\n\tsys_config->set_start_user_profiles(start_user_profiles);\n\t\n\t// Web browser command\n    sys_config->set_gui_browser_cmd(browserLineEdit->text().trimmed().toStdString());\n\t\n\t// Network\n\tif (sys_config->get_config_sip_port() != sipUdpPortSpinBox->value()) {\n\t\tsys_config->set_config_sip_port(sipUdpPortSpinBox->value());\n\t\temit sipUdpPortChanged();\n\t}\n\tif (sys_config->get_rtp_port() != rtpPortSpinBox->value()) {\n\t\tsys_config->set_rtp_port(rtpPortSpinBox->value());\n\t\temit rtpPortChanged();\n\t}\n\tsys_config->set_sip_max_udp_size(sip_max_udp_size);\n\tsys_config->set_sip_max_tcp_size(sip_max_tcp_size);\n\t\n\t// Ring tones\n\tsys_config->set_play_ringtone(playRingtoneCheckBox->isChecked());\n\tif (sys_config->get_play_ringtone()) {\n        if (defaultRingtoneRadioButton->isChecked()) {\n\t\t\tsys_config->set_ringtone_file(\"\");\n\t\t} else {\n\t\t\tsys_config->set_ringtone_file(ringtoneLineEdit->\n                    text().trimmed().toStdString());\n\t\t}\n\t} else {\n\t\tsys_config->set_ringtone_file(\"\");\n\t}\n\t\n\tsys_config->set_play_ringback(playRingbackCheckBox->isChecked());\n\tif (sys_config->get_play_ringback()) {\n        if (defaultRingbackRadioButton->isChecked()) {\n\t\t\tsys_config->set_ringback_file(\"\");\n\t\t} else {\n\t\t\tsys_config->set_ringback_file(ringbackLineEdit->\n                    text().trimmed().toStdString());\n\t\t}\n\t} else {\n\t\tsys_config->set_ringback_file(\"\");\n\t}\n\t\n\t// Address book settings\n\tsys_config->set_ab_lookup_name(abLookupNameCheckBox->isChecked());\n\tsys_config->set_ab_override_display(abOverrideDisplayCheckBox->isChecked());\n\tsys_config->set_ab_lookup_photo(abLookupPhotoCheckBox->isChecked());\n\t\n\t// Save user config\n\tstring error_msg;\n\tif (!sys_config->write_config(error_msg)) {\n\t\t// Failed to write config file\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\taccept();\n}\n\nvoid SysSettingsForm::show()\n{\n\tpopulate();\n\tQDialog::show();\n}\n\nint SysSettingsForm::exec()\n{\n\tpopulate();\n\treturn QDialog::exec();\n}\n\nvoid SysSettingsForm::chooseRingtone()\n{\n    QString file = QFileDialog::getOpenFileName(this, tr(\"Choose ring tone\"),\n\t\t\t((t_gui *)ui)->get_last_file_browse_path(),\n            tr(\"Ring tones\", \"Description of .wav files in file dialog\").append(\" (*.wav)\"));\n\tif (!file.isEmpty()) {\n\t\tringtoneLineEdit->setText(file);\n        ((t_gui *)ui)->set_last_file_browse_path(QFileInfo(file).absolutePath());\n\t}\n}\n\nvoid SysSettingsForm::chooseRingback()\n{\n    QString file = QFileDialog::getOpenFileName(this, tr(\"Choose ring back tone\"),\n\t\t\t((t_gui *)ui)->get_last_file_browse_path(),\n            tr(\"Ring back tones\", \"Description of .wav files in file dialog\").append(\" (*.wav)\"));\n\tif (!file.isEmpty()) {\n\t\tringbackLineEdit->setText(file);\n        ((t_gui *)ui)->set_last_file_browse_path(QFileInfo(file).absolutePath());\n\t}\n}\n\nvoid SysSettingsForm::devRingtoneSelected(int idx) {\n\tbool b = (idx == idxOtherPlaybackDevAlsa || idx == idxOtherPlaybackDevOss);\n\totherRingtoneTextLabel->setEnabled(b);\n\totherRingtoneLineEdit->setEnabled(b);\n}\n\nvoid SysSettingsForm::devSpeakerSelected(int idx) {\n\tbool b = (idx == idxOtherPlaybackDevAlsa || idx == idxOtherPlaybackDevOss);\n\totherSpeakerTextLabel->setEnabled(b);\n\totherSpeakerLineEdit->setEnabled(b);\n}\n\nvoid SysSettingsForm::devMicSelected(int idx) {\n\tbool b = (idx == idxOtherCaptureDevAlsa || idx == idxOtherCaptureDevOss);\n\totherMicTextLabel->setEnabled(b);\n\totherMicLineEdit->setEnabled(b);\n}\n\nvoid SysSettingsForm::playRingToneCheckBoxToggles(bool on) {\n\tif (on) {\n\t\tringtoneLineEdit->setEnabled(customRingtoneRadioButton->isChecked());\n\t} else {\n\t\tringtoneLineEdit->setEnabled(false);\n\t}\n}\n\nvoid SysSettingsForm::playRingBackToneCheckBoxToggles(bool on) {\n\tif (on) {\n\t\tringbackLineEdit->setEnabled(customRingbackRadioButton->isChecked());\n\t} else {\n\t\tringbackLineEdit->setEnabled(false);\n\t}\n}\n"
  },
  {
    "path": "src/gui/syssettingsform.h",
    "content": "#ifndef SYSSETTINGSFORM_H\n#define SYSSETTINGSFORM_H\n\n#include \"sys_settings.h\"\n#include <QDialog>\n#include \"ui_syssettingsform.h\"\n\nclass SysSettingsForm : public QDialog, public Ui::SysSettingsForm\n{\n\tQ_OBJECT\n\npublic:\n    SysSettingsForm(QWidget* parent = 0);\n\t~SysSettingsForm();\n\n\tvirtual string comboItem2audio_dev( QString item, QLineEdit * qleOther, bool playback );\n\npublic slots:\n\tvirtual void showCategory( int index );\n\tvirtual void populateComboBox( QComboBox * cb, const QString & s );\n\tvirtual void populate();\n\tvirtual void validate();\n\tvirtual void show();\n\tvirtual int exec();\n\tvirtual void chooseRingtone();\n\tvirtual void chooseRingback();\n\tvirtual void devRingtoneSelected( int idx );\n\tvirtual void devSpeakerSelected( int idx );\n\tvirtual void devMicSelected( int idx );\n\tvirtual void playRingToneCheckBoxToggles( bool on );\n\tvirtual void playRingBackToneCheckBoxToggles( bool on );\n\nsignals:\n\tvoid inhibitIdleSessionChanged();\n\tvoid sipUdpPortChanged();\n\tvoid rtpPortChanged();\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tint idxOtherCaptureDevOss;\n\tint idxOtherCaptureDevAlsa;\n\tint idxOtherPlaybackDevOss;\n\tint idxOtherPlaybackDevAlsa;\n\tlist<t_audio_device> list_audio_playback_dev;\n\tlist<t_audio_device> list_audio_capture_dev;\n\n\tvoid init();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/syssettingsform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SysSettingsForm</class>\n <widget class=\"QDialog\" name=\"SysSettingsForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>763</width>\n    <height>616</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - System Settings</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QListWidget\" name=\"categoryListBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"whatsThis\">\n      <string>Select a category for which you want to see or modify the settings.</string>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>32</width>\n       <height>32</height>\n      </size>\n     </property>\n     <property name=\"currentRow\">\n      <number>0</number>\n     </property>\n     <item>\n      <property name=\"text\">\n       <string>General</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/twinkle32.png</normaloff>:/icons/images/twinkle32.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Audio</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/kmix.png</normaloff>:/icons/images/kmix.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Ring tones</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/knotify.png</normaloff>:/icons/images/knotify.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Address book</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/kontact_contacts32.png</normaloff>:/icons/images/kontact_contacts32.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Network</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/network.png</normaloff>:/icons/images/network.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Log</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/log.png</normaloff>:/icons/images/log.png</iconset>\n      </property>\n     </item>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\" colspan=\"2\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>321</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"okPushButton\">\n       <property name=\"whatsThis\">\n        <string>Accept and save your changes.</string>\n       </property>\n       <property name=\"text\">\n        <string>&amp;OK</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+O</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelPushButton\">\n       <property name=\"whatsThis\">\n        <string>Undo all your changes and close the window.</string>\n       </property>\n       <property name=\"text\">\n        <string>&amp;Cancel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"0\" column=\"1\">\n    <widget class=\"QStackedWidget\" name=\"settingsWidgetStack\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"frameShape\">\n      <enum>QFrame::StyledPanel</enum>\n     </property>\n     <property name=\"currentIndex\">\n      <number>2</number>\n     </property>\n     <widget class=\"QWidget\" name=\"pageAudio\">\n      <layout class=\"QGridLayout\">\n       <item row=\"1\" column=\"0\" colspan=\"2\">\n        <widget class=\"QGroupBox\" name=\"soundcardGroupBox\">\n         <property name=\"title\">\n          <string>Sound Card</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"ringtoneComboBox\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"whatsThis\">\n             <string>Select the sound card for playing the ring tone for incoming calls.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"micComboBox\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"whatsThis\">\n             <string>Select the sound card to which your microphone is connected.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"speakerComboBox\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"whatsThis\">\n             <string>Select the sound card for the speaker function during a call.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"speakerTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Speaker:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>speakerComboBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"ringtoneTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Ring tone:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>ringtoneComboBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"otherRingtoneTextLabel\">\n            <property name=\"text\">\n             <string>Other device:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>otherRingtoneLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"otherSpeakerTextLabel\">\n            <property name=\"text\">\n             <string>Other device:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>otherSpeakerLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"0\">\n           <widget class=\"QLabel\" name=\"otherMicTextLabel\">\n            <property name=\"text\">\n             <string>Other device:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>otherMicLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"0\">\n           <widget class=\"QLabel\" name=\"micTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Microphone:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>micComboBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"otherRingtoneLineEdit\"/>\n          </item>\n          <item row=\"5\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"otherMicLineEdit\"/>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"otherSpeakerLineEdit\"/>\n          </item>\n          <item row=\"7\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"validateAudioCheckBox\">\n            <property name=\"whatsThis\">\n             <string>&lt;p&gt;\nTwinkle validates the audio devices before usage to avoid an established call without an audio channel.\n&lt;p&gt;\nOn startup of Twinkle a warning is given if an audio device is inaccessible.\n&lt;p&gt;\nIf before making a call, the microphone or speaker appears to be invalid, a warning is given and no call can be made.\n&lt;p&gt;\nIf before answering a call, the microphone or speaker appears to be invalid, a warning is given and the call will not be answered.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Validate devices before usage</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+V</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"0\" colspan=\"2\">\n        <widget class=\"QGroupBox\" name=\"advancedSoundGroupBox\">\n         <property name=\"title\">\n          <string>Advanced</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\">\n           <layout class=\"QGridLayout\">\n            <item row=\"0\" column=\"0\">\n             <widget class=\"QLabel\" name=\"ossFragmnetTextLabel\">\n              <property name=\"text\">\n               <string>OSS &amp;fragment size:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>ossFragmentComboBox</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"1\" column=\"1\">\n             <widget class=\"QComboBox\" name=\"alsaPlayPeriodComboBox\">\n              <property name=\"whatsThis\">\n               <string>The ALSA play period size influences the real time behaviour of your soundcard for playing sound. If your sound frequently drops while using ALSA, you might try a different value here.</string>\n              </property>\n              <item>\n               <property name=\"text\">\n                <string>16</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>32</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>64</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>128</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>256</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>512</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>1024</string>\n               </property>\n              </item>\n             </widget>\n            </item>\n            <item row=\"1\" column=\"0\">\n             <widget class=\"QLabel\" name=\"alsaPlayPeriodTextLabel\">\n              <property name=\"text\">\n               <string>ALSA &amp;play period size:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>alsaPlayPeriodComboBox</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"2\" column=\"0\">\n             <widget class=\"QLabel\" name=\"alsaCapturePeriosTextLabel\">\n              <property name=\"text\">\n               <string>&amp;ALSA capture period size:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>alsaCapturePeriodComboBox</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"0\" column=\"1\">\n             <widget class=\"QComboBox\" name=\"ossFragmentComboBox\">\n              <property name=\"whatsThis\">\n               <string>The OSS fragment size influences the real time behaviour of your soundcard. If your sound frequently drops while using OSS, you might try a different value here.</string>\n              </property>\n              <item>\n               <property name=\"text\">\n                <string>16</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>32</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>64</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>128</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>256</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>512</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>1024</string>\n               </property>\n              </item>\n             </widget>\n            </item>\n            <item row=\"2\" column=\"1\">\n             <widget class=\"QComboBox\" name=\"alsaCapturePeriodComboBox\">\n              <property name=\"whatsThis\">\n               <string>The ALSA capture period size influences the real time behaviour of your soundcard for capturing sound. If the other side of your call complains about frequently dropping sound, you might try a different value here.</string>\n              </property>\n              <item>\n               <property name=\"text\">\n                <string>16</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>32</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>64</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>128</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>256</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>512</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>1024</string>\n               </property>\n              </item>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <spacer>\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"sizeType\">\n             <enum>QSizePolicy::Expanding</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>121</width>\n              <height>20</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n          <item row=\"1\" column=\"0\" colspan=\"2\">\n           <widget class=\"QLabel\" name=\"label\">\n            <property name=\"text\">\n             <string>Tip: for crackling sound with PulseAudio, set play period size to maximum.</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>true</bool>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"1\">\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"0\" column=\"0\" colspan=\"2\">\n        <widget class=\"QLabel\" name=\"audioTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Audio</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageLog\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"logTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Log</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"logMaxSizeTextLabel\">\n           <property name=\"text\">\n            <string>&amp;Max log size:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>logMaxSizeSpinBox</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"logMaxSizeSpinBox\">\n           <property name=\"whatsThis\">\n            <string>The maximum size of a log file in MB. When the log file exceeds this size, a backup of the log file is created and the current log file is zapped. Only one backup log file will be kept.</string>\n           </property>\n           <property name=\"minimum\">\n            <number>1</number>\n           </property>\n           <property name=\"maximum\">\n            <number>100</number>\n           </property>\n           <property name=\"singleStep\">\n            <number>5</number>\n           </property>\n           <property name=\"value\">\n            <number>5</number>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"logSizeMbTextLabel\">\n           <property name=\"text\">\n            <string>MB</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>211</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"logDebugCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Indicates if reports marked as &quot;debug&quot; will be logged.</string>\n         </property>\n         <property name=\"text\">\n          <string>Log &amp;debug reports</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+D</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"logSipCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Indicates if SIP messages will be logged.</string>\n         </property>\n         <property name=\"text\">\n          <string>Log &amp;SIP reports</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+S</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"logStunCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Indicates if STUN messages will be logged.</string>\n         </property>\n         <property name=\"text\">\n          <string>Log S&amp;TUN reports</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+T</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"logMemoryCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Indicates if reports concerning memory management will be logged.</string>\n         </property>\n         <property name=\"text\">\n          <string>Log m&amp;emory reports</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+E</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>61</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageGeneral\">\n      <layout class=\"QGridLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"generalTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>General</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QGroupBox\" name=\"systrayGroupBox\">\n         <property name=\"title\">\n          <string>System tray</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"guiUseSystrayCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Enable this option if you want a system tray icon for Twinkle. The system tray icon is created when you start Twinkle.</string>\n            </property>\n            <property name=\"text\">\n             <string>Create &amp;system tray icon on startup</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+S</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"guiHideCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Enable this option if you want Twinkle to hide in the system tray when you close the main window.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Hide in system tray when closing main window</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+H</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QGroupBox\" name=\"startupGroupBox\">\n         <property name=\"title\">\n          <string>Startup</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"startHiddenCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Next time you start Twinkle it will immediately hide in the system tray. This works best when you also select a default user profile.</string>\n            </property>\n            <property name=\"text\">\n             <string>S&amp;tartup hidden in system tray</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+T</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\" colspan=\"2\">\n           <widget class=\"QListWidget\" name=\"profileListView\">\n            <property name=\"whatsThis\">\n             <string>If you always use the same profile(s), then you can mark these profiles as default here. The next time you start Twinkle, you will not be asked to select which profiles to run. The default profiles will automatically run.</string>\n            </property>\n            <property name=\"selectionMode\">\n             <enum>QAbstractItemView::NoSelection</enum>\n            </property>\n            <property name=\"resizeMode\">\n             <enum>QListView::Fixed</enum>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"0\">\n        <widget class=\"QGroupBox\" name=\"srvGroupBox\">\n         <property name=\"title\">\n          <string>Services</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"callWaitingCheckBox\">\n            <property name=\"whatsThis\">\n             <string>With call waiting an incoming call is accepted when only one line is busy. When you disable call waiting an incoming call will be rejected when one line is busy.</string>\n            </property>\n            <property name=\"text\">\n             <string>Call &amp;waiting</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+W</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"hangupBothCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Hang up both lines when you press bye to end a 3-way conference call. When this option is disabled, only the active line will be hung up and you can continue talking with the party on the other line.</string>\n            </property>\n            <property name=\"text\">\n             <string>Hang up &amp;both lines when ending a 3-way conference call.</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+B</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"4\" column=\"0\">\n        <widget class=\"QCheckBox\" name=\"osdCheckBox\">\n         <property name=\"text\">\n          <string>Enable in-call OSD</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"5\" column=\"0\">\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"histSizeTextLabel\">\n           <property name=\"text\">\n            <string>&amp;Maximum calls in call history:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>histSizeSpinBox</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"histSizeSpinBox\">\n           <property name=\"whatsThis\">\n            <string>The maximum number of calls that will be kept in the call history.</string>\n           </property>\n           <property name=\"maximum\">\n            <number>1000</number>\n           </property>\n           <property name=\"singleStep\">\n            <number>10</number>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>191</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item row=\"6\" column=\"0\">\n        <widget class=\"QCheckBox\" name=\"incomingPopupCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Display a popup window with \"Answer\" and \"Reject\" buttons on an incoming call.</string>\n         </property>\n         <property name=\"text\">\n          <string>Show &amp;popup window on incoming call</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+P</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"7\" column=\"0\">\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QCheckBox\" name=\"autoShowCheckBox\">\n           <property name=\"whatsThis\">\n            <string>When the main window is hidden, it will be automatically shown on an incoming call after the number of specified seconds.</string>\n           </property>\n           <property name=\"text\">\n            <string>&amp;Auto show main window on incoming call after</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+A</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"autoShowTimeoutSpinBox\">\n           <property name=\"whatsThis\">\n            <string>Number of seconds after which the main window should be shown.</string>\n           </property>\n           <property name=\"maximum\">\n            <number>60</number>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"secAutoShowTextLabel\">\n           <property name=\"text\">\n            <string>secs</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>29</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item row=\"9\" column=\"0\">\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"browserTextLabel\">\n           <property name=\"text\">\n            <string>W&amp;eb browser command:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>browserLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"browserLineEdit\">\n           <property name=\"whatsThis\">\n            <string>Command to start your web browser. If you leave this field empty Twinkle will try to figure out your default web browser.</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item row=\"8\" column=\"0\">\n        <widget class=\"QCheckBox\" name=\"inhibitIdleSessionCheckBox\">\n         <property name=\"whatsThis\">\n          <string>If the session is marked as idle, it may trigger certain actions (such as locking the screen, logging out automatically, or suspending the system) depending on your configuration.  Enabling this option will prevent this from happening while a call is in progress.</string>\n         </property>\n         <property name=\"enabled\">\n          <bool>false</bool>\n         </property>\n         <property name=\"text\">\n          <string>Prevent &amp;idle session while a call is in progress</string>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageNetwork\">\n      <layout class=\"QGridLayout\">\n       <item row=\"0\" column=\"0\" colspan=\"4\">\n        <widget class=\"QLabel\" name=\"networkTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Network</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"5\" column=\"3\">\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>230</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"2\" column=\"2\" colspan=\"2\">\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>314</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"3\" column=\"3\">\n        <widget class=\"QLineEdit\" name=\"maxUdpSizeLineEdit\">\n         <property name=\"whatsThis\">\n          <string>Maximum allowed size (0-65535) in bytes of an incoming SIP message over UDP.</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"sipUdpPortTextLabel\">\n         <property name=\"text\">\n          <string>&amp;SIP port:</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"buddy\">\n          <cstring>sipUdpPortSpinBox</cstring>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QLabel\" name=\"rtpPortTextLabel\">\n         <property name=\"text\">\n          <string>&amp;RTP port:</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"buddy\">\n          <cstring>rtpPortSpinBox</cstring>\n         </property>\n        </widget>\n       </item>\n       <item row=\"4\" column=\"0\" colspan=\"3\">\n        <widget class=\"QLabel\" name=\"maxTcpSizeTextLabel\">\n         <property name=\"text\">\n          <string>Max. SIP message size (&amp;TCP):</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"buddy\">\n          <cstring>maxTcpSizeLineEdit</cstring>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"2\" colspan=\"2\">\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>314</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QSpinBox\" name=\"sipUdpPortSpinBox\">\n         <property name=\"whatsThis\">\n          <string>The UDP/TCP port used for sending and receiving SIP messages.</string>\n         </property>\n         <property name=\"minimum\">\n          <number>1025</number>\n         </property>\n         <property name=\"maximum\">\n          <number>65535</number>\n         </property>\n         <property name=\"value\">\n          <number>5060</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"0\" colspan=\"3\">\n        <widget class=\"QLabel\" name=\"maxUdpSizeTextLabel\">\n         <property name=\"text\">\n          <string>Max. SIP message size (&amp;UDP):</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"buddy\">\n          <cstring>maxUdpSizeLineEdit</cstring>\n         </property>\n        </widget>\n       </item>\n       <item row=\"4\" column=\"3\">\n        <widget class=\"QLineEdit\" name=\"maxTcpSizeLineEdit\">\n         <property name=\"whatsThis\">\n          <string>Maximum allowed size (0-4294967295) in bytes of an incoming SIP message over TCP.</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QSpinBox\" name=\"rtpPortSpinBox\">\n         <property name=\"whatsThis\">\n          <string>The UDP port used for sending and receiving RTP for the first line. The UDP port for the second line is 2 higher. E.g. if port 8000 is used for the first line, then the second line uses port 8002. When you use call transfer then the next even port (eg. 8004) is also used.</string>\n         </property>\n         <property name=\"minimum\">\n          <number>1025</number>\n         </property>\n         <property name=\"maximum\">\n          <number>65535</number>\n         </property>\n         <property name=\"singleStep\">\n          <number>2</number>\n         </property>\n         <property name=\"value\">\n          <number>8000</number>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageRingtones\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"ringtonesTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Ring tones</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"ringtoneButtonGroup\">\n         <property name=\"title\">\n          <string>Ring tone</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"playRingtoneCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Indicates if a ring tone should be played when a call comes in.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Play ring tone on incoming call</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+P</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QRadioButton\" name=\"defaultRingtoneRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Play the default ring tone when a call comes in.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Default ring tone</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+D</string>\n            </property>\n            <property name=\"checked\">\n             <bool>true</bool>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QRadioButton\" name=\"customRingtoneRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Play a custom ring tone when a call comes in.</string>\n            </property>\n            <property name=\"text\">\n             <string>C&amp;ustom ring tone</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+U</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Fixed</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>20</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n            <item>\n             <widget class=\"QLineEdit\" name=\"ringtoneLineEdit\">\n              <property name=\"whatsThis\">\n               <string>Specify the file name of a .wav file that you want to be played as ring tone.</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QToolButton\" name=\"openRingtoneToolButton\">\n              <property name=\"focusPolicy\">\n               <enum>Qt::TabFocus</enum>\n              </property>\n              <property name=\"whatsThis\">\n               <string>Select ring tone file.</string>\n              </property>\n              <property name=\"text\">\n               <string/>\n              </property>\n              <property name=\"icon\">\n               <iconset resource=\"icons.qrc\">\n                <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"ringbackButtonGroup\">\n         <property name=\"title\">\n          <string>Ring back tone</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"playRingbackCheckBox\">\n            <property name=\"whatsThis\">\n             <string>&lt;p&gt;\nPlay ring back tone while you are waiting for the far-end to answer your call.\n&lt;/p&gt;\n&lt;p&gt;\nDepending on your SIP provider the network might provide ring back tone or an announcement.\n&lt;/p&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>P&amp;lay ring back tone when network does not play ring back tone</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+L</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QRadioButton\" name=\"defaultRingbackRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Play the default ring back tone.</string>\n            </property>\n            <property name=\"text\">\n             <string>D&amp;efault ring back tone</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+E</string>\n            </property>\n            <property name=\"checked\">\n             <bool>true</bool>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QRadioButton\" name=\"customRingbackRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Play a custom ring back tone.</string>\n            </property>\n            <property name=\"text\">\n             <string>Cu&amp;stom ring back tone</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+S</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Fixed</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>20</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n            <item>\n             <widget class=\"QLineEdit\" name=\"ringbackLineEdit\">\n              <property name=\"whatsThis\">\n               <string>Specify the file name of a .wav file that you want to be played as ring back tone.</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QToolButton\" name=\"openRingbackToolButton\">\n              <property name=\"focusPolicy\">\n               <enum>Qt::TabFocus</enum>\n              </property>\n              <property name=\"whatsThis\">\n               <string>Select ring back tone file.</string>\n              </property>\n              <property name=\"text\">\n               <string/>\n              </property>\n              <property name=\"icon\">\n               <iconset resource=\"icons.qrc\">\n                <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageAddressBook\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Address book</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"abLookupNameCheckBox\">\n         <property name=\"whatsThis\">\n          <string>On an incoming call, Twinkle will try to find the name belonging to the incoming SIP address in your address book. This name will be displayed.</string>\n         </property>\n         <property name=\"text\">\n          <string>&amp;Lookup name for incoming call</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+L</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"abOverrideDisplayCheckBox\">\n         <property name=\"whatsThis\">\n          <string>The caller may have provided a display name already. Tick this box if you want to override that name with the name you have in your address book.</string>\n         </property>\n         <property name=\"text\">\n          <string>Ove&amp;rride received display name</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+R</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"abLookupPhotoCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Lookup the photo of a caller in your address book and display it on an incoming call.</string>\n         </property>\n         <property name=\"text\">\n          <string>Lookup &amp;photo for incoming call</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+P</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>121</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>categoryListBox</tabstop>\n  <tabstop>guiUseSystrayCheckBox</tabstop>\n  <tabstop>guiHideCheckBox</tabstop>\n  <tabstop>startHiddenCheckBox</tabstop>\n  <tabstop>profileListView</tabstop>\n  <tabstop>callWaitingCheckBox</tabstop>\n  <tabstop>hangupBothCheckBox</tabstop>\n  <tabstop>histSizeSpinBox</tabstop>\n  <tabstop>autoShowCheckBox</tabstop>\n  <tabstop>autoShowTimeoutSpinBox</tabstop>\n  <tabstop>ringtoneComboBox</tabstop>\n  <tabstop>otherRingtoneLineEdit</tabstop>\n  <tabstop>speakerComboBox</tabstop>\n  <tabstop>otherSpeakerLineEdit</tabstop>\n  <tabstop>micComboBox</tabstop>\n  <tabstop>otherMicLineEdit</tabstop>\n  <tabstop>validateAudioCheckBox</tabstop>\n  <tabstop>ossFragmentComboBox</tabstop>\n  <tabstop>alsaPlayPeriodComboBox</tabstop>\n  <tabstop>alsaCapturePeriodComboBox</tabstop>\n  <tabstop>playRingtoneCheckBox</tabstop>\n  <tabstop>defaultRingtoneRadioButton</tabstop>\n  <tabstop>ringtoneLineEdit</tabstop>\n  <tabstop>openRingtoneToolButton</tabstop>\n  <tabstop>playRingbackCheckBox</tabstop>\n  <tabstop>defaultRingbackRadioButton</tabstop>\n  <tabstop>ringbackLineEdit</tabstop>\n  <tabstop>openRingbackToolButton</tabstop>\n  <tabstop>abLookupNameCheckBox</tabstop>\n  <tabstop>abOverrideDisplayCheckBox</tabstop>\n  <tabstop>abLookupPhotoCheckBox</tabstop>\n  <tabstop>sipUdpPortSpinBox</tabstop>\n  <tabstop>rtpPortSpinBox</tabstop>\n  <tabstop>maxUdpSizeLineEdit</tabstop>\n  <tabstop>maxTcpSizeLineEdit</tabstop>\n  <tabstop>logMaxSizeSpinBox</tabstop>\n  <tabstop>logDebugCheckBox</tabstop>\n  <tabstop>logSipCheckBox</tabstop>\n  <tabstop>logStunCheckBox</tabstop>\n  <tabstop>logMemoryCheckBox</tabstop>\n  <tabstop>okPushButton</tabstop>\n  <tabstop>cancelPushButton</tabstop>\n  <tabstop>customRingtoneRadioButton</tabstop>\n  <tabstop>customRingbackRadioButton</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">sys_settings.h</include>\n </includes>\n <resources>\n  <include location=\"icons.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>okPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>validate()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>categoryListBox</sender>\n   <signal>currentRowChanged(int)</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>showCategory(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>guiUseSystrayCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>guiHideCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>guiUseSystrayCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>guiHideCheckBox</receiver>\n   <slot>setChecked(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>guiUseSystrayCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>startHiddenCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingtoneCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>customRingtoneRadioButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingtoneCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>defaultRingtoneRadioButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingtoneCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>playRingToneCheckBoxToggles(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingtoneCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>openRingtoneToolButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingbackCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>customRingbackRadioButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingbackCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>defaultRingbackRadioButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingbackCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>playRingBackToneCheckBoxToggles(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>playRingbackCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>openRingbackToolButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openRingtoneToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>chooseRingtone()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openRingbackToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>chooseRingback()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>customRingtoneRadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>ringtoneLineEdit</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>customRingtoneRadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>openRingtoneToolButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>customRingbackRadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>ringbackLineEdit</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>customRingbackRadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>openRingbackToolButton</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>abLookupNameCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>abOverrideDisplayCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>ringtoneComboBox</sender>\n   <signal>activated(int)</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>devRingtoneSelected(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>speakerComboBox</sender>\n   <signal>activated(int)</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>devSpeakerSelected(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>micComboBox</sender>\n   <signal>activated(int)</signal>\n   <receiver>SysSettingsForm</receiver>\n   <slot>devMicSelected(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/termcapform.cpp",
    "content": "#include \"termcapform.h\"\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <qvariant.h>\n#include <qimage.h>\n#include <qpixmap.h>\n\n#include \"gui.h\"\n#include \"audits/memman.h\"\n#include \"termcapform.h\"\n/*\n *  Constructs a TermCapForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nTermCapForm::TermCapForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nTermCapForm::~TermCapForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid TermCapForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid TermCapForm::init()\n{\n\tgetAddressForm = 0;\n\t\n\t// Set toolbutton icons for disabled options.\n\tsetDisabledIcon(addressToolButton, \":/icons/images/kontact_contacts-disabled.png\");\n}\n\nvoid TermCapForm::show(t_user *user_config, const QString &dest)\n{\n\t((t_gui *)ui)->fill_user_combo(fromComboBox);\n\t\n\t// Select from user\n\tif (user_config) {\n\t\tfor (int i = 0; i < fromComboBox->count(); i++) {\n            if (fromComboBox->itemText(i) ==\n\t\t\t    user_config->get_profile_name().c_str())\n\t\t\t{\n                fromComboBox->setCurrentIndex(i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpartyLineEdit->setText(dest);\n\tQDialog::show();\n}\n\nvoid TermCapForm::destroy()\n{\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n}\n\nvoid TermCapForm::validate()\n{\n\tstring display, dest_str;\n\tt_user *from_user = phone->ref_user_profile(\n                fromComboBox->currentText().toStdString());\n\t\n\tui->expand_destination(from_user, \n                   partyLineEdit->text().trimmed().toStdString(),\n\t\t\t       display, dest_str);\n\tt_url dest(dest_str);\n\t\n\tif (dest.is_valid()) {\n\t\temit destination(from_user, dest);\n\t\taccept();\n\t} else {\n\t\tpartyLineEdit->selectAll();\n\t}\n}\n\nvoid TermCapForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid TermCapForm::selectedAddress(const QString &address)\n{\n\tpartyLineEdit->setText(address);\n}\n"
  },
  {
    "path": "src/gui/termcapform.h",
    "content": "#ifndef TERMCAPFORM_H\n#define TERMCAPFORM_H\n#include \"ui_termcapform.h\"\n#include \"getaddressform.h\"\n#include \"phone.h\"\n#include \"sockets/url.h\"\n#include \"user.h\"\n\nclass t_phone;\nextern t_phone *phone;\n\nclass TermCapForm : public QDialog, public Ui::TermCapForm\n{\n\tQ_OBJECT\n\npublic:\n    TermCapForm(QWidget* parent = 0);\n\t~TermCapForm();\n\npublic slots:\n\tvirtual void show( t_user * user_config, const QString & dest );\n\tvirtual void validate();\n\tvirtual void showAddressBook();\n\tvirtual void selectedAddress( const QString & address );\n\nsignals:\n\tvoid destination(t_user *, const t_url &);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tGetAddressForm *getAddressForm;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/termcapform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>TermCapForm</class>\n  <widget class=\"QDialog\" name=\"TermCapForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>581</width>\n        <height>168</height>\n      </rect>\n    </property>\n    <property name=\"sizePolicy\">\n      <sizepolicy>\n        <hsizetype>5</hsizetype>\n        <vsizetype>5</vsizetype>\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n      </sizepolicy>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Terminal Capabilities</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <widget class=\"QLabel\" name=\"fromTextLabel\">\n              <property name=\"text\">\n                <string>&amp;From:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>fromComboBox</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QComboBox\" name=\"fromComboBox\">\n              <property name=\"sizePolicy\">\n                <sizepolicy>\n                  <hsizetype>7</hsizetype>\n                  <vsizetype>0</vsizetype>\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                </sizepolicy>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <widget class=\"QGroupBox\" name=\"termCapGroupBox\">\n          <property name=\"title\">\n            <string>Get terminal capabilities of</string>\n          </property>\n          <layout class=\"QHBoxLayout\">\n            <item>\n              <widget class=\"QLabel\" name=\"partyTextLabel\">\n                <property name=\"text\">\n                  <string>&amp;To:</string>\n                </property>\n                <property name=\"buddy\" stdset=\"0\">\n                  <cstring>partyLineEdit</cstring>\n                </property>\n                <property name=\"wordWrap\">\n                  <bool>false</bool>\n                </property>\n              </widget>\n            </item>\n            <item>\n              <widget class=\"QLineEdit\" name=\"partyLineEdit\">\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>The address that you want to query for capabilities (OPTION request). This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</string>\n                </property>\n              </widget>\n            </item>\n            <item>\n              <widget class=\"QToolButton\" name=\"addressToolButton\">\n                <property name=\"focusPolicy\">\n                  <enum>Qt::TabFocus</enum>\n                </property>\n                <property name=\"text\">\n                  <string/>\n                </property>\n                <property name=\"shortcut\">\n                  <string>F10</string>\n                </property>\n                <property name=\"icon\">\n                  <iconset>:/icons/images/kontact_contacts.png</iconset>\n                </property>\n                <property name=\"toolTip\" stdset=\"0\">\n                  <string>Address book</string>\n                </property>\n                <property name=\"whatsThis\" stdset=\"0\">\n                  <string>Select an address from the address book.</string>\n                </property>\n              </widget>\n            </item>\n          </layout>\n        </widget>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>16</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>131</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>partyLineEdit</tabstop>\n    <tabstop>addressToolButton</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n    <tabstop>fromComboBox</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"local\">sockets/url.h</include>\n    <include location=\"local\">ui_getaddressform.h</include>\n    <include location=\"local\">user.h</include>\n    <include location=\"local\">phone.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>TermCapForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>TermCapForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>addressToolButton</sender>\n      <signal>clicked()</signal>\n      <receiver>TermCapForm</receiver>\n      <slot>showAddressBook()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/transferform.cpp",
    "content": "//Added by qt3to4:\n#include <QPixmap>\n#include <QCloseEvent>\n#include <QVariant>\n#include <QImage>\n#include <QPixmap>\n#include \"gui.h\"\n#include \"audits/memman.h\"\n#include \"transferform.h\"\n/*\n *  Constructs a TransferForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nTransferForm::TransferForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nTransferForm::~TransferForm()\n{\n\tdestroy();\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid TransferForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\n/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nvoid TransferForm::init()\n{\n\tgetAddressForm = 0;\n\t\n\t// Set toolbutton icons for disabled options.\n\tQIcon i;\n    i = addressToolButton->icon();\n    i.addPixmap(QPixmap(\":/icons/images/kontact_contacts-disabled.png\"), QIcon::Disabled);\n    addressToolButton->setIcon(i);\n}\n\nvoid TransferForm::destroy()\n{\n\tif (getAddressForm) {\n\t\tMEMMAN_DELETE(getAddressForm);\n\t\tdelete getAddressForm;\n\t}\n}\n\nvoid TransferForm::initTransferOptions() \n{\n\t// Show possible transfer type options\n\t// Basic transfer is always possible.\n\t// If a line is idle, then a transfer with consultation is possible.\n\t// The line will be seized, so an incoming call cannot occupy it.\n\t// If both lines are busy, then the active line can be transferred\n\t// to the other line.\n\tunsigned short idle_line;\n\tif (phone->get_idle_line(idle_line)) {\n\t\tconsult_line = (int)idle_line;\n\t\tphone->pub_seize(consult_line);\t\n\t\tconsultRadioButton->show();\n\t\tconsultRadioButton->setChecked(true);\n\t\totherLineRadioButton->hide();\n\t} else {\n\t\tconsult_line = -1;\n\t\tconsultRadioButton->hide();\n\t\totherLineRadioButton->show();\n\t\totherLineRadioButton->setChecked(true);\n\t}\n}\n\nvoid TransferForm::show(t_user *user)\n{\n\tuser_config = user;\n\tinitTransferOptions();\n\tQDialog::show();\n}\n\nvoid TransferForm::show(t_user *user, const string &dest, t_transfer_type transfer_type)\n{\n\tuser_config = user;\n\tinitTransferOptions();\n\ttoLineEdit->setText(dest.c_str());\n\t\n\tswitch (transfer_type) {\n\tcase TRANSFER_CONSULT:\n\t\tconsultRadioButton->setChecked(true);\n\t\tbreak;\n\tcase TRANSFER_OTHER_LINE:\n\t\totherLineRadioButton->setChecked(true);\n\t\tbreak;\n\tdefault:\n\t\tbasicRadioButton->setChecked(true);\n\t\tbreak;\n\t}\n\t\n\tQDialog::show();\n}\n\nvoid TransferForm::hide()\n{\n\tif (consult_line > -1) {\n\t\tphone->pub_unseize(consult_line);\n\t}\n\t\n\tQDialog::hide();\n}\n\nvoid TransferForm::reject()\n{\n\tif (user_config->get_referrer_hold()) {\n\t\t((t_gui *)ui)->action_retrieve();\n\t}\n\t\n\tif (consult_line > -1) {\n\t\tphone->pub_unseize(consult_line);\n\t}\n\t\n\tQDialog::reject();\n}\n\nvoid TransferForm::validate()\n{\n\tt_display_url dest;\n    ui->expand_destination(user_config, toLineEdit->text().trimmed().toStdString(), dest);\n\t\n\tt_transfer_type transfer_type;\n    if (consultRadioButton->isChecked()) {\n\t\ttransfer_type = TRANSFER_CONSULT;\n    } else if (otherLineRadioButton->isChecked()) {\n\t\ttransfer_type = TRANSFER_OTHER_LINE;\n\t} else {\n\t\ttransfer_type = TRANSFER_BASIC;\n\t}\n\t\n\t\n\tif (transfer_type == TRANSFER_OTHER_LINE || dest.is_valid()) {\n\t\tif (consult_line > -1) {\n\t\t\tphone->pub_unseize(consult_line);\n\t\t}\t\n\t\temit destination(dest, transfer_type);\n\t\taccept();\n\t} else {\n\t\ttoLineEdit->selectAll();\n\t}\n}\n\nvoid TransferForm::closeEvent(QCloseEvent *)\n{\n\treject();\n}\n\nvoid TransferForm::showAddressBook()\n{\n\tif (!getAddressForm) {\n        getAddressForm = new GetAddressForm(this);\n        getAddressForm->setModal(true);\n\t\tMEMMAN_NEW(getAddressForm);\n\t}\n\t\n\tconnect(getAddressForm, \n\t\tSIGNAL(address(const QString &)),\n\t\tthis, SLOT(selectedAddress(const QString &)));\n\t\n\tgetAddressForm->show();\n}\n\nvoid TransferForm::selectedAddress(const QString &address)\n{\n\ttoLineEdit->setText(address);\n}\n\nvoid TransferForm::setOtherLineAddress(bool on)\n{\n\tif (on) {\n\t\tpreviousAddress = toLineEdit->text();\n\t\tunsigned short active_line = phone->get_active_line();\n\t\tunsigned short other_line = (active_line == 0 ? 1 : 0);\n\t\tQString address = ui->format_sip_address(user_config,\n\t\t\tphone->get_remote_display(other_line),\n\t\t\tphone->get_remote_uri(other_line)).c_str();\n\t\ttoLineEdit->setText(address);\n\t\ttoLineEdit->setEnabled(false);\n\t\ttoLabel->setEnabled(false);\n#ifdef HAVE_KDE\n\t\taddressToolButton->setEnabled(false);\n#endif\n\t} else {\n\t\ttoLineEdit->setText(previousAddress);\n\t\ttoLineEdit->setEnabled(true);\n\t\ttoLabel->setEnabled(true);\n#ifdef HAVE_KDE\n\t\taddressToolButton->setEnabled(true);\n#endif\n\t}\n}\n"
  },
  {
    "path": "src/gui/transferform.h",
    "content": "#ifndef TRANSFERFORM_H\n#define TRANSFERFORM_H\nclass t_phone;\nextern t_phone *phone;\n\n#include \"getaddressform.h\"\n#include \"phone.h\"\n#include \"protocol.h\"\n#include <QtCore/QStringRef>\n#include \"sockets/url.h\"\n#include \"user.h\"\n#include \"ui_transferform.h\"\n\nclass TransferForm : public QDialog, public Ui::TransferForm\n{\n\tQ_OBJECT\n\npublic:\n    TransferForm(QWidget* parent = 0);\n\t~TransferForm();\n\npublic slots:\n\tvirtual void initTransferOptions();\n\tvirtual void show( t_user * user );\n\tvirtual void show( t_user * user, const string & dest, t_transfer_type transfer_type );\n\tvirtual void hide();\n\tvirtual void reject();\n\tvirtual void validate();\n\tvirtual void closeEvent( QCloseEvent * );\n\tvirtual void showAddressBook();\n\tvirtual void selectedAddress( const QString & address );\n\tvirtual void setOtherLineAddress( bool on );\n\nsignals:\n\tvoid destination(const t_display_url&, t_transfer_type);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tint consult_line;\n\tt_user *user_config;\n\tGetAddressForm *getAddressForm;\n\tQString previousAddress;\n\n\tvoid init();\n\tvoid destroy();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/transferform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>TransferForm</class>\n <widget class=\"QDialog\" name=\"TransferForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>532</width>\n    <height>251</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - Transfer</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QGroupBox\" name=\"transferGroupBox\">\n     <property name=\"title\">\n      <string>Transfer call to</string>\n     </property>\n     <layout class=\"QHBoxLayout\">\n      <item>\n       <widget class=\"QLabel\" name=\"toLabel\">\n        <property name=\"text\">\n         <string>&amp;To:</string>\n        </property>\n        <property name=\"wordWrap\">\n         <bool>false</bool>\n        </property>\n        <property name=\"buddy\">\n         <cstring>toLineEdit</cstring>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLineEdit\" name=\"toLineEdit\">\n        <property name=\"whatsThis\">\n         <string>The address of the person you want to transfer the call to. This can be a full SIP address like &lt;b&gt;sip:example@example.com&lt;/b&gt; or just the user part or telephone number of the full address. When you do not specify a full address, then Twinkle will complete the address by using the domain value of your user profile.</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"addressToolButton\">\n        <property name=\"focusPolicy\">\n         <enum>Qt::TabFocus</enum>\n        </property>\n        <property name=\"toolTip\">\n         <string>Address book</string>\n        </property>\n        <property name=\"whatsThis\">\n         <string>Select an address from the address book.</string>\n        </property>\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"icons.qrc\">\n          <normaloff>:/icons/images/kontact_contacts.png</normaloff>:/icons/images/kontact_contacts.png</iconset>\n        </property>\n        <property name=\"shortcut\">\n         <string>F10</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QRadioButton\" name=\"basicRadioButton\">\n     <property name=\"whatsThis\">\n      <string>Transfer the call to a third party without contacting that third party yourself.</string>\n     </property>\n     <property name=\"text\">\n      <string>&amp;Blind transfer</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+B</string>\n     </property>\n     <property name=\"checked\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <widget class=\"QRadioButton\" name=\"consultRadioButton\">\n     <property name=\"whatsThis\">\n      <string>Before transferring the call to a third party, first consult the party yourself.</string>\n     </property>\n     <property name=\"text\">\n      <string>T&amp;ransfer with consultation</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+R</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"0\">\n    <widget class=\"QRadioButton\" name=\"otherLineRadioButton\">\n     <property name=\"whatsThis\">\n      <string>Connect the remote party on the active line with the remote party on the other line.</string>\n     </property>\n     <property name=\"text\">\n      <string>Transfer to other &amp;line</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Alt+L</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"4\" column=\"0\">\n    <spacer>\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item row=\"5\" column=\"0\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>121</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"okPushButton\">\n       <property name=\"text\">\n        <string>&amp;OK</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+O</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelPushButton\">\n       <property name=\"text\">\n        <string>&amp;Cancel</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <includes>\n  <include location=\"local\">qstring.h</include>\n  <include location=\"local\">sockets/url.h</include>\n  <include location=\"local\">ui_getaddressform.h</include>\n  <include location=\"local\">user.h</include>\n  <include location=\"local\">protocol.h</include>\n  <include location=\"local\">phone.h</include>\n </includes>\n <resources>\n  <include location=\"icons.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>okPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>TransferForm</receiver>\n   <slot>validate()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>TransferForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>addressToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>TransferForm</receiver>\n   <slot>showAddressBook()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/twinkle-uri-handler",
    "content": "#!/bin/sh\n#\n# Invoke Twinkle, relaying an optional URI to call (sip:, sips:, tel: or\n# callto:) passed as argument.  This is mostly meant to be used with a desktop\n# entry, since a URI may be present or not, and Twinkle must be invoked\n# differently in each case.  (That, and it currently cannot handle URIs in the\n# first place.)\n\n# Command to run (with optional arguments, if needed)\nTWINKLE=\"twinkle\"\n\nif [ $# -gt 1 ]; then\n\techo \"Usage: $0 [URI]\" >&2\n\texit 1\nfi\n\nif [ $# -eq 0 ]; then\n\t# No arguments, invoke Twinkle as-is\n\texec $TWINKLE\nelse\n\t# Convert a URI into a proper `--call` parameter\n\tcase \"$1\" in\n\t\tsip:* | sips:*)\n\t\t\tCALL_ARG=\"$1\"\n\t\t\t;;\n\t\ttel:*)\n\t\t\tCALL_ARG=\"${1#tel:}\"\n\t\t\t;;\n\t\tcallto:*)\n\t\t\tCALL_ARG=\"${1#callto:}\"\n\t\t\t;;\n\t\t*)\n\t\t\techo \"Unrecognized URI: $1\" >&2\n\t\t\texit 1\n\t\t\t;;\n\tesac\n\texec $TWINKLE --call \"$CALL_ARG\"\nfi\n"
  },
  {
    "path": "src/gui/twinkleapplication.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"twinkleapplication.h\"\n\n#include \"phone.h\"\n#include \"sys_settings.h\"\n#include \"user.h\"\n#include \"gui.h\"\n\nextern t_phone *phone;\n\n#ifdef HAVE_KDE\nt_twinkle_application::t_twinkle_application() : KApplication() {}\n#else\nt_twinkle_application::t_twinkle_application(int &argc, char **argv) :\n\t\tQApplication(argc,argv)\n{}\n#endif\nvoid t_twinkle_application::commitData (QSessionManager &sm) {\n    sys_config->set_ui_session_id(sessionId().toStdString());\n\t\n\t// Create list of active profile file names\n\tlist<t_user *> user_list = phone->ref_users();\n\tlist<string> profile_filenames;\n\tfor (list<t_user *>::const_iterator it = user_list.begin(); it != user_list.end(); ++it) {\n\t\tprofile_filenames.push_back((*it)->get_filename());\n\t}\n\t\n\tsys_config->set_ui_session_active_profiles(profile_filenames);\n\t((t_gui*)ui)->save_session_state();\n}\n"
  },
  {
    "path": "src/gui/twinkleapplication.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TWINKLEAPPLICATION_H\n#define _TWINKLEAPPLICATION_H\n\n#include \"twinkle_config.h\"\n\n#ifdef HAVE_KDE\n#include <kapplication.h>\n#else\n#include <qapplication.h>\n#endif\n\n#ifdef HAVE_KDE\nclass t_twinkle_application : public KApplication {\n#else\nclass t_twinkle_application : public QApplication {\n#endif\npublic:\n#ifdef HAVE_KDE\n\tt_twinkle_application();\n#else\n\tt_twinkle_application(int &argc, char **argv);\n#endif\n\tvirtual void commitData ( QSessionManager &sm );\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/userprofileform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    \n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    \n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    \n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n#include <QPixmap>\n#include <QList>\n#include <QLineEdit>\n#include <QLabel>\n#include <QComboBox>\n#include <QSpinBox>\n#include <QRegularExpression>\n#include \"sdp/sdp.h\"\n#include <QValidator>\n#include \"protocol.h\"\n#include <QMessageBox>\n#include \"gui.h\"\n#include <QFileDialog>\n#include <QFileInfo>\n#include <QStringList>\n#include \"twinkle_config.h\"\n#include <QListWidget>\n#include <QRegularExpressionValidator>\n#include \"numberconversionform.h\"\n#include \"util.h\"\n#include \"userprofileform.h\"\n\n\n// Indices of categories in the category list box\n#define idxCatUser\t0\n#define idxCatSipServer\t1\n#define idxCatVoiceMail\t2\n#define idxCatIM\t\t3\n#define idxCatPresence\t4\n#define idxCatRtpAudio\t5\n#define idxCatSipProtocol\t6\n#define idxCatNat\t7\n#define idxCatAddrFmt\t8\n#define idxCatTimers\t9\n#define idxCatRingTones\t10\n#define idxCatScripts\t11\n#define idxCatSecurity\t12\n\n// Indices of call hold variants in the call hold variant list box\n#define idxHoldRfc2543\t0\n#define idxHoldRfc3264\t1\n\n// Indices of SIP extension support types in the list box\n#define idxExtDisabled\t0\n#define idxExtSupported\t1\n#define idxExtRequired\t2\n#define idxExtPreferred\t3\n\n// Indices of RTP audio tabs\n#define idxRtpCodecs\t0\n#define idxRtpPreprocessing 1\n#define idxRtpIlbc\t    2\n#define idxRtpSpeex\t    3\n#define idxRtpG726\t    4\n#define idxRtpDtmf\t    5\n\n// Codec labels\n#define labelCodecG711a\t\t\"G.711 A-law\"\n#define labelCodecG711u\t\t\"G.711 u-law\"\n#define labelCodecGSM\t\t\"GSM\"\n#define labelCodecSpeexNb\t\t\"speex-nb (8 kHz)\"\n#define labelCodecSpeexWb\t\"speex-wb (16 kHz)\"\n#define labelCodecSpeexUwb\t\"speex-uwb (32 kHz)\"\n#define labelCodecIlbc\t\t\"iLBC\"\n#define labelCodecG722\t\t\"G.722\"\n#define labelCodecG726_16\t\t\"G.726 16 kbps\"\n#define labelCodecG726_24\t\t\"G.726 24 kbps\"\n#define labelCodecG726_32\t\t\"G.726 32 kbps\"\n#define labelCodecG726_40\t\t\"G.726 40 kbps\"\n#define labelCodecG729A\t\t\t\"G.729A\"\n\n// Indices of iLBC modes\n#define idxIlbcMode20\t0\n#define idxIlbcMode30\t1\n\n// Indices of G.726 packing modes\n#define idxG726PackRfc3551\t0\n#define idxG726PackAal2\t\t1\n\n// Indices of DTMF transport modes in the DTMF transport list box\n#define idxDtmfAuto\t0\n#define idxDtmfRfc2833\t1\n#define idxDtmfInband\t2\n#define idxDtmfInfo\t\t3\n\n// Columns in the number conversion list view\n#define colExpr\t\t0\n#define colReplace\t\t1\n\n// MWI type indices\n#define idxMWIUnsolicited\t0\n#define idxMWISolicited\t1\n\n// SIP transport protocol indices\n#define idxSipTransportAuto\t0\n#define idxSipTransportUDP\t1\n#define idxSipTransportTCP\t2\n\n/*\n *  Constructs a UserProfileForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nUserProfileForm::UserProfileForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nUserProfileForm::~UserProfileForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid UserProfileForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid UserProfileForm::init()\n{\n\tQRegularExpression rxNoSpace(\"\\\\S*\");\n\tQRegularExpression rxNoAtSign(\"[^@]*\");\n\tQRegularExpression rxQvalue(\"(0\\\\.[0-9]{0,3})|(1\\\\.0{0,3})\");\n\tQRegularExpression rxAkaOpValue(\"[a-zA-Z0-9]{0,32}\");\n\tQRegularExpression rxAkaAmfValue(\"[a-zA-Z0-9]{0,4}\");\n\t\n\t// Set validators\n\t// USER\n\tdomainLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\tauthAkaOpLineEdit->setValidator(new QRegularExpressionValidator(rxAkaOpValue, this));\n\tauthAkaAmfLineEdit->setValidator(new QRegularExpressionValidator(rxAkaAmfValue, this));\n\t\n\t// SIP SERVER\n\tregistrarLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\tregQvalueLineEdit->setValidator(new QRegularExpressionValidator(rxQvalue, this));\n\tproxyLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\t\n\t// Voice mail\n\tmwiServerLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\t\n\t// NAT\n\tpublicIPLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\t\n\t// Address format\n\ttestConversionLineEdit->setValidator(new QRegularExpressionValidator(rxNoAtSign, this));\n\t\n#ifndef HAVE_SPEEX\n\t// Speex & (Speex) Preprocessing\n\tspeexGroupBox->hide();\n\tpreprocessingGroupBox->hide();\n    rtpAudioTabWidget->setTabEnabled(idxRtpSpeex, false);\n    rtpAudioTabWidget->setTabEnabled(idxRtpPreprocessing, false);\n#endif\n#ifndef HAVE_ILBC\n\t// iLBC\n\tilbcGroupBox->hide();\n    rtpAudioTabWidget->setTabEnabled(idxRtpIlbc, false);\n#endif\n#ifndef HAVE_ZRTP\n\t// Zrtp\n\tzrtpEnabledCheckBox->setEnabled(false);\n\tzrtpSettingsGroupBox->hide();\n#endif\n\t\n\t// Set toolbutton icons for disabled options.\n\tQIcon i;\n    i = openRingtoneToolButton->icon();\n    i.addPixmap(QPixmap(\":/icons/images/fileopen-disabled.png\"), QIcon::Disabled);\n    openRingtoneToolButton->setIcon(i);\n    openRingbackToolButton->setIcon(i);\n    openIncomingCallScriptToolButton->setIcon(i);\n}\n\nvoid UserProfileForm::showCategory( int index )\n{\n\tif (index == idxCatUser) {\n        settingsWidgetStack->setCurrentWidget(pageUser);\n\t} else if (index == idxCatSipServer) {\n        settingsWidgetStack->setCurrentWidget(pageSipServer);\n\t} else if (index == idxCatVoiceMail) {\n        settingsWidgetStack->setCurrentWidget(pageVoiceMail);\n\t} else if (index == idxCatIM) {\n        settingsWidgetStack->setCurrentWidget(pageIM);\n\t} else if (index == idxCatPresence) {\n        settingsWidgetStack->setCurrentWidget(pagePresence);\n\t} else if (index == idxCatRtpAudio) {\n        settingsWidgetStack->setCurrentWidget(pageRtpAudio);\n\t} else if (index == idxCatSipProtocol) {\n        settingsWidgetStack->setCurrentWidget(pageSipProtocol);\n\t} else if (index == idxCatNat) {\n        settingsWidgetStack->setCurrentWidget(pageNat);\n\t} else if (index == idxCatAddrFmt) {\n        settingsWidgetStack->setCurrentWidget(pageAddressFormat);\n\t} else if (index == idxCatTimers) {\n        settingsWidgetStack->setCurrentWidget(pageTimers);\n\t} else if (index == idxCatRingTones) {\n        settingsWidgetStack->setCurrentWidget(pageRingTones);\n\t} else if (index == idxCatScripts) {\n        settingsWidgetStack->setCurrentWidget(pageScripts);\n\t} else if (index == idxCatSecurity) {\n        settingsWidgetStack->setCurrentWidget(pageSecurity);\n\t}\n}\n\n// Convert a label to a codec\nt_audio_codec UserProfileForm::label2codec(const QString &label) {\n\tif (label == labelCodecG711a) {\n\t\treturn CODEC_G711_ALAW;\n\t} else if (label == labelCodecG711u) {\n\t\treturn CODEC_G711_ULAW;\n\t} else if (label == labelCodecGSM) {\n\t\treturn CODEC_GSM;\n\t} else if (label == labelCodecSpeexNb) {\n\t\treturn CODEC_SPEEX_NB;\n\t} else if (label == labelCodecSpeexWb) {\n\t\treturn CODEC_SPEEX_WB;\n\t} else if (label == labelCodecSpeexUwb) {\n\t\treturn CODEC_SPEEX_UWB;\n\t} else if (label == labelCodecIlbc) {\n\t\treturn CODEC_ILBC;\n\t} else if (label == labelCodecG722) {\n\t\treturn CODEC_G722;\n\t} else if (label == labelCodecG726_16) {\n\t\treturn CODEC_G726_16;\n\t} else if (label == labelCodecG726_24) {\n\t\treturn CODEC_G726_24;\n\t} else if (label == labelCodecG726_32) {\n\t\treturn CODEC_G726_32;\n\t} else if (label == labelCodecG726_40) {\n\t\treturn CODEC_G726_40;\n\t} else if (label == labelCodecG729A) {\n\t\treturn CODEC_G729A;\n\t}\n\treturn CODEC_NULL;\n}\n\n// Convert a codec to a label\nQString UserProfileForm::codec2label(t_audio_codec &codec) {\n\tswitch (codec) {\n\tcase CODEC_G711_ALAW:\n\t\treturn labelCodecG711a;\n\tcase CODEC_G711_ULAW:\n\t\treturn labelCodecG711u;\n\tcase CODEC_GSM:\n\t\treturn labelCodecGSM;\n\tcase CODEC_SPEEX_NB:\n\t\treturn labelCodecSpeexNb;\n\tcase CODEC_SPEEX_WB:\n\t\treturn labelCodecSpeexWb;\n\tcase CODEC_SPEEX_UWB:\n\t\treturn labelCodecSpeexUwb;\n\tcase CODEC_ILBC:\n\t\treturn labelCodecIlbc;\n\tcase CODEC_G722:\n\t\treturn labelCodecG722;\n\tcase CODEC_G726_16:\n\t\treturn labelCodecG726_16;\n\tcase CODEC_G726_24:\n\t\treturn labelCodecG726_24;\n\tcase CODEC_G726_32:\n\t\treturn labelCodecG726_32;\n\tcase CODEC_G726_40:\n\t\treturn labelCodecG726_40;\n\tcase CODEC_G729A:\n\t\treturn labelCodecG729A;\n\tdefault:\n\t\treturn \"\";\n\t}\n}\n\n// Convert t_ext_support to an index in the SIP extension combo box\nint UserProfileForm::ext_support2indexComboItem(t_ext_support ext) {\n\tswitch(ext) {\n\tcase EXT_DISABLED:\n\t\treturn idxExtDisabled;\n\tcase EXT_SUPPORTED:\n\t\treturn idxExtSupported;\n\tcase EXT_REQUIRED:\n\t\treturn idxExtRequired;\n\tcase EXT_PREFERRED:\n\t\treturn idxExtPreferred;\n\tdefault:\n\t\treturn idxExtDisabled;\n\t}\n\t\n\treturn idxExtDisabled;\n}\n\nt_ext_support UserProfileForm::indexComboItem2ext_support(int index) {\n\tswitch(index) {\n\tcase idxExtDisabled:\n\t\treturn EXT_DISABLED;\n\tcase idxExtSupported:\n\t\treturn EXT_SUPPORTED;\n\tcase idxExtRequired:\n\t\treturn EXT_REQUIRED;\n\tcase idxExtPreferred:\n\t\treturn EXT_PREFERRED;\n\t}\n\t\n\treturn EXT_DISABLED;\n}\n\n// Populate the form\nvoid UserProfileForm::populate()\n{\n\tQString s;\n\t\n\t// Set user profile name in the titlebar\n\ts = PRODUCT_NAME;\n\ts.append(\" - \").append(tr(\"User profile:\")).append(\" \");\n\ts.append(current_profile->get_profile_name().c_str());\n    setWindowTitle(s);\n\t\n\t// Select the User category\n    categoryListBox->setCurrentRow(idxCatUser);\n    settingsWidgetStack->setCurrentWidget(pageUser);\n\t\n\t// Set focus on first field\n\tdisplayLineEdit->setFocus();\n\t\n\t// Set the values of the current_profile object in the form\n\t// USER\n\tdisplayLineEdit->setText(current_profile->get_display(false).c_str());\n\tusernameLineEdit->setText(current_profile->get_name().c_str());\n\tdomainLineEdit->setText(current_profile->get_domain().c_str());\n\torganizationLineEdit->setText(current_profile->get_organization().c_str());\n\tauthRealmLineEdit->setText(current_profile->get_auth_realm().c_str());\n\tauthNameLineEdit->setText(current_profile->get_auth_name().c_str());\n\tauthPasswordLineEdit->setText(current_profile->get_auth_pass().c_str());\n\t\n\tuint8 aka_op[AKA_OPLEN];\n\tcurrent_profile->get_auth_aka_op(aka_op);\n\tauthAkaOpLineEdit->setText(binary2hex(aka_op, AKA_OPLEN).c_str());\n\t\n\tuint8 aka_amf[AKA_AMFLEN];\n\tcurrent_profile->get_auth_aka_amf(aka_amf);\n\tauthAkaAmfLineEdit->setText(binary2hex(aka_amf, AKA_AMFLEN).c_str());\n\t\n\t// SIP SERVER\n\tregistrarLineEdit->setText(current_profile->get_registrar().encode_noscheme().c_str());\n\texpirySpinBox->setValue(current_profile->get_registration_time());\n\tregAtStartupCheckBox->setChecked(current_profile->get_register_at_startup());\n\tregAddQvalueCheckBox->setChecked(current_profile->get_reg_add_qvalue());\n\tregQvalueLineEdit->setEnabled(current_profile->get_reg_add_qvalue());\n\tregQvalueLineEdit->setText(float2str(current_profile->get_reg_qvalue(), 3).c_str());\n\tuseProxyCheckBox->setChecked(current_profile->get_use_outbound_proxy());\n\tproxyTextLabel->setEnabled(current_profile->get_use_outbound_proxy());\n\tproxyLineEdit->setEnabled(current_profile->get_use_outbound_proxy());\n\tif (current_profile->get_use_outbound_proxy()) {\n\t\tproxyLineEdit->setText(current_profile->\n\t\t\t\t       get_outbound_proxy().encode_noscheme().c_str());\n\t} else {\n\t\tproxyLineEdit->clear();\n\t}\n\tallRequestsCheckBox->setChecked(current_profile->get_all_requests_to_proxy());\n\tallRequestsCheckBox->setEnabled(current_profile->get_use_outbound_proxy());\n\tproxyNonResolvableCheckBox->setChecked(current_profile->get_non_resolvable_to_proxy());\n\tproxyNonResolvableCheckBox->setEnabled(current_profile->get_use_outbound_proxy());\n\t\n\t// VOICE MAIL\n\tvmAddressLineEdit->setText(current_profile->get_mwi_vm_address().c_str());\n\tif (current_profile->get_mwi_solicited()) {\n        mwiTypeComboBox->setCurrentIndex(idxMWISolicited);\n\t\tmwiSolicitedGroupBox->setEnabled(true);\n\t} else {\n        mwiTypeComboBox->setCurrentIndex(idxMWIUnsolicited);\n\t\tmwiSolicitedGroupBox->setEnabled(false);\n\t}\n\tmwiUserLineEdit->setText(current_profile->get_mwi_user().c_str());\n\tmwiServerLineEdit->setText(current_profile->\n\t\t\t\t   get_mwi_server().encode_noscheme().c_str());\n\tmwiViaProxyCheckBox->setChecked(current_profile->get_mwi_via_proxy());\n\tmwiDurationSpinBox->setValue(current_profile->get_mwi_subscription_time());\n\t\n\t// INSTANT MESSAGE\n\timMaxSessionsSpinBox->setValue(current_profile->get_im_max_sessions());\n\tisComposingCheckBox->setChecked(current_profile->get_im_send_iscomposing());\n\t\n\t// PRESENCE\n\tpresPublishCheckBox->setChecked(current_profile->get_pres_publish_startup());\n\tpresPublishTimeSpinBox->setValue(current_profile->get_pres_publication_time());\n\tpresSubscribeTimeSpinBox->setValue(current_profile->get_pres_subscription_time());\n\t\n\t// RTP AUDIO\n\t// Codecs\n\tQStringList allCodecs;\n\tallCodecs.append(labelCodecG711a);\n\tallCodecs.append(labelCodecG711u);\n\tallCodecs.append(labelCodecGSM);\n#ifdef HAVE_SPEEX\n\tallCodecs.append(labelCodecSpeexNb);\n\tallCodecs.append(labelCodecSpeexWb);\n\tallCodecs.append(labelCodecSpeexUwb);\n#endif\n#ifdef HAVE_ILBC\n\tallCodecs.append(labelCodecIlbc);\n#endif\n\tallCodecs.append(labelCodecG722);\n\tallCodecs.append(labelCodecG726_16);\n\tallCodecs.append(labelCodecG726_24);\n\tallCodecs.append(labelCodecG726_32);\n\tallCodecs.append(labelCodecG726_40);\n#ifdef HAVE_BCG729\n\tallCodecs.append(labelCodecG729A);\n#endif\n\tactiveCodecListBox->clear();\n\tlist<t_audio_codec> audio_codecs = current_profile->get_codecs();\n\tfor (list<t_audio_codec>::iterator i = audio_codecs.begin(); i != audio_codecs.end(); i++)\n\t{\n        activeCodecListBox->addItem(codec2label(*i));\n        allCodecs.removeAll(codec2label(*i));\n\t}\n\tavailCodecListBox->clear();\n    if (!allCodecs.empty()) availCodecListBox->addItems(allCodecs);\n\t\n\t// G.711/G.722/G.726 ptime\n\tptimeSpinBox->setValue(current_profile->get_ptime());\n\t\n\t// Codec preference\n\tinFarEndCodecPrefCheckBox->setChecked(current_profile->get_in_obey_far_end_codec_pref());\n\toutFarEndCodecPrefCheckBox->setChecked(current_profile->get_out_obey_far_end_codec_pref());\n\t\n\t// Speex preprocessing and AEC\n\tspxDspVadCheckBox->setChecked(current_profile->get_speex_dsp_vad());\n\tspxDspAgcCheckBox->setChecked(current_profile->get_speex_dsp_agc());\n\tspxDspAecCheckBox->setChecked(current_profile->get_speex_dsp_aec());\n\tspxDspNrdCheckBox->setChecked(current_profile->get_speex_dsp_nrd());\n\tspxDspAgcLevelSpinBox->setValue(current_profile->get_speex_dsp_agc_level());\n\tspxDspAgcLevelTextLabel->setEnabled(current_profile->get_speex_dsp_agc());\n\tspxDspAgcLevelSpinBox->setEnabled(current_profile->get_speex_dsp_agc());\n\t\n\t// Speex ([en/de]coding)\n\tspxVbrCheckBox->setChecked(current_profile->get_speex_bit_rate_type() == BIT_RATE_VBR);\n\tspxDtxCheckBox->setChecked(current_profile->get_speex_dtx());\n\tspxPenhCheckBox->setChecked(current_profile->get_speex_penh());\n\tspxQualitySpinBox->setValue(current_profile->get_speex_quality());\n\tspxComplexitySpinBox->setValue(current_profile->get_speex_complexity());\n\tspxNbPayloadSpinBox->setValue(current_profile->get_speex_nb_payload_type());\n\tspxWbPayloadSpinBox->setValue(current_profile->get_speex_wb_payload_type());\n\tspxUwbPayloadSpinBox->setValue(current_profile->get_speex_uwb_payload_type());\n\t\n\t// iLBC\n\tilbcPayloadSpinBox->setValue(current_profile->get_ilbc_payload_type());\n\t\n\tif (current_profile->get_ilbc_mode() == 20) {\n        ilbcPayloadSizeComboBox->setCurrentIndex(idxIlbcMode20);\n\t} else {\n        ilbcPayloadSizeComboBox->setCurrentIndex(idxIlbcMode30);\n\t}\n\t\n\t// G.726\n\tg72616PayloadSpinBox->setValue(current_profile->get_g726_16_payload_type());\n\tg72624PayloadSpinBox->setValue(current_profile->get_g726_24_payload_type());\n\tg72632PayloadSpinBox->setValue(current_profile->get_g726_32_payload_type());\n\tg72640PayloadSpinBox->setValue(current_profile->get_g726_40_payload_type());\n\t\n\tif (current_profile->get_g726_packing() == G726_PACK_RFC3551) {\n        g726PackComboBox->setCurrentIndex(idxG726PackRfc3551);\n\t} else {\n        g726PackComboBox->setCurrentIndex(idxG726PackAal2);\n\t}\n\t\n\t// DTMF\n\tswitch (current_profile->get_dtmf_transport()) {\n\tcase DTMF_RFC2833:\n        dtmfTransportComboBox->setCurrentIndex(idxDtmfRfc2833);\n\t\tbreak;\n\tcase DTMF_INBAND:\n        dtmfTransportComboBox->setCurrentIndex(idxDtmfInband);\n\t\tbreak;\n\tcase DTMF_INFO:\n        dtmfTransportComboBox->setCurrentIndex(idxDtmfInfo);\n\t\tbreak;\n\tdefault:\n        dtmfTransportComboBox->setCurrentIndex(idxDtmfAuto);\n\t\tbreak;\n\t}\n\t\n\tdtmfPayloadTypeSpinBox->setValue(current_profile->get_dtmf_payload_type());\n\tdtmfDurationSpinBox->setValue(current_profile->get_dtmf_duration());\n\tdtmfPauseSpinBox->setValue(current_profile->get_dtmf_pause());\n\tdtmfVolumeSpinBox->setValue(-(current_profile->get_dtmf_volume()));\n\t\n\t// SIP PROTOCOL\n\tswitch (current_profile->get_hold_variant()) {\n\tcase HOLD_RFC2543:\n        holdVariantComboBox->setCurrentIndex(idxHoldRfc2543);\n\t\tbreak;\n\tdefault:\n        holdVariantComboBox->setCurrentIndex(idxHoldRfc3264);\n\t\tbreak;\n\t}\n\t\n\tmaxForwardsCheckBox->setChecked(current_profile->get_check_max_forwards());\n\tmissingContactCheckBox->setChecked(current_profile->get_allow_missing_contact_reg());\n\tregTimeCheckBox->setChecked(current_profile->get_registration_time_in_contact());\n\tcompactHeadersCheckBox->setChecked(current_profile->get_compact_headers());\n\tmultiValuesListCheckBox->setChecked(\n\t\t\tcurrent_profile->get_encode_multi_values_as_list());\n\tuseDomainInContactCheckBox->setChecked(\n\t\t\tcurrent_profile->get_use_domain_in_contact());\n\tallowSdpChangeCheckBox->setChecked(current_profile->get_allow_sdp_change());\n\tallowRedirectionCheckBox->setChecked(current_profile->get_allow_redirection());\n\taskUserRedirectCheckBox->setEnabled(current_profile->get_allow_redirection());\n\taskUserRedirectCheckBox->setChecked(current_profile->get_ask_user_to_redirect());\n\tmaxRedirectTextLabel->setEnabled(current_profile->get_allow_redirection());\n\tmaxRedirectSpinBox->setEnabled(current_profile->get_allow_redirection());\n\tmaxRedirectSpinBox->setValue(current_profile->get_max_redirections());\n    ext100relComboBox->setCurrentIndex(\n\t\t\text_support2indexComboItem(current_profile->get_ext_100rel()));\n\textReplacesCheckBox->setChecked(current_profile->get_ext_replaces());\n\tallowReferCheckBox->setChecked(current_profile->get_allow_refer());\n\taskUserReferCheckBox->setEnabled(current_profile->get_allow_refer());\n\taskUserReferCheckBox->setChecked(current_profile->get_ask_user_to_refer());\n\trefereeHoldCheckBox->setEnabled(current_profile->get_allow_refer());\n\trefereeHoldCheckBox->setChecked(current_profile->get_referee_hold());\n\treferrerHoldCheckBox->setChecked(current_profile->get_referrer_hold());\n\trefreshReferSubCheckBox->setChecked(current_profile->get_auto_refresh_refer_sub());\n\treferAorCheckBox->setChecked(current_profile->get_attended_refer_to_aor());\n\ttransferConsultInprogCheckBox->setChecked(\n\t\t\tcurrent_profile->get_allow_transfer_consultation_inprog());\n\tpPreferredIdCheckBox->setChecked(current_profile->get_send_p_preferred_id());\n\tpAssertedIdCheckBox->setChecked(current_profile->get_send_p_asserted_id());\n\t\n\t// Transport/NAT\n\tswitch (current_profile->get_sip_transport()) {\n\tcase SIP_TRANS_UDP:\n        sipTransportComboBox->setCurrentIndex(idxSipTransportUDP);\n\t\tbreak;\n\tcase SIP_TRANS_TCP:\n        sipTransportComboBox->setCurrentIndex(idxSipTransportTCP);\n\t\tbreak;\n\tdefault:\n        sipTransportComboBox->setCurrentIndex(idxSipTransportAuto);\n\t\tbreak;\n\t}\n\t\n\tudpThresholdSpinBox->setValue(current_profile->get_sip_transport_udp_threshold());\n\tudpThresholdTextLabel->setEnabled(current_profile->get_sip_transport() == SIP_TRANS_AUTO);\n\tudpThresholdSpinBox->setEnabled(current_profile->get_sip_transport() == SIP_TRANS_AUTO);\n\t\n\tif (current_profile->get_use_nat_public_ip()) {\n\t\tnatStaticRadioButton->setChecked(true);\n\t} else if (current_profile->get_use_stun()) {\n\t\tnatStunRadioButton->setChecked(true);\n\t} else {\n\t\tnatNoneRadioButton->setChecked(true);\n\t}\n\t\n\tpublicIPTextLabel->setEnabled(current_profile->get_use_nat_public_ip());\n\tpublicIPLineEdit->setEnabled(current_profile->get_use_nat_public_ip());\n\tpublicIPLineEdit->setText(current_profile->get_nat_public_ip().c_str());\n\tstunServerTextLabel->setEnabled(current_profile->get_use_stun());\n\tstunServerLineEdit->setEnabled(current_profile->get_use_stun());\n\tstunServerLineEdit->setText(current_profile->get_stun_server().\n\t\t\t\t    encode_noscheme().c_str());\n\tpersistentTcpCheckBox->setChecked(current_profile->get_persistent_tcp());\n\tpersistentTcpCheckBox->setEnabled(current_profile->get_sip_transport() == SIP_TRANS_TCP);\n\tnatKeepaliveCheckBox->setChecked(current_profile->get_enable_nat_keepalive());\n\tnatKeepaliveCheckBox->setDisabled(current_profile->get_use_stun());\n\t\n\t// ADDRESS FORMAT\n\tdisplayTelUserCheckBox->setChecked(current_profile->get_display_useronly_phone());\n\tnumericalUserIsTelCheckBox->setChecked(\n\t\t\tcurrent_profile->get_numerical_user_is_phone());\n\tremoveSpecialCheckBox->setChecked(\n\t\t\tcurrent_profile->get_remove_special_phone_symbols());\n\tspecialLineEdit->setText(current_profile->get_special_phone_symbols().c_str());\n\tuseTelUriCheckBox->setChecked(current_profile->get_use_tel_uri_for_phone());\n\t\n\tlist<t_number_conversion> conversions = current_profile->get_number_conversions();\n\n    conversionListView->horizontalHeader()->resizeSection(0, 200);\n    conversionListView->setRowCount(conversions.size());\n\n    int j = 0;\n    for (list<t_number_conversion>::iterator i = conversions.begin(); i != conversions.end(); i++, j++)\n\t{\n        QTableWidgetItem* item = new QTableWidgetItem(QString::fromStdString(i->re));\n        conversionListView->setItem(j, 0, item);\n        item = new QTableWidgetItem(QString::fromStdString(i->fmt));\n        conversionListView->setItem(j, 1, item);\n\t}\n\t\n\t// TIMERS\n\ttmrNoanswerSpinBox->setValue(current_profile->get_timer_noanswer());\n\ttmrNatKeepaliveSpinBox->setValue(current_profile->get_timer_nat_keepalive());\n\t\n\t// RING TONES\n\tringtoneLineEdit->setText(current_profile->get_ringtone_file().c_str());\n\tringbackLineEdit->setText(current_profile->get_ringback_file().c_str());\n\t\n\t// SCRIPTS\n\tincomingCallScriptLineEdit->setText(current_profile->get_script_incoming_call().c_str());\n\tinCallAnsweredLineEdit->setText(current_profile->get_script_in_call_answered().c_str());\n\tinCallFailedLineEdit->setText(current_profile->get_script_in_call_failed().c_str());\n\toutCallLineEdit->setText(current_profile->get_script_outgoing_call().c_str());\n\toutCallAnsweredLineEdit->setText(current_profile->get_script_out_call_answered().c_str());\n\toutCallFailedLineEdit->setText(current_profile->get_script_out_call_failed().c_str());\n\tlocalReleaseLineEdit->setText(current_profile->get_script_local_release().c_str());\n\tremoteReleaseLineEdit->setText(current_profile->get_script_remote_release().c_str());\n\t\n\t// Security\n\tzrtpEnabledCheckBox->setChecked(current_profile->get_zrtp_enabled());\n\tzrtpSettingsGroupBox->setEnabled(current_profile->get_zrtp_enabled());\n\tzrtpSendIfSupportedCheckBox->setChecked(current_profile->get_zrtp_send_if_supported());\n\tzrtpSdpCheckBox->setChecked(current_profile->get_zrtp_sdp());\n\tzrtpGoClearWarningCheckBox->setChecked(current_profile->get_zrtp_goclear_warning());\n}\n\nvoid UserProfileForm::initProfileList(list<t_user *> profiles, QString show_profile_name)\n{\n\tprofile_list = profiles;\n\t\n\t// Initialize user profile combo box\n\tcurrent_profile_idx = -1;\n\tprofileComboBox->clear();\n\t\n\tt_user *show_profile = NULL;\n\tint show_idx = 0;\n\tint idx = 0;\n\tfor (list<t_user *>::iterator i = profile_list.begin(); i != profile_list.end(); i++) {\n        profileComboBox->addItem((*i)->get_profile_name().c_str());\n\t\tif (show_profile_name == (*i)->get_profile_name().c_str()) {\n\t\t\tshow_idx = idx;\n\t\t\tshow_profile = *i;\n\t\t}\n\t\tidx++;\n\t}\n\t\n\tprofileComboBox->setEnabled(profile_list.size() > 1);\n\tcurrent_profile_idx = show_idx;\n\t\n\tif (show_profile == NULL) {\n\t\tcurrent_profile = profile_list.front();\n\t} else {\n\t\tcurrent_profile = show_profile;\n\t}\n    profileComboBox->setCurrentIndex(current_profile_idx);\n}\n\n// Show the form\nvoid UserProfileForm::show(list<t_user *> profiles, QString show_profile)\n{\n\tmap_last_cat.clear();\n\tinitProfileList(profiles, show_profile);\n\tpopulate();\n\t\n\t// Show form\n\tQDialog::show();\n}\n\n// Modal execution\nint UserProfileForm::exec(list<t_user *> profiles, QString show_profile)\n{\n\tmap_last_cat.clear();\n\tinitProfileList(profiles, show_profile);\n\tpopulate();\n\treturn QDialog::exec();\n}\n\nbool UserProfileForm::check_dynamic_payload(QSpinBox *spb,\n                        QList<int> &checked_list)\n{\n\tif (checked_list.contains(spb->value())) {\n        categoryListBox->setCurrentRow(idxCatRtpAudio);\n        settingsWidgetStack->setCurrentWidget(pageRtpAudio);\n\t\tQString msg = tr(\"Dynamic payload type %1 is used more than once.\").arg(spb->value());\n        ((t_gui *)ui)->cb_show_msg(this, msg.toStdString(), MSG_CRITICAL);\n\t\tspb->setFocus();\n\t\treturn false;\n\t}\n\t\n    checked_list << spb->value();\n\treturn true;\n}\n\nlist<t_number_conversion> UserProfileForm::get_number_conversions()\n{\n\tlist<t_number_conversion> conversions;\n\n    for (int i = 0; i < conversionListView->rowCount(); i++)\n    {\n        QTableWidgetItem* item;\n\t\tt_number_conversion c;\n\t\t\n\t\ttry {\n            item = conversionListView->item(i, 0);\n            c.re.assign(item->text().toStdString());\n            item = conversionListView->item(i, 1);\n            c.fmt = item->text().toStdString();\n\t\t\tconversions.push_back(c);\n        } catch (std::regex_error) {\n\t\t\t// Should never happen as validity has been\n\t\t\t// checked already. Just being defensive here.\n\t\t}\n\t}\n\t\n\treturn conversions;\n}\n\t    \nbool UserProfileForm::validateValues()\n{\n\tQString s;\n\t\n\t// Validity check user page\n\t// SIP username is mandatory\n\tif (usernameLineEdit->text().isEmpty()) {\n        categoryListBox->setCurrentRow(idxCatUser);\n        settingsWidgetStack->setCurrentWidget(pageUser);\n        ((t_gui *)ui)->cb_show_msg(this, tr(\"You must fill in a user name for your SIP account.\").toStdString(),\n\t\t\t\tMSG_CRITICAL);\n\t\tusernameLineEdit->setFocus();\n\t\treturn false;\n\t}\n\t\n\t// SIP user domain is mandatory\n\tif (domainLineEdit->text().isEmpty()) {\n        categoryListBox->setCurrentRow(idxCatUser);\n        settingsWidgetStack->setCurrentWidget(pageUser);\n\t\t((t_gui *)ui)->cb_show_msg(this, tr(\n\t\t\t\t\"You must fill in a domain name for your SIP account.\\n\"\n\t\t\t\t\"This could be the hostname or IP address of your PC \"\n                \"if you want direct PC to PC dialing.\").toStdString(),\n\t\t\t\tMSG_CRITICAL);\n\t\tdomainLineEdit->setFocus();\n\t\treturn false;\n\t}\n\t\n\t// Check validity of domain\n\ts = USER_SCHEME;\n\ts.append(':').append(domainLineEdit->text());\n    t_url u_domain(s.toStdString());\n\tif (!u_domain.is_valid() || u_domain.get_user() != \"\") {\n        categoryListBox->setCurrentRow(idxCatUser);\n        settingsWidgetStack->setCurrentWidget(pageUser);\n        ((t_gui *)ui)->cb_show_msg(this,  tr(\"Invalid domain.\").toStdString(), MSG_CRITICAL);\n\t\tdomainLineEdit->setFocus();\n\t\treturn false;\n\t}\n\t\n\t// Check validity of user\n\ts = USER_SCHEME;\n\ts.append(':').append(usernameLineEdit->text()).append('@');\n\ts.append(domainLineEdit->text());\n    t_url u_user_domain(s.toStdString());\n\tif (!u_user_domain.is_valid()) {\n        categoryListBox->setCurrentRow(idxCatUser);\n        settingsWidgetStack->setCurrentWidget(pageUser);\n        ((t_gui *)ui)->cb_show_msg(this,  tr(\"Invalid user name.\").toStdString(), MSG_CRITICAL);\n\t\tusernameLineEdit->setFocus();\n\t\treturn false;\n\t}\n\t\n\t// Registrar\n\tif (!registrarLineEdit->text().isEmpty()) {\n\t\ts = USER_SCHEME;\n\t\ts.append(':').append(registrarLineEdit->text());\n        t_url u(s.toStdString());\n\t\tif (!u.is_valid() || u.get_user() != \"\") {\n            categoryListBox->setCurrentRow(idxCatSipServer);\n            settingsWidgetStack->setCurrentWidget(pageSipServer);\n            ((t_gui *)ui)->cb_show_msg(this, tr(\"Invalid value for registrar.\").toStdString(),\n\t\t\t\t\t\t   MSG_CRITICAL);\n\t\t\tregistrarLineEdit->setFocus();\n\t\t\tregistrarLineEdit->selectAll();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Outbound proxy\n\tif (useProxyCheckBox->isChecked()) {\n\t\ts = USER_SCHEME;\n\t\ts.append(':').append(proxyLineEdit->text());\n        t_url u(s.toStdString());\n\t\tif (!u.is_valid() || u.get_user() != \"\") {\n            categoryListBox->setCurrentRow(idxCatSipServer);\n            settingsWidgetStack->setCurrentWidget(pageSipServer);\n            ((t_gui *)ui)->cb_show_msg(this, tr(\"Invalid value for outbound proxy.\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tproxyLineEdit->setFocus();\n\t\t\tproxyLineEdit->selectAll();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\n\t// Validity check voice mail page\n    if (mwiTypeComboBox->currentIndex() == idxMWISolicited) {\n\t\t// Mailbox user name is mandatory\n\t\tif (mwiUserLineEdit->text().isEmpty()) {\n            categoryListBox->setCurrentRow(idxCatVoiceMail);\n            settingsWidgetStack->setCurrentWidget(pageVoiceMail);\n\t\t\t((t_gui *)ui)->cb_show_msg(this, \n                    tr(\"You must fill in a mailbox user name.\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tmwiUserLineEdit->setFocus();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Mailbox server is mandatory\n\t\tif (mwiServerLineEdit->text().isEmpty()) {\n            categoryListBox->setCurrentRow(idxCatVoiceMail);\n            settingsWidgetStack->setCurrentWidget(pageVoiceMail);\n\t\t\t((t_gui *)ui)->cb_show_msg(this, \n                    tr(\"You must fill in a mailbox server\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tmwiServerLineEdit->setFocus();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Check validity of mailbox server\n\t\ts = USER_SCHEME;\n\t\ts.append(':').append(mwiServerLineEdit->text());\n        t_url u_server(s.toStdString());\n\t\tif (!u_server.is_valid() || u_server.get_user() != \"\") {\n            categoryListBox->setCurrentRow(idxCatVoiceMail);\n            settingsWidgetStack->setCurrentWidget(pageVoiceMail);\n            ((t_gui *)ui)->cb_show_msg(this,  tr(\"Invalid mailbox server.\").toStdString(),\n\t\t\t\t\t\t   MSG_CRITICAL);\n\t\t\tmwiServerLineEdit->setFocus();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Check validity of mailbox user name\n\t\ts = USER_SCHEME;\n\t\ts.append(':').append(mwiUserLineEdit->text()).append('@');\n\t\ts.append(mwiServerLineEdit->text());\n        t_url u_user_server(s.toStdString());\n\t\tif (!u_user_server.is_valid()) {\n            categoryListBox->setCurrentRow(idxCatVoiceMail);\n            settingsWidgetStack->setCurrentWidget(pageVoiceMail);\n            ((t_gui *)ui)->cb_show_msg(this,  tr(\"Invalid mailbox user name.\").toStdString(),\n\t\t\t\t\t\t   MSG_CRITICAL);\n\t\t\tmwiUserLineEdit->setFocus();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// NAT public IP\n\tif (natStaticRadioButton->isChecked()) {\n\t\tif (publicIPLineEdit->text().isEmpty()){\n            categoryListBox->setCurrentRow(idxCatNat);\n            settingsWidgetStack->setCurrentWidget(pageNat);\n            ((t_gui *)ui)->cb_show_msg(this, tr(\"Value for public IP address missing.\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tpublicIPLineEdit->setFocus();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Check for double RTP dynamic payload types\n    QList<int> checked_types;\n\tif (!check_dynamic_payload(spxNbPayloadSpinBox, checked_types) ||\n\t    !check_dynamic_payload(spxWbPayloadSpinBox, checked_types) ||\n\t    !check_dynamic_payload(spxUwbPayloadSpinBox, checked_types))\n\t{\n        rtpAudioTabWidget->setCurrentWidget(tabSpeex);\n\t\treturn false;\n\t}\n\t\n\tif (!check_dynamic_payload(ilbcPayloadSpinBox, checked_types)) {\n        rtpAudioTabWidget->setCurrentWidget(tabIlbc);\n\t\treturn false;\n\t}\n\t\n\tif (!check_dynamic_payload(g72616PayloadSpinBox, checked_types) ||\n\t    !check_dynamic_payload(g72624PayloadSpinBox, checked_types) ||\n\t    !check_dynamic_payload(g72632PayloadSpinBox, checked_types) ||\n\t    !check_dynamic_payload(g72640PayloadSpinBox, checked_types)) {\n        rtpAudioTabWidget->setCurrentWidget(tabG726);\n\t\treturn false;\n\t}\n\t\n\tif (!check_dynamic_payload(dtmfPayloadTypeSpinBox, checked_types)) {\n        rtpAudioTabWidget->setCurrentWidget(tabDtmf);\n\t\treturn false;\n\t}\n\t\n\t// STUN server\n\tif (natStunRadioButton->isChecked()) {\n\t\ts = \"stun:\";\n\t\ts.append(stunServerLineEdit->text());\n        t_url u(s.toStdString());\n\t\tif (!u.is_valid() || u.get_user() != \"\") {\n            categoryListBox->setCurrentRow(idxCatNat);\n            settingsWidgetStack->setCurrentWidget(pageNat);\n            ((t_gui *)ui)->cb_show_msg(this, tr(\"Invalid value for STUN server.\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tstunServerLineEdit->setFocus();\n\t\t\tstunServerLineEdit->selectAll();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Clear outbound proxy if not used\n\tif (!useProxyCheckBox->isChecked()) {\n\t\tproxyLineEdit->clear();\n\t}\n\t\n\t// Clear solicited MWI settings if unsolicited MWI is used\n    if (mwiTypeComboBox->currentIndex() == idxMWIUnsolicited) {\n\t\tt_user user_default;\n\t\tmwiUserLineEdit->clear();\n\t\tmwiServerLineEdit->clear();\n\t\tmwiViaProxyCheckBox->setChecked(user_default.get_mwi_via_proxy());\n\t\tmwiDurationSpinBox->setValue(user_default.get_mwi_subscription_time());\n\t}\n\t\n\t// Clear NAT public IP if not used\n\tif (!natStaticRadioButton->isChecked()) {\n\t\tpublicIPLineEdit->clear();\n\t}\n\t\n\t// Clear STUN server if not used\n\tif (!natStunRadioButton->isChecked()) {\n\t\tstunServerLineEdit->clear();\n\t}\n\t\n\t// Set all values in the current_profile object\n\t// USER\n    if (current_profile->get_name() != usernameLineEdit->text().toStdString() ||\n        current_profile->get_display(false) != displayLineEdit->text().toStdString() ||\n        current_profile->get_domain() != domainLineEdit->text().toStdString())\n\t{\n        current_profile->set_display(displayLineEdit->text().toStdString());\n        current_profile->set_name(usernameLineEdit->text().toStdString());\n        current_profile->set_domain (domainLineEdit->text().toStdString());\n\t\temit sipUserChanged(current_profile);\n\t}\n\t\n    current_profile->set_organization(organizationLineEdit->text().toStdString());\n\t\n\tuint8 new_aka_op[AKA_OPLEN];\n\tuint8 new_aka_amf[AKA_AMFLEN];\n\tuint8 current_aka_op[AKA_OPLEN];\n\tuint8 current_aka_amf[AKA_AMFLEN];\n\t\n    hex2binary(padleft(authAkaOpLineEdit->text().toStdString(), '0', 32), new_aka_op);\n    hex2binary(padleft(authAkaAmfLineEdit->text().toStdString(), '0', 4), new_aka_amf);\n\tcurrent_profile->get_auth_aka_op(current_aka_op);\n\tcurrent_profile->get_auth_aka_amf(current_aka_amf);\n\t\n    if (current_profile->get_auth_realm() != authRealmLineEdit->text().toStdString() ||\n        current_profile->get_auth_name() != authNameLineEdit->text().toStdString() ||\n        current_profile->get_auth_pass() != authPasswordLineEdit->text().toStdString() ||\n\t    memcmp(current_aka_op, new_aka_op, AKA_OPLEN) != 0 ||\n\t    memcmp(current_aka_amf, new_aka_amf, AKA_AMFLEN) != 0)\n\t{\n\t\temit authCredentialsChanged(current_profile,\n\t\t\t\t\tcurrent_profile->get_auth_realm());\n\t\t\n        current_profile->set_auth_realm(authRealmLineEdit->text().toStdString());\n        current_profile->set_auth_name(authNameLineEdit->text().toStdString());\n        current_profile->set_auth_pass(authPasswordLineEdit->text().toStdString());\n\t\tcurrent_profile->set_auth_aka_op(new_aka_op);\n\t\tcurrent_profile->set_auth_aka_amf(new_aka_amf);\n\t}\n\n\t// SIP SERVER\n\tcurrent_profile->set_use_registrar(!registrarLineEdit->text().isEmpty());\n\ts = USER_SCHEME;\n\ts.append(':').append(registrarLineEdit->text());\n    current_profile->set_registrar(t_url(s.toStdString()));\n\tcurrent_profile->set_registration_time(expirySpinBox->value());\n\tcurrent_profile->set_register_at_startup(regAtStartupCheckBox->isChecked());\n\tcurrent_profile->set_reg_add_qvalue(regAddQvalueCheckBox->isChecked());\n    current_profile->set_reg_qvalue(regQvalueLineEdit->text().toDouble());\n\t\n\tcurrent_profile->set_use_outbound_proxy(useProxyCheckBox->isChecked());\n\ts = USER_SCHEME;\n\ts.append(':').append(proxyLineEdit->text());\n    current_profile->set_outbound_proxy(t_url(s.toStdString()));\n\tcurrent_profile->set_all_requests_to_proxy(allRequestsCheckBox->isChecked());\n\tcurrent_profile->set_non_resolvable_to_proxy(\n\t\t\tproxyNonResolvableCheckBox->isChecked());\n\t\n\t// VOICE MAIL\n    current_profile->set_mwi_vm_address(vmAddressLineEdit->text().toStdString());\n\t\n\tbool mustTriggerMWISubscribe = false;\n    bool mwiSolicited = (mwiTypeComboBox->currentIndex() == idxMWISolicited);\n\tif (mwiSolicited) {\n\t\tif (!current_profile->get_mwi_solicited()) {\n\t\t\t// Solicited MWI now enabled. Subscribe after all MWI\n\t\t\t// settings have been changed.\n\t\t\tmustTriggerMWISubscribe = true;\n\t\t} else {\n\t\t\ts = USER_SCHEME;\n\t\t\ts.append(':').append(mwiServerLineEdit->text());\n            if (mwiUserLineEdit->text().toStdString() != current_profile->get_mwi_user() ||\n                t_url(s.toStdString()) != current_profile->get_mwi_server() ||\n\t\t\t    mwiViaProxyCheckBox->isChecked() != current_profile->get_mwi_via_proxy())\n\t\t\t{\n\t\t\t\t// Solicited MWI settings changed. Trigger unsubscribe\n\t\t\t\t// of current MWI subscription.\n\t\t\t\temit mwiChangeUnsubscribe(current_profile);\n\t\t\t\t\n\t\t\t\t// Subscribe after the settings have been changed.\n\t\t\t\tmustTriggerMWISubscribe = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (current_profile->get_mwi_solicited()) {\n\t\t\t// MWI type changes to unsolicited. Trigger unsubscribe of\n\t\t\t// current MWI subscription.\n\t\t\temit mwiChangeUnsubscribe(current_profile);\n\t\t}\n\t}\n\t\n\tcurrent_profile->set_mwi_solicited(mwiSolicited);\n    current_profile->set_mwi_user(mwiUserLineEdit->text().toStdString());\n\ts = USER_SCHEME;\n\ts.append(':').append(mwiServerLineEdit->text());\n    current_profile->set_mwi_server(t_url(s.toStdString()));\n\tcurrent_profile->set_mwi_via_proxy(mwiViaProxyCheckBox->isChecked());\n\tcurrent_profile->set_mwi_subscription_time(mwiDurationSpinBox->value());\n\t\n\tif (mustTriggerMWISubscribe) {\n\t\temit mwiChangeSubscribe(current_profile);\n\t}\n\t\n\t// INSTANT MESSAGE\n\tcurrent_profile->set_im_max_sessions(imMaxSessionsSpinBox->value());\n\tcurrent_profile->set_im_send_iscomposing(isComposingCheckBox->isChecked());\n\t\n\t// PRESENCE\n\tcurrent_profile->set_pres_publish_startup(presPublishCheckBox->isChecked());\n\tcurrent_profile->set_pres_publication_time(presPublishTimeSpinBox->value());\n\tcurrent_profile->set_pres_subscription_time(presSubscribeTimeSpinBox->value());\n\t\n\t// RTP AUDIO\n\t// Codecs\n\tlist<t_audio_codec> audio_codecs;\n\tfor (int i = 0; i < activeCodecListBox->count(); i++) {\n        audio_codecs.push_back(label2codec(activeCodecListBox->item(i)->text()));\n\t}\n\tcurrent_profile->set_codecs(audio_codecs);\n\t\n\t// G.711/G.722/G.726 ptime\n\tcurrent_profile->set_ptime(ptimeSpinBox->value());\n\t\n\t// Codec preference\n\tcurrent_profile->set_in_obey_far_end_codec_pref(inFarEndCodecPrefCheckBox->isChecked());\n\tcurrent_profile->set_out_obey_far_end_codec_pref(outFarEndCodecPrefCheckBox->isChecked());\n\t\n\t// Speex preprocessing & AEC\n \tcurrent_profile->set_speex_dsp_vad(spxDspVadCheckBox->isChecked());\n\tcurrent_profile->set_speex_dsp_agc(spxDspAgcCheckBox->isChecked());\n\tcurrent_profile->set_speex_dsp_aec(spxDspAecCheckBox->isChecked());\n\tcurrent_profile->set_speex_dsp_nrd(spxDspNrdCheckBox->isChecked());\n\tcurrent_profile->set_speex_dsp_agc_level(spxDspAgcLevelSpinBox->value());\n\n\t// Speex ([en/de]coding)\n\tcurrent_profile->set_speex_bit_rate_type((spxVbrCheckBox->isChecked() ? BIT_RATE_VBR : BIT_RATE_CBR));\n\tcurrent_profile->set_speex_dtx(spxDtxCheckBox->isChecked());\n\tcurrent_profile->set_speex_penh(spxPenhCheckBox->isChecked());\n\tcurrent_profile->set_speex_quality(spxQualitySpinBox->value());\n\tcurrent_profile->set_speex_complexity(spxComplexitySpinBox->value());\n\tcurrent_profile->set_speex_nb_payload_type(spxNbPayloadSpinBox->value());\n\tcurrent_profile->set_speex_wb_payload_type(spxWbPayloadSpinBox->value());\n\tcurrent_profile->set_speex_uwb_payload_type(spxUwbPayloadSpinBox->value());\n\t\n\t// iLBC\n\tcurrent_profile->set_ilbc_payload_type(ilbcPayloadSpinBox->value());\n    switch (ilbcPayloadSizeComboBox->currentIndex()) {\n\tcase idxIlbcMode20:\n\t\tcurrent_profile->set_ilbc_mode(20);\n\t\tbreak;\n\tdefault:\n\t\tcurrent_profile->set_ilbc_mode(30);\n\t\tbreak;\n\t}\n\t\n\t// G726\n\tcurrent_profile->set_g726_16_payload_type(g72616PayloadSpinBox->value());\n\tcurrent_profile->set_g726_24_payload_type(g72624PayloadSpinBox->value());\n\tcurrent_profile->set_g726_32_payload_type(g72632PayloadSpinBox->value());\n\tcurrent_profile->set_g726_40_payload_type(g72640PayloadSpinBox->value());\n\t\n    switch (g726PackComboBox->currentIndex()) {\n\tcase idxG726PackRfc3551:\n\t\tcurrent_profile->set_g726_packing(G726_PACK_RFC3551);\n\t\tbreak;\n\tdefault:\n\t\tcurrent_profile->set_g726_packing(G726_PACK_AAL2);\n\t\tbreak;\n\t}\n\t\n\t// DTMF\n    switch (dtmfTransportComboBox->currentIndex()) {\n\tcase idxDtmfRfc2833:\n\t\tcurrent_profile->set_dtmf_transport(DTMF_RFC2833);\n\t\tbreak;\n\tcase idxDtmfInband:\n\t\tcurrent_profile->set_dtmf_transport(DTMF_INBAND);\n\t\tbreak;\n\tcase idxDtmfInfo:\n\t\tcurrent_profile->set_dtmf_transport(DTMF_INFO);\n\t\tbreak;\n\tdefault:\n\t\tcurrent_profile->set_dtmf_transport(DTMF_AUTO);\n\t\tbreak;\n\t}\n\t\n\tcurrent_profile->set_dtmf_payload_type(dtmfPayloadTypeSpinBox->value());\n\tcurrent_profile->set_dtmf_duration(dtmfDurationSpinBox->value());\n\tcurrent_profile->set_dtmf_pause(dtmfPauseSpinBox->value());\n\tcurrent_profile->set_dtmf_volume(-(dtmfVolumeSpinBox->value()));\n\t\n\t// SIP PROTOCOL\n    switch (holdVariantComboBox->currentIndex()) {\n\tcase idxHoldRfc2543:\n\t\tcurrent_profile->set_hold_variant(HOLD_RFC2543);\n\t\tbreak;\n\tdefault:\n\t\tcurrent_profile->set_hold_variant(HOLD_RFC3264);\n\t\tbreak;\n\t}\n\t\n\tcurrent_profile->set_check_max_forwards(maxForwardsCheckBox->isChecked());\n\tcurrent_profile->set_allow_missing_contact_reg(missingContactCheckBox->isChecked());\n\tcurrent_profile->set_registration_time_in_contact(regTimeCheckBox->isChecked());\n\tcurrent_profile->set_compact_headers(compactHeadersCheckBox->isChecked());\n\tcurrent_profile->set_encode_multi_values_as_list(\n\t\t\tmultiValuesListCheckBox->isChecked());\n\tcurrent_profile->set_use_domain_in_contact(\n\t\t\tuseDomainInContactCheckBox->isChecked());\n\tcurrent_profile->set_allow_sdp_change(allowSdpChangeCheckBox->isChecked());\n\tcurrent_profile->set_allow_redirection(allowRedirectionCheckBox->isChecked());\n\tcurrent_profile->set_ask_user_to_redirect(askUserRedirectCheckBox->isChecked());\n\tcurrent_profile->set_max_redirections(maxRedirectSpinBox->value());\n\tcurrent_profile->set_ext_100rel(indexComboItem2ext_support(\n            ext100relComboBox->currentIndex()));\n\tcurrent_profile->set_ext_replaces(extReplacesCheckBox->isChecked());\n\tcurrent_profile->set_allow_refer(allowReferCheckBox->isChecked());\n\tcurrent_profile->set_ask_user_to_refer(askUserReferCheckBox->isChecked());\n\tcurrent_profile->set_referee_hold(refereeHoldCheckBox->isChecked());\n\tcurrent_profile->set_referrer_hold(referrerHoldCheckBox->isChecked());\n\tcurrent_profile->set_auto_refresh_refer_sub(refreshReferSubCheckBox->isChecked());\n\tcurrent_profile->set_attended_refer_to_aor(referAorCheckBox->isChecked());\n\tcurrent_profile->set_allow_transfer_consultation_inprog(\n\t\t\ttransferConsultInprogCheckBox->isChecked());\n\tcurrent_profile->set_send_p_preferred_id(pPreferredIdCheckBox->isChecked());\n\tcurrent_profile->set_send_p_asserted_id(pAssertedIdCheckBox->isChecked());\n\t\n\t// Transport/NAT\n    switch (sipTransportComboBox->currentIndex()) {\n\tcase idxSipTransportUDP:\n\t\tcurrent_profile->set_sip_transport(SIP_TRANS_UDP);\n\t\tbreak;\n\tcase idxSipTransportTCP:\n\t\tcurrent_profile->set_sip_transport(SIP_TRANS_TCP);\n\t\tbreak;\n\tdefault:\n\t\tcurrent_profile->set_sip_transport(SIP_TRANS_AUTO);\n\t\tbreak;\n\t}\n\t\n\tcurrent_profile->set_sip_transport_udp_threshold(udpThresholdSpinBox->value());\n\t\n\tcurrent_profile->set_use_nat_public_ip(natStaticRadioButton->isChecked());\n    current_profile->set_nat_public_ip(publicIPLineEdit->text().toStdString());\n\tcurrent_profile->set_use_stun(natStunRadioButton->isChecked());\n\t\n    if (current_profile->get_stun_server().encode_noscheme() != stunServerLineEdit->text().toStdString() ||\n\t    current_profile->get_enable_nat_keepalive() != natKeepaliveCheckBox->isChecked()) \n\t{\n\t\ts = \"stun:\";\n\t\ts.append(stunServerLineEdit->text());\n        current_profile->set_stun_server(t_url(s.toStdString()));\n\t\tcurrent_profile->set_enable_nat_keepalive(natKeepaliveCheckBox->isChecked());\n\t\temit stunServerChanged(current_profile);\n\t}\n\t\n\tcurrent_profile->set_persistent_tcp(persistentTcpCheckBox->isChecked());\n\t\n\t// ADDRESS FORMAT\n\tcurrent_profile->set_display_useronly_phone(\n\t\t\tdisplayTelUserCheckBox->isChecked());\n\tcurrent_profile->set_numerical_user_is_phone(\n\t\t\tnumericalUserIsTelCheckBox->isChecked());\n\tcurrent_profile->set_remove_special_phone_symbols(\n\t\t\tremoveSpecialCheckBox->isChecked());\n\tcurrent_profile->set_special_phone_symbols(\n            specialLineEdit->text().trimmed().toStdString());\n\tcurrent_profile->set_number_conversions(get_number_conversions());\n\tcurrent_profile->set_use_tel_uri_for_phone(useTelUriCheckBox->isChecked());\n\t\n\t// TIMERS\n\tcurrent_profile->set_timer_noanswer(tmrNoanswerSpinBox->value());\n\tcurrent_profile->set_timer_nat_keepalive(tmrNatKeepaliveSpinBox->value());\n\t\n\t// RING TONES\n    current_profile->set_ringtone_file(ringtoneLineEdit->text().trimmed().toStdString());\n    current_profile->set_ringback_file(ringbackLineEdit->text().trimmed().toStdString());\n\t\n\t// SCRIPTS\n\tcurrent_profile->set_script_incoming_call(incomingCallScriptLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_in_call_answered(inCallAnsweredLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_in_call_failed(inCallFailedLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_outgoing_call(outCallLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_out_call_answered(outCallAnsweredLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_out_call_failed(outCallFailedLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_local_release(localReleaseLineEdit->\n                    text().trimmed().toStdString());\n\tcurrent_profile->set_script_remote_release(remoteReleaseLineEdit->\n                    text().trimmed().toStdString());\n\t\n\t// Security\n\tcurrent_profile->set_zrtp_enabled(zrtpEnabledCheckBox->isChecked());\n\tcurrent_profile->set_zrtp_send_if_supported(zrtpSendIfSupportedCheckBox->isChecked());\n\tcurrent_profile->set_zrtp_sdp(zrtpSdpCheckBox->isChecked());\n\tcurrent_profile->set_zrtp_goclear_warning(zrtpGoClearWarningCheckBox->isChecked());\n\t\n\t// Save user config\n\tstring error_msg;\n\tif (!current_profile->write_config(current_profile->get_filename(), error_msg)) {\n\t\t// Failed to write config file\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_CRITICAL);\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nvoid UserProfileForm::validate() {\n\tif (validateValues()) {\n\t\temit success();\n\t\taccept();\n\t}\n}\n\n// User wants to change to another profile\nvoid UserProfileForm::changeProfile(const QString &profileName) {\n\tif (current_profile_idx == -1) {\n\t\t// Initializing combo box\n\t\treturn;\n\t}\n\t\n\t// Make the current profile permanent.\n\tif (!validateValues()) {\n\t\t// Current values are not valid.\n\t\t// Do not change to the new profile.\n        profileComboBox->setCurrentIndex(current_profile_idx);\n\t\treturn;\n\t}\n\t\n\t// Store the current viewed category\n    map_last_cat[current_profile] = categoryListBox->currentRow();\n\t\n\t// Change to new profile.\n\tfor (list<t_user *>::iterator i = profile_list.begin(); i != profile_list.end(); i++) {\n        if ((*i)->get_profile_name() == profileName.toStdString()) {\n\t\t\tcurrent_profile = *i;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n    current_profile_idx = profileComboBox->currentIndex();\n\tpopulate();\n\t\n\t// Restore last viewed category\n\tint idxCat = map_last_cat[current_profile];\n    categoryListBox->setCurrentRow(idxCat);\n\tshowCategory(idxCat);\n}\n\nvoid UserProfileForm::chooseFile(QLineEdit *qle, const QString &filter, const QString &caption) \n{\n\tQString file = QFileDialog::getOpenFileName(this, caption,\n\t\t\t((t_gui *)ui)->get_last_file_browse_path(),\n\t\t\tfilter);\n\tif (!file.isEmpty()) {\n\t\tqle->setText(file);\n        ((t_gui *)ui)->set_last_file_browse_path(QFileInfo(file).absolutePath());\n\t}\t\n}\n\nvoid UserProfileForm::chooseRingtone()\n{\n\tchooseFile(ringtoneLineEdit, tr(\"Ring tones\", \"Description of .wav files in file dialog\").append(\" (*.wav)\"), tr(\"Choose ring tone\"));\n}\n\nvoid UserProfileForm::chooseRingback()\n{\n\tchooseFile(ringbackLineEdit, tr(\"Ring back tones\", \"Description of .wav files in file dialog\").append(\" (*.wav)\"), \"Choose ring back tone\");\n}\n\nvoid UserProfileForm::chooseIncomingCallScript()\n{\n\tchooseFile(incomingCallScriptLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose incoming call script\"));\n}\n\nvoid UserProfileForm::chooseInCallAnsweredScript()\n{\n\tchooseFile(inCallAnsweredLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose incoming call answered script\"));\n}\n\nvoid UserProfileForm::chooseInCallFailedScript()\n{\n\tchooseFile(inCallFailedLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose incoming call failed script\"));\n}\n\nvoid UserProfileForm::chooseOutgoingCallScript()\n{\n\tchooseFile(outCallLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose outgoing call script\"));\n}\n\nvoid UserProfileForm::chooseOutCallAnsweredScript()\n{\n\tchooseFile(outCallAnsweredLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose outgoing call answered script\"));\n}\n\nvoid UserProfileForm::chooseOutCallFailedScript()\n{\n\tchooseFile(outCallFailedLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose outgoing call failed script\"));\n}\n\nvoid UserProfileForm::chooseLocalReleaseScript()\n{\n\tchooseFile(localReleaseLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose local release script\"));\n}\n\nvoid UserProfileForm::chooseRemoteReleaseScript()\n{\n\tchooseFile(remoteReleaseLineEdit, tr(\"All files\").append(\" (*)\"), tr(\"Choose remote release script\"));\n}\n\nvoid UserProfileForm::addCodec() {\n\tfor (int i = 0; i < availCodecListBox->count(); i++) {\n\n        if (availCodecListBox->item(i)->isSelected()) {\n            activeCodecListBox->addItem(availCodecListBox->item(i)->text());\n            activeCodecListBox->item(activeCodecListBox->count()-1)->setSelected(true);\n            delete availCodecListBox->takeItem(i);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid UserProfileForm::removeCodec() {\n\tfor (int i = 0; i < activeCodecListBox->count(); i++) {\n        if (activeCodecListBox->item(i)->isSelected()) {\n            availCodecListBox->addItem(activeCodecListBox->item(i)->text());\n            availCodecListBox->item(availCodecListBox->count() - 1)->setSelected(true);\n            delete activeCodecListBox->takeItem(i);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid UserProfileForm::upCodec() {\n    int row = activeCodecListBox->currentRow();\n    if (row <= 0)\n        return;\n\n    QListWidgetItem* item = activeCodecListBox->takeItem(row);\n    activeCodecListBox->insertItem(row-1, item);\n    activeCodecListBox->setCurrentRow(row-1);\n}\n\nvoid UserProfileForm::downCodec() {\n    int row = activeCodecListBox->currentRow();\n    if (row < 0 || row >= activeCodecListBox->count()-1)\n        return;\n\n    QListWidgetItem* item = activeCodecListBox->takeItem(row);\n    activeCodecListBox->insertItem(row+1, item);\n    activeCodecListBox->setCurrentRow(row+1);\n}\n\nvoid UserProfileForm::upConversion() {\n    QTableWidgetItem *c1, *c2;\n    QModelIndexList ilist = conversionListView->selectionModel()->selectedRows();\n    int row;\n\n    if (ilist.isEmpty())\n        return;\n\n    row = ilist[0].row();\n    if (row == 0)\n        return;\n\n    c1 = conversionListView->takeItem(row, 0);\n    c2 = conversionListView->takeItem(row, 1);\n\n    conversionListView->setItem(row, 0, conversionListView->takeItem(row-1, 0));\n    conversionListView->setItem(row, 1, conversionListView->takeItem(row-1, 1));\n\n    conversionListView->setItem(row-1, 0, c1);\n    conversionListView->setItem(row-1, 1, c2);\n\n    conversionListView->selectRow(row-1);\n}\n\nvoid UserProfileForm::downConversion() {\n    QTableWidgetItem *c1, *c2;\n    QModelIndexList ilist = conversionListView->selectionModel()->selectedRows();\n    int row;\n\n    if (ilist.isEmpty())\n        return;\n\n    row = ilist[0].row();\n    if (row == conversionListView->rowCount()-1)\n        return;\n\n    c1 = conversionListView->takeItem(row, 0);\n    c2 = conversionListView->takeItem(row, 1);\n\n    conversionListView->setItem(row, 0, conversionListView->takeItem(row+1, 0));\n    conversionListView->setItem(row, 1, conversionListView->takeItem(row+1, 1));\n\n    conversionListView->setItem(row+1, 0, c1);\n    conversionListView->setItem(row+1, 1, c2);\n\n    conversionListView->selectRow(row+1);\n}\n\nvoid UserProfileForm::addConversion() {\n\tQString expr;\n\tQString replace;\n\t\n\tNumberConversionForm f;\n\tif (f.exec(expr, replace) == QDialog::Accepted) {\n        QTableWidgetItem* item;\n        int row = conversionListView->rowCount();\n\n        conversionListView->setRowCount(row + 1);\n\n        item = new QTableWidgetItem(expr);\n        conversionListView->setItem(row, 0, item);\n\n        item = new QTableWidgetItem(replace);\n        conversionListView->setItem(row, 1, item);\n\t}\n}\n\nvoid UserProfileForm::editConversion() {\n    QModelIndexList ilist = conversionListView->selectionModel()->selectedRows();\n    int row;\n\n    if (ilist.isEmpty())\n        return;\n\n    row = ilist[0].row();\n\n    QString expr = conversionListView->item(row, 0)->text();\n    QString replace = conversionListView->item(row, 1)->text();\n\t\n\tNumberConversionForm f;\n\tif (f.exec(expr, replace) == QDialog::Accepted) {\n        conversionListView->item(row, 0)->setText(expr);\n        conversionListView->item(row, 1)->setText(replace);\n\t}\n}\n\nvoid UserProfileForm::removeConversion() {\n    QModelIndexList ilist = conversionListView->selectionModel()->selectedRows();\n    int row;\n\n    if (ilist.isEmpty())\n        return;\n\n    row = ilist[0].row();\n    conversionListView->removeRow(row);\n}\n\nvoid UserProfileForm::testConversion() {\n\tQString number = testConversionLineEdit->text();\n\tif (number.isEmpty()) return;\n\t\n\tbool remove_special_phone_symbols = removeSpecialCheckBox->isChecked();\n\tQString special_phone_symbols = specialLineEdit->text();\n\t\n    number = remove_white_space(number.toStdString()).c_str();\n\t\n\t// Remove special symbols\n\tif (remove_special_phone_symbols &&\n        looks_like_phone(number.toStdString(), special_phone_symbols.toStdString()))\n\t{\n\t\tnumber = remove_symbols(\n                number.toStdString(), special_phone_symbols.toStdString()).c_str();\n\t}\n\t\n\tQString msg = tr(\"%1 converts to %2\")\n\t\t      .arg(number)\n              .arg(current_profile->convert_number(number.toStdString(), get_number_conversions()).c_str());\n\t\n    ((t_gui *)ui)->cb_show_msg(this,  msg.toStdString(), MSG_INFO);\n}\n\nvoid UserProfileForm::changeMWIType(int idxMWIType) {\n\tif (idxMWIType == idxMWISolicited) {\n\t\tmwiSolicitedGroupBox->setEnabled(true);\n\t\t\n\t\t// Set defaults\n\t\tif (mwiUserLineEdit->text().isEmpty()) {\n\t\t\tmwiUserLineEdit->setText(usernameLineEdit->text());\n\t\t}\n\t\tif (mwiServerLineEdit->text().isEmpty()) {\n\t\t\tmwiServerLineEdit->setText(domainLineEdit->text());\n\t\t\tmwiViaProxyCheckBox->setChecked(useProxyCheckBox->isChecked());\n\t\t}\n\t} else {\n\t\tmwiSolicitedGroupBox->setEnabled(false);\n\t}\n}\n\nvoid UserProfileForm::changeSipTransportProtocol(int idx) {\n\tudpThresholdTextLabel->setEnabled(idx == idxSipTransportAuto);\n\tudpThresholdSpinBox->setEnabled(idx == idxSipTransportAuto);\n\tpersistentTcpCheckBox->setEnabled(idx == idxSipTransportTCP);\n}\n"
  },
  {
    "path": "src/gui/userprofileform.h",
    "content": "#ifndef USERPROFILEFORM_H\n#define USERPROFILEFORM_H\n#include <list>\n#include <map>\n#include <QList>\n#include \"user.h\"\n#include \"ui_userprofileform.h\"\n\nclass UserProfileForm : public QDialog, public Ui::UserProfileForm\n{\n\tQ_OBJECT\n\npublic:\n    UserProfileForm(QWidget* parent = 0);\n\t~UserProfileForm();\n\n\tvirtual t_audio_codec label2codec( const QString & label );\n\tvirtual QString codec2label( t_audio_codec & codec );\n\tvirtual int ext_support2indexComboItem( t_ext_support ext );\n\tvirtual t_ext_support indexComboItem2ext_support( int index );\n\tvirtual int exec( list<t_user *> profiles, QString show_profile );\n    virtual bool check_dynamic_payload( QSpinBox * spb, QList<int> & checked_list );\n\tvirtual list<t_number_conversion> get_number_conversions();\n\tvirtual bool validateValues();\n\npublic slots:\n\tvirtual void showCategory( int index );\n\tvirtual void populate();\n\tvirtual void initProfileList( list<t_user *> profiles, QString show_profile_name );\n\tvirtual void show( list<t_user *> profiles, QString show_profile );\n\tvirtual void validate();\n\tvirtual void changeProfile( const QString & profileName );\n\tvirtual void chooseFile( QLineEdit * qle, const QString & filter, const QString & caption );\n\tvirtual void chooseRingtone();\n\tvirtual void chooseRingback();\n\tvirtual void chooseIncomingCallScript();\n\tvirtual void chooseInCallAnsweredScript();\n\tvirtual void chooseInCallFailedScript();\n\tvirtual void chooseOutgoingCallScript();\n\tvirtual void chooseOutCallAnsweredScript();\n\tvirtual void chooseOutCallFailedScript();\n\tvirtual void chooseLocalReleaseScript();\n\tvirtual void chooseRemoteReleaseScript();\n\tvirtual void addCodec();\n\tvirtual void removeCodec();\n\tvirtual void upCodec();\n\tvirtual void downCodec();\n\tvirtual void upConversion();\n\tvirtual void downConversion();\n\tvirtual void addConversion();\n\tvirtual void editConversion();\n\tvirtual void removeConversion();\n\tvirtual void testConversion();\n\tvirtual void changeMWIType( int idxMWIType );\n\tvirtual void changeSipTransportProtocol( int idx );\n\nsignals:\n\tvoid stunServerChanged(t_user *);\n\tvoid authCredentialsChanged(t_user *, const string &);\n\tvoid sipUserChanged(t_user *);\n\tvoid success();\n\tvoid mwiChangeUnsubscribe(t_user *);\n\tvoid mwiChangeSubscribe(t_user *);\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tmap<t_user *, int> map_last_cat;\n\tt_user *current_profile;\n\tint current_profile_idx;\n\tlist<t_user *> profile_list;\n\n\tvoid init();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/userprofileform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>UserProfileForm</class>\n <widget class=\"QDialog\" name=\"UserProfileForm\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>777</width>\n    <height>592</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Twinkle - User Profile</string>\n  </property>\n  <layout class=\"QGridLayout\">\n   <item row=\"0\" column=\"0\" colspan=\"2\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"profileTextLabel\">\n       <property name=\"text\">\n        <string>User profile:</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QComboBox\" name=\"profileComboBox\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"whatsThis\">\n        <string>Select which profile you want to edit.</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QListWidget\" name=\"categoryListBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>150</width>\n       <height>0</height>\n      </size>\n     </property>\n     <property name=\"whatsThis\">\n      <string>Select a category for which you want to see or modify the settings.</string>\n     </property>\n     <property name=\"midLineWidth\">\n      <number>0</number>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>32</width>\n       <height>32</height>\n      </size>\n     </property>\n     <item>\n      <property name=\"text\">\n       <string>User</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/penguin.png</normaloff>:/icons/images/penguin.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>SIP server</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/package_network.png</normaloff>:/icons/images/package_network.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Voice mail</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/mwi_none.png</normaloff>:/icons/images/mwi_none.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Instant message</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/message32.png</normaloff>:/icons/images/message32.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Presence</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/presence.png</normaloff>:/icons/images/presence.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>RTP audio</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/kmix.png</normaloff>:/icons/images/kmix.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>SIP protocol</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/package_system.png</normaloff>:/icons/images/package_system.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Transport/NAT</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/yast_babelfish.png</normaloff>:/icons/images/yast_babelfish.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Address format</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/yast_PhoneTTOffhook.png</normaloff>:/icons/images/yast_PhoneTTOffhook.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Timers</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/clock.png</normaloff>:/icons/images/clock.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Ring tones</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/knotify.png</normaloff>:/icons/images/knotify.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Scripts</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/edit.png</normaloff>:/icons/images/edit.png</iconset>\n      </property>\n     </item>\n     <item>\n      <property name=\"text\">\n       <string>Security</string>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"icons.qrc\">\n        <normaloff>:/icons/images/encrypted32.png</normaloff>:/icons/images/encrypted32.png</iconset>\n      </property>\n     </item>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <layout class=\"QHBoxLayout\">\n     <item>\n      <spacer>\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Expanding</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>441</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"okPushButton\">\n       <property name=\"whatsThis\">\n        <string>Accept and save your changes.</string>\n       </property>\n       <property name=\"text\">\n        <string>&amp;OK</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+O</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelPushButton\">\n       <property name=\"whatsThis\">\n        <string>Undo all your changes and close the window.</string>\n       </property>\n       <property name=\"text\">\n        <string>&amp;Cancel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"1\" column=\"1\" rowspan=\"2\">\n    <widget class=\"QStackedWidget\" name=\"settingsWidgetStack\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"frameShape\">\n      <enum>QFrame::StyledPanel</enum>\n     </property>\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"pageUser\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"userTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>User</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"accountGroupBox\">\n         <property name=\"title\">\n          <string>SIP account</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"usernameTextLabel\">\n            <property name=\"text\">\n             <string>&amp;User name*:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>usernameLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"domainTextLabel\">\n            <property name=\"text\">\n             <string>Do&amp;main*:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>domainLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"organizationTextLabel\">\n            <property name=\"text\">\n             <string>Organi&amp;zation:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>organizationLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"usernameLineEdit\">\n            <property name=\"whatsThis\">\n             <string>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"domainLineEdit\">\n            <property name=\"whatsThis\">\n             <string>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"organizationLineEdit\">\n            <property name=\"whatsThis\">\n             <string>You may fill in the name of your organization. When you make a call, this might be shown to the called party.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"displayLineEdit\">\n            <property name=\"whatsThis\">\n             <string>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"dislpayTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Your name:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>displayLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"authenticationGroupBox\">\n         <property name=\"title\">\n          <string>SIP authentication</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"authRealmTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Realm:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>authRealmLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"authNameTextLabel\">\n            <property name=\"text\">\n             <string>Authentication &amp;name:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>authNameLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"authRealmLineEdit\">\n            <property name=\"whatsThis\">\n             <string>The realm for authentication. This value must be provided by your SIP provider. If you leave this field empty, then Twinkle will try the user name and password for any realm that it will be challenged with.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"authNameLineEdit\">\n            <property name=\"whatsThis\">\n             <string>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"0\">\n           <widget class=\"QLabel\" name=\"authAkaAmfTextLabel\">\n            <property name=\"text\">\n             <string>AKA AM&amp;F:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>authAkaAmfLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"authAkaOpTextLabel\">\n            <property name=\"text\">\n             <string>A&amp;KA OP:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>authAkaOpLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"authPasswordLineEdit\">\n            <property name=\"whatsThis\">\n             <string>Your password for authentication.</string>\n            </property>\n            <property name=\"echoMode\">\n             <enum>QLineEdit::Password</enum>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"authPasswordTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Password:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>authPasswordLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"authAkaAmfLineEdit\">\n            <property name=\"whatsThis\">\n             <string>Authentication management field for AKAv1-MD5 authentication.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"authAkaOpLineEdit\">\n            <property name=\"whatsThis\">\n             <string>Operator variant key for AKAv1-MD5 authentication.</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>110</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageSipServer\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"sipServerTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>SIP server</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"registrarGroupBox\">\n         <property name=\"title\">\n          <string>Registrar</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"registrarTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Registrar:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>registrarLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"registrarLineEdit\">\n            <property name=\"whatsThis\">\n             <string>The hostname, domain name or IP address of your registrar. If you use an outbound proxy that is the same as your registrar, then you may leave this field empty and only fill in the address of the outbound proxy.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"expiryTextLabel\">\n            <property name=\"text\">\n             <string>E&amp;xpiry:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>expirySpinBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QSpinBox\" name=\"expirySpinBox\">\n              <property name=\"minimumSize\">\n               <size>\n                <width>90</width>\n                <height>0</height>\n               </size>\n              </property>\n              <property name=\"whatsThis\">\n               <string>The registration expiry time that Twinkle will request.</string>\n              </property>\n              <property name=\"maximum\">\n               <number>999999</number>\n              </property>\n              <property name=\"singleStep\">\n               <number>100</number>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLabel\" name=\"secondsTextLabel\">\n              <property name=\"text\">\n               <string>seconds</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>260</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </item>\n          <item row=\"2\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"regAtStartupCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Indicates if Twinkle should automatically register when you run this user profile. You should disable this when you want to do direct IP phone to IP phone communication without a SIP proxy.</string>\n            </property>\n            <property name=\"text\">\n             <string>Re&amp;gister at startup</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+G</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\" colspan=\"2\">\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QCheckBox\" name=\"regAddQvalueCheckBox\">\n              <property name=\"whatsThis\">\n               <string>The q-value indicates the priority of your registered device. If besides Twinkle you register other SIP devices for this account, then the network may use these values to determine which device to try first when delivering a call.</string>\n              </property>\n              <property name=\"text\">\n               <string>Add q-value to registration</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLineEdit\" name=\"regQvalueLineEdit\">\n              <property name=\"whatsThis\">\n               <string>The q-value is a value between 0.000 and 1.000. A higher value means a higher priority.</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>210</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"outboundProxyGroupBox\">\n         <property name=\"title\">\n          <string>Outbound Proxy</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"useProxyCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Indicates if Twinkle should use an outbound proxy. If an outbound proxy is used then all SIP requests are sent to this proxy. Without an outbound proxy, Twinkle will try to resolve the SIP address that you type for a call invitation for example to an IP address and send the SIP request there.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Use outbound proxy</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+U</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"proxyTextLabel\">\n            <property name=\"enabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"text\">\n             <string>Outbound &amp;proxy:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>proxyLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"proxyNonResolvableCheckBox\">\n            <property name=\"whatsThis\">\n             <string>When you tick this option Twinkle will first try to resolve a SIP address to an IP address itself. If it can, then the SIP request will be sent there. Only when it cannot resolve the address, it will send the SIP request to the proxy (note that an in-dialog request will only be sent to the proxy in this case when you also ticked the previous option.)</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Don't send a request to proxy if its destination can be resolved locally.</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+D</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"proxyLineEdit\">\n            <property name=\"enabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"whatsThis\">\n             <string>The hostname, domain name or IP address of your outbound proxy.</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"allRequestsCheckBox\">\n            <property name=\"whatsThis\">\n             <string>SIP requests within a SIP dialog are normally sent to the address in the contact-headers exchanged during call setup. If you tick this box, that address is ignored and in-dialog request are also sent to the outbound proxy.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Send in-dialog requests to proxy</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+S</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>100</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageRtpAudio\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"rtpAudioTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>RTP audio</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QTabWidget\" name=\"rtpAudioTabWidget\">\n         <widget class=\"QWidget\" name=\"tabCodecs\">\n          <attribute name=\"title\">\n           <string>Co&amp;decs</string>\n          </attribute>\n          <layout class=\"QGridLayout\">\n           <item row=\"1\" column=\"0\">\n            <layout class=\"QHBoxLayout\">\n             <item>\n              <widget class=\"QLabel\" name=\"ptimeTextLabel\">\n               <property name=\"text\">\n                <string>&amp;G.711/G.722/G.726 payload size:</string>\n               </property>\n               <property name=\"wordWrap\">\n                <bool>false</bool>\n               </property>\n               <property name=\"buddy\">\n                <cstring>ptimeSpinBox</cstring>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"ptimeSpinBox\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>46</width>\n                 <height>0</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>32767</width>\n                 <height>32767</height>\n                </size>\n               </property>\n               <property name=\"whatsThis\">\n                <string>The preferred payload size for the G.711, G.722 and G.726 codecs.</string>\n               </property>\n               <property name=\"minimum\">\n                <number>10</number>\n               </property>\n               <property name=\"maximum\">\n                <number>80</number>\n               </property>\n               <property name=\"singleStep\">\n                <number>10</number>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"payloadMsTextLabel\">\n               <property name=\"text\">\n                <string>ms</string>\n               </property>\n               <property name=\"wordWrap\">\n                <bool>false</bool>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <spacer>\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>121</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n            </layout>\n           </item>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QCheckBox\" name=\"inFarEndCodecPrefCheckBox\">\n             <property name=\"whatsThis\">\n              <string>&lt;p&gt;\nFor incoming calls, follow the preference from the far-end (SDP offer). Pick the first codec from the SDP offer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP offer is picked.</string>\n             </property>\n             <property name=\"text\">\n              <string>&amp;Follow codec preference from far end on incoming calls</string>\n             </property>\n             <property name=\"shortcut\">\n              <string>Alt+F</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"0\">\n            <widget class=\"QCheckBox\" name=\"outFarEndCodecPrefCheckBox\">\n             <property name=\"whatsThis\">\n              <string>&lt;p&gt;\nFor outgoing calls, follow the preference from the far-end (SDP answer). Pick the first codec from the SDP answer that is also in the list of active codecs.\n&lt;p&gt;\nIf you disable this option, then the first codec from the active codecs that is also in the SDP answer is picked.</string>\n             </property>\n             <property name=\"text\">\n              <string>Follow codec &amp;preference from far end on outgoing calls</string>\n             </property>\n             <property name=\"shortcut\">\n              <string>Alt+P</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"0\">\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>16</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QGroupBox\" name=\"codecsGroupBox\">\n             <property name=\"title\">\n              <string>Codecs</string>\n             </property>\n             <layout class=\"QHBoxLayout\">\n              <item>\n               <layout class=\"QVBoxLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"availCodecTextLabel\">\n                  <property name=\"text\">\n                   <string>Available codecs:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QListWidget\" name=\"availCodecListBox\">\n                  <property name=\"whatsThis\">\n                   <string>List of available codecs.</string>\n                  </property>\n                  <item>\n                   <property name=\"text\">\n                    <string>G.711 A-law</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>G.711 u-law</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>GSM</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>speex-nb (8 kHz)</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>speex-wb (16 kHz)</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>speex-uwb (32 kHz)</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <layout class=\"QVBoxLayout\">\n                <item>\n                 <spacer>\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>20</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n                <item>\n                 <widget class=\"QPushButton\" name=\"addCodecPushButton\">\n                  <property name=\"whatsThis\">\n                   <string>Move a codec from the list of available codecs to the list of active codecs.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string/>\n                  </property>\n                  <property name=\"icon\">\n                   <iconset resource=\"icons.qrc\">\n                    <normaloff>:/icons/images/1rightarrow.png</normaloff>:/icons/images/1rightarrow.png</iconset>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QPushButton\" name=\"rmvCodecPushButton\">\n                  <property name=\"whatsThis\">\n                   <string>Move a codec from the list of active codecs to the list of available codecs.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string/>\n                  </property>\n                  <property name=\"icon\">\n                   <iconset resource=\"icons.qrc\">\n                    <normaloff>:/icons/images/1leftarrow.png</normaloff>:/icons/images/1leftarrow.png</iconset>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <spacer>\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>21</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <layout class=\"QVBoxLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"useCodecTextLabel\">\n                  <property name=\"text\">\n                   <string>Active codecs:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QListWidget\" name=\"activeCodecListBox\">\n                  <property name=\"whatsThis\">\n                   <string>List of active codecs. These are the codecs that will be used for media negotiation during call setup. The order of the codecs is the order of preference of use.</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <layout class=\"QVBoxLayout\">\n                <item>\n                 <spacer>\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>21</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n                <item>\n                 <widget class=\"QPushButton\" name=\"upCodecPushButton\">\n                  <property name=\"whatsThis\">\n                   <string>Move a codec upwards in the list of active codecs, i.e. increase its preference of use.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string/>\n                  </property>\n                  <property name=\"icon\">\n                   <iconset resource=\"icons.qrc\">\n                    <normaloff>:/icons/images/1uparrow.png</normaloff>:/icons/images/1uparrow.png</iconset>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QPushButton\" name=\"downCodecPushButton\">\n                  <property name=\"whatsThis\">\n                   <string>Move a codec downwards in the list of active codecs, i.e. decrease its preference of use.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string/>\n                  </property>\n                  <property name=\"icon\">\n                   <iconset resource=\"icons.qrc\">\n                    <normaloff>:/icons/images/1downarrow.png</normaloff>:/icons/images/1downarrow.png</iconset>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <spacer>\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>31</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </item>\n             </layout>\n            </widget>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tabPreprocessing\">\n          <attribute name=\"title\">\n           <string>Prepr&amp;ocessing</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"preprocessingGroupBox\">\n             <property name=\"title\">\n              <string>Preprocessing (improves quality at remote end)</string>\n             </property>\n             <layout class=\"QHBoxLayout\">\n              <item>\n               <layout class=\"QGridLayout\">\n                <item row=\"0\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxDspAgcCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>Automatic gain control (AGC) is a feature that deals with the fact that the recording volume may vary by a large amount between different setups. The AGC provides a way to adjust a signal to a reference volume. This is useful because it removes the need for manual adjustment of the microphone gain. A secondary advantage is that by setting the microphone gain to a conservative (low) level, it is easier to avoid clipping.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>&amp;Automatic gain control</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+A</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"spxDspAgcLevelTextLabel\">\n                  <property name=\"enabled\">\n                   <bool>true</bool>\n                  </property>\n                  <property name=\"text\">\n                   <string>Automatic gain control &amp;level:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>spxDspAgcLevelSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"spxDspAgcLevelSpinBox\">\n                  <property name=\"enabled\">\n                   <bool>true</bool>\n                  </property>\n                  <property name=\"whatsThis\">\n                   <string>Automatic gain control level represents percentual value of automatic gain setting of a microphone. Recommended value is about 25%.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>1</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>100</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxDspVadCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>When enabled, voice activity detection detects whether the input signal represents a speech or a silence/background noise.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>&amp;Voice activity detection</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+V</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxDspNrdCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>The noise reduction can be used to reduce the amount of background noise present in the input signal. This provides higher quality speech.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>&amp;Noise reduction</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+N</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxDspAecCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>In any VoIP communication, if a speech from the remote end is played in the local loudspeaker, then it propagates in the room and is captured by the microphone. If the audio captured from the microphone is sent directly to the remote end, then the remote user hears an echo of his voice. An acoustic echo cancellation is designed to remove the acoustic echo before it is sent to the remote end. It is important to understand that the echo canceller is meant to improve the quality on the remote end.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Acoustic &amp;Echo Cancellation</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+E</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>31</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>121</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tabIlbc\">\n          <attribute name=\"title\">\n           <string>&amp;iLBC</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"ilbcGroupBox\">\n             <property name=\"title\">\n              <string>iLBC</string>\n             </property>\n             <layout class=\"QHBoxLayout\">\n              <item>\n               <layout class=\"QVBoxLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"ilbcPayloadTextLabel\">\n                  <property name=\"text\">\n                   <string>i&amp;LBC payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>ilbcPayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QLabel\" name=\"ilbcPayloadSizeTextLabel\">\n                  <property name=\"text\">\n                   <string>iLBC &amp;payload size (ms):</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>ilbcPayloadSizeComboBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <layout class=\"QVBoxLayout\">\n                <item>\n                 <widget class=\"QSpinBox\" name=\"ilbcPayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for iLBC.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QComboBox\" name=\"ilbcPayloadSizeComboBox\">\n                  <property name=\"whatsThis\">\n                   <string>The preferred payload size for iLBC.</string>\n                  </property>\n                  <item>\n                   <property name=\"text\">\n                    <string>20</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>30</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>71</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>81</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tabSpeex\">\n          <attribute name=\"title\">\n           <string>&amp;Speex</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"speexGroupBox\">\n             <property name=\"title\">\n              <string>Speex</string>\n             </property>\n             <layout class=\"QHBoxLayout\">\n              <item>\n               <layout class=\"QGridLayout\">\n                <item row=\"2\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxPenhCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>Perceptual enhancement is a part of the decoder which, when turned on, tries to reduce (the perception of) the noise produced by the coding/decoding process. In most cases, perceptual enhancement make the sound further from the original objectively (if you use SNR), but in the end it still sounds better (subjective improvement).</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Perceptual &amp;enhancement</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+E</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"4\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"spxUwbPayloadTextLabel\">\n                  <property name=\"text\">\n                   <string>&amp;Ultra wide band payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>spxUwbPayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"spxWbPayloadTextLabel\">\n                  <property name=\"text\">\n                   <string>&amp;Wide band payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>spxWbPayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxVbrCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to the &quot;difficulty&quot; of the audio being encoded. In the example of Speex, sounds like vowels and high-energy transients require a higher bit-rate to achieve good quality, while fricatives (e.g. s,f sounds) can be coded adequately with less bits. For this reason, VBR can achieve a lower bit-rate for the same quality, or a better quality for a certain bit-rate. Despite its advantages, VBR has two main drawbacks: first, by only specifying quality, there's no guarantee about the final average bit-rate. Second, for some real-time applications like voice over IP (VoIP), what counts is the maximum bit-rate, which must be low enough for the communication channel.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Variable &amp;bit-rate</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+B</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"4\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"spxUwbPayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for speex wide band.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"0\">\n                 <widget class=\"QCheckBox\" name=\"spxDtxCheckBox\">\n                  <property name=\"whatsThis\">\n                   <string>Discontinuous transmission is an addition to VAD/VBR operation, that allows one to stop transmitting completely when the background noise is stationary.</string>\n                  </property>\n                  <property name=\"text\">\n                   <string>Discontinuous &amp;Transmission</string>\n                  </property>\n                  <property name=\"shortcut\">\n                   <string>Alt+T</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"spxWbPayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for speex wide band.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"spxNbPayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for speex narrow band.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"spxQualityTextLabel\">\n                  <property name=\"text\">\n                   <string>&amp;Quality:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>spxQualitySpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"spxQualitySpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>Speex is a lossy codec, which means that it achieves compression at the expense of fidelity of the input speech signal. Unlike some other speech codecs, it is possible to control the tradeoff made between quality and bit-rate. The Speex encoding process is controlled most of the time by a quality parameter that ranges from 0 to 10.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>0</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>10</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"spxComplexityTextLabel\">\n                  <property name=\"text\">\n                   <string>Co&amp;mplexity:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>spxComplexitySpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"spxComplexitySpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>With Speex, it is possible to vary the complexity allowed for the encoder. This is done by controlling how the search is performed with an integer ranging from 1 to 10 in a way that's similar to the -1 to -9 options to gzip and bzip2 compression utilities. For normal use, the noise level at complexity 1 is between 1 and 2 dB higher than at complexity 10, but the CPU requirements for complexity 10 is about 5 times higher than for complexity 1. In practice, the best trade-off is between complexity 2 and 4, though higher settings are often useful when encoding non-speech sounds like DTMF tones.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>1</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>10</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"spxNbPayloadTextLabel\">\n                  <property name=\"text\">\n                   <string>&amp;Narrow band payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>spxNbPayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>31</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>121</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tabG726\">\n          <attribute name=\"title\">\n           <string>G.726</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"g726GroupBox\">\n             <property name=\"title\">\n              <string>G.726</string>\n             </property>\n             <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\">\n               <layout class=\"QGridLayout\">\n                <item row=\"3\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"g72640PayloadTypeTextLabel\">\n                  <property name=\"text\">\n                   <string>G.726 &amp;40 kbps payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>g72640PayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"1\">\n                 <widget class=\"QSpinBox\" name=\"g72640PayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for G.726 40 kbps.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"1\">\n                 <widget class=\"QSpinBox\" name=\"g72632PayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for G.726 32 kbps.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>0</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"g72624PayloadTypeTextLabel\">\n                  <property name=\"text\">\n                   <string>G.726 &amp;24 kbps payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>g72624PayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"1\">\n                 <widget class=\"QSpinBox\" name=\"g72624PayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for G.726 24 kbps.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"g72632PayloadTypeTextLabel\">\n                  <property name=\"text\">\n                   <string>G.726 &amp;32 kbps payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>g72632PayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"1\">\n                 <widget class=\"QSpinBox\" name=\"g72616PayloadSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for G.726 16 kbps.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"g72616PayloadTypeTextLabel\">\n                  <property name=\"text\">\n                   <string>G.726 &amp;16 kbps payload type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>g72616PayloadSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>231</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item row=\"1\" column=\"0\" colspan=\"2\">\n               <layout class=\"QHBoxLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"g726PackingTextLabel\">\n                  <property name=\"text\">\n                   <string>Codeword &amp;packing order:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>g726PackComboBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QComboBox\" name=\"g726PackComboBox\">\n                  <property name=\"whatsThis\">\n                   <string>There are 2 standards to pack the G.726 codewords into an RTP packet. RFC 3551 is the default packing method. Some SIP devices use ATM AAL2 however. If you experience bad quality using G.726 with RFC 3551 packing, then try ATM AAL2 packing.</string>\n                  </property>\n                  <item>\n                   <property name=\"text\">\n                    <string>RFC 3551</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>ATM AAL2</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n                <item>\n                 <spacer>\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>141</width>\n                    <height>20</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>150</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tabDtmf\">\n          <attribute name=\"title\">\n           <string>DT&amp;MF</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"dtmfGroupBox\">\n             <property name=\"title\">\n              <string>DTMF</string>\n             </property>\n             <layout class=\"QGridLayout\">\n              <item row=\"1\" column=\"1\">\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>280</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <layout class=\"QGridLayout\">\n                <item row=\"0\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"dtmfPayloadTypeSpinBox\">\n                  <property name=\"sizePolicy\">\n                   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                    <horstretch>0</horstretch>\n                    <verstretch>0</verstretch>\n                   </sizepolicy>\n                  </property>\n                  <property name=\"minimumSize\">\n                   <size>\n                    <width>49</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                  <property name=\"maximumSize\">\n                   <size>\n                    <width>32767</width>\n                    <height>32767</height>\n                   </size>\n                  </property>\n                  <property name=\"whatsThis\">\n                   <string>The dynamic type value (96 or higher) to be used for DTMF events (RFC 2833).</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>96</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>127</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"3\">\n                 <widget class=\"QLabel\" name=\"dtmfDurationMsTextLabel\">\n                  <property name=\"text\">\n                   <string>ms</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"dtmfVolumeTextLabel\">\n                  <property name=\"text\">\n                   <string>DTMF vo&amp;lume:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>dtmfVolumeSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"dtmfVolumeSpinBox\">\n                  <property name=\"whatsThis\">\n                   <string>The power level of the DTMF tone in dB.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>-63</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>0</number>\n                  </property>\n                  <property name=\"singleStep\">\n                   <number>10</number>\n                  </property>\n                  <property name=\"value\">\n                   <number>-10</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"dtmfPauseSpinBox\">\n                  <property name=\"sizePolicy\">\n                   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                    <horstretch>0</horstretch>\n                    <verstretch>0</verstretch>\n                   </sizepolicy>\n                  </property>\n                  <property name=\"minimumSize\">\n                   <size>\n                    <width>49</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                  <property name=\"maximumSize\">\n                   <size>\n                    <width>32767</width>\n                    <height>32767</height>\n                   </size>\n                  </property>\n                  <property name=\"whatsThis\">\n                   <string>The pause after a DTMF tone.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>20</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>100</number>\n                  </property>\n                  <property name=\"singleStep\">\n                   <number>10</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"0\" colspan=\"2\">\n                 <widget class=\"QLabel\" name=\"dtmfDurationTextLabel\">\n                  <property name=\"text\">\n                   <string>DTMF &amp;duration:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>dtmfDurationSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"3\">\n                 <widget class=\"QLabel\" name=\"dtmfPauseMsTextLabel\">\n                  <property name=\"text\">\n                   <string>ms</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"0\" colspan=\"2\">\n                 <widget class=\"QLabel\" name=\"dtmfPayloadTypeTextLabel\">\n                  <property name=\"text\">\n                   <string>DTMF payload &amp;type:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>dtmfPayloadTypeSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"2\" column=\"0\" colspan=\"2\">\n                 <widget class=\"QLabel\" name=\"dtmfPauseTextLabel\">\n                  <property name=\"text\">\n                   <string>DTMF &amp;pause:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>dtmfPauseSpinBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"3\">\n                 <widget class=\"QLabel\" name=\"dtmfVolDbmTextLabel\">\n                  <property name=\"text\">\n                   <string>dB</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"dtmfDurationSpinBox\">\n                  <property name=\"sizePolicy\">\n                   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                    <horstretch>0</horstretch>\n                    <verstretch>0</verstretch>\n                   </sizepolicy>\n                  </property>\n                  <property name=\"minimumSize\">\n                   <size>\n                    <width>49</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                  <property name=\"maximumSize\">\n                   <size>\n                    <width>32767</width>\n                    <height>32767</height>\n                   </size>\n                  </property>\n                  <property name=\"whatsThis\">\n                   <string>Duration of a DTMF tone.</string>\n                  </property>\n                  <property name=\"minimum\">\n                   <number>40</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>500</number>\n                  </property>\n                  <property name=\"singleStep\">\n                   <number>10</number>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </item>\n              <item row=\"0\" column=\"0\" colspan=\"2\">\n               <layout class=\"QHBoxLayout\">\n                <item>\n                 <widget class=\"QLabel\" name=\"dtmfTransportTextLabel\">\n                  <property name=\"text\">\n                   <string>DTMF t&amp;ransport:</string>\n                  </property>\n                  <property name=\"wordWrap\">\n                   <bool>false</bool>\n                  </property>\n                  <property name=\"buddy\">\n                   <cstring>dtmfTransportComboBox</cstring>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QComboBox\" name=\"dtmfTransportComboBox\">\n                  <property name=\"whatsThis\">\n                   <string>&lt;h2&gt;RFC 2833&lt;/h2&gt;\n&lt;p&gt;Send DTMF tones as RFC 2833 telephone events.&lt;/p&gt;\n&lt;h2&gt;Inband&lt;/h2&gt;\n&lt;p&gt;Send DTMF inband.&lt;/p&gt;\n&lt;h2&gt;Auto&lt;/h2&gt;\n&lt;p&gt;If the far end of your call supports RFC 2833, then a DTMF tone will be send as RFC 2833 telephone event, otherwise it will be sent inband.\n&lt;/p&gt;\n&lt;h2&gt;Out-of-band (SIP INFO)&lt;/h2&gt;\n&lt;p&gt;\nSend DTMF out-of-band via a SIP INFO request.\n&lt;/p&gt;</string>\n                  </property>\n                  <item>\n                   <property name=\"text\">\n                    <string>Auto</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>RFC 2833</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>Inband</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>Out-of-band (SIP INFO)</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n                <item>\n                 <spacer>\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>161</width>\n                    <height>20</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>120</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageSipProtocol\">\n      <layout class=\"QGridLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"sipProtocolTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>SIP protocol</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QTabWidget\" name=\"sipProtoclTabWidget\">\n         <widget class=\"QWidget\">\n          <attribute name=\"title\">\n           <string>General</string>\n          </attribute>\n          <layout class=\"QGridLayout\">\n           <item row=\"2\" column=\"1\">\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>16</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item row=\"0\" column=\"0\" colspan=\"2\">\n            <widget class=\"QGroupBox\" name=\"optionsGroupBox\">\n             <property name=\"title\">\n              <string>Protocol options</string>\n             </property>\n             <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"holdVariantTextLabel\">\n                <property name=\"text\">\n                 <string>Call Hold &amp;variant:</string>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n                <property name=\"buddy\">\n                 <cstring>holdVariantComboBox</cstring>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QComboBox\" name=\"holdVariantComboBox\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"minimumSize\">\n                 <size>\n                  <width>110</width>\n                  <height>0</height>\n                 </size>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Indicates if RFC 2543 (set media IP address in SDP to 0.0.0.0) or RFC 3264 (use direction attributes in SDP) is used to put a call on-hold.</string>\n                </property>\n                <item>\n                 <property name=\"text\">\n                  <string>RFC 2543</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>RFC 3264</string>\n                 </property>\n                </item>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"2\">\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>70</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item row=\"2\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"missingContactCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>A 200 OK response on a REGISTER request must contain a Contact header. Some registrars however, do not include a Contact header or include a wrong Contact header. This option allows for such a deviation from the specs.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Allow m&amp;issing Contact header in 200 OK on REGISTER</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+I</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"maxForwardsCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>According to RFC 3261 the Max-Forwards header is mandatory. But many implementations do not send this header. If you tick this box, Twinkle will reject a SIP request if Max-Forwards is missing.</string>\n                </property>\n                <property name=\"text\">\n                 <string>&amp;Max-Forwards header is mandatory</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+M</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"3\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"regTimeCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>In a REGISTER message the expiry time for registration can be put in the Contact header or in the Expires header. If you tick this box it will be put in the Contact header, otherwise it goes in the Expires header.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Put &amp;registration expiry time in contact header</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+R</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"4\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"compactHeadersCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if compact header names should be used for headers that have a compact form.</string>\n                </property>\n                <property name=\"text\">\n                 <string>&amp;Use compact header names</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+U</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"7\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"allowSdpChangeCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>&lt;p&gt;A SIP UAS may send SDP in a 1XX response for early media, e.g. ringing tone. When the call is answered the SIP UAS should send the same SDP in the 200 OK response according to RFC 3261. Once SDP has been received, SDP in subsequent responses should be discarded.&lt;/p&gt;\n&lt;p&gt;By allowing SDP to change during call setup, Twinkle will not discard SDP in subsequent responses and modify the media stream if the SDP is changed. When the SDP in a response is changed, it must have a new version number in the o= line.&lt;/p&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Allow SDP change during call setup</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"6\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"useDomainInContactCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>&lt;p&gt;\nTwinkle creates a unique contact header value by combining the SIP user name and domain:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user_domain@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis way 2 user profiles, having the same user name but different domain names, have unique contact addresses and hence can be activated simultaneously.\n&lt;/p&gt;\n&lt;p&gt;\nSome proxies do not handle a contact header value like this. You can disable this option to get a contact header value like this:\n&lt;/p&gt;\n&lt;p&gt;\n&lt;tt&gt;&amp;nbsp;user@local_ip&lt;/tt&gt;\n&lt;/p&gt;\n&lt;p&gt;\nThis format is what most SIP phones use.\n&lt;/p&gt;</string>\n                </property>\n                <property name=\"text\">\n                 <string>Use domain &amp;name to create a unique contact header value</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+N</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"5\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"multiValuesListCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>The Via, Route and Record-Route headers can be encoded as a list of comma separated values or as multiple occurrences of the same header.</string>\n                </property>\n                <property name=\"text\">\n                 <string>&amp;Encode Via, Route, Record-Route as list</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+E</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QGroupBox\" name=\"redirectionGroupBox\">\n             <property name=\"title\">\n              <string>Redirection</string>\n             </property>\n             <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"0\" colspan=\"2\">\n               <widget class=\"QCheckBox\" name=\"allowRedirectionCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if Twinkle should redirect a request if a 3XX response is received.</string>\n                </property>\n                <property name=\"text\">\n                 <string>&amp;Allow redirection</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+A</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\" colspan=\"3\">\n               <widget class=\"QCheckBox\" name=\"askUserRedirectCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if Twinkle should ask the user before redirecting a request when a 3XX response is received.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Ask user &amp;permission to redirect</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+P</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"0\">\n               <widget class=\"QLabel\" name=\"maxRedirectTextLabel\">\n                <property name=\"text\">\n                 <string>&amp;Max redirections:</string>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n                <property name=\"buddy\">\n                 <cstring>maxRedirectSpinBox</cstring>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"1\">\n               <widget class=\"QSpinBox\" name=\"maxRedirectSpinBox\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"minimumSize\">\n                 <size>\n                  <width>46</width>\n                  <height>0</height>\n                 </size>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>The number of redirect addresses that Twinkle tries at a maximum before it gives up redirecting a request. This prevents a request from getting redirected forever.</string>\n                </property>\n                <property name=\"minimum\">\n                 <number>1</number>\n                </property>\n                <property name=\"maximum\">\n                 <number>5</number>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"2\">\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>80</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QGroupBox\" name=\"sipExtensionsGroupBox\">\n             <property name=\"title\">\n              <string>SIP extensions</string>\n             </property>\n             <layout class=\"QGridLayout\">\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QComboBox\" name=\"ext100relComboBox\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"minimumSize\">\n                 <size>\n                  <width>120</width>\n                  <height>0</height>\n                 </size>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>Indicates if the 100rel extension (PRACK) is supported:&lt;br&gt;&lt;br&gt;\n&lt;b&gt;disabled&lt;/b&gt;: 100rel extension is disabled\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;supported&lt;/b&gt;: 100rel is supported (it is added in the supported header of an outgoing INVITE). A far-end can now require a PRACK on a 1xx response.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;required&lt;/b&gt;: 100rel is required (it is put in the require header of an outgoing INVITE). If an incoming INVITE indicates that it supports 100rel, then Twinkle will require a PRACK when sending a 1xx response. A call will fail when the far-end does not support 100rel.\n&lt;br&gt;&lt;br&gt;\n&lt;b&gt;preferred&lt;/b&gt;: Similar to required, but if a call fails because the far-end indicates it does not support 100rel (420 response) then the call will be re-attempted without the 100rel requirement.</string>\n                </property>\n                <item>\n                 <property name=\"text\">\n                  <string>disabled</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>supported</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>required</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>preferred</string>\n                 </property>\n                </item>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"ext100relTextLabel\">\n                <property name=\"text\">\n                 <string>&amp;100 rel (PRACK):</string>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n                <property name=\"buddy\">\n                 <cstring>ext100relComboBox</cstring>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QCheckBox\" name=\"extReplacesCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if the Replaces-extension is supported.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Replaces</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\">\n          <attribute name=\"title\">\n           <string>REFER</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"referGroupBox\">\n             <property name=\"title\">\n              <string>Call transfer (REFER)</string>\n             </property>\n             <layout class=\"QVBoxLayout\">\n              <item>\n               <widget class=\"QCheckBox\" name=\"allowReferCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if Twinkle should transfer a call if a REFER request is received.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Accept call &amp;transfer request (incoming REFER)</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+T</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"askUserReferCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if Twinkle should ask the user before transferring a call when a REFER request is received.</string>\n                </property>\n                <property name=\"text\">\n                 <string>As&amp;k user permission to transfer</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+K</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"refereeHoldCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if Twinkle should put the current call on hold when a REFER request to transfer a call is received.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Hold call &amp;with referrer while setting up call to transfer target</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+W</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"referrerHoldCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Indicates if Twinkle should put the current call on hold when you transfer a call.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Ho&amp;ld call with referee before sending REFER</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+L</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"refreshReferSubCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>While a call is being transferred, the referee sends NOTIFY messages to the referrer about the progress of the transfer. These messages are only sent for a short interval which length is determined by the referee. If you tick this box, the referrer will automatically send a SUBSCRIBE to lengthen this interval if it is about to expire and the transfer has not yet been completed.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Auto re&amp;fresh subscription to refer event while call transfer is not finished</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+F</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"referAorCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>An attended call transfer should use the contact URI as a refer target. A contact URI may not be globally routable however. Alternatively the AoR (Address of Record) may be used. A disadvantage is that the AoR may route to multiple endpoints in case of forking whereas the contact URI routes to a single endpoint.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Attended refer to AoR (Address of Record)</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"transferConsultInprogCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>When you perform an attended call transfer, you normally transfer the call after you established a consultation call. If you enable this option you can transfer the call while the consultation call is still in progress. This is a non-standard implementation and may not work with all SIP devices.</string>\n                </property>\n                <property name=\"text\">\n                 <string>Allow call transfer while consultation in progress</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>200</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"TabPage\">\n          <attribute name=\"title\">\n           <string>Privacy</string>\n          </attribute>\n          <layout class=\"QVBoxLayout\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"privacyGroupBox\">\n             <property name=\"title\">\n              <string>Privacy options</string>\n             </property>\n             <layout class=\"QVBoxLayout\">\n              <item>\n               <widget class=\"QCheckBox\" name=\"pPreferredIdCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Include a P-Preferred-Identity header with your identity in an INVITE request for a call with identity hiding.</string>\n                </property>\n                <property name=\"text\">\n                 <string>&amp;Send P-Preferred-Identity header when hiding user identity</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+S</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"pAssertedIdCheckBox\">\n                <property name=\"whatsThis\">\n                 <string>Include a P-Asserted-Identity header with your identity in an INVITE request for a call with identity hiding.</string>\n                </property>\n                <property name=\"text\">\n                 <string>&amp;Send P-Asserted-Identity header when hiding user identity</string>\n                </property>\n                <property name=\"shortcut\">\n                 <string>Alt+A</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>331</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageNat\">\n      <layout class=\"QGridLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"NatTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Transport/NAT</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QGroupBox\" name=\"transportGroupBox\">\n         <property name=\"title\">\n          <string>SIP transport</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"1\" colspan=\"2\">\n           <widget class=\"QComboBox\" name=\"sipTransportComboBox\">\n            <property name=\"whatsThis\">\n             <string>Transport mode for SIP. In auto mode, the size of a message determines which transport protocol is used. Messages larger than the UDP threshold are sent via TCP. Smaller messages are sent via UDP.</string>\n            </property>\n            <item>\n             <property name=\"text\">\n              <string>Auto</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>UDP</string>\n             </property>\n            </item>\n            <item>\n             <property name=\"text\">\n              <string>TCP</string>\n             </property>\n            </item>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"3\">\n           <spacer>\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"sizeType\">\n             <enum>QSizePolicy::Expanding</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>151</width>\n              <height>20</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"sipTransportTextLabel\">\n            <property name=\"text\">\n             <string>T&amp;ransport protocol:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>sipTransportComboBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"udpThresholdTextLabel\">\n            <property name=\"text\">\n             <string>UDP t&amp;hreshold:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>udpThresholdSpinBox</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"udpThresholdSpinBox\">\n            <property name=\"whatsThis\">\n             <string>Messages larger than the threshold are sent via TCP. Smaller messages are sent via UDP.</string>\n            </property>\n            <property name=\"suffix\">\n             <string>bytes</string>\n            </property>\n            <property name=\"maximum\">\n             <number>65535</number>\n            </property>\n            <property name=\"singleStep\">\n             <number>100</number>\n            </property>\n            <property name=\"value\">\n             <number>1300</number>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"2\" colspan=\"2\">\n           <spacer>\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"sizeType\">\n             <enum>QSizePolicy::Expanding</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>81</width>\n              <height>20</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QGroupBox\" name=\"natTraversalGroupBox\">\n         <property name=\"title\">\n          <string>NAT traversal</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QRadioButton\" name=\"natNoneRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Choose this option when there is no NAT device between you and your SIP proxy or when your SIP provider offers hosted NAT traversal.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;NAT traversal not needed</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+N</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QRadioButton\" name=\"natStaticRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Indicates if Twinkle should use the public IP address specified in the next field inside SIP message, i.e. in SIP headers and SDP body instead of the IP address of your network interface.&lt;br&gt;&lt;br&gt;\nWhen you choose this option you have to create static address mappings in your NAT device as well. You have to map the RTP ports on the public IP address to the same ports on the private IP address of your PC.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Use statically configured public IP address inside SIP messages</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+U</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"publicIPTextLabel\">\n              <property name=\"text\">\n               <string>&amp;Public IP address:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"indent\">\n               <number>21</number>\n              </property>\n              <property name=\"buddy\">\n               <cstring>publicIPLineEdit</cstring>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLineEdit\" name=\"publicIPLineEdit\">\n              <property name=\"whatsThis\">\n               <string>The public IP address of your NAT.</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <widget class=\"QRadioButton\" name=\"natStunRadioButton\">\n            <property name=\"whatsThis\">\n             <string>Choose this option when your SIP provider offers a STUN server for NAT traversal.</string>\n            </property>\n            <property name=\"text\">\n             <string>Use STUN (does not wor&amp;k for incoming TCP)</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+S</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"stunServerTextLabel\">\n              <property name=\"text\">\n               <string>STUN ser&amp;ver:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"indent\">\n               <number>21</number>\n              </property>\n              <property name=\"buddy\">\n               <cstring>stunServerLineEdit</cstring>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLineEdit\" name=\"stunServerLineEdit\">\n              <property name=\"whatsThis\">\n               <string>The hostname, domain name or IP address of the STUN server.</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"persistentTcpCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Keep the TCP connection established during registration open such that the SIP proxy can reuse this connection to send incoming requests. Application ping packets are sent to test if the connection is still alive.</string>\n            </property>\n            <property name=\"text\">\n             <string>P&amp;ersistent TCP connection</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+E</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"natKeepaliveCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Send UDP NAT keep alive packets.</string>\n            </property>\n            <property name=\"text\">\n             <string>Enable NAT &amp;keep alive</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+K</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"0\">\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>80</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageAddressFormat\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"addressFormatTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Address format</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"telNumberGroupBox\">\n         <property name=\"title\">\n          <string>Telephone numbers</string>\n         </property>\n         <layout class=\"QGridLayout\">\n          <item row=\"0\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"displayTelUserCheckBox\">\n            <property name=\"whatsThis\">\n             <string>If a URI indicates a telephone number, then only display the user part. E.g. if a call comes in from sip:123456@twinklephone.com then display only &quot;123456&quot; to the user. A URI indicates a telephone number if it contains the &quot;user=phone&quot; parameter or when it has a numerical user part and you ticked the next option.</string>\n            </property>\n            <property name=\"text\">\n             <string>Only &amp;display user part of URI for telephone number</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+D</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"numericalUserIsTelCheckBox\">\n            <property name=\"whatsThis\">\n             <string>If you tick this option, then Twinkle considers a SIP address that has a user part that consists of digits, *, #, + and special symbols only as a telephone number. In an outgoing message, Twinkle will add the &quot;user=phone&quot; parameter to such a URI.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;URI with numerical user part is a telephone number</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+U</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"removeSpecialCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Telephone numbers are often written with special symbols like dashes and brackets to make them readable to humans. When you dial such a number the special symbols must not be dialed. To allow you to simply copy/paste such a number into Twinkle, Twinkle can remove these symbols when you hit the dial button.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Remove special symbols from numerical dial strings</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+R</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"0\" colspan=\"2\">\n           <widget class=\"QCheckBox\" name=\"useTelUriCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Expand a dialed telephone number to a tel-URI instead of a sip-URI.</string>\n            </property>\n            <property name=\"text\">\n             <string>Use tel-URI for telephone &amp;number</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+N</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"specialTextLabel\">\n            <property name=\"text\">\n             <string>&amp;Special symbols:</string>\n            </property>\n            <property name=\"wordWrap\">\n             <bool>false</bool>\n            </property>\n            <property name=\"buddy\">\n             <cstring>specialLineEdit</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"specialLineEdit\">\n            <property name=\"whatsThis\">\n             <string>The special symbols that may be part of a telephone number for nice formatting, but must be removed when dialing.</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"conversionGroupBox\">\n         <property name=\"title\">\n          <string>Number conversion</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QTableWidget\" name=\"conversionListView\">\n              <property name=\"whatsThis\">\n               <string>&lt;p&gt;\nOften the format of the telephone numbers you need to dial is different from the format of the telephone numbers stored in your address book, e.g. your numbers start with a +-symbol followed by a country code, but your provider expects '00' instead of the '+', or you are at the office and all your numbers need to be prefixed with a '9' to access an outside line. Here you can specify number format conversion using Perl style regular expressions and format strings.\n&lt;/p&gt;\n&lt;p&gt;\nFor each number you dial, Twinkle will try to find a match in the list of match expressions. For the first match it finds, the number will be replaced with the format string. If no match is found, the number stays unchanged.\n&lt;/p&gt;\n&lt;p&gt;\nThe number conversion rules are also applied to incoming calls, so the numbers are displayed in the format you want.\n&lt;/p&gt;\n&lt;h3&gt;Example 1&lt;/h3&gt;\n&lt;p&gt;\nAssume your country code is 31 and you have stored all numbers in your address book in full international number format, e.g. +318712345678. For dialling numbers in your own country you want to strip of the '+31' and replace it by a '0'. For dialling numbers abroad you just want to replace the '+' by '00'.\n&lt;/p&gt;\n&lt;p&gt;\nThe following rules will do the trick:\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = \\+31([0-9]*) , Replace =  0$1&lt;br&gt;\nMatch expression = \\+([0-9]*) , Replace = 00$1&lt;/br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;h3&gt;Example 2&lt;/h3&gt;\n&lt;p&gt;\nYou are at work and all telephone numbers starting with a 0 should be prefixed with a 9 for an outside line.\n&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\nMatch expression = 0[0-9]* , Replace =  9$&amp;&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;</string>\n              </property>\n              <property name=\"selectionMode\">\n               <enum>QAbstractItemView::SingleSelection</enum>\n              </property>\n              <property name=\"selectionBehavior\">\n               <enum>QAbstractItemView::SelectRows</enum>\n              </property>\n              <property name=\"allColumnsShowFocus\" stdset=\"0\">\n               <bool>true</bool>\n              </property>\n              <attribute name=\"horizontalHeaderHighlightSections\">\n               <bool>false</bool>\n              </attribute>\n              <attribute name=\"verticalHeaderVisible\">\n               <bool>false</bool>\n              </attribute>\n              <column>\n               <property name=\"text\">\n                <string>Match expression</string>\n               </property>\n              </column>\n              <column>\n               <property name=\"text\">\n                <string>Replace</string>\n               </property>\n              </column>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QVBoxLayout\">\n              <item>\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Vertical</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>20</width>\n                  <height>21</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"upConversionPushButton\">\n                <property name=\"whatsThis\">\n                 <string>Move the selected number conversion rule upwards in the list.</string>\n                </property>\n                <property name=\"text\">\n                 <string/>\n                </property>\n                <property name=\"icon\">\n                 <iconset resource=\"icons.qrc\">\n                  <normaloff>:/icons/images/1uparrow.png</normaloff>:/icons/images/1uparrow.png</iconset>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"downConversionPushButton\">\n                <property name=\"whatsThis\">\n                 <string>Move the selected number conversion rule downwards in the list.</string>\n                </property>\n                <property name=\"text\">\n                 <string/>\n                </property>\n                <property name=\"icon\">\n                 <iconset resource=\"icons.qrc\">\n                  <normaloff>:/icons/images/1downarrow.png</normaloff>:/icons/images/1downarrow.png</iconset>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Vertical</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>20</width>\n                  <height>31</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QPushButton\" name=\"addConversionPushButton\">\n              <property name=\"whatsThis\">\n               <string>Add a number conversion rule.</string>\n              </property>\n              <property name=\"text\">\n               <string>&amp;Add</string>\n              </property>\n              <property name=\"shortcut\">\n               <string>Alt+A</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"removePushButton\">\n              <property name=\"whatsThis\">\n               <string>Remove the selected number conversion rule.</string>\n              </property>\n              <property name=\"text\">\n               <string>Re&amp;move</string>\n              </property>\n              <property name=\"shortcut\">\n               <string>Alt+M</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"editConversionPushButton\">\n              <property name=\"whatsThis\">\n               <string>Edit the selected number conversion rule.</string>\n              </property>\n              <property name=\"text\">\n               <string>&amp;Edit</string>\n              </property>\n              <property name=\"shortcut\">\n               <string>Alt+E</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>291</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QLineEdit\" name=\"testConversionLineEdit\">\n              <property name=\"whatsThis\">\n               <string>Type a telephone number here an press the Test button to see how it is converted by the list of number conversion rules.</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"testConversionPushButton\">\n              <property name=\"whatsThis\">\n               <string>Test how a number is converted by the number conversion rules.</string>\n              </property>\n              <property name=\"text\">\n               <string>&amp;Test</string>\n              </property>\n              <property name=\"shortcut\">\n               <string>Alt+T</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageTimers\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"timersTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Timers</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <layout class=\"QGridLayout\">\n           <item row=\"0\" column=\"2\">\n            <widget class=\"QLabel\" name=\"secNoanswerTextLabel\">\n             <property name=\"text\">\n              <string>seconds</string>\n             </property>\n             <property name=\"wordWrap\">\n              <bool>false</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"tmrNatKeepaliveSpinBox\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>55</width>\n               <height>0</height>\n              </size>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>55</width>\n               <height>32767</height>\n              </size>\n             </property>\n             <property name=\"whatsThis\">\n              <string>If you have enabled STUN or NAT keep alive, then Twinkle will send keep alive packets at this interval rate to keep the address bindings in your NAT device alive.</string>\n             </property>\n             <property name=\"minimum\">\n              <number>10</number>\n             </property>\n             <property name=\"maximum\">\n              <number>900</number>\n             </property>\n             <property name=\"singleStep\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"tmrNoanswerSpinBox\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>55</width>\n               <height>0</height>\n              </size>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>55</width>\n               <height>32767</height>\n              </size>\n             </property>\n             <property name=\"whatsThis\">\n              <string>When an incoming call is received, this timer is started. If the user answers the call, the timer is stopped. If the timer expires before the user answers the call, then Twinkle will reject the call with a &quot;480 User Not Responding&quot;.</string>\n             </property>\n             <property name=\"maximum\">\n              <number>600</number>\n             </property>\n             <property name=\"singleStep\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"tmrNatKeepaliveTextLabel\">\n             <property name=\"text\">\n              <string>NAT &amp;keep alive:</string>\n             </property>\n             <property name=\"wordWrap\">\n              <bool>false</bool>\n             </property>\n             <property name=\"buddy\">\n              <cstring>tmrNatKeepaliveSpinBox</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"tmrNoanswerTextLabel\">\n             <property name=\"text\">\n              <string>&amp;No answer:</string>\n             </property>\n             <property name=\"wordWrap\">\n              <bool>false</bool>\n             </property>\n             <property name=\"buddy\">\n              <cstring>tmrNoanswerSpinBox</cstring>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>270</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>450</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageRingTones\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"ringtonesTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Ring tones</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QGridLayout\">\n         <item row=\"1\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openRingbackToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select ring back tone file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openRingtoneToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select ring tone file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"ringbackTextLabel\">\n           <property name=\"text\">\n            <string>Ring &amp;back tone:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>ringbackLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"ringbackLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring back tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring back tone overrides the ring back tone settings in the system settings.\n&lt;/p&gt;</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"ringtoneLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nSpecify the file name of a .wav file that you want to be played as ring tone for this user.\n&lt;/p&gt;\n&lt;p&gt;\nThis ring tone overrides the ring tone settings in the system settings.\n&lt;/p&gt;</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"ringtoneTextLabel\">\n           <property name=\"text\">\n            <string>&amp;Ring tone:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>ringtoneLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>391</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageScripts\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"scriptsTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Scripts</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QGridLayout\">\n         <item row=\"6\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"localReleaseLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when you release a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=local_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openInCallFailedToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openIncomingCallScriptToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openOutCallAnsweredToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"inCallFailedLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when an incoming call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openInCallAnsweredToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"remoteReleaseLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when the remote party releases a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP BYE request are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=remote_release&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=BYE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the BYE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openOutCallToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"incomingCallScriptLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nYou can customize the way Twinkle handles incoming calls. Twinkle can call a script when a call comes in. Based on the output of the script Twinkle accepts, rejects or redirects the call. When accepting the call, the ring tone can be customized by the script as well. The script can be any executable program.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;Note:&lt;/b&gt; Twinkle pauses while your script runs. It is recommended that your script does not take more than 200 ms. When you need more time, you can send the parameters followed by &lt;b&gt;end&lt;/b&gt; and keep on running. Twinkle will continue when it receives the &lt;b&gt;end&lt;/b&gt; parameter.\n&lt;/p&gt;\n&lt;p&gt;\nWith your script you can customize call handling by outputting one or more of the following parameters to stdout. Each parameter should be on a separate line.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;blockquote&gt;\n&lt;tt&gt;\naction=[ continue | reject | dnd | redirect | autoanswer ]&lt;br&gt;\nreason=&amp;lt;string&amp;gt;&lt;br&gt;\ncontact=&amp;lt;address to redirect to&amp;gt;&lt;br&gt;\ncaller_name=&amp;lt;name of caller to display&amp;gt;&lt;br&gt;\nringtone=&amp;lt;file name of .wav file&amp;gt;&lt;br&gt;\ndisplay_msg=&amp;lt;message to show on display&amp;gt;&lt;br&gt;\nend&lt;br&gt;\n&lt;/tt&gt;\n&lt;/blockquote&gt;\n&lt;/p&gt;\n&lt;h2&gt;Parameters&lt;/h2&gt;\n&lt;h3&gt;action&lt;/h3&gt;\n&lt;p&gt;\n&lt;b&gt;continue&lt;/b&gt; - continue call handling as usual&lt;br&gt;\n&lt;b&gt;reject&lt;/b&gt; - reject call&lt;br&gt;\n&lt;b&gt;dnd&lt;/b&gt; - deny call with do not disturb indication&lt;br&gt;\n&lt;b&gt;redirect&lt;/b&gt; - redirect call to address specified by &lt;b&gt;contact&lt;/b&gt;&lt;br&gt;\n&lt;b&gt;autoanswer&lt;/b&gt; - automatically answer a call&lt;br&gt;\n&lt;/p&gt;\n&lt;p&gt;\nWhen the script does not write an action to stdout, then the default action is continue.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;reason: &lt;/b&gt;\nWith the reason parameter you can set the reason string for reject or dnd. This might be shown to the far-end user.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;caller_name: &lt;/b&gt;\nThis parameter will override the display name of the caller.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;ringtone: &lt;/b&gt;\nThe ringtone parameter specifies the .wav file that will be played as ring tone when action is continue.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers in the incoming INVITE message are passed in environment variables to your script. The variable names are formatted as &lt;b&gt;SIP_&amp;lt;HEADER_NAME&amp;gt;&lt;/b&gt; E.g. SIP_FROM contains the value of the from header.\n&lt;/p&gt;\n&lt;p&gt;\nTWINKLE_TRIGGER=in_call. SIPREQUEST_METHOD=INVITE. The request-URI of the INVITE will be passed in &lt;b&gt;SIPREQUEST_URI&lt;/b&gt;. The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"outCallAnsweredLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when the remote party answers your call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"inCallAnsweredLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when you answer an incoming call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing 200 OK are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=in_call_answered&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE=200&lt;/b&gt;. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"0\">\n          <widget class=\"QLabel\" name=\"localReleaseTextLabel\">\n           <property name=\"text\">\n            <string>Call released locall&amp;y:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>inCallFailedLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openOutCallFailedToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"outCallFailedLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when an outgoing call fails.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the incoming SIP failure response are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call_failed&lt;/b&gt;. &lt;b&gt;SIPSTATUS_CODE&lt;/b&gt; contains the status code of the failure response. &lt;b&gt;SIPSTATUS_REASON&lt;/b&gt; contains the reason phrase.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"outCallLineEdit\">\n           <property name=\"whatsThis\">\n            <string>&lt;p&gt;\nThis script is called when you make a call.\n&lt;/p&gt;\n&lt;h2&gt;Environment variables&lt;/h2&gt;\n&lt;p&gt;\nThe values of all SIP headers of the outgoing INVITE are passed in environment variables to your script.\n&lt;/p&gt;\n&lt;p&gt;\n&lt;b&gt;TWINKLE_TRIGGER=out_call&lt;/b&gt;. &lt;b&gt;SIPREQUEST_METHOD=INVITE&lt;/b&gt;. &lt;b&gt;SIPREQUEST_URI&lt;/b&gt; contains the request-URI of the INVITE.  The name of the user profile will be passed in &lt;b&gt;TWINKLE_USER_PROFILE&lt;/b&gt;.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"0\">\n          <widget class=\"QLabel\" name=\"outCallAnsweredTextLabel\">\n           <property name=\"text\">\n            <string>Outgoing call a&amp;nswered:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>inCallAnsweredLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"inCallFailedTextLabel\">\n           <property name=\"text\">\n            <string>Incoming call &amp;failed:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>inCallFailedLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"incomingCallScriptTextLabel\">\n           <property name=\"text\">\n            <string>&amp;Incoming call:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>incomingCallScriptLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openLocalReleaseToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"0\">\n          <widget class=\"QLabel\" name=\"remoteReleaseTextLabel\">\n           <property name=\"text\">\n            <string>Call released &amp;remotely:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>inCallFailedLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"inCallAnsweredTextLabel\">\n           <property name=\"text\">\n            <string>Incoming call &amp;answered:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>inCallAnsweredLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"2\">\n          <widget class=\"QToolButton\" name=\"openRemoteReleaseToolButton\">\n           <property name=\"focusPolicy\">\n            <enum>Qt::TabFocus</enum>\n           </property>\n           <property name=\"whatsThis\">\n            <string>Select script file.</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"icon\">\n            <iconset resource=\"icons.qrc\">\n             <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QLabel\" name=\"outCallTextLabel\">\n           <property name=\"text\">\n            <string>O&amp;utgoing call:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>incomingCallScriptLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"0\">\n          <widget class=\"QLabel\" name=\"outCallFailedTextLabel\">\n           <property name=\"text\">\n            <string>Out&amp;going call failed:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>inCallFailedLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>190</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageSecurity\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"securityTitleTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Security</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"zrtpEnabledCheckBox\">\n         <property name=\"whatsThis\">\n          <string>When ZRTP/SRTP is enabled, then Twinkle will try to encrypt the audio of each call you originate or receive. Encryption will only succeed if the remote party has ZRTP/SRTP support enabled. If the remote party does not support ZRTP/SRTP, then the audio channel will stay unencrypted.</string>\n         </property>\n         <property name=\"text\">\n          <string>&amp;Enable ZRTP/SRTP encryption</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+E</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"zrtpSettingsGroupBox\">\n         <property name=\"title\">\n          <string>ZRTP settings</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"zrtpSendIfSupportedCheckBox\">\n            <property name=\"whatsThis\">\n             <string>A SIP endpoint supporting ZRTP may indicate ZRTP support during call setup in its signalling. Enabling this option will cause Twinkle only to encrypt calls when the remote party indicates ZRTP support.</string>\n            </property>\n            <property name=\"text\">\n             <string>O&amp;nly encrypt audio if remote party indicated ZRTP support in SDP</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+N</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"zrtpSdpCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Twinkle will indicate ZRTP support during call setup in its signalling.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Indicate ZRTP support in SDP</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+I</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"zrtpGoClearWarningCheckBox\">\n            <property name=\"whatsThis\">\n             <string>A remote party of an encrypted call may send a ZRTP go-clear command to stop encryption. When Twinkle receives this command it will popup a warning if this option is enabled.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Popup warning when remote party disables encryption during call</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+P</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>241</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageVoiceMail\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"voiceMailTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Voice mail</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QGridLayout\">\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"vmAddressTextLabel\">\n           <property name=\"text\">\n            <string>&amp;Voice mail address:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>vmAddressLineEdit</cstring>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"vmAddressLineEdit\">\n           <property name=\"whatsThis\">\n            <string>The SIP address or telephone number to access your voice mail.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <layout class=\"QHBoxLayout\">\n           <item>\n            <widget class=\"QComboBox\" name=\"mwiTypeComboBox\">\n             <property name=\"whatsThis\">\n              <string>&lt;H2&gt;Message waiting indication type&lt;/H2&gt;\n&lt;p&gt;\nIf your provider offers the message waiting indication service, then Twinkle can show you when new voice mail messages are waiting. Ask your provider which type of message waiting indication is offered.\n&lt;/p&gt;\n&lt;H3&gt;Unsolicited&lt;/H3&gt;\n&lt;p&gt;\nAsterisk provides unsolicited message waiting indication.\n&lt;/p&gt;\n&lt;H3&gt;Solicited&lt;/H3&gt;\n&lt;p&gt;\nSolicited message waiting indication as specified by RFC 3842.\n&lt;/p&gt;</string>\n             </property>\n             <item>\n              <property name=\"text\">\n               <string>Unsolicited</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Solicited</string>\n              </property>\n             </item>\n            </widget>\n           </item>\n           <item>\n            <spacer>\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Expanding</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>221</width>\n               <height>20</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"mwiTypeTextLabel\">\n           <property name=\"text\">\n            <string>&amp;MWI type:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>mwiTypeComboBox</cstring>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"mwiSolicitedGroupBox\">\n         <property name=\"title\">\n          <string>Solicited MWI</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <layout class=\"QGridLayout\">\n            <item row=\"2\" column=\"0\">\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>120</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n            <item row=\"3\" column=\"0\">\n             <widget class=\"QLabel\" name=\"mwiDurationTextLabel\">\n              <property name=\"text\">\n               <string>Subscription &amp;duration:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>mwiDurationSpinBox</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"0\" column=\"0\">\n             <widget class=\"QLabel\" name=\"mwiUserTextLabel\">\n              <property name=\"text\">\n               <string>Mailbox &amp;user name:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>mwiUserLineEdit</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"1\" column=\"1\">\n             <widget class=\"QLineEdit\" name=\"mwiServerLineEdit\">\n              <property name=\"whatsThis\">\n               <string>The hostname, domain name or IP address of your voice mailbox server.</string>\n              </property>\n             </widget>\n            </item>\n            <item row=\"3\" column=\"1\">\n             <layout class=\"QHBoxLayout\">\n              <item>\n               <widget class=\"QSpinBox\" name=\"mwiDurationSpinBox\">\n                <property name=\"minimumSize\">\n                 <size>\n                  <width>90</width>\n                  <height>0</height>\n                 </size>\n                </property>\n                <property name=\"whatsThis\">\n                 <string>For solicited MWI, an endpoint subscribes to the message status for a limited duration. Just before the duration expires, the endpoint should refresh the subscription.</string>\n                </property>\n                <property name=\"maximum\">\n                 <number>999999</number>\n                </property>\n                <property name=\"singleStep\">\n                 <number>100</number>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"mwiSecondsTextLabel\">\n                <property name=\"text\">\n                 <string>seconds</string>\n                </property>\n                <property name=\"wordWrap\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <spacer>\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeType\">\n                 <enum>QSizePolicy::Expanding</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>190</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </item>\n            <item row=\"0\" column=\"1\">\n             <widget class=\"QLineEdit\" name=\"mwiUserLineEdit\">\n              <property name=\"whatsThis\">\n               <string>Your user name for accessing your voice mailbox.</string>\n              </property>\n             </widget>\n            </item>\n            <item row=\"1\" column=\"0\">\n             <widget class=\"QLabel\" name=\"mwiServerTextLabel\">\n              <property name=\"text\">\n               <string>Mailbox &amp;server:</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>mwiServerLineEdit</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"2\" column=\"1\">\n             <widget class=\"QCheckBox\" name=\"mwiViaProxyCheckBox\">\n              <property name=\"whatsThis\">\n               <string>Check this option if Twinkle should send SIP messages to the mailbox server via the outbound proxy.</string>\n              </property>\n              <property name=\"text\">\n               <string>Via outbound &amp;proxy</string>\n              </property>\n              <property name=\"shortcut\">\n               <string>Alt+P</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>211</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pageIM\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"imTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Instant message</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"imMaxSessionsTextLabel\">\n           <property name=\"text\">\n            <string>&amp;Maximum number of sessions:</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"buddy\">\n            <cstring>imMaxSessionsSpinBox</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"imMaxSessionsSpinBox\">\n           <property name=\"whatsThis\">\n            <string>When you have this number of instant message sessions open, new incoming message sessions will be rejected.</string>\n           </property>\n           <property name=\"maximum\">\n            <number>65535</number>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer>\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>201</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"isComposingCheckBox\">\n         <property name=\"whatsThis\">\n          <string>Twinkle sends a composing indication when you type a message. This way the recipient can see that you are typing.</string>\n         </property>\n         <property name=\"text\">\n          <string>&amp;Send composing indications when typing a message.</string>\n         </property>\n         <property name=\"shortcut\">\n          <string>Alt+S</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>350</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pagePresence\">\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"presTextLabel\">\n         <property name=\"font\">\n          <font>\n           <pointsize>21</pointsize>\n          </font>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::StyledPanel</enum>\n         </property>\n         <property name=\"text\">\n          <string>Presence</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>false</bool>\n         </property>\n         <property name=\"indent\">\n          <number>10</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"presYourGroupBox\">\n         <property name=\"title\">\n          <string>Your presence</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"presPublishCheckBox\">\n            <property name=\"whatsThis\">\n             <string>Publish your availability at startup.</string>\n            </property>\n            <property name=\"text\">\n             <string>&amp;Publish availability at startup</string>\n            </property>\n            <property name=\"shortcut\">\n             <string>Alt+P</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"presPublishTimerTextLabel\">\n              <property name=\"text\">\n               <string>Publication &amp;refresh interval (sec):</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>presPublishTimeSpinBox</cstring>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QSpinBox\" name=\"presPublishTimeSpinBox\">\n              <property name=\"whatsThis\">\n               <string>Refresh rate of presence publications.</string>\n              </property>\n              <property name=\"maximum\">\n               <number>999999</number>\n              </property>\n              <property name=\"singleStep\">\n               <number>100</number>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>231</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\">\n         <property name=\"title\">\n          <string>Buddy presence</string>\n         </property>\n         <layout class=\"QVBoxLayout\">\n          <item>\n           <layout class=\"QHBoxLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"presSubscribeTimerTextLabel\">\n              <property name=\"text\">\n               <string>&amp;Subscription refresh interval (sec):</string>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"buddy\">\n               <cstring>presSubscribeTimeSpinBox</cstring>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QSpinBox\" name=\"presSubscribeTimeSpinBox\">\n              <property name=\"whatsThis\">\n               <string>Refresh rate of presence subscriptions.</string>\n              </property>\n              <property name=\"maximum\">\n               <number>999999</number>\n              </property>\n              <property name=\"singleStep\">\n               <number>100</number>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeType\">\n               <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>191</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <spacer>\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Expanding</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>281</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <tabstops>\n  <tabstop>displayLineEdit</tabstop>\n  <tabstop>usernameLineEdit</tabstop>\n  <tabstop>domainLineEdit</tabstop>\n  <tabstop>organizationLineEdit</tabstop>\n  <tabstop>authRealmLineEdit</tabstop>\n  <tabstop>authNameLineEdit</tabstop>\n  <tabstop>authPasswordLineEdit</tabstop>\n  <tabstop>authAkaOpLineEdit</tabstop>\n  <tabstop>authAkaAmfLineEdit</tabstop>\n  <tabstop>registrarLineEdit</tabstop>\n  <tabstop>expirySpinBox</tabstop>\n  <tabstop>regAtStartupCheckBox</tabstop>\n  <tabstop>regAddQvalueCheckBox</tabstop>\n  <tabstop>regQvalueLineEdit</tabstop>\n  <tabstop>useProxyCheckBox</tabstop>\n  <tabstop>proxyLineEdit</tabstop>\n  <tabstop>allRequestsCheckBox</tabstop>\n  <tabstop>proxyNonResolvableCheckBox</tabstop>\n  <tabstop>vmAddressLineEdit</tabstop>\n  <tabstop>mwiTypeComboBox</tabstop>\n  <tabstop>mwiUserLineEdit</tabstop>\n  <tabstop>mwiServerLineEdit</tabstop>\n  <tabstop>mwiViaProxyCheckBox</tabstop>\n  <tabstop>mwiDurationSpinBox</tabstop>\n  <tabstop>imMaxSessionsSpinBox</tabstop>\n  <tabstop>isComposingCheckBox</tabstop>\n  <tabstop>presPublishCheckBox</tabstop>\n  <tabstop>presPublishTimeSpinBox</tabstop>\n  <tabstop>presSubscribeTimeSpinBox</tabstop>\n  <tabstop>rtpAudioTabWidget</tabstop>\n  <tabstop>availCodecListBox</tabstop>\n  <tabstop>addCodecPushButton</tabstop>\n  <tabstop>rmvCodecPushButton</tabstop>\n  <tabstop>activeCodecListBox</tabstop>\n  <tabstop>upCodecPushButton</tabstop>\n  <tabstop>downCodecPushButton</tabstop>\n  <tabstop>ptimeSpinBox</tabstop>\n  <tabstop>inFarEndCodecPrefCheckBox</tabstop>\n  <tabstop>outFarEndCodecPrefCheckBox</tabstop>\n  <tabstop>spxDspAgcCheckBox</tabstop>\n  <tabstop>spxDspAgcLevelSpinBox</tabstop>\n  <tabstop>spxDspVadCheckBox</tabstop>\n  <tabstop>spxDspNrdCheckBox</tabstop>\n  <tabstop>spxDspAecCheckBox</tabstop>\n  <tabstop>ilbcPayloadSpinBox</tabstop>\n  <tabstop>ilbcPayloadSizeComboBox</tabstop>\n  <tabstop>spxVbrCheckBox</tabstop>\n  <tabstop>spxDtxCheckBox</tabstop>\n  <tabstop>spxPenhCheckBox</tabstop>\n  <tabstop>spxQualitySpinBox</tabstop>\n  <tabstop>spxComplexitySpinBox</tabstop>\n  <tabstop>spxNbPayloadSpinBox</tabstop>\n  <tabstop>spxWbPayloadSpinBox</tabstop>\n  <tabstop>spxUwbPayloadSpinBox</tabstop>\n  <tabstop>g72616PayloadSpinBox</tabstop>\n  <tabstop>g72624PayloadSpinBox</tabstop>\n  <tabstop>g72632PayloadSpinBox</tabstop>\n  <tabstop>g72640PayloadSpinBox</tabstop>\n  <tabstop>g726PackComboBox</tabstop>\n  <tabstop>dtmfTransportComboBox</tabstop>\n  <tabstop>dtmfPayloadTypeSpinBox</tabstop>\n  <tabstop>dtmfDurationSpinBox</tabstop>\n  <tabstop>dtmfPauseSpinBox</tabstop>\n  <tabstop>dtmfVolumeSpinBox</tabstop>\n  <tabstop>sipProtoclTabWidget</tabstop>\n  <tabstop>holdVariantComboBox</tabstop>\n  <tabstop>maxForwardsCheckBox</tabstop>\n  <tabstop>missingContactCheckBox</tabstop>\n  <tabstop>regTimeCheckBox</tabstop>\n  <tabstop>compactHeadersCheckBox</tabstop>\n  <tabstop>multiValuesListCheckBox</tabstop>\n  <tabstop>useDomainInContactCheckBox</tabstop>\n  <tabstop>allowSdpChangeCheckBox</tabstop>\n  <tabstop>allowRedirectionCheckBox</tabstop>\n  <tabstop>askUserRedirectCheckBox</tabstop>\n  <tabstop>maxRedirectSpinBox</tabstop>\n  <tabstop>ext100relComboBox</tabstop>\n  <tabstop>extReplacesCheckBox</tabstop>\n  <tabstop>allowReferCheckBox</tabstop>\n  <tabstop>askUserReferCheckBox</tabstop>\n  <tabstop>refereeHoldCheckBox</tabstop>\n  <tabstop>referrerHoldCheckBox</tabstop>\n  <tabstop>refreshReferSubCheckBox</tabstop>\n  <tabstop>referAorCheckBox</tabstop>\n  <tabstop>pPreferredIdCheckBox</tabstop>\n  <tabstop>pAssertedIdCheckBox</tabstop>\n  <tabstop>sipTransportComboBox</tabstop>\n  <tabstop>udpThresholdSpinBox</tabstop>\n  <tabstop>displayTelUserCheckBox</tabstop>\n  <tabstop>numericalUserIsTelCheckBox</tabstop>\n  <tabstop>removeSpecialCheckBox</tabstop>\n  <tabstop>specialLineEdit</tabstop>\n  <tabstop>useTelUriCheckBox</tabstop>\n  <tabstop>conversionListView</tabstop>\n  <tabstop>upConversionPushButton</tabstop>\n  <tabstop>downConversionPushButton</tabstop>\n  <tabstop>addConversionPushButton</tabstop>\n  <tabstop>removePushButton</tabstop>\n  <tabstop>editConversionPushButton</tabstop>\n  <tabstop>testConversionLineEdit</tabstop>\n  <tabstop>testConversionPushButton</tabstop>\n  <tabstop>tmrNoanswerSpinBox</tabstop>\n  <tabstop>tmrNatKeepaliveSpinBox</tabstop>\n  <tabstop>ringtoneLineEdit</tabstop>\n  <tabstop>ringbackLineEdit</tabstop>\n  <tabstop>openRingtoneToolButton</tabstop>\n  <tabstop>openRingbackToolButton</tabstop>\n  <tabstop>incomingCallScriptLineEdit</tabstop>\n  <tabstop>openIncomingCallScriptToolButton</tabstop>\n  <tabstop>inCallAnsweredLineEdit</tabstop>\n  <tabstop>openInCallAnsweredToolButton</tabstop>\n  <tabstop>inCallFailedLineEdit</tabstop>\n  <tabstop>openInCallFailedToolButton</tabstop>\n  <tabstop>outCallLineEdit</tabstop>\n  <tabstop>openOutCallToolButton</tabstop>\n  <tabstop>outCallAnsweredLineEdit</tabstop>\n  <tabstop>openOutCallAnsweredToolButton</tabstop>\n  <tabstop>outCallFailedLineEdit</tabstop>\n  <tabstop>openOutCallFailedToolButton</tabstop>\n  <tabstop>localReleaseLineEdit</tabstop>\n  <tabstop>openLocalReleaseToolButton</tabstop>\n  <tabstop>remoteReleaseLineEdit</tabstop>\n  <tabstop>openRemoteReleaseToolButton</tabstop>\n  <tabstop>zrtpEnabledCheckBox</tabstop>\n  <tabstop>zrtpSendIfSupportedCheckBox</tabstop>\n  <tabstop>zrtpSdpCheckBox</tabstop>\n  <tabstop>zrtpGoClearWarningCheckBox</tabstop>\n  <tabstop>okPushButton</tabstop>\n  <tabstop>cancelPushButton</tabstop>\n  <tabstop>profileComboBox</tabstop>\n  <tabstop>categoryListBox</tabstop>\n </tabstops>\n <includes>\n  <include location=\"local\">user.h</include>\n  <include location=\"global\">map</include>\n  <include location=\"global\">list</include>\n </includes>\n <resources>\n  <include location=\"icons.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>categoryListBox</sender>\n   <signal>currentRowChanged(int)</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>showCategory(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>31</x>\n     <y>63</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>cancelPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>126</x>\n     <y>576</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>okPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>validate()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>39</x>\n     <y>576</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>useProxyCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>proxyTextLabel</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>240</x>\n     <y>329</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>240</x>\n     <y>357</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>useProxyCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>proxyLineEdit</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>240</x>\n     <y>329</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>353</x>\n     <y>357</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>useProxyCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>allRequestsCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>240</x>\n     <y>329</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>240</x>\n     <y>387</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>allowRedirectionCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>askUserRedirectCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>456</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>242</x>\n     <y>484</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>allowRedirectionCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>maxRedirectTextLabel</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>456</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>242</x>\n     <y>512</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>allowRedirectionCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>maxRedirectSpinBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>456</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>358</x>\n     <y>512</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>useProxyCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>proxyNonResolvableCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>240</x>\n     <y>329</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>240</x>\n     <y>415</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>allowReferCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>askUserReferCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>178</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>242</x>\n     <y>174</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>allowReferCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>refereeHoldCheckBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>178</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>242</x>\n     <y>170</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>profileComboBox</sender>\n   <signal>activated(QString)</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>changeProfile(QString)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>116</x>\n     <y>32</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openRingtoneToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseRingtone()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>60</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openRingbackToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseRingback()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>64</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openIncomingCallScriptToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseIncomingCallScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>60</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>addCodecPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>addCodec()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>451</x>\n     <y>266</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>rmvCodecPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>removeCodec()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>451</x>\n     <y>300</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>upCodecPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>upCodec()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>702</x>\n     <y>266</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>downCodecPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>downCodec()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>702</x>\n     <y>300</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>availCodecListBox</sender>\n   <signal>itemDoubleClicked(QListWidgetItem*)</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>addCodec()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>243</x>\n     <y>211</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>activeCodecListBox</sender>\n   <signal>itemDoubleClicked(QListWidgetItem*)</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>removeCodec()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>493</x>\n     <y>211</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openInCallAnsweredToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseInCallAnsweredScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>62</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openInCallFailedToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseInCallFailedScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>63</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openLocalReleaseToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseLocalReleaseScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>68</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openOutCallAnsweredToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseOutCallAnsweredScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>66</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openOutCallFailedToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseOutCallFailedScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>67</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openOutCallToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseOutgoingCallScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>64</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>openRemoteReleaseToolButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>chooseRemoteReleaseScript()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>281</x>\n     <y>70</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>upConversionPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>upConversion()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>266</x>\n     <y>101</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>downConversionPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>downConversion()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>266</x>\n     <y>103</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>addConversionPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>addConversion()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>228</x>\n     <y>89</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>editConversionPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>editConversion()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>260</x>\n     <y>89</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>removePushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>removeConversion()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>244</x>\n     <y>89</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>testConversionPushButton</sender>\n   <signal>clicked()</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>testConversion()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>267</x>\n     <y>80</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>zrtpEnabledCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>zrtpSettingsGroupBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>225</x>\n     <y>58</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>225</x>\n     <y>62</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>mwiTypeComboBox</sender>\n   <signal>activated(int)</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>changeMWIType(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>269</x>\n     <y>64</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>regAddQvalueCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>regQvalueLineEdit</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>241</x>\n     <y>251</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>447</x>\n     <y>250</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>sipTransportComboBox</sender>\n   <signal>activated(int)</signal>\n   <receiver>UserProfileForm</receiver>\n   <slot>changeSipTransportProtocol(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>368</x>\n     <y>158</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>spxDspAgcCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>spxDspAgcLevelTextLabel</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>179</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>258</x>\n     <y>179</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>spxDspAgcCheckBox</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>spxDspAgcLevelSpinBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>242</x>\n     <y>179</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>274</x>\n     <y>179</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>natStunRadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>stunServerLineEdit</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>418</x>\n     <y>351</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>431</x>\n     <y>374</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>natStaticRadioButton</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>publicIPLineEdit</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>334</x>\n     <y>285</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>437</x>\n     <y>312</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/wizardform.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    \n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    \n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    \n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <QRegularExpression>\n#include <QLineEdit>\n#include <QLabel>\n#include <QValidator>\n#include <QComboBox>\n#include <QTextStream>\n#include \"gui.h\"\n#include <QFile>\n#include <QRegularExpressionValidator>\n#include \"wizardform.h\"\n\n#define PROV_NONE\tQT_TRANSLATE_NOOP(\"WizardForm\", \"None (direct IP to IP calls)\")\n#define PROV_OTHER\tQT_TRANSLATE_NOOP(\"WizardForm\", \"Other\")\n\nstruct t_provider {\n\tQString domain;\n\tQString sip_proxy;\n\tQString stun_server;\n};\n\n/*\n *  Constructs a WizardForm as a child of 'parent', with the\n *  name 'name' and widget flags set to 'f'.\n *\n *  The dialog will by default be modeless, unless you set 'modal' to\n *  true to construct a modal dialog.\n */\nWizardForm::WizardForm(QWidget* parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n\n\tinit();\n}\n\n/*\n *  Destroys the object and frees any allocated resources\n */\nWizardForm::~WizardForm()\n{\n\t// no need to delete child widgets, Qt does it all for us\n}\n\n/*\n *  Sets the strings of the subwidgets using the current\n *  language.\n */\nvoid WizardForm::languageChange()\n{\n\tretranslateUi(this);\n}\n\n\nvoid WizardForm::init()\n{\n\tQRegularExpression rxNoSpace(\"\\\\S*\");\n\t\n\t// Set validators\n\tusernameLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\tdomainLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\tauthNameLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\tproxyLineEdit->setValidator(new QRegularExpressionValidator(rxNoSpace, this));\n\t\n\tinitProviders();\n    serviceProviderComboBox->setCurrentIndex(serviceProviderComboBox->count() - 1);\n\tupdate(tr(PROV_OTHER));\n}\n\nvoid WizardForm::initProviders()\n{\n\tserviceProviderComboBox->clear();\n    serviceProviderComboBox->addItem(tr(PROV_NONE));\n\t\n\tQString fname = sys_config->get_dir_share().c_str();\n\tfname.append(\"/\").append(FILE_PROVIDERS);\n\tQFile providersFile(fname);\n\tif (providersFile.open(QIODevice::ReadOnly)) {\n\t\tQTextStream providersStream(&providersFile);\n\t\tQString entry;\n\t\twhile (!(entry = providersStream.readLine()).isNull()) {\n\t\t\t// Skip comment\n\t\t\tif (entry[0] == '#') continue;\n\t\t\t\n            QStringList l = entry.split(\";\", QString::KeepEmptyParts);\n\t\t\t\n\t\t\t// Skip invalid lines\n\t\t\tif (l.size() != 4) continue;\n\t\t\t\n\t\t\tt_provider p;\n\t\t\tp.domain = l[1];\n\t\t\tp.sip_proxy = l[2];\n\t\t\tp.stun_server = l[3];\n\t\t\tmapProviders[l[0]] = p;\n\t\t\t\n            serviceProviderComboBox->addItem(l[0]);\n\t\t}\n\t\tprovidersFile.close();\n\t}\n\t\n    serviceProviderComboBox->addItem(tr(PROV_OTHER));\n}\n\nint WizardForm::exec(t_user *user)\n{\n\tuser_config = user;\n\t\n\t// Set user profile name in the titlebar\n\tQString s = PRODUCT_NAME;\n\ts.append(\" - \").append(tr(\"User profile wizard:\")).append(\" \");\n\ts.append(user_config->get_profile_name().c_str());\n    setWindowTitle(s);\n\t\n\treturn QDialog::exec();\n}\n\nvoid WizardForm::show(t_user *user)\n{\n\tuser_config = user;\n\t\n\t// Set user profile name in the titlebar\n\tQString s = PRODUCT_NAME;\n\ts.append(\" - \").append(tr(\"User profile wizard:\")).append(\" \");\n\ts.append(user_config->get_profile_name().c_str());\n    setWindowTitle(s);\n\t\n\tQDialog::show();\n}\n\nvoid WizardForm::update(const QString &item)\n{\n\t// Disable/Enable controls\n\tif (item == tr(PROV_NONE)) {\n\t\tsuggestAuthName = false;\n\t\tauthNameTextLabel->setEnabled(false);\n\t\tauthNameLineEdit->setEnabled(false);\n\t\tauthPasswordTextLabel->setEnabled(false);\n\t\tauthPasswordLineEdit->setEnabled(false);\n\t\tproxyTextLabel->setEnabled(false);\n\t\tproxyLineEdit->setEnabled(false);\n\t\tstunServerTextLabel->setEnabled(false);\n\t\tstunServerLineEdit->setEnabled(false);\n\t} else {\n\t\tif (usernameLineEdit->text() == authNameLineEdit->text()) {\n\t\t\tsuggestAuthName = true;\n\t\t} else {\n\t\t\tsuggestAuthName = false;\n\t\t}\n\t\t\n\t\tauthNameTextLabel->setEnabled(true);\n\t\tauthNameLineEdit->setEnabled(true);\n\t\tauthPasswordTextLabel->setEnabled(true);\n\t\tauthPasswordLineEdit->setEnabled(true);\n\t\tproxyTextLabel->setEnabled(true);\n\t\tproxyLineEdit->setEnabled(true);\n\t\tstunServerTextLabel->setEnabled(true);\n\t\tstunServerLineEdit->setEnabled(true);\n\t}\n\t\n\t// Set values\n\tif (item == tr(PROV_NONE)) {\n\t\tdomainLineEdit->clear();\n\t\tauthNameLineEdit->clear();\n\t\tauthPasswordLineEdit->clear();\n\t\tproxyLineEdit->clear();\n\t\tstunServerLineEdit->clear();\n\t} else if (item == tr(PROV_OTHER)) {\n\t\tdomainLineEdit->clear();\n\t\tstunServerLineEdit->clear();\n\t\tproxyLineEdit->clear();\n\t} else {\n\t\tt_provider p = mapProviders[item];\n\t\tdomainLineEdit->setText(p.domain);\n\t\tproxyLineEdit->setText(p.sip_proxy);\n\t\tstunServerLineEdit->setText(p.stun_server);\n\t}\n}\n\nvoid WizardForm::updateAuthName(const QString &s)\n{\n\tif (suggestAuthName) {\n\t\tauthNameLineEdit->setText(s);\n\t}\n}\n\nvoid WizardForm::disableSuggestAuthName()\n{\n\tsuggestAuthName = false;\n}\n\nvoid WizardForm::validate()\n{\n\tQString s;\n\t\n\t// Validity check user page\n\t// SIP username is mandatory\n\tif (usernameLineEdit->text().isEmpty()) {\n        ((t_gui *)ui)->cb_show_msg(this, tr(\"You must fill in a user name for your SIP account.\").toStdString(),\n\t\t\t\tMSG_CRITICAL);\n\t\tusernameLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\t// SIP user domain is mandatory\n\tif (domainLineEdit->text().isEmpty()) {\n\t\t((t_gui *)ui)->cb_show_msg(this, tr(\n\t\t\t\t\"You must fill in a domain name for your SIP account.\\n\"\n\t\t\t\t\"This could be the hostname or IP address of your PC \"\n                \"if you want direct PC to PC dialing.\").toStdString(),\n\t\t\t\tMSG_CRITICAL);\n\t\tdomainLineEdit->setFocus();\n\t\treturn;\n\t}\n\t\n\t// SIP proxy\n\tif (proxyLineEdit->text() != \"\") {\n\t\ts = USER_SCHEME;\n\t\ts.append(':').append(proxyLineEdit->text());\n        t_url u(s.toStdString());\n\t\tif (!u.is_valid() || u.get_user() != \"\") {\n            ((t_gui *)ui)->cb_show_msg(this, tr(\"Invalid value for SIP proxy.\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tproxyLineEdit->setFocus();\n\t\t\tproxyLineEdit->selectAll();\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// Register and publish presence at startup\n\tif (serviceProviderComboBox->currentText() == tr(PROV_NONE)) {\n\t\tuser_config->set_register_at_startup(false);\n\t\tuser_config->set_pres_publish_startup(false);\n\t}\n\t\n\t// STUN server\n\tif (stunServerLineEdit->text() != \"\") {\n\t\ts = \"stun:\";\n\t\ts.append(stunServerLineEdit->text());\n        t_url u(s.toStdString());\n\t\tif (!u.is_valid() || u.get_user() != \"\") {\n            ((t_gui *)ui)->cb_show_msg(this, tr(\"Invalid value for STUN server.\").toStdString(),\n\t\t\t\t\tMSG_CRITICAL);\n\t\t\tstunServerLineEdit->setFocus();\n\t\t\tstunServerLineEdit->selectAll();\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// Set all values in the user_config object\n\t// USER\n    user_config->set_display(displayLineEdit->text().toStdString());\n    user_config->set_name(usernameLineEdit->text().toStdString());\n    user_config->set_domain(domainLineEdit->text().toStdString());\n    user_config->set_auth_name(authNameLineEdit->text().toStdString());\n    user_config->set_auth_pass(authPasswordLineEdit->text().toStdString());\n\t\n\t// SIP SERVER\n\tuser_config->set_use_outbound_proxy(!proxyLineEdit->text().isEmpty());\n\ts = USER_SCHEME;\n\ts.append(':').append(proxyLineEdit->text());\n    user_config->set_outbound_proxy(t_url(s.toStdString()));\n\t\n\t// NAT\n\tuser_config->set_use_stun(!stunServerLineEdit->text().isEmpty());\n\ts = \"stun:\";\n\ts.append(stunServerLineEdit->text());\n    user_config->set_stun_server(t_url(s.toStdString()));\n\t\n\t// Save user config\n\tstring error_msg;\n\tif (!user_config->write_config(user_config->get_filename(), error_msg)) {\n\t\t// Failed to write config file\n\t\t((t_gui *)ui)->cb_show_msg(this, error_msg, MSG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\temit success();\n\taccept();\n}\n"
  },
  {
    "path": "src/gui/wizardform.h",
    "content": "#ifndef WIZARDFORM_H\n#define WIZARDFORM_H\n\nstruct t_provider;\n\n#include <map>\n#include \"user.h\"\n#include \"ui_wizardform.h\"\n\nclass WizardForm : public QDialog, public Ui::WizardForm\n{\n\tQ_OBJECT\n\npublic:\n    WizardForm(QWidget* parent = 0);\n\t~WizardForm();\n\n\tvirtual void show( t_user * user );\n\npublic slots:\n\tvirtual void initProviders();\n\tvirtual int exec( t_user * user );\n\tvirtual void update( const QString & item );\n\tvirtual void updateAuthName( const QString & s );\n\tvirtual void disableSuggestAuthName();\n\tvirtual void validate();\n\nsignals:\n\tvoid success();\n\nprotected slots:\n\tvirtual void languageChange();\n\nprivate:\n\tbool suggestAuthName;\n\tstd::map<QString, t_provider> mapProviders;\n\tt_user *user_config;\n\n\tvoid init();\n\n};\n\n\n#endif\n"
  },
  {
    "path": "src/gui/wizardform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\" stdsetdef=\"1\">\n  <author></author>\n  <comment></comment>\n  <exportmacro></exportmacro>\n  <class>WizardForm</class>\n  <widget class=\"QDialog\" name=\"WizardForm\">\n    <property name=\"geometry\">\n      <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>596</width>\n        <height>321</height>\n      </rect>\n    </property>\n    <property name=\"windowTitle\">\n      <string>Twinkle - Wizard</string>\n    </property>\n    <layout class=\"QVBoxLayout\">\n      <item>\n        <layout class=\"QGridLayout\">\n          <item row=\"8\" column=\"1\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"stunServerLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The hostname, domain name or IP address of the STUN server.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"8\" column=\"0\">\n            <widget class=\"QLabel\" name=\"stunServerTextLabel\">\n              <property name=\"text\">\n                <string>S&amp;TUN server:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>stunServerLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"1\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"usernameLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The SIP user name given to you by your provider. It is the user part in your SIP address, &lt;b&gt;username&lt;/b&gt;@domain.com This could be a telephone number.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"domainTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Domain*:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>domainLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"serviceProviderComboBox\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Choose your SIP service provider. If your SIP service provider is not in the list, then select &lt;b&gt;Other&lt;/b&gt; and fill in the settings you received from your provider.&lt;br&gt;&lt;br&gt;\nIf you select one of the predefined SIP service providers then you only have to fill in your name, user name, authentication name and password.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"authNameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Authentication name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>authNameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"dislpayTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Your name:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>displayLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"4\" column=\"1\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"authNameLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Your SIP authentication name. Quite often this is the same as your SIP user name. It can be a different name though.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"2\">\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>206</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item row=\"3\" column=\"1\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"domainLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The domain part of your SIP address, username@&lt;b&gt;domain.com&lt;/b&gt;. Instead of a real domain this could also be the hostname or IP address of your &lt;b&gt;SIP proxy&lt;/b&gt;. If you want direct IP phone to IP phone communications then you fill in the hostname or IP address of your computer.\n&lt;br&gt;&lt;br&gt;\nThis field is mandatory.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"1\" column=\"1\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"displayLineEdit\">\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>This is just your full name, e.g. John Doe. It is used as a display name. When you make a call, this display name might be shown to the called party.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"6\" column=\"0\" rowspan=\"2\" colspan=\"1\">\n            <widget class=\"QLabel\" name=\"proxyTextLabel\">\n              <property name=\"enabled\">\n                <bool>true</bool>\n              </property>\n              <property name=\"text\">\n                <string>SIP pro&amp;xy:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>proxyLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"7\" column=\"1\" rowspan=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"proxyLineEdit\">\n              <property name=\"enabled\">\n                <bool>true</bool>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>The hostname, domain name or IP address of your SIP proxy. If this is the same value as your domain, you may leave this field empty.</string>\n              </property>\n            </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"serviceProviderTextLabel\">\n              <property name=\"text\">\n                <string>&amp;SIP service provider:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>serviceProviderComboBox</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"5\" column=\"0\">\n            <widget class=\"QLabel\" name=\"authPasswordTextLabel\">\n              <property name=\"text\">\n                <string>&amp;Password:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>authPasswordLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"usernameTextLabel\">\n              <property name=\"text\">\n                <string>&amp;User name*:</string>\n              </property>\n              <property name=\"buddy\" stdset=\"0\">\n                <cstring>usernameLineEdit</cstring>\n              </property>\n              <property name=\"wordWrap\">\n                <bool>false</bool>\n              </property>\n            </widget>\n          </item>\n          <item row=\"5\" column=\"1\" rowspan=\"2\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"authPasswordLineEdit\">\n              <property name=\"echoMode\">\n                <enum>QLineEdit::Password</enum>\n              </property>\n              <property name=\"whatsThis\" stdset=\"0\">\n                <string>Your password for authentication.</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n      <item>\n        <spacer>\n          <property name=\"sizeHint\">\n            <size>\n              <width>20</width>\n              <height>20</height>\n            </size>\n          </property>\n          <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n          </property>\n        </spacer>\n      </item>\n      <item>\n        <layout class=\"QHBoxLayout\">\n          <item>\n            <spacer>\n              <property name=\"sizeHint\">\n                <size>\n                  <width>371</width>\n                  <height>20</height>\n                </size>\n              </property>\n              <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n              </property>\n              <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n              </property>\n            </spacer>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"okPushButton\">\n              <property name=\"text\">\n                <string>&amp;OK</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+O</string>\n              </property>\n              <property name=\"default\">\n                <bool>true</bool>\n              </property>\n            </widget>\n          </item>\n          <item>\n            <widget class=\"QPushButton\" name=\"cancelPushButton\">\n              <property name=\"text\">\n                <string>&amp;Cancel</string>\n              </property>\n              <property name=\"shortcut\">\n                <string>Alt+C</string>\n              </property>\n            </widget>\n          </item>\n        </layout>\n      </item>\n    </layout>\n  </widget>\n  <layoutdefault spacing=\"6\" margin=\"11\"/>\n  <pixmapfunction></pixmapfunction>\n  <tabstops>\n    <tabstop>serviceProviderComboBox</tabstop>\n    <tabstop>displayLineEdit</tabstop>\n    <tabstop>usernameLineEdit</tabstop>\n    <tabstop>domainLineEdit</tabstop>\n    <tabstop>authNameLineEdit</tabstop>\n    <tabstop>authPasswordLineEdit</tabstop>\n    <tabstop>proxyLineEdit</tabstop>\n    <tabstop>stunServerLineEdit</tabstop>\n    <tabstop>okPushButton</tabstop>\n    <tabstop>cancelPushButton</tabstop>\n  </tabstops>\n  <includes>\n    <include location=\"global\">map</include>\n    <include location=\"local\">user.h</include>\n  </includes>\n  <connections>\n    <connection>\n      <sender>okPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>WizardForm</receiver>\n      <slot>validate()</slot>\n    </connection>\n    <connection>\n      <sender>cancelPushButton</sender>\n      <signal>clicked()</signal>\n      <receiver>WizardForm</receiver>\n      <slot>reject()</slot>\n    </connection>\n    <connection>\n      <sender>usernameLineEdit</sender>\n      <signal>textChanged(QString)</signal>\n      <receiver>WizardForm</receiver>\n      <slot>updateAuthName(QString)</slot>\n    </connection>\n    <connection>\n      <sender>serviceProviderComboBox</sender>\n      <signal>activated(QString)</signal>\n      <receiver>WizardForm</receiver>\n      <slot>update(QString)</slot>\n    </connection>\n    <connection>\n      <sender>authNameLineEdit</sender>\n      <signal>editingFinished()</signal>\n      <receiver>WizardForm</receiver>\n      <slot>disableSuggestAuthName()</slot>\n    </connection>\n  </connections>\n</ui>\n"
  },
  {
    "path": "src/gui/yesnodialog.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"yesnodialog.h\"\n\n#include \"qlabel.h\"\n#include \"qlayout.h\"\n//Added by qt3to4:\n#include <QVBoxLayout>\n#include <QBoxLayout>\n#include <QHBoxLayout>\n\n#include \"userintf.h\"\n\n// class YesNoDialog\n\nvoid YesNoDialog::actionYes() {\n\tQDialog::accept();\n}\n\nvoid YesNoDialog::actionNo() {\n\tQDialog::reject();\n}\n\nYesNoDialog::YesNoDialog() {\n    setAttribute(Qt::WA_DeleteOnClose);\n}\n\nYesNoDialog::YesNoDialog(QWidget *parent, const QString &caption, const QString &text) :\n        QDialog(parent)\n{\n    setModal(true);\n    setAttribute(Qt::WA_DeleteOnClose);\n    setWindowTitle(caption);\n\n    QBoxLayout *vb = new QVBoxLayout(this);\n    vb->setContentsMargins(11, 11, 11, 11);\n    vb->setSpacing(6);\n\tQLabel *lblQuestion = new QLabel(text, this);\n\tvb->addWidget(lblQuestion);\n    QHBoxLayout *hb = new QHBoxLayout(this);\n    hb->setSpacing(6);\n\tQSpacerItem *spacer1 = new QSpacerItem(1, 1, QSizePolicy::Expanding, \n\t\t\t\t\t       QSizePolicy::Minimum );\n\thb->addItem(spacer1);\n\tpbYes = new QPushButton(tr(\"&Yes\"), this);\n\thb->addWidget(pbYes);\n\tpbNo = new QPushButton(tr(\"&No\"), this);\n\thb->addWidget(pbNo);\n\tQSpacerItem *spacer2 = new QSpacerItem(1, 1, QSizePolicy::Expanding, \n\t\t\t\t\t       QSizePolicy::Minimum );\n\thb->addItem(spacer2);\n\tvb->addLayout(hb);\n\t\n\tconnect(pbYes, SIGNAL(clicked()), this, SLOT(actionYes()));\n\tconnect(pbNo, SIGNAL(clicked()), this, SLOT(actionNo()));\n}\n\nYesNoDialog::~YesNoDialog() {}\n\nvoid YesNoDialog::reject() {\n\tpbNo->animateClick();\n}\n\n\n// class ReferPermissionDialog\n\nvoid ReferPermissionDialog::actionYes() {\n\tui->send_refer_permission(true);\n\tYesNoDialog::actionYes();\n}\n\nvoid ReferPermissionDialog::actionNo() {\n\tui->send_refer_permission(false);\n\tYesNoDialog::actionNo();\n}\n\nReferPermissionDialog::ReferPermissionDialog(QWidget *parent, const QString &caption, \n\t\t\t\t\t     const QString &text) :\n\t\tYesNoDialog(parent, caption, text)\n{}\n"
  },
  {
    "path": "src/gui/yesnodialog.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _YESNODIALOG_H\n#define _YESNODIALOG_H\n\n#include \"qdialog.h\"\n#include \"qpushbutton.h\"\n#include \"qstring.h\"\n\nclass YesNoDialog : public QDialog {\nprivate:\n\tQ_OBJECT\n\tQPushButton *pbYes;\n\tQPushButton *pbNo;\n\t\nprotected slots:\n\tvirtual void actionYes();\n\tvirtual void actionNo();\n\t\npublic:\n\tYesNoDialog();\n\tYesNoDialog(QWidget *parent, const QString &caption, const QString &text);\n\tvirtual ~YesNoDialog();\n\t\n\tvoid reject();\n};\n\nclass ReferPermissionDialog : public YesNoDialog {\nprivate:\n\tQ_OBJECT\n\nprotected slots:\n\tvirtual void actionYes();\n\tvirtual void actionNo();\n\npublic:\n\tReferPermissionDialog(QWidget *parent, const QString &caption, const QString &text);\n};\n\n#endif\n"
  },
  {
    "path": "src/id_object.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"id_object.h\"\n\n// Initialization of static members\nt_mutex t_id_object::mtx_next_id;\nt_object_id t_id_object::next_id = 1;\n\nt_id_object::t_id_object() {\n\tmtx_next_id.lock();\n\tid = next_id++;\n\tif (next_id == 65535) next_id = 1;\n\tmtx_next_id.unlock();\n}\n\nt_object_id t_id_object::get_object_id() {\n\treturn id;\n}\n\nvoid t_id_object::generate_new_id() {\n\tmtx_next_id.lock();\n\tid = next_id++;\n\tif (next_id == 65535) next_id = 1;\n\tmtx_next_id.unlock();\n}\n"
  },
  {
    "path": "src/id_object.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Objects with a unique object id.\n */\n\n#ifndef _ID_OBJECT_H\n#define _ID_OBJECT_H\n\n#include \"threads/mutex.h\"\n\n/**\n * Object identifier.\n */\ntypedef unsigned short\t\tt_object_id;\n\n/**\n * Parent class for objects that need a unique object id.\n */\nclass t_id_object {\nprivate:\n\t/** Mutex for concurrent object id creation. */\n\tstatic t_mutex\t\tmtx_next_id;\n\t\n\t/** Id for the next object. */\n\tstatic t_object_id\tnext_id;\n\t\n\t/** Unique object identifier. */\n\tt_object_id\t\tid;\n\t\npublic:\n\t/** Constructor */\n\tt_id_object();\n\t\n\t/** \n\t * Get the object id.\n\t * @return Object id.\n\t */\n\tt_object_id get_object_id();\n\t\n\t/**\n\t * Generate a new object identifier. This can be useful\n\t * after making a copy of an object.\n\t */\n\tvoid generate_new_id();\n};\n\n#endif\n"
  },
  {
    "path": "src/im/CMakeLists.txt",
    "content": "project(libtwinkle-im)\n\nset(LIBTWINKLE_IM-SRCS\n\tim_iscomposing_body.cpp\n\tmsg_session.cpp\n)\n\nadd_library(libtwinkle-im OBJECT ${LIBTWINKLE_IM-SRCS})\n"
  },
  {
    "path": "src/im/im_iscomposing_body.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"im_iscomposing_body.h\"\n\n#include <cassert>\n#include <libxml/parser.h>\n\n#include \"log.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\n#define IM_ISCOMPOSING_NAMESPACE\t\t\"urn:ietf:params:xml:ns:im-iscomposing\"\n\n#define IS_IM_ISCOMPOSING_TAG(node, tag)\tIS_XML_TAG(node, tag, IM_ISCOMPOSING_NAMESPACE)\n\t\t\t\t\n#define IS_IM_ISCOMPOSING_ATTR(attr, attr_name) IS_XML_ATTR(attr, attr_name, IM_ISCOMPOSING_NAMESPACE)\n\nbool t_im_iscomposing_xml_body::extract_data(void) {\n\tassert(xml_doc);\n\t\n\tstate_.clear();\n\trefresh_ = 0;\n\n\txmlNode *root_element = NULL;\n\t\n\t// Get root\n\troot_element = xmlDocGetRootElement(xml_doc);\n\tif (!root_element) {\n\t\tlog_file->write_report(\"im-iscomposing document has no root element.\",\n\t\t\t\"t_im_iscomposing_xml_body::extract_data\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\treturn false;\n\t}\n\t\n\t// Check if root is <isComposing>\n\tif (!IS_IM_ISCOMPOSING_TAG(root_element, \"isComposing\")) {\n\t\tlog_file->write_report(\"im-iscomposing document has invalid root element.\",\n\t\t\t\"t_im_iscomposing_xml_body::extract_data\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\treturn false;\n\t}\n\t\n\txmlNode *child = root_element->children;\n\t\n\t// Process children of root.\n\tbool state_present = false;\n\t\n\tfor (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) {\n\t\tif (IS_IM_ISCOMPOSING_TAG(cur_node, \"state\")) {\n\t\t\tstate_present = process_node_state(cur_node);\n\t\t} else if (IS_IM_ISCOMPOSING_TAG(cur_node, \"refresh\")) {\n\t\t\tprocess_node_refresh(cur_node);\n\t\t}\n\t}\n\t\n\t// The state node is mandatory, so return only true if it is present.\n\treturn state_present;\n}\n\nbool t_im_iscomposing_xml_body::process_node_state(xmlNode *node) {\n\tassert(node);\n\t\n\txmlNode *child = node->children;\n\tif (child && child->type == XML_TEXT_NODE) {\n\t\tstate_ = tolower((char*)child->content);\n\t} else {\n\t\tlog_file->write_report(\"<state> element has no content.\",\n\t\t\t\"t_im_iscomposing_xml_body::process_node_state\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nvoid t_im_iscomposing_xml_body::process_node_refresh(xmlNode *node) {\n\tassert(node);\n\t\n\txmlNode *child = node->children;\n\tif (child && child->type == XML_TEXT_NODE) {\n\t\trefresh_ = atoi((char*)child->content);\n\t} else {\n\t\tlog_file->write_report(\"<refresh> element has no content.\",\n\t\t\t\"t_im_iscomposing_xml_body::process_node_refresh\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t}\n}\n\nvoid t_im_iscomposing_xml_body::create_xml_doc(\n\t\tconst string &xml_version, \n\t\tconst string &charset) \n{\n\tt_sip_body_xml::create_xml_doc(xml_version, charset);\n\t\n\t// isComposing\n\txmlNode *node_iscomposing = xmlNewNode(NULL, BAD_CAST \"isComposing\");\n\txmlNs *ns_im_iscomposing = xmlNewNs(node_iscomposing, BAD_CAST IM_ISCOMPOSING_NAMESPACE, NULL);\n\txmlDocSetRootElement(xml_doc, node_iscomposing);\n\t\n\t// state\n\txmlNewChild(node_iscomposing, ns_im_iscomposing, \n\t\t\tBAD_CAST \"state\", \n\t\t\tBAD_CAST state_.c_str());\n\t\t\t\n\t// refresh\n\tif (refresh_ > 0) {\n\t\txmlNewChild(node_iscomposing, ns_im_iscomposing,\n\t\t\t\tBAD_CAST \"refresh\",\n\t\t\t\tBAD_CAST int2str(refresh_).c_str());\n\t}\n}\n\nt_im_iscomposing_xml_body::t_im_iscomposing_xml_body() : t_sip_body_xml (),\n\tstate_(IM_ISCOMPOSING_STATE_IDLE),\n\trefresh_(0)\n{}\n\nt_sip_body *t_im_iscomposing_xml_body::copy(void) const {\n\tt_im_iscomposing_xml_body *body = new t_im_iscomposing_xml_body(*this);\n\tMEMMAN_NEW(body);\n\t\n\t// Clear the xml_doc pointer in the new body, as a copy of the\n\t// XML document must be copied to the body.\n\tbody->xml_doc = NULL;\n\t\n\tcopy_xml_doc(body);\n\t\n\treturn body;\n}\n\nt_body_type t_im_iscomposing_xml_body::get_type(void) const {\n\treturn BODY_IM_ISCOMPOSING_XML;\n}\n\nt_media t_im_iscomposing_xml_body::get_media(void) const {\n\treturn t_media(\"application\", \"im-iscomposing+xml\");\n}\n\nbool t_im_iscomposing_xml_body::parse(const string &s) {\n\tif (t_sip_body_xml::parse(s)) {\n\t\tif (!extract_data()) {\n\t\t\tMEMMAN_DELETE(xml_doc);\n\t\t\txmlFreeDoc(xml_doc);\n\t\t\txml_doc = NULL;\n\t\t}\n\t}\n\t\n\treturn (xml_doc != NULL);\n}\n\nstring t_im_iscomposing_xml_body::get_state(void) const {\n\treturn state_;\n}\n\ntime_t t_im_iscomposing_xml_body::get_refresh(void) const {\n\treturn refresh_;\n}\n\nvoid t_im_iscomposing_xml_body::set_state(const string &state) {\n\tstate_ = state;\n}\n\nvoid t_im_iscomposing_xml_body::set_refresh(time_t refresh) {\n\trefresh_ = refresh;\n}\n"
  },
  {
    "path": "src/im/im_iscomposing_body.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * RFC 3994 im-iscomposing+xml body\n */\n\n#ifndef _IM_ISCOMPOSING_BODY_H\n#define _IM_ISCOMPOSING_BODY_H\n\n#include <string>\n#include <time.h>\n#include <libxml/tree.h>\n#include \"parser/sip_body.h\"\n\nusing namespace std;\n\n//@{\n/** @name Message composition states */\n#define IM_ISCOMPOSING_STATE_IDLE\t\"idle\"\n#define IM_ISCOMPOSING_STATE_ACTIVE\t\"active\"\n//@}\n\n/** RFC 3994 im-iscomposing+xml body */\nclass t_im_iscomposing_xml_body : public t_sip_body_xml {\nprivate:\n\tstring\t\tstate_;\t\t/**< Composition state */\n\ttime_t\t\trefresh_;\t/**< Refresh interval in seconds */\n\t\n\t/** Extract information elements from the XML document. */\n\tbool extract_data(void);\n\t\n\t/** \n\t * Process the state element.\n\t * @param node [in] The state element.\n\t */\n\tbool process_node_state(xmlNode *node);\n\t\n\t/** \n\t * Process the refresh element.\n\t * @param node [in] The refresh element.\n\t */\n\tvoid process_node_refresh(xmlNode *node);\n\t\nprotected:\n\t/**\n\t * Create a im-iscomposing document from the values stored in the attributes.\n\t */\n\tvirtual void create_xml_doc(const string &xml_version = \"1.0\", const string &charset = \"UTF-8\");\n\t\npublic:\n\t/** Constructor */\n\tt_im_iscomposing_xml_body();\n\t\n\tvirtual t_sip_body *copy(void) const;\n\tvirtual t_body_type get_type(void) const;\n\tvirtual t_media get_media(void) const;\n\tvirtual bool parse(const string &s);\n\t\n\t/** @name Getters */\n\t//@{\n\tstring get_state(void) const;\n\ttime_t get_refresh(void) const;\n\t//@}\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_state(const string &state);\n\tvoid set_refresh(time_t refresh);\n\t//@}\n};\n\n#endif\n"
  },
  {
    "path": "src/im/msg_session.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"msg_session.h\"\n\n#include <cassert>\n#include <sys/time.h>\n\n#include \"im_iscomposing_body.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"translator.h\"\n#include \"parser/media_type.h\"\n#include \"utils/file_utils.h\"\n\n#define COMPOSING_LOCAL_IDLE_TIMEOUT\t15\n#define COMPOSING_LOCAL_REFRESH_TIMEOUT\t90\n\nextern t_phone *phone;\n\nusing namespace im;\nusing namespace utils;\n\nt_composing_state im::string2composing_state(const string &state_name) {\n\tif (state_name == IM_ISCOMPOSING_STATE_ACTIVE) {\n\t\treturn COMPOSING_STATE_ACTIVE;\n\t}\n\t\n\treturn COMPOSING_STATE_IDLE;\n}\n\nstring im::composing_state2string(t_composing_state state) {\n\tswitch (state) {\n\tcase COMPOSING_STATE_IDLE:\n\t\treturn \"idle\";\n\tcase COMPOSING_STATE_ACTIVE:\n\t\treturn \"active\";\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\treturn \"idle\";\n}\n\n// class t_msg\n\nt_msg::t_msg() :\n\thas_attachment(false)\n{\n\tstruct timeval t;\n\n\tgettimeofday(&t, NULL);\n\ttimestamp = t.tv_sec;\n}\n\nt_msg::t_msg(const string &msg, t_direction dir, t_text_format fmt) :\n\tmessage(msg),\n\tdirection(dir),\n\tformat(fmt),\n\thas_attachment(false)\n{\n\tstruct timeval t;\n\n\tgettimeofday(&t, NULL);\n\ttimestamp = t.tv_sec;\n}\n\nvoid t_msg::set_attachment(const string &filename, const t_media &media, const string &save_as) {\n\tattachment_filename = filename;\n\tattachment_media = media;\n\tattachment_save_as_name = save_as;\n\thas_attachment = true;\n}\n\n// class t_msg_session\n\nt_msg_session::t_msg_session(t_user *u) :\n\tuser_config(u),\n\tnew_message_added(false),\n\terror_recvd(false),\n\tdelivery_notification_recvd(false),\n\tmsg_in_flight(false),\n\tsend_composing_state(u->get_im_send_iscomposing()),\n\tlocal_composing_state(COMPOSING_STATE_IDLE),\n\tlocal_idle_timeout(0),\n\tlocal_refresh_timeout(0),\n\tremote_composing_state(COMPOSING_STATE_IDLE),\n\tremote_idle_timeout(0)\n{}\n\nt_msg_session::t_msg_session(t_user *u, t_display_url _remote_party) :\n\tuser_config(u),\n\tremote_party(_remote_party),\n\tnew_message_added(false),\n\terror_recvd(false),\n\tdelivery_notification_recvd(false),\n\tmsg_in_flight(false),\n\tsend_composing_state(u->get_im_send_iscomposing()),\n\tlocal_composing_state(COMPOSING_STATE_IDLE),\n\tlocal_idle_timeout(0),\n\tlocal_refresh_timeout(0),\n\tremote_composing_state(COMPOSING_STATE_IDLE),\n\tremote_idle_timeout(0)\n{}\n\nt_msg_session::~t_msg_session() {\n\t// Remove temporary files.\n\tfor (list<t_msg>::iterator it = messages.begin(); it != messages.end(); ++it) {\n\t\t// Temporary files are created for incoming messages only.\n\t\tif (it->has_attachment && it->direction == MSG_DIR_IN) {\n\t\t\t// Defensive check to make sure we are deleting tmp files only.\n\t\t\tif (sys_config->is_tmpfile(it->attachment_filename)) {\n\t\t\t\tlog_file->write_header(\"t_msg_session::~t_msg_session\");\n\t\t\t\tlog_file->write_raw(\"Remove tmp file \");\n\t\t\t\tlog_file->write_raw(it->attachment_filename);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\t\tunlink(it->attachment_filename.c_str());\n\t\t\t}\n\t\t}\n\t}\n}\n\nt_user *t_msg_session::get_user(void) const {\n\treturn user_config;\n}\n\nt_display_url t_msg_session::get_remote_party(void) const {\n\treturn remote_party;\n}\n\nt_composing_state t_msg_session::get_remote_composing_state(void) const {\n\treturn remote_composing_state;\n}\n\nvoid t_msg_session::set_user(t_user *u) {\n\tuser_config = u;\n}\n\nvoid t_msg_session::set_remote_party(const t_display_url &du) {\n\tremote_party = du;\n}\n\nvoid t_msg_session::set_send_composing_state(bool enable) {\n\tsend_composing_state = enable;\n}\n\nt_msg t_msg_session::get_last_message(void) {\n\tnew_message_added = false;\n\tif (messages.empty()) {\n\t\tthrow empty_list_exception();\n\t}\n\treturn messages.back();\n}\n\nbool t_msg_session::is_new_message_added(void) const {\n\treturn new_message_added;\n}\n\nvoid t_msg_session::set_display_if_empty(const string &display) {\n\tif (remote_party.display.empty()) {\n\t\tremote_party.display = display;\n\t}\n}\n\nconst list<t_msg> &t_msg_session::get_messages(void) const {\n\treturn messages;\n}\n\nvoid t_msg_session::recv_msg(const t_msg &msg) {\n\t// RFC 3994 3.3\n\t// The composing state of the remote party transitions to idle\n\t// when a message is received.\n\tremote_composing_state = COMPOSING_STATE_IDLE;\n\tremote_idle_timeout = 0;\n\n\tmessages.push_back(msg);\n\tnew_message_added = true;\n\tnotify();\n}\n\nvoid t_msg_session::send_msg(const string &message, t_text_format format) {\n\t// RFC 3994 3.2\n\t// If a content message is sent before the idle threshold expires, no\n\t// \"idle\" state indication is needed.\n\t// The local state is set to idle without sending an indication to the\n\t// remote party.\n\tlocal_composing_state = COMPOSING_STATE_IDLE;\n\tlocal_idle_timeout = 0;\n\tlocal_refresh_timeout = 0;\n\n\tt_msg msg(message, im::MSG_DIR_OUT, format);\n\tmessages.push_back(msg);\n\tnew_message_added = true;\n\t\n\tbool ret = phone->pub_send_message(user_config, remote_party.url, remote_party.display, msg);\n\t\n\tif (ret) {\n\t\tmsg_in_flight = true;\n\t} else {\n\t\tset_error(TRANSLATE(\"Failed to send message.\"));\n\t}\n\t\n\tnotify();\n}\n\nvoid t_msg_session::send_file(const string &filename, const t_media &media, const string &subject) {\n\t// RFC 3994 3.2\n\t// If a content message is sent before the idle threshold expires, no\n\t// \"idle\" state indication is needed.\n\t// The local state is set to idle without sending an indication to the\n\t// remote party.\n\tlocal_composing_state = COMPOSING_STATE_IDLE;\n\tlocal_idle_timeout = 0;\n\tlocal_refresh_timeout = 0;\n\n\tt_msg msg;\n\tmsg.set_attachment(filename, media, strip_path_from_filename(filename));\n\tmsg.subject = subject;\n\tmsg.direction = MSG_DIR_OUT;\n\tmessages.push_back(msg);\n\tnew_message_added = true;\n\t\n\tbool ret = phone->pub_send_message(user_config, remote_party.url, remote_party.display, msg);\n\t\n\tif (ret) {\n\t\tmsg_in_flight = true;\n\t\tnotify();\n\t} else {\n\t\t// Notify user interface about the sent message before\n\t\t// setting the error.\n\t\tnotify();\n\t\t\n\t\tset_error(TRANSLATE(\"Failed to send message.\"));\n\t}\n}\n\nvoid t_msg_session::set_error(const string &message) {\n\terror_msg = message;\n\terror_recvd = true;\n\tnotify();\n}\n\nbool t_msg_session::error_received(void) const {\n\treturn error_recvd;\n}\n\nstring t_msg_session::take_error(void) {\n\tif (!error_recvd) return \"\";\n\terror_recvd = false;\n\treturn error_msg;\n}\n\nvoid t_msg_session::set_delivery_notification(const string &notification) {\n\tdelivery_notification = notification;\n\tdelivery_notification_recvd = true;\n\tnotify();\n}\n\nbool t_msg_session::delivery_notification_received(void) const {\n\treturn delivery_notification_recvd;\n}\n\nstring t_msg_session::take_delivery_notification(void) {\n\tif (!delivery_notification_recvd) return \"\";\n\tdelivery_notification_recvd = false;\n\treturn delivery_notification;\n}\n\nbool t_msg_session::match(t_user *user, t_url _remote_party) {\n\treturn user == user_config && _remote_party == remote_party.url;\n}\n\nvoid t_msg_session::set_msg_in_flight(bool in_flight) {\n\tmsg_in_flight = in_flight;\n\tnotify();\n}\n\nbool t_msg_session::is_msg_in_flight(void) const {\n\treturn msg_in_flight;\n}\n\nvoid t_msg_session::set_local_composing_state(t_composing_state state) {\n\tif (!remote_party.is_valid()) {\n\t\t// The session is not yet established\n\t\treturn;\n\t}\n\n\tswitch (local_composing_state) {\n\tcase COMPOSING_STATE_IDLE:\n\t\tswitch (state) {\n\t\tcase COMPOSING_STATE_IDLE:\n\t\t\tbreak;\n\t\tcase COMPOSING_STATE_ACTIVE:\n\t\t\tlocal_composing_state = state;\n\t\t\tlocal_idle_timeout = COMPOSING_LOCAL_IDLE_TIMEOUT;\n\t\t\tlocal_refresh_timeout = COMPOSING_LOCAL_REFRESH_TIMEOUT - 10;\n\t\t\t\n\t\t\tif (send_composing_state) {\n\t\t\t\t(void)phone->pub_send_im_iscomposing(\n\t\t\t\t\tuser_config, remote_party.url, \n\t\t\t\t\tremote_party.display,\n\t\t\t\t\tIM_ISCOMPOSING_STATE_ACTIVE,\n\t\t\t\t\tCOMPOSING_LOCAL_REFRESH_TIMEOUT);\n\t\t\t}\n\t\t\t\t\t\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t\t\n\t\tbreak;\t\n\tcase COMPOSING_STATE_ACTIVE:\n\t\tswitch (state) {\n\t\tcase COMPOSING_STATE_IDLE:\n\t\t\tlocal_composing_state = state;\n\t\t\tlocal_idle_timeout = 0;\n\t\t\tlocal_refresh_timeout = 0;\n\t\t\t\n\t\t\tif (send_composing_state) {\n\t\t\t\t(void)phone->pub_send_im_iscomposing(\n\t\t\t\t\tuser_config, remote_party.url, \n\t\t\t\t\tremote_party.display,\n\t\t\t\t\tIM_ISCOMPOSING_STATE_IDLE,\n\t\t\t\t\tCOMPOSING_LOCAL_REFRESH_TIMEOUT);\n\t\t\t}\n\t\t\t\t\t\n\t\t\tbreak;\n\t\tcase COMPOSING_STATE_ACTIVE:\n\t\t\tlocal_idle_timeout = COMPOSING_LOCAL_IDLE_TIMEOUT;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t\t\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_msg_session::set_remote_composing_state(t_composing_state state, time_t idle_timeout) {\n\tswitch (remote_composing_state) {\n\tcase COMPOSING_STATE_IDLE:\n\t\tswitch (state) {\n\t\tcase COMPOSING_STATE_IDLE:\n\t\t\tbreak;\n\t\tcase COMPOSING_STATE_ACTIVE:\n\t\t\tremote_composing_state = state;\n\t\t\tremote_idle_timeout = idle_timeout;\n\t\t\tnotify();\n\t\t\t\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t\t\n\t\tbreak;\n\tcase COMPOSING_STATE_ACTIVE:\n\t\tswitch (state) {\n\t\tcase COMPOSING_STATE_IDLE:\n\t\t\tremote_composing_state = state;\n\t\t\tremote_idle_timeout = 0;\n\t\t\tnotify();\n\t\t\t\n\t\t\tbreak;\n\t\tcase COMPOSING_STATE_ACTIVE:\n\t\t\tremote_idle_timeout = idle_timeout;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_msg_session::dec_local_composing_timeout(void) {\n\tif (local_composing_state == COMPOSING_STATE_IDLE) return;\n\t\n\tlocal_idle_timeout--;\n\tif (local_idle_timeout == 0) {\n\t\tset_local_composing_state(COMPOSING_STATE_IDLE);\n\t} else {\n\t\tlocal_refresh_timeout--;\n\t\tif (local_refresh_timeout == 0) {\n\t\t\tlocal_refresh_timeout = COMPOSING_LOCAL_REFRESH_TIMEOUT - 10;\n\t\t\t\n\t\t\tif (send_composing_state) {\n\t\t\t\t(void)phone->pub_send_im_iscomposing(\n\t\t\t\t\tuser_config, remote_party.url, \n\t\t\t\t\tremote_party.display,\n\t\t\t\t\tIM_ISCOMPOSING_STATE_ACTIVE,\n\t\t\t\t\tCOMPOSING_LOCAL_REFRESH_TIMEOUT);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid t_msg_session::dec_remote_composing_timeout(void) {\n\tif (remote_composing_state == COMPOSING_STATE_IDLE) return;\n\t\n\tremote_idle_timeout--;\n\tif (remote_idle_timeout == 0) {\n\t\tset_remote_composing_state(COMPOSING_STATE_IDLE);\n\t}\n}\n"
  },
  {
    "path": "src/im/msg_session.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Instant message session.\n * SIP does not have a concept of message sessions. It's up to the\n * user interface to create the illusion of a session by grouping\n * messages between 2 users.\n */\n\n#ifndef _MSG_SESSION_H\n#define _MSG_SESSION_H\n\n#include <string>\n#include <time.h>\n\n#include \"id_object.h\"\n#include \"exceptions.h\"\n#include \"user.h\"\n#include \"sockets/url.h\"\n#include \"parser/media_type.h\"\n#include \"patterns/observer.h\"\n\nusing namespace std;\n\nnamespace im {\n\n/** \n * Maximum length for inline text messages. Longer texts are shown\n * as attachments.\n */\nconst size_t MAX_INLINE_TEXT_LEN = 10240;\n\n/** Message direction. */\nenum t_direction {\n\tMSG_DIR_IN,\t/**< Incoming. */\n\tMSG_DIR_OUT\t/**< Outgoing. */\n};\n\n/** Text format. */\nenum t_text_format {\n\tTXT_PLAIN,\t/**< Plain */\n\tTXT_HTML,\t/**< HTML */\n};\n\n/** Message composing state. */\nenum t_composing_state {\n\tCOMPOSING_STATE_IDLE,\n\tCOMPOSING_STATE_ACTIVE\n};\n\n/**\n * Convert a state name a conveyed in an im_iscomposing body to a state.\n * @param state_name [in] The state name to convert.\n * @return The composing state. If the state name is unknown, then \n *         COMPOSING_STATE_IDE is returned.\n */\nt_composing_state string2composing_state(const string &state_name);\n\n/**\n * Convert a composing state to a string for an im_iscomposing body.\n * @param state [in] The composing state to convert.\n * @return The string.\n */\nstring composing_state2string(t_composing_state state);\n\n/** \n * Single message with meta information. \n * In the current implementation a message either contains a text message\n * or an attachment.\n * TODO: use multipart MIME to send a text message with an attachment.\n */\nclass t_msg : public t_id_object {\npublic:\n\tstring\t\tsubject;\t/**< Subject of the message. */\n\tstring\t\tmessage;\t/**< The message text. */\n\tt_direction\tdirection;\t/**< Direction of the message. */\n\ttime_t\t\ttimestamp;\t/**< Timestamp of the message. */\n\tt_text_format\tformat;\t\t/**< Text format. */\n\tbool\t\thas_attachment;\t/**< Indicates if an attachment is present. */\n\tstring\t\tattachment_filename; /**< File name of stored attachment. */\n\tt_media\t\tattachment_media; /**< Media type of attachment */\n\tstring\t\tattachment_save_as_name; /**< Suggested 'save as' filename */\n\t\n\t/** Constructor. */\n\tt_msg();\n\t\n\t/**\n\t * Constructor.\n\t * Sets the timestamp to the current time.\n\t * @param msg [in] The message.\n\t * @param dir [in] Direction of the message.\n\t * @param fmt [in] Text format of the message.\n\t */\n\tt_msg(const string &msg, t_direction dir, t_text_format fmt);\n\t\n\t/**\n\t * Add an attachment to the message.\n\t * @param filename [in] File name (full path) of the attachment.\n\t * @param media [in] Media type of the attachment.\n\t * @param save_as [in] Suggested file name for saving.\n\t */\n\tvoid set_attachment(const string &filename, const t_media &media, const string &save_as);\n};\n\n/** \n * Message session. \n * It's up to the user interface to create a message session and store\n * all messages in it. The session is just a container to store a\n * collection of page-mode messages.\n */\nclass t_msg_session : public patterns::t_subject {\nprivate:\n\tt_user\t\t*user_config;\t/**< User profile of the local user. */\n\tt_display_url\tremote_party;\t/**< Remote party. */\n\tlist<t_msg>\tmessages;\t/**< Messages sent/received. */\n\t\n\t/** Indicates if a new message has been added to the list of message. */\n\tbool\t\tnew_message_added;\n\t\n\tbool\t\terror_recvd;\t/**< Indicates that an error has been received. */\n\tstring\t\terror_msg;\t/**< Received error message. */\n\t\n\t/** Indicates that a delivery notification has been received. */\n\tbool\t\tdelivery_notification_recvd;\n\t\n\t/** Received delivery notification. */\n\tstring\t\tdelivery_notification;\n\t\n\tbool\t\tmsg_in_flight;\t/**< Indicates if an outgoing message is in flight. */\n\t\n\t/** Indicates if a composing state indication must be sent to the remote party. */\n\tbool\t\tsend_composing_state;\n\t\n\t/** Message composing state of the local party. */\n\tt_composing_state\tlocal_composing_state;\n\t\n\t/** Timeout in seconds till the active state of the local party expires. */\n\ttime_t\t\tlocal_idle_timeout;\n\t\n\t/** Timeout in seconds till a refresh of the active state indication must be sent. */\n\ttime_t\t\tlocal_refresh_timeout;\n\t\n\t/** Message composing state of the remote party. */\n\tt_composing_state\tremote_composing_state;\n\t\n\t/** Timeout in seconds till the active state of the remote party expires. */\n\ttime_t\t\tremote_idle_timeout;\n\t\npublic:\n\t/**\n\t * Constructor.\n\t * @param u [in] User profile of the local user.\n\t */\n\tt_msg_session(t_user *u);\n\n\t/**\n\t * Constructor.\n\t * @param u [in] User profile of the local user.\n\t * @param _remote_party [in] URL of remote party.\n\t */\n\tt_msg_session(t_user *u, t_display_url _remote_party);\n\t\n\t/** Destructor. */\n\t~t_msg_session();\n\t\n\t/** @name getters */\n\t//@{\n\tt_user *get_user(void) const;\n\tt_display_url get_remote_party(void) const;\n\tconst list<t_msg> &get_messages(void) const;\n\tt_composing_state get_remote_composing_state(void) const;\n\t//@}\n\t\n\t/** @name setters */\n\t//@{\n\tvoid set_user(t_user *u);\n\tvoid set_remote_party(const t_display_url &du);\n\tvoid set_send_composing_state(bool enable);\n\t//@}\n\t\n\t/**\n\t * Get the last message of the session.\n\t * @return The last message.\n\t * @throws empty_list_exception  There are no messages.\n\t */\n\tt_msg get_last_message(void);\n\t\n\t/** Check if a new message has been added. */\n\tbool is_new_message_added(void) const;\n\t\n\t/**\n\t * Set the display name of the remote party if it is not yet set.\n\t * @param display [in] The display name to set.\n\t */\n\tvoid set_display_if_empty(const string &display);\n\t\n\t/**\n\t * Add a received message to the session.\n\t * @param msg [in] The message to add.\n\t */\n\tvoid recv_msg(const t_msg &msg);\n\t\n\t/**\n\t * Send a message to the remote party.\n\t * The message will be added to the list of messages.\n\t * @param message [in] Message to be sent.\n\t * @param format [in] Text format of the message.\n\t */\n\tvoid send_msg(const string &message, t_text_format format);\n\t\n\t/**\n\t * Send a message with file attachment to the remote party.\n\t * The message will be added to the list of messages.\n\t * @param filename [in] Name of file to be sent.\n\t * @param media [in] Mime type of the file.\n\t * @param subject [in] Subject of message.\n\t */\n\tvoid send_file(const string &filename, const t_media &media, const string &subject);\n\t\n\t/**\n\t * Set the error message of the session.\n\t * @param message [in] Error message.\n\t * @post @ref error_msg == message\n\t * @post @ref error_recvd == true\n\t */\n\tvoid set_error(const string &message);\n\t\n\t/**\n\t * Check if an error has been received.\n\t * @return true, if an error has been received.\n\t * @return false, otherwise\n\t */\n\tbool error_received(void) const;\n\t\n\t/**\n\t * Take the error message from the session.\n\t * @return Error message.\n\t * @pre @ref error_received() == true\n\t * @post @ref error_received() == false\n\t */\n\tstring take_error(void);\n\t\n\t/**\n\t * Set the delivery notification of the session.\n\t * @param notification [in] Delivery notification.\n\t * @post @ref delivery_notification == notification\n\t * @post @ref delivery_notification_recvd == true\n\t */\n\tvoid set_delivery_notification(const string &notification);\n\t\n\t/**\n\t * Check if a delivery notification has been received.\n\t * @return true, if an error has been received.\n\t * @return false, otherwise\n\t */\n\tbool delivery_notification_received(void) const;\n\t\n\t/**\n\t * Take the delivery notification from the session.\n\t * @return Delivery notification.\n\t * @pre @ref delivery_notification_received() == true\n\t * @post @ref delivery_notification_received() == false\n\t */\n\tstring take_delivery_notification(void);\n\t\n\t/**\n\t * Check if the session matches with a particular user and\n\t * remote party.\n\t * @param user [in] The user\n\t * @param remote_party [in] URL of the remote party\n\t * @return true, if there is a match\n\t * @return false, otherwise\n\t */\n\tbool match(t_user *user, t_url _remote_party);\n\t\n\t/**\n\t * Set the message in flight indicator.\n\t * @param in_flight [in] Indicator value to set.\n\t */\n\tvoid set_msg_in_flight(bool in_flight);\n\t\n\t/**\n\t * Check if a message is in flight.\n\t * @return true, message is in flight.\n\t * @return false, no message is in flight.\n\t */\n\tbool is_msg_in_flight(void) const;\n\t\n\t/**\n\t * Set the local composing state.\n\t * If the state transitions to a new state, then a composing indication\n\t * is sent to the remote party.\n\t * The local idle timeout and refresh timeout timers are updated depending\n\t * on the current state.\n\t * @param state [in] The new local composing state.\n\t */\n\tvoid set_local_composing_state(t_composing_state state);\n\t\n\t/**\n\t * Set the remote composing state.\n\t * The remote idle timeout timer is updated depending on the state.\n\t * @param state [in] The new remote composing state.\n\t * @param idle_timeout [in] The idle timeout value when state == active.\n\t * @note When state == idle, then the idle_timout argument has no meaning.\n\t */\n\tvoid set_remote_composing_state(t_composing_state state, time_t idle_timeout = 120);\n\t\n\t/**\n\t * Decrement the timeout values for the local composing state if\n\t * the current state is active.\n\t * If the idle timeout reaches zero, then the state transitions\n\t * to idle, and an idle indication is sent to the remote party.\n\t * If the refresh timeout reaches zero, then an active indication\n\t * is sent to the remote party.\n\t * If the current state is idle, then nothing is done.\n\t */\n\tvoid dec_local_composing_timeout(void);\n\t\n\t/** Decrement the timeout values for the remote composing state if\n\t * the current state is active.\n\t * If the idle timeout reaches zero, then the state transitions\n\t * to idle.\n\t * If the current state is idle, then nothing is done.\n\t */\n\tvoid dec_remote_composing_timeout(void);\n};\n\n}; // end namespace\n\n#endif\n"
  },
  {
    "path": "src/line.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include <signal.h>\n#include \"exceptions.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"sdp/sdp.h\"\n#include \"util.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n\nextern t_event_queue\t*evq_timekeeper;\n\n///////////////\n// t_call_info\n///////////////\n\nt_call_info::t_call_info() {\n\tclear();\n}\n\nt_call_info::t_call_info(const t_call_info& that) {\n\t*this = that;\n}\n\nt_call_info& t_call_info::operator=(const t_call_info& that) {\n\tif (this != &that) {\n\t\t// FIXME: This may deadlock if \"a=b\" and \"b=a\" are run in\n\t\t//        parallel.  The proper solution would be to switch\n\t\t//        to std::mutex and call std::lock(this,that).\n\t\tt_mutex_guard x1(that.mutex);\n\t\tt_mutex_guard x2(this->mutex);\n\n\t\tfrom_uri = that.from_uri;\n\t\tfrom_display = that.from_display;\n\n\t\tfrom_display_override = that.from_display_override;\n\n\t\tfrom_organization = that.from_organization;\n\t\tto_uri = that.to_uri;\n\t\tto_display = that.to_display;\n\t\tto_organization = that.to_organization;\n\t\tsubject = that.subject;\n\t\tdtmf_supported = that.dtmf_supported;\n\t\tdtmf_inband = that.dtmf_inband;\n\t\tdtmf_info = that.dtmf_info;\n\t\thdr_referred_by = that.hdr_referred_by;\n\n\t\tlast_provisional_reason = that.last_provisional_reason;\n\n\t\tsend_codec = that.send_codec;\n\t\trecv_codec = that.recv_codec;\n\t\trefer_supported = that.refer_supported;\n\t}\n\n\treturn *this;\n}\n\nvoid t_call_info::clear(void) {\n\tt_mutex_guard g(mutex);\n\n\tfrom_uri.set_url(\"\");\n\tfrom_display.clear();\n\tfrom_display_override.clear();\n\tfrom_organization.clear();\n\tto_uri.set_url(\"\");\n\tto_display.clear();\n\tto_organization.clear();\n\tsubject.clear();\n\tdtmf_supported = false;\n\thdr_referred_by = t_hdr_referred_by();\n\tlast_provisional_reason.clear();\n\tsend_codec = CODEC_NULL;\n\trecv_codec = CODEC_NULL;\n\trefer_supported = false;\n}\n\nstring t_call_info::get_from_display_presentation(void) const {\n\tt_mutex_guard g(mutex);\n\n\tif (from_display_override.empty()) {\n\t\treturn from_display;\n\t} else {\n\t\treturn from_display_override;\n\t}\n}\n\n\n///////////\n// t_line\n///////////\n\n///////////\n// Private\n///////////\n\nt_dialog *t_line::match_response(t_response *r,\n\t\tconst list<t_dialog *> &l) const\n{\n\tlist<t_dialog *>::const_iterator i;\n\tfor (i = l.begin(); i != l.end(); i++) {\n\t\tif ((*i)->match_response(r, 0)) return *i;\n\t}\n\n\treturn NULL;\n}\n\nt_dialog *t_line::match_response(StunMessage *r, t_tuid tuid,\n\t\tconst list<t_dialog *> &l) const\n{\n\tlist<t_dialog *>::const_iterator i;\n\tfor (i = l.begin(); i != l.end(); i++) {\n\t\tif ((*i)->match_response(r, tuid)) return *i;\n\t}\n\n\treturn NULL;\n}\n\nt_dialog *t_line::match_call_id_tags(const string &call_id,\n\t\tconst string &to_tag, const string &from_tag,\n\t\tconst list<t_dialog *> &l) const\n{\n\tlist<t_dialog *>::const_iterator i;\n\tfor (i = l.begin(); i != l.end(); i++) {\n\t\tif ((*i)->match(call_id, to_tag, from_tag)) return *i;\n\t}\n\n\treturn NULL;\n}\n\nt_dialog *t_line::get_dialog(t_object_id did) const {\n\tlist<t_dialog *>::const_iterator i;\n\n\tif (did == 0) return NULL;\n\n\tif (open_dialog && open_dialog->get_object_id() == did) {\n\t\treturn open_dialog;\n\t}\n\n\tif (active_dialog && active_dialog->get_object_id() == did) {\n\t\treturn active_dialog;\n\t}\n\n\tfor (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {\n\t\tif ((*i)->get_object_id() == did) return *i;\n\t}\n\n\tfor (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {\n\t\tif ((*i)->get_object_id() == did) return *i;\n\t}\n\n\treturn NULL;\n}\n\nvoid t_line::cleanup(void) {\n\tlist<t_dialog *>::iterator i;\n\n\tif (open_dialog && open_dialog->get_state() == DS_TERMINATED) {\n\t\tMEMMAN_DELETE(open_dialog);\n\t\tdelete open_dialog;\n\t\topen_dialog = NULL;\n\t}\n\n\tif (active_dialog && active_dialog->get_state() == DS_TERMINATED) {\n\t\tMEMMAN_DELETE(active_dialog);\n\t\tdelete active_dialog;\n\t\tactive_dialog = NULL;\n\n\t\tstop_timer(LTMR_INVITE_COMP);\n\t\tstop_timer(LTMR_NO_ANSWER);\n\n\t\t// If the call has been ended within 64*T1 seconds\n\t\t// after the reception of the first 2XX response, there\n\t\t// might still be open and pending dialogs. To be nice these\n\t\t// dialogs should be kept till the 64*T1 timer expires.\n\t\t// This complicates the setup of new call however. For\n\t\t// now the dialogs will be killed. If a slow UAS\n\t\t// still responds, it has bad luck and will time out.\n\t\t//\n\t\t// TODO:\n\t\t// A nice solution would be to move the pending and open\n\t\t// dialog to the dying dialog and start a new time 64*T1\n\t\t// timer to keep the dying dialogs alive. A sequence of\n\t\t// a few short calls would add to the dying dialogs and\n\t\t// keep some dialogs alive longer than necessary. This\n\t\t// only has an impact on resources, not on signalling.\n\t\t// Note that the open dialog must be appended after the\n\t\t// pending dialogs, otherwise all received responses for\n\t\t// a pending dialog will match the open dialog if that\n\t\t// match is tried first by match_response()\n\t\tfor (i = pending_dialogs.begin(); i != pending_dialogs.end();\n\t\t\ti++)\n\t\t{\n\t\t\tMEMMAN_DELETE(*i);\n\t\t\tdelete *i;\n\t\t}\n\t\tpending_dialogs.clear();\n\n\t\tif (open_dialog) {\n\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\tdelete open_dialog;\n\t\t}\n\t\topen_dialog = NULL;\n\t}\n\n\tif (active_dialog) {\n\t\tif (active_dialog->get_state() == DS_CONFIRMED_SUB) {\n\t\t\t// The calls have been released but a subscription is\n\t\t\t// still active.\n\t\t\tsubstate = LSSUB_RELEASING;\n\t\t} else if (active_dialog->will_release()) {\n\t\t\tsubstate = LSSUB_RELEASING;\n\t\t}\n\t}\n\n\tfor (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {\n\t\tif ((*i)->get_state() == DS_TERMINATED) {\n\t\t\tMEMMAN_DELETE(*i);\n\t\t\tdelete *i;\n\t\t\t*i = NULL;\n\t\t}\n\t}\n\tpending_dialogs.remove(NULL);\n\n\tfor (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {\n\t\tif ((*i)->get_state() == DS_TERMINATED) {\n\t\t\tMEMMAN_DELETE(*i);\n\t\t\tdelete *i;\n\t\t\t*i = NULL;\n\t\t}\n\t}\n\tdying_dialogs.remove(NULL);\n\n\tif (!open_dialog && !active_dialog && pending_dialogs.size() == 0) {\n\t\tstate = LS_IDLE;\n\t\t\n\t\tif (keep_seized) {\n\t\t\tsubstate = LSSUB_SEIZED;\n\t\t} else {\n\t\t\tsubstate = LSSUB_IDLE;\n\t\t}\n\t\t\n\t\tis_on_hold = false;\n\t\tis_muted = false;\n\t\thide_user = false;\n\t\tcleanup_transfer_consult_state();\n\t\ttry_to_encrypt = false;\n\t\tauto_answer = false;\n\t\tcall_info.clear();\n\t\tcall_history->add_call_record(call_hist_record);\n\t\tcall_hist_record.renew();\n\t\tphone_user = NULL;\n\t\tuser_defined_ringtone.clear();\n\t\tui->cb_line_state_changed();\n\t}\n}\n\nvoid t_line::cleanup_open_pending(void) {\n\tif (open_dialog) {\n\t\tMEMMAN_DELETE(open_dialog);\n\t\tdelete open_dialog;\n\t\topen_dialog = NULL;\n\t}\n\n\tlist<t_dialog *>::iterator i;\n\tfor (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n\tpending_dialogs.clear();\n\n\tif (!active_dialog) {\n\t\tis_on_hold = false;\n\t\tis_muted = false;\n\t\thide_user = false;\n\t\tcleanup_transfer_consult_state();\n\t\ttry_to_encrypt = false;\n\t\tauto_answer = false;\n\t\tstate = LS_IDLE;\n\t\t\n\t\tif (keep_seized) {\n\t\t\tsubstate = LSSUB_SEIZED;\n\t\t} else {\n\t\t\tsubstate = LSSUB_IDLE;\n\t\t}\n\n\t\tcall_info.clear();\n\t\tcall_history->add_call_record(call_hist_record);\n\t\tcall_hist_record.renew();\n\t\tphone_user = NULL;\n\t\tuser_defined_ringtone.clear();\n\t\tui->cb_line_state_changed();\n\t}\n}\n\nvoid t_line::cleanup_forced(void) {\n\tlist<t_dialog *>::iterator i;\n\n\tif (open_dialog) {\n\t\tMEMMAN_DELETE(open_dialog);\n\t\tdelete open_dialog;\n\t\topen_dialog = NULL;\n\t}\n\n\tif (active_dialog) {\n\t\tMEMMAN_DELETE(active_dialog);\n\t\tdelete active_dialog;\n\t\tactive_dialog = NULL;\n\t}\n\n\tfor (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t\t*i = NULL;\n\t}\n\tpending_dialogs.remove(NULL);\n\n\tfor (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t\t*i = NULL;\n\t}\n\tdying_dialogs.remove(NULL);\n\t\n\t// TODO: stop running timers?\n\n\tstate = LS_IDLE;\n\tsubstate = LSSUB_IDLE;\n\tkeep_seized = false;\n\tis_on_hold = false;\n\tis_muted = false;\n\thide_user = false;\n\tcleanup_transfer_consult_state();\n\tauto_answer = false;\n\tcall_info.clear();\n\tcall_history->add_call_record(call_hist_record);\n\tcall_hist_record.renew();\n\tphone_user = NULL;\n\tuser_defined_ringtone.clear();\n\tui->cb_line_state_changed();\n}\n\nvoid t_line::cleanup_transfer_consult_state(void) {\n\tif (is_transfer_consult) {\n\t\tt_line *from_line = phone->get_line(consult_transfer_from_line);\n\t\tfrom_line->set_to_be_transferred(false, 0);\n\t\tis_transfer_consult = false;\n\t}\n\t\n\tif (to_be_transferred) {\n\t\tt_line *to_line = phone->get_line(consult_transfer_to_line);\n\t\tto_line->set_is_transfer_consult(false, 0);\n\t\tto_be_transferred = false;\n\t}\n}\n\n\n///////////\n// Public\n///////////\n\nt_line::t_line(t_phone *_phone, unsigned short _line_number) : \n\tt_id_object()\n{\n\t// NOTE: The rtp_port attribute can only be initialized when\n\t//       a user profile has been selected.\n\n\tphone = _phone;\n\tstate = LS_IDLE;\n\tsubstate = LSSUB_IDLE;\n\topen_dialog = NULL;\n\tactive_dialog = NULL;\n\tis_on_hold = false;\n\tis_muted = false;\n\thide_user = false;\n\tis_transfer_consult = false;\n\tto_be_transferred = false;\n\ttry_to_encrypt = false;\n\tauto_answer = false;\n\tline_number = _line_number;\n\tid_invite_comp = 0;\n\tid_no_answer = 0;\n\tphone_user = NULL;\n\tuser_defined_ringtone.clear();\n\tkeep_seized = false;\n}\n\nt_line::~t_line() {\n\tlist<t_dialog *>::iterator i;\n\n\t// Stop timers\n\tif (id_invite_comp) stop_timer(LTMR_INVITE_COMP);\n\tif (id_no_answer) stop_timer(LTMR_NO_ANSWER);\n\n\t// Delete pointers\n\tif (open_dialog) {\n\t\tMEMMAN_DELETE(open_dialog);\n\t\tdelete open_dialog;\n\t}\n\tif (active_dialog) {\n\t\tMEMMAN_DELETE(active_dialog);\n\t\tdelete active_dialog;\n\t}\n\n\t// Delete dialogs\n\tfor (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n\n\tfor (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n}\n\nt_line_state t_line::get_state(void) const {\n\treturn state;\n}\n\nt_line_substate t_line::get_substate(void) const {\n\treturn substate;\n}\n\nt_refer_state t_line::get_refer_state(void) const {\n\tif (active_dialog) return active_dialog->refer_state;\n\treturn REFST_NULL;\n}\n\nvoid t_line::start_timer(t_line_timer timer, t_object_id did) {\n\tt_tmr_line\t*t;\n\tt_dialog\t*dialog = get_dialog(did);\n\tunsigned long\tdur;\n\t\n\tassert(phone_user);\n\n\tswitch(timer) {\n\tcase LTMR_ACK_TIMEOUT:\n\t\tassert(dialog);\n\t\t// RFC 3261 13.3.1.4\n\t\tif (dialog->dur_ack_timeout == 0) {\n\t\t\tdialog->dur_ack_timeout = DURATION_T1;\n\t\t} else {\n\t\t\tdialog->dur_ack_timeout *= 2;\n\t\t\tif (dialog->dur_ack_timeout > DURATION_T2 ) {\n\t\t\t\tdialog->dur_ack_timeout = DURATION_T2;\n\t\t\t}\n\t\t}\n\t\tt = new t_tmr_line(dialog->dur_ack_timeout , timer, get_object_id(),\n\t\t\t\t\tdid);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_ack_timeout = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_ACK_GUARD:\n\t\tassert(dialog);\n\t\t// RFC 3261 13.3.1.4\n\t\tt = new t_tmr_line(64 * DURATION_T1, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_ack_guard = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_INVITE_COMP:\n\t\t// RFC 3261 13.2.2.4\n\t\tt = new t_tmr_line(64 * DURATION_T1, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tid_invite_comp = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_NO_ANSWER:\n\t\tt = new t_tmr_line(DUR_NO_ANSWER(phone_user->get_user_profile()), \n\t\t\t\ttimer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tid_no_answer = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_RE_INVITE_GUARD:\n\t\tassert(dialog);\n\t\tt = new t_tmr_line(DUR_RE_INVITE_GUARD, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_re_invite_guard = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_GLARE_RETRY:\n\t\tassert(dialog);\n\t\tif (dialog->is_call_id_owner()) {\n\t\t\tdur = DUR_GLARE_RETRY_OWN;\n\t\t} else {\n\t\t\tdur = DUR_GLARE_RETRY_NOT_OWN;\n\t\t}\n\t\tt = new t_tmr_line(dur, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_glare_retry = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_100REL_TIMEOUT:\n\t\tassert(dialog);\n\t\t// RFC 3262 3\n\t\tif (dialog->dur_100rel_timeout == 0) {\n\t\t\tdialog->dur_100rel_timeout = DUR_100REL_TIMEOUT;\n\t\t} else {\n\t\t\tdialog->dur_100rel_timeout *= 2;\n\t\t}\n\t\tt = new t_tmr_line(dialog->dur_100rel_timeout , timer, get_object_id(),\n\t\t\t\t\tdid);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_100rel_timeout = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_100REL_GUARD:\n\t\tassert(dialog);\n\t\t// RFC 3262 3\n\t\tt = new t_tmr_line(DUR_100REL_GUARD, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_100rel_guard = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_CANCEL_GUARD:\n\t\tassert(dialog);\n\t\tt = new t_tmr_line(DUR_CANCEL_GUARD, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_cancel_guard = t->get_object_id();\n\t\tbreak;\t\t\n\tcase LTMR_SESSION_REFRESH:\n\t\tassert(dialog);\n\t\t// Half the session interval is recommended (RFC 4028 7.2)\n\t\tdur = (dialog->session_interval / 2) * 1000;\n\t\tassert(dur > 0);\n\t\tt = new t_tmr_line(dur, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_session_refresh = t->get_object_id();\n\t\tbreak;\n\tcase LTMR_SESSION_EXPIRE:\n\t\tassert(dialog);\n\t\tdur = dialog->session_interval * 1000;\n\t\tassert(dur > 0);\n\t\tif (!dialog->is_session_refresher) {\n\t\t\t// The minimum of 32 seconds and one third of the\n\t\t\t// session interval is recommended (RFC 4028 10)\n\t\t\tunsigned long d1 = 32 * 1000;\n\t\t\tunsigned long d2 = dur / 3;\n\t\t\tunsigned long d = (d1 < d2) ? d1 : d2;\n\t\t\tassert(dur > d);  // Prevent an underflow\n\t\t\tdur -= d;\n\t\t}\n\t\tt = new t_tmr_line(dur, timer, get_object_id(), did);\n\t\tMEMMAN_NEW(t);\n\t\tdialog->id_session_expire = t->get_object_id();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_line::stop_timer(t_line_timer timer, t_object_id did) {\n\tt_object_id\t*id;\n\tt_dialog\t*dialog = get_dialog(did);\n\n\tswitch(timer) {\n\tcase LTMR_ACK_TIMEOUT:\n\t\tassert(dialog);\n\t\tdialog->dur_ack_timeout = 0;\n\t\tid = &dialog->id_ack_timeout;\n\t\tbreak;\n\tcase LTMR_ACK_GUARD:\n\t\tassert(dialog);\n\t\tid = &dialog->id_ack_guard;\n\t\tbreak;\n\tcase LTMR_INVITE_COMP:\n\t\tid = &id_invite_comp;\n\t\tbreak;\n\tcase LTMR_NO_ANSWER:\n\t\tid = &id_no_answer;\n\t\tbreak;\n\tcase LTMR_RE_INVITE_GUARD:\n\t\tassert(dialog);\n\t\tid = &dialog->id_re_invite_guard;\n\t\tbreak;\n\tcase LTMR_GLARE_RETRY:\n\t\tassert(dialog);\n\t\tid = &dialog->id_glare_retry;\n\t\tbreak;\n\tcase LTMR_100REL_TIMEOUT:\n\t\tassert(dialog);\n\t\tdialog->dur_100rel_timeout = 0;\n\t\tid = &dialog->id_100rel_timeout;\n\t\tbreak;\n\tcase LTMR_100REL_GUARD:\n\t\tassert(dialog);\n\t\tid = &dialog->id_100rel_guard;\n\t\tbreak;\n\tcase LTMR_CANCEL_GUARD:\n\t\tassert(dialog);\n\t\tid = &dialog->id_cancel_guard;\n\t\t\n\t\t// KLUDGE\n\t\tif (*id == 0) {\n\t\t\t// Cancel is always sent on the open dialog.\n\t\t\t// The timer is probably stopped from a pending dialog,\n\t\t\t// therefore the timer is stopped on the wrong dialog.\n\t\t\t// Check if the open dialog has a CANCEL guard timer.\n\t\t\tif (open_dialog) id = &open_dialog->id_cancel_guard;\n\t\t}\n\t\tbreak;\n\tcase LTMR_SESSION_REFRESH:\n\t\tassert(dialog);\n\t\tid = &dialog->id_session_refresh;\n\t\tbreak;\n\tcase LTMR_SESSION_EXPIRE:\n\t\tassert(dialog);\n\t\tid = &dialog->id_session_expire;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tif (*id != 0) evq_timekeeper->push_stop_timer(*id);\n\t*id = 0;\n}\n\nvoid t_line::invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, bool no_fork, bool anonymous)\n{\n\tt_hdr_request_disposition hdr_request_disposition;\n\t\n\tif (no_fork) {\n\t\thdr_request_disposition.set_fork_directive(\n\t\t\tt_hdr_request_disposition::NO_FORK);\n\t}\n\n\tinvite(pu, to_uri, to_display, subject, t_hdr_referred_by(), \n\t\t\tt_hdr_replaces(), t_hdr_require(), hdr_request_disposition,\n\t\t\tanonymous);\n}\n\nvoid t_line::invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, const t_hdr_referred_by &hdr_referred_by,\n\t\tconst t_hdr_replaces &hdr_replaces,\n\t\tconst t_hdr_require &hdr_require, \n\t\tconst t_hdr_request_disposition &hdr_request_disposition,\n\t\tbool anonymous)\n{\n\tassert(pu);\n\t\n\t// Ignore if line is not idle\n\tif (state != LS_IDLE) {\n\t\treturn;\n\t}\n\n\tassert(!open_dialog);\n\t\n\t// Validate speaker and mic\n\tstring error_msg;\n\tif (!sys_config->exec_audio_validation(false, true, true, error_msg)) {\n\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\treturn;\n\t}\n\t\n\tphone_user = pu;\n\tt_user *user_config = pu->get_user_profile();\n\n\tcall_info.mutex.lock();\n\tcall_info.from_uri = create_user_uri(); // NOTE: hide_user is not set yet\n\tcall_info.from_display = user_config->get_display(false);\n\tcall_info.from_organization = user_config->get_organization();\n\tcall_info.to_uri = to_uri;\n\tcall_info.to_display = to_display;\n\tcall_info.to_organization.clear();\n\tcall_info.subject = subject;\n\tcall_info.hdr_referred_by = hdr_referred_by;\n\tcall_info.mutex.unlock();\n\t\n\ttry_to_encrypt = user_config->get_zrtp_enabled();\n\n\tstate = LS_BUSY;\n\tsubstate = LSSUB_OUTGOING_PROGRESS;\n\thide_user = anonymous;\n\tui->cb_line_state_changed();\n\n\topen_dialog = new t_dialog(this);\n\tMEMMAN_NEW(open_dialog);\n\topen_dialog->send_invite(to_uri, to_display, subject, hdr_referred_by, \n\t\t\thdr_replaces, hdr_require, hdr_request_disposition,\n\t\t\tanonymous);\n\n\tcleanup();\n}\n\nvoid t_line::answer(void) {\n\t// Ignore if line is idle\n\tif (state == LS_IDLE) return;\n\tassert(active_dialog);\n\t\n\t// Validate speaker and mic\n\tstring error_msg;\n\tif (!sys_config->exec_audio_validation(false, true, true, error_msg)) {\n\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\treturn;\n\t}\n\n\tstop_timer(LTMR_NO_ANSWER);\n\n\ttry {\n\t\tsubstate = LSSUB_ANSWERING;\n\t\tui->cb_line_state_changed();\n\t\tactive_dialog->answer();\n\t}\n\tcatch (t_exception x) {\n\t\t// TODO: there is no call to answer\n\t}\n\n\tcleanup();\n}\n\nvoid t_line::reject(void) {\n\t// Ignore if line is idle\n\tif (state == LS_IDLE) return;\n\tassert(active_dialog);\n\n\tstop_timer(LTMR_NO_ANSWER);\n\n\ttry {\n\t\tactive_dialog->reject(R_486_BUSY_HERE);\n\t}\n\tcatch (t_exception x) {\n\t\t// TODO: there is no call to reject\n\t}\n\n\tcleanup();\n}\n\nvoid t_line::redirect(const list<t_display_url> &destinations, int code, string reason)\n{\n\t// Ignore if line is idle\n\tif (state == LS_IDLE) return;\n\tassert(active_dialog);\n\n\tstop_timer(LTMR_NO_ANSWER);\n\n\ttry {\n\t\tactive_dialog->redirect(destinations, code, reason);\n\t}\n\tcatch (t_exception x) {\n\t\t// TODO: there is no call to redirect\n\t}\n\n\tcleanup();\n}\n\nvoid t_line::end_call(void) {\n\t// Ignore if phone is idle\n\tif (state == LS_IDLE) return;\n\n\tif (active_dialog) {\n\t\tsubstate = LSSUB_RELEASING;\n\t\tui->cb_line_state_changed();\n\t\tui->cb_stop_call_notification(line_number);\n\t\tactive_dialog->send_bye();\n\t\t\n\t\t// If the line was part of a transfer with consultation,\n\t\t// then clean the consultation state as the transfer cannot\n\t\t// proceed anymore.\n\t\tcleanup_transfer_consult_state();\n\t\t\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// Always send the CANCEL on the open dialog.\n\t// The pending dialogs will be cleared when the INVITE gets\n\t// terminated.\n\t// CANCEL is send on the open dialog as the CANCEL must have\n\t// the same tags as the INVITE.\n\tif (open_dialog) {\n\t\tsubstate = LSSUB_RELEASING;\n\t\tui->cb_line_state_changed();\n\t\tui->cb_stop_call_notification(line_number);\n\t\topen_dialog->send_cancel(!pending_dialogs.empty());\n\t\t\n\t\t// Make sure dialog is terminated if CANCEL glares with\n\t\t// 2XX on INVITE.\n\t\tfor (list<t_dialog *>::iterator i = pending_dialogs.begin();\n\t\t     i != pending_dialogs.end(); i++)\n\t\t{\n\t\t\t(*i)->set_end_after_2xx_invite(true);\n\t\t}\n\t\t\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// NOTE:\n\t// The call is only ended for real when the dialog reaches\n\t// the DS_TERMINATED state, i.e. a 200 OK on BYE is received\n\t// or a 487 TERMINATED on INVITE is received.\n}\n\nvoid t_line::send_dtmf(char digit, bool inband, bool info) {\n\t// DTMF may be sent on an early media session, so find\n\t// a dialog that has an RTP session. There can be at most 1.\n\tt_dialog *d = get_dialog_with_active_session();\n\n\tif (d) {\n\t\td->send_dtmf(digit, inband, info);\n\t\tcleanup();\n\t\treturn;\n\t}\n}\n\nvoid t_line::options(void) {\n\tif (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {\n\t\tactive_dialog->send_options();\n\t\tcleanup();\n\t\treturn;\n\t}\n}\n\nbool t_line::hold(bool rtponly) {\n\tif (is_on_hold) return true;\n\n\tif (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {\n\t\tactive_dialog->hold(rtponly);\n\t\tis_on_hold = true;\n\t\tui->cb_line_state_changed();\n\t\tcleanup();\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid t_line::retrieve(void) {\n\tif (!is_on_hold) return;\n\n\tif (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {\n\t\tactive_dialog->retrieve();\n\t\tis_on_hold = false;\n\t\tui->cb_line_state_changed();\n\t\tcleanup();\n\t\treturn;\n\t}\n}\n\nvoid t_line::kill_rtp(void) {\n\tif (active_dialog) active_dialog->kill_rtp();\n\t\n\tfor (list<t_dialog *>::iterator i = pending_dialogs.begin();\n\t\t     i != pending_dialogs.end(); i++)\n\t{\n\t\t(*i)->kill_rtp();\n\t}\n\t\n\tfor (list<t_dialog *>::iterator i = dying_dialogs.begin();\n\t\t     i != dying_dialogs.end(); i++)\n\t{\n\t\t(*i)->kill_rtp();\n\t}\n}\n\nvoid t_line::refer(const t_url &uri, const string &display) {\n\tif (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {\n\t\tactive_dialog->send_refer(uri, display);\n\t\tui->cb_line_state_changed();\n\t\tcleanup();\n\t\treturn;\n\t}\n}\n\nvoid t_line::mute(bool enable) {\n\tis_muted = enable;\n}\n\nvoid t_line::recvd_provisional(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_dialog *d;\n\n\tif (active_dialog && active_dialog->match_response(r, 0)) {\n\t\tactive_dialog->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, pending_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, dying_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tif (r->hdr_to.tag.size() > 0) {\n\t\t\t\t// Create a new pending dialog\n\t\t\t\td = open_dialog->copy();\n\t\t\t\tpending_dialogs.push_back(d);\n\t\t\t\td->recvd_response(r, tuid, tid);\n\t\t\t} else {\n\t\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t\t}\n\t\t} else {\n\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// out-of-dialog response\n\t// Provisional responses should only be given for INVITE.\n\t// A response for an INVITE is always in a dialog.\n\t// Ignore provisional responses for other requests.\n}\n\nvoid t_line::recvd_success(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_dialog *d;\n\n\tif (active_dialog && active_dialog->match_response(r, 0)) {\n\t\tactive_dialog->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, pending_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tif (!active_dialog) {\n\t\t\t\t// Make the dialog the active dialog\n\t\t\t\tactive_dialog = d;\n\t\t\t\tpending_dialogs.remove(d);\n\t\t\t\tstart_timer(LTMR_INVITE_COMP);\n\t\t\t\tsubstate = LSSUB_ESTABLISHED;\n\t\t\t\tui->cb_line_state_changed();\n\t\t\t} else {\n\t\t\t\t// An active dialog already exists.\n\t\t\t\t// Terminate this dialog by sending BYE\n\t\t\t\td->send_bye();\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, dying_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\td->send_bye();\n\t\t}\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\t// Create a new dialog\n\t\t\td = open_dialog->copy();\n\n\t\t\tif (!active_dialog) {\n\t\t\t\tactive_dialog = d;\n\t\t\t\tactive_dialog->recvd_response(r, tuid, tid);\n\t\t\t\tstart_timer(LTMR_INVITE_COMP);\n\t\t\t\tsubstate = LSSUB_ESTABLISHED;\n\t\t\t\tui->cb_line_state_changed();\n\t\t\t} else {\n\t\t\t\tpending_dialogs.push_back(d);\n\t\t\t\td->recvd_response(r, tuid, tid);\n\n\t\t\t\t// An active dialog already exists.\n\t\t\t\t// Terminate this dialog by sending BYE\n\t\t\t\td->send_bye();\n\t\t\t}\n\t\t} else {\n\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// Response does not match with any pending request. Discard.\n}\n\nvoid t_line::recvd_redirect(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_dialog *d;\n\t\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (active_dialog) {\n\t\t// If an active dialog exists then non-2XX should\n\t\t// only be for this dialog.\n\t\tif (active_dialog->match_response(r, 0)) {\n\t\t\t// Redirection of mid-dialog request\n\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t    !active_dialog->redirect_request(r))\n\t\t\t{\n\t\t\t\t// Redirection not allowed/failed\n\t\t\t\tactive_dialog->recvd_response(r, tuid, tid);\n\t\t\t}\n\t\t\t\n\t\t\t// Retrieve a held line after a REFER failure\n\t\t\tif (r->hdr_cseq.method == REFER &&\n\t\t\t    active_dialog->out_refer_req_failed)\n\t\t\t{\n\t\t\t\tactive_dialog->out_refer_req_failed = false;\n\t\t\t\tif (phone->get_active_line() == line_number &&\n\t\t\t\t    user_config->get_referrer_hold()) \n\t\t\t\t{\n\t\t\t\t\tretrieve();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, pending_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tpending_dialogs.remove(d);\n\t\t\tMEMMAN_DELETE(d);\n\t\t\tdelete d;\n\n\t\t\t// RFC 3261 13.2.2.3\n\t\t\t// All early dialogs are considered terminated\n\t\t\t// upon reception of the non-2xx final response.\n\t\t\tlist<t_dialog *>::iterator i;\n\t\t\tfor (i = pending_dialogs.begin();\n\t\t\t     i != pending_dialogs.end(); i++)\n\t\t\t{\n\t\t\t\tMEMMAN_DELETE(*i);\n\t\t\t\tdelete *i;\n\t\t\t}\n\t\t\tpending_dialogs.clear();\n\n\t\t\tif (open_dialog) {\n\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t    !open_dialog->redirect_invite(r))\n\t\t\t\t{\n\t\t\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\t\t\tdelete open_dialog;\n\t\t\t\t\topen_dialog = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, dying_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\tif (r->hdr_cseq.method != INVITE) {\n\t\t\t// TODO: can there be a non-INVITE response for an\n\t\t\t//       open dialog??\n\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t}\n\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t    !open_dialog->redirect_invite(r))\n\t\t\t{\n\t\t\t\t// Redirection failed/not allowed\n\t\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\t\tdelete open_dialog;\n\t\t\t\topen_dialog = NULL;\n\t\t\t}\n\n\t\t\t// RFC 3261 13.2.2.3\n\t\t\t// All early dialogs are considered terminated\n\t\t\t// upon reception of the non-2xx final response.\n\t\t\tlist<t_dialog *>::iterator i;\n\t\t\tfor (i = pending_dialogs.begin();\n\t\t\t     i != pending_dialogs.end(); i++)\n\t\t\t{\n\t\t\t\tMEMMAN_DELETE(*i);\n\t\t\t\tdelete *i;\n\t\t\t}\n\t\t\tpending_dialogs.clear();\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// out-of-dialog responses should be handled by the phone\n}\n\nvoid t_line::recvd_client_error(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_dialog *d;\n\t\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tif (active_dialog) {\n\t\t// If an active dialog exists then non-2XX should\n\t\t// only be for this dialog.\n\t\tif (active_dialog->match_response(r, 0)) {\n\t\t\tbool response_processed = false;\n\n\t\t\tif (r->must_authenticate()) {\n\t\t\t\t// Authentication for mid-dialog request\n\t\t\t\tif (active_dialog->resend_request_auth(r))\n\t\t\t\t{\n\t\t\t\t\t// Authorization successul.\n\t\t\t\t\t// The response does not need to be\n\t\t\t\t\t// processed any further\n\t\t\t\t\tresponse_processed = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!response_processed) {\n\t\t\t\t// The request failed, redirect it if there\n\t\t\t\t// are other destinations available.\n\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t    !active_dialog->redirect_request(r))\n\t\t\t\t{\n\t\t\t\t\t// Request failed\n\t\t\t\t\tactive_dialog->\n\t\t\t\t\t\trecvd_response(r, tuid, tid);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// Retrieve a held line after a REFER failure\n\t\t\tif (r->hdr_cseq.method == REFER &&\n\t\t\t    active_dialog->out_refer_req_failed)\n\t\t\t{\n\t\t\t\tactive_dialog->out_refer_req_failed = false;\n\t\t\t\tif (phone->get_active_line() == line_number &&\n\t\t\t\t    user_config->get_referrer_hold()) \n\t\t\t\t{\n\t\t\t\t\tretrieve();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, pending_dialogs);\n\tif (d) {\n\t\tif (r->hdr_cseq.method != INVITE) {\n\t\t\tif (r->must_authenticate()) {\n\t\t\t\t// Authentication for non-INVITE request in pending dialog\n\t\t\t\tif (!d->resend_request_auth(r)) {\n\t\t\t\t\t// Could not authorize, send response to dialog\n\t\t\t\t\t// where it will be handle as a client failure.\n\t\t\t\t\td->recvd_response(r, tuid, tid);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\td->recvd_response(r, tuid, tid);\n\t\t\t}\n\t\t} else {\n\t\t\td->recvd_response(r, tuid, tid);\n\t\t\tpending_dialogs.remove(d);\n\t\t\tMEMMAN_DELETE(d);\n\t\t\tdelete d;\n\n\t\t\t// RFC 3261 13.2.2.3\n\t\t\t// All early dialogs are considered terminated\n\t\t\t// upon reception of the non-2xx final response.\n\t\t\tlist<t_dialog *>::iterator i;\n\t\t\tfor (i = pending_dialogs.begin();\n\t\t\t     i != pending_dialogs.end(); i++)\n\t\t\t{\n\t\t\t\tMEMMAN_DELETE(*i);\n\t\t\t\tdelete *i;\n\t\t\t}\n\t\t\tpending_dialogs.clear();\n\n\t\t\tif (open_dialog) {\n\t\t\t\tbool response_processed = false;\n\n\t\t\t\tif (r->must_authenticate()) {\n\t\t\t\t\t// INVITE authentication\n\t\t\t\t\tif (open_dialog->resend_invite_auth(r))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Authorization successul.\n\t\t\t\t\t\t// The response does not need to\n\t\t\t\t\t\t// be processed any further\n\t\t\t\t\t\tresponse_processed = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Resend INVITE if the response indicated that\n\t\t\t\t// required extensions are not supported.\n\t\t\t\tif (!response_processed &&\n\t\t\t\t    open_dialog->resend_invite_unsupported(r))\n\t\t\t\t{\n\t\t\t\t\tresponse_processed = true;\n\t\t\t\t}\n\n\t\t\t\tif (!response_processed) {\n\t\t\t\t\t// The request failed, redirect it if there\n\t\t\t\t\t// are other destinations available.\n\t\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t\t    !open_dialog->redirect_invite(r))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Request failed\n\t\t\t\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\t\t\t\tdelete open_dialog;\n\t\t\t\t\t\topen_dialog = NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, dying_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\t// If the response is a 401/407 then do not send the\n\t\t// response to the dialog as the request must be resent.\n\t\t// For an INVITE request, the transaction layer has already\n\t\t// sent ACK for a failure response.\n\t\tif (r->hdr_cseq.method != INVITE) {\n\t\t\tif (r->must_authenticate()) {\n\t\t\t\t// Authenticate non-INVITE request\n\t\t\t\tif (!open_dialog->resend_request_auth(r)) {\n\t\t\t\t\t// Could not authorize, handle as other client\n\t\t\t\t\t// errors.\n\t\t\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t\t}\n\t\t}\n\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tbool response_processed = false;\n\n\t\t\tif (r->must_authenticate()) {\n\t\t\t\t// INVITE authentication\n\t\t\t\tif (open_dialog->resend_invite_auth(r))\n\t\t\t\t{\n\t\t\t\t\t// Authorization successul.\n\t\t\t\t\t// The response does not need to\n\t\t\t\t\t// be processed any further\n\t\t\t\t\tresponse_processed = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resend INVITE if the response indicated that\n\t\t\t// required extensions are not supported.\n\t\t\tif (!response_processed &&\n\t\t\t    open_dialog->resend_invite_unsupported(r))\n\t\t\t{\n\t\t\t\tresponse_processed = true;\n\t\t\t}\n\n\t\t\tif (!response_processed) {\n\t\t\t\t// The request failed, redirect it if there\n\t\t\t\t// are other destinations available.\n\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t    !open_dialog->redirect_invite(r))\n\t\t\t\t{\n\t\t\t\t\t// Request failed\n\t\t\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\t\t\tdelete open_dialog;\n\t\t\t\t\topen_dialog = NULL;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// RFC 3261 13.2.2.3\n\t\t\t// All early dialogs are considered terminated\n\t\t\t// upon reception of the non-2xx final response.\n\t\t\tlist<t_dialog *>::iterator i;\n\t\t\tfor (i = pending_dialogs.begin();\n\t\t\t     i != pending_dialogs.end(); i++)\n\t\t\t{\n\t\t\t\tMEMMAN_DELETE(*i);\n\t\t\t\tdelete *i;\n\t\t\t}\n\t\t\tpending_dialogs.clear();\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// out-of-dialog responses should be handled by the phone\n}\n\nvoid t_line::recvd_server_error(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_dialog *d;\n\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (active_dialog) {\n\t\t// If an active dialog exists then non-2XX should\n\t\t// only be for this dialog.\n\t\tif (active_dialog->match_response(r, 0)) {\n\t\t\tbool response_processed = false;\n\n\t\t\tif (r->code == R_503_SERVICE_UNAVAILABLE) {\n\t\t\t\t// RFC 3263 4.3\n\t\t\t\t// Failover to next destination\n\t\t\t\tif (active_dialog->failover_request(r))\n\t\t\t\t{\n\t\t\t\t\t// Failover successul.\n\t\t\t\t\t// The response does not need to be\n\t\t\t\t\t// processed any further\n\t\t\t\t\tresponse_processed = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!response_processed) {\n\t\t\t\t// The request failed, redirect it if there\n\t\t\t\t// are other destinations available.\n\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t    !active_dialog->redirect_request(r))\n\t\t\t\t{\n\t\t\t\t\t// Request failed\n\t\t\t\t\tactive_dialog->\n\t\t\t\t\t\trecvd_response(r, tuid, tid);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// Retrieve a held line after a REFER failure\n\t\t\tif (r->hdr_cseq.method == REFER &&\n\t\t\t    active_dialog->out_refer_req_failed)\n\t\t\t{\n\t\t\t\tactive_dialog->out_refer_req_failed = false;\n\t\t\t\tif (phone->get_active_line() == line_number &&\n\t\t\t\t    user_config->get_referrer_hold()) \n\t\t\t\t{\n\t\t\t\t\tretrieve();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, pending_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tpending_dialogs.remove(d);\n\t\t\tMEMMAN_DELETE(d);\n\t\t\tdelete d;\n\n\t\t\t// RFC 3261 13.2.2.3\n\t\t\t// All early dialogs are considered terminated\n\t\t\t// upon reception of the non-2xx final response.\n\t\t\tlist<t_dialog *>::iterator i;\n\t\t\tfor (i = pending_dialogs.begin();\n\t\t\t     i != pending_dialogs.end(); i++)\n\t\t\t{\n\t\t\t\tMEMMAN_DELETE(*i);\n\t\t\t\tdelete *i;\n\t\t\t}\n\t\t\tpending_dialogs.clear();\n\n\t\t\tif (open_dialog) {\n\t\t\t\tbool response_processed = false;\n\n\t\t\t\tif (r->code == R_503_SERVICE_UNAVAILABLE) {\n\t\t\t\t\t// INVITE failover\n\t\t\t\t\tif (open_dialog->failover_invite())\n\t\t\t\t\t{\n\t\t\t\t\t\t// Failover successul.\n\t\t\t\t\t\t// The response does not need to\n\t\t\t\t\t\t// be processed any further\n\t\t\t\t\t\tresponse_processed = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!response_processed) {\n\t\t\t\t\t// The request failed, redirect it if there\n\t\t\t\t\t// are other destinations available.\n\t\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t\t    !open_dialog->redirect_invite(r))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Request failed\n\t\t\t\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\t\t\t\tdelete open_dialog;\n\t\t\t\t\t\topen_dialog = NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\td = match_response(r, dying_dialogs);\n\tif (d) {\n\t\td->recvd_response(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\t// If the response is a 503 then do not send the\n\t\t// response to the dialog as the request must be resent.\n\t\t// For an INVITE request, the transaction layer has already\n\t\t// sent ACK for a failure response.\n\t\tif (r->code != R_503_SERVICE_UNAVAILABLE && r->hdr_cseq.method != INVITE) {\n\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t}\n\n\t\tif (r->hdr_cseq.method == INVITE) {\n\t\t\tbool response_processed = false;\n\n\t\t\tif (r->code == R_503_SERVICE_UNAVAILABLE) {\n\t\t\t\t// INVITE failover\n\t\t\t\tif (open_dialog->failover_invite())\n\t\t\t\t{\n\t\t\t\t\t// Failover successul.\n\t\t\t\t\t// The response does not need to\n\t\t\t\t\t// be processed any further\n\t\t\t\t\tresponse_processed = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!response_processed) {\n\t\t\t\t// The request failed, redirect it if there\n\t\t\t\t// are other destinations available.\n\t\t\t\tif (!user_config->get_allow_redirection() ||\n\t\t\t\t    !open_dialog->redirect_invite(r))\n\t\t\t\t{\n\t\t\t\t\t// Request failed\n\t\t\t\t\topen_dialog->recvd_response(r, tuid, tid);\n\t\t\t\t\tMEMMAN_DELETE(open_dialog);\n\t\t\t\t\tdelete open_dialog;\n\t\t\t\t\topen_dialog = NULL;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// RFC 3261 13.2.2.3\n\t\t\t// All early dialogs are considered terminated\n\t\t\t// upon reception of the non-2xx final response.\n\t\t\tlist<t_dialog *>::iterator i;\n\t\t\tfor (i = pending_dialogs.begin();\n\t\t\t     i != pending_dialogs.end(); i++)\n\t\t\t{\n\t\t\t\tMEMMAN_DELETE(*i);\n\t\t\t\tdelete *i;\n\t\t\t}\n\t\t\tpending_dialogs.clear();\n\t\t}\n\n\t\tcleanup();\n\t\treturn;\n\t}\n\n\t// out-of-dialog responses should be handled by the phone\n}\n\nvoid t_line::recvd_global_error(t_response *r, t_tuid tuid, t_tid tid) {\n\trecvd_redirect(r, tuid, tid);\n}\n\nvoid t_line::recvd_invite(t_phone_user *pu, t_request *r, t_tid tid, const string &ringtone) {\n\tt_user *user_config = NULL;\n\t\n\tswitch (state) {\n\tcase LS_IDLE:\n\t\tassert(!active_dialog);\n\t\tassert(r->hdr_to.tag == \"\");\n\n\t\t/*\n\t\t// TEST ONLY\n\t\t// Test code to test INVITE authentication\n\t\tif (!r->hdr_authorization.is_populated()) {\n\t\t\tresp = r->create_response(R_401_UNAUTHORIZED);\n\t\t\tt_challenge c;\n\t\t\tc.auth_scheme = AUTH_DIGEST;\n\t\t\tc.digest_challenge.realm = \"mtel.nl\";\n\t\t\tc.digest_challenge.nonce = \"0123456789abcdef\";\n\t\t\tc.digest_challenge.opaque = \"secret\";\n\t\t\tc.digest_challenge.algorithm = ALG_MD5;\n\t\t\tc.digest_challenge.qop_options.push_back(QOP_AUTH);\n\t\t\tc.digest_challenge.qop_options.push_back(QOP_AUTH_INT);\n\t\t\tresp->hdr_www_authenticate.set_challenge(c);\n\t\t\tsend_response(resp, 0, tid);\n\t\t\treturn;\n\t\t}\n\t\t*/\n\t\t\n\t\tassert(pu);\n\t\tphone_user = pu;\n\t\tuser_config = phone_user->get_user_profile();\n\t\tuser_defined_ringtone = ringtone;\n\t\t\n\t\tcall_info.mutex.lock();\n\t\tcall_info.from_uri = r->hdr_from.uri;\n\t\tcall_info.from_display = r->hdr_from.display;\n\t\tcall_info.from_display_override = r->hdr_from.display_override;\n\t\tif (r->hdr_organization.is_populated()) {\n\t\t\tcall_info.from_organization = r->hdr_organization.name;\n\t\t} else {\n\t\t\tcall_info.from_organization.clear();\n\t\t}\n\t\tcall_info.to_uri = r->hdr_to.uri;\n\t\tcall_info.to_display = r->hdr_to.display;\n\t\tcall_info.to_organization.clear();\n\t\tcall_info.subject = r->hdr_subject.subject;\n\t\tcall_info.mutex.unlock();\n\t\t\n\t\ttry_to_encrypt = user_config->get_zrtp_enabled();\n\n\t\t// Check for REFER support\n\t\t// If the Allow header is not present then assume REFER\n\t\t// is supported.\n\t\tif (!r->hdr_allow.is_populated() ||\n\t\t    r->hdr_allow.contains_method(REFER))\n\t\t{\n\t\t\tcall_info.refer_supported = true;\n\t\t}\n\n\t\tactive_dialog = new t_dialog(this);\n\t\tMEMMAN_NEW(active_dialog);\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t\tstate = LS_BUSY;\n\t\tsubstate = LSSUB_INCOMING_PROGRESS;\n\t\tui->cb_line_state_changed();\n\t\tstart_timer(LTMR_NO_ANSWER);\n\t\tcleanup();\n\t\t\n\t\t// Answer if auto answer mode is activated\n\t\tif (auto_answer) {\n\t\t\t// Validate speaker and mic\n\t\t\tstring error_msg;\n\t\t\tif (!sys_config->exec_audio_validation(false, true, true, error_msg)) {\n\t\t\t\tui->cb_display_msg(error_msg, MSG_CRITICAL);\n\t\t\t} else {\n\t\t\t\tanswer();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase LS_BUSY:\n\t\t// Only re-INVITEs can be sent to a busy line\n\t\tassert(r->hdr_to.tag != \"\");\n\n\t\t/*\n\t\t// TEST ONLY\n\t\t// Test code to test re-INVITE authentication\n\t\tif (!r->hdr_authorization.is_populated()) {\n\t\t\tresp = r->create_response(R_401_UNAUTHORIZED);\n\t\t\tt_challenge c;\n\t\t\tc.auth_scheme = AUTH_DIGEST;\n\t\t\tc.digest_challenge.realm = \"mtel.nl\";\n\t\t\tc.digest_challenge.nonce = \"0123456789abcdef\";\n\t\t\tc.digest_challenge.opaque = \"secret\";\n\t\t\tc.digest_challenge.algorithm = ALG_MD5;\n\t\t\tc.digest_challenge.qop_options.push_back(QOP_AUTH);\n\t\t\tc.digest_challenge.qop_options.push_back(QOP_AUTH_INT);\n\t\t\tresp->hdr_www_authenticate.set_challenge(c);\n\t\t\tsend_response(resp, 0, tid);\n\t\t\treturn;\n\t\t}\n\t\t*/\n\n\t\tif (active_dialog && active_dialog->match_request(r)) {\n\t\t\t// re-INVITE\n\t\t\tactive_dialog->recvd_request(r, 0, tid);\n\t\t\tcleanup();\n\t\t\treturn;\n\t\t}\n\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_line::recvd_ack(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t\tsubstate = LSSUB_ESTABLISHED;\n\t\tui->cb_line_state_changed();\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_cancel(t_request *r, t_tid cancel_tid,\n\t\tt_tid target_tid)\n{\n\t// A CANCEL matches a dialog if the target tid equals the tid\n\t// of the INVITE request. This will be checked by\n\t// dialog::recvd_cancel() itself.\n\tif (active_dialog) {\n\t\tactive_dialog->recvd_cancel(r, cancel_tid, target_tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_bye(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_options(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_prack(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_subscribe(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_notify(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_info(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_message(t_request *r, t_tid tid) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n}\n\nbool t_line::recvd_refer(t_request *r, t_tid tid) {\n\tbool retval = false;\n\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_request(r, 0, tid);\n\t\tretval = active_dialog->refer_accepted;\n\t} else {\n\t\t// Should not get here as phone already checked that\n\t\t// the request matched with this line\n\t\tassert(false);\n\t}\n\tcleanup();\n\treturn retval;\n}\n\nvoid t_line::recvd_refer_permission(bool permission, t_request *r) {\n\tif (active_dialog && active_dialog->match_request(r)) {\n\t\tactive_dialog->recvd_refer_permission(permission, r);\n\t}\n\tcleanup();\n}\n\nvoid t_line::recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid) {\n\tt_dialog *d;\n\n\tif (active_dialog && active_dialog->match_response(r, tuid)) {\n\t\tactive_dialog->recvd_stun_resp(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\t\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\topen_dialog->recvd_stun_resp(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\t\n\td = match_response(r, tuid, pending_dialogs);\n\tif (d) {\n\t\td->recvd_stun_resp(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n\t\n\td = match_response(r, tuid, dying_dialogs);\n\tif (d) {\n\t\td->recvd_stun_resp(r, tuid, tid);\n\t\tcleanup();\n\t\treturn;\n\t}\n}\n\nvoid t_line::failure(t_failure failure, t_tid tid) {\n\t// TODO\n}\n\nvoid t_line::timeout(t_line_timer timer, t_object_id did) {\n\tt_dialog *dialog = get_dialog(did);\n\tlist<t_display_url> cf_dest; // call forwarding destinations\n\n\tswitch (timer) {\n\tcase LTMR_ACK_TIMEOUT:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_ack_timeout = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_ACK_GUARD:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_ack_guard = 0;\n\t\t\tdialog->dur_ack_timeout = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_INVITE_COMP:\n\t\tid_invite_comp = 0;\n\t\t// RFC 3261 13.2.2.4\n\t\t// The UAC core considers the INVITE transaction completed\n\t\t// 64*T1 seconds after the reception of the first 2XX\n\t\t// response.\n\t\t// Cleanup all open and pending dialogs\n\t\tcleanup_open_pending();\n\t\tbreak;\n\tcase LTMR_NO_ANSWER:\n\t\t// User did not answer the call.\n\t\t// Reject call or redirect it if CF_NOANSWER is active.\n\t\t// If there is no active dialog then ignore the timeout.\n\t\t// The timer should have been stopped already.\n\t\tlog_file->write_report(\"No answer timeout\",\n\t\t\t\t\t\"t_line::timeout\");\n\t\t\n\t\tif (active_dialog) {\n\t\t\tassert(phone_user);\n\t\t\tt_user *user_config = phone_user->get_user_profile();\n\t\t\tt_service *srv = phone->ref_service(user_config);\n\t\t\tif (srv->get_cf_active(CF_NOANSWER, cf_dest)) {\n\t\t\t\tlog_file->write_report(\"Call redirection no answer\",\n\t\t\t\t\t\"t_line::timeout\");\n\t\t\t\tactive_dialog->redirect(cf_dest,\n\t\t\t\t\tR_302_MOVED_TEMPORARILY);\n\t\t\t} else {\n\t\t\t\tactive_dialog->reject(R_480_TEMP_NOT_AVAILABLE,\n\t\t\t\t\tREASON_480_NO_ANSWER);\n\t\t\t}\n\t\t\t\n\t\t\tui->cb_answer_timeout(get_line_number());\n\t\t}\n\t\tbreak;\n\tcase LTMR_RE_INVITE_GUARD:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_re_invite_guard = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_GLARE_RETRY:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_glare_retry = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_100REL_TIMEOUT:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_100rel_timeout = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_100REL_GUARD:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_100rel_guard = 0;\n\t\t\tdialog->dur_100rel_timeout = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_CANCEL_GUARD:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_cancel_guard = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_SESSION_REFRESH:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_session_refresh = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tcase LTMR_SESSION_EXPIRE:\n\t\t// If there is no dialog then ignore the timeout\n\t\tif (dialog) {\n\t\t\tdialog->id_session_expire = 0;\n\t\t\tdialog->timeout(timer);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tcleanup();\n}\n\nvoid t_line::timeout_sub(t_subscribe_timer timer, t_object_id did,\n\t\tconst string &event_type, const string &event_id)\n{\n\tt_dialog *dialog = get_dialog(did);\n\tif (dialog) dialog->timeout_sub(timer, event_type, event_id);\n\tcleanup();\n}\n\nbool t_line::match(t_response *r, t_tuid tuid) const {\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\treturn true;\n\t}\n\n\tif (active_dialog && active_dialog->match_response(r, 0)) {\n\t\treturn true;\n\t}\n\n\tif (match_response(r, pending_dialogs)) {\n\t\treturn true;\n\t}\n\n\tif (match_response(r, dying_dialogs)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool t_line::match(t_request *r) const {\n\tassert(r->method != CANCEL);\n\treturn (active_dialog && active_dialog->match_request(r));\n}\n\nbool t_line::match_cancel(t_request *r, t_tid target_tid) const {\n\tassert(r->method == CANCEL);\n\n\t// A CANCEL matches a dialog if the target tid equals the tid\n\t// of the INVITE request.\n\treturn (active_dialog && active_dialog->match_cancel(r, target_tid));\n}\n\nbool t_line::match(StunMessage *r, t_tuid tuid) const {\n\tif (open_dialog && open_dialog->match_response(r, tuid)) {\n\t\treturn true;\n\t}\n\n\tif (active_dialog && active_dialog->match_response(r, tuid)) {\n\t\treturn true;\n\t}\n\n\tif (match_response(r, tuid, pending_dialogs)) {\n\t\treturn true;\n\t}\n\n\tif (match_response(r, tuid, dying_dialogs)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool t_line::match_replaces(const string &call_id, const string &to_tag, \n\t\tconst string &from_tag, bool no_fork_req_disposition,\n\t\tbool &early_matched) const\n{\n\tif (active_dialog && active_dialog->match(call_id, to_tag, from_tag)) {\n\t\tearly_matched = false;\n\t\treturn true;\n\t}\n\n\t// RFC 3891 3\n\t// An early dialog only matches when it was created by the UA\n\t// As an exception to this rule we accept a match when the incoming\n\t// request contained a no-fork request disposition. This disposition\n\t// indicated that the request did not fork. The reason why RFC 3891 3\n\t// does not allow a match is to avoid problems with forked requests.\n\t// With this exception, call transfer scenario's during ringing can\n\t// be implemented.\n\tt_dialog *d;\n\tif ((d = match_call_id_tags(call_id, to_tag, from_tag, pending_dialogs)) != NULL &&\n\t    (d->is_call_id_owner() || no_fork_req_disposition)) \n\t{\n\t\tearly_matched = true;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool t_line::is_invite_retrans(t_request *r) {\n\tassert(r->method == INVITE);\n\treturn (active_dialog && active_dialog->is_invite_retrans(r));\n}\n\nvoid t_line::process_invite_retrans(void) {\n\tif (active_dialog) active_dialog->process_invite_retrans();\n}\n\nstring t_line::create_user_contact(const string &auto_ip) const {\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\treturn user_config->create_user_contact(hide_user, auto_ip);\n}\n\nstring t_line::create_user_uri(void) const {\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\treturn user_config->create_user_uri(hide_user);\n}\n\nt_response *t_line::create_options_response(t_request *r, bool in_dialog) const\n{\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\treturn phone->create_options_response(user_config, r, in_dialog);\n}\n\nvoid t_line::send_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tif (hide_user) {\n\t\tr->hdr_privacy.add_privacy(PRIVACY_ID);\n\t}\n\tphone->send_response(r, tuid, tid);\n}\n\nvoid t_line::send_request(t_request *r, t_tuid tuid) {\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\tphone->send_request(user_config, r, tuid);\n}\n\nt_phone *t_line::get_phone(void) const {\n\treturn phone;\n}\n\nunsigned short t_line::get_line_number(void) const {\n\treturn line_number;\n}\n\nbool t_line::get_is_on_hold(void) const {\n\treturn is_on_hold;\n}\n\nbool t_line::get_is_muted(void) const {\n\treturn is_muted;\n}\n\nbool t_line::get_hide_user(void) const {\n\treturn hide_user;\n}\n\nbool t_line::get_is_transfer_consult(unsigned short &lineno) const {\n\tlineno = consult_transfer_from_line;\n\treturn is_transfer_consult;\n}\n\nvoid t_line::set_is_transfer_consult(bool enable, unsigned short lineno) {\n\tis_transfer_consult = enable;\n\tconsult_transfer_from_line = lineno;\n}\n\nbool t_line::get_to_be_transferred(unsigned short &lineno) const {\n\tlineno = consult_transfer_to_line;\n\treturn to_be_transferred;\n}\n\nvoid t_line::set_to_be_transferred(bool enable, unsigned short lineno) {\n\tto_be_transferred = enable;\n\tconsult_transfer_to_line = lineno;\n}\n\nbool t_line::get_is_encrypted(void) const {\n\tt_audio_session *as = get_audio_session();\n\tif (as) return as->get_is_encrypted();\n\treturn false;\n}\n\nbool t_line::get_try_to_encrypt(void) const {\n\treturn try_to_encrypt;\n}\n\nbool t_line::get_auto_answer(void) const {\n\treturn auto_answer;\n}\n\nvoid t_line::set_auto_answer(bool enable) {\n\tauto_answer = enable;\n}\n\nbool t_line::is_refer_succeeded(void) const {\n\tif (active_dialog) return active_dialog->refer_succeeded;\n\treturn false;\n}\n\nbool t_line::has_media(void) const {\n\tt_session *session = get_session();\n\treturn (session && !session->receive_host.empty() && !session->dst_rtp_host.empty());\n}\n\nt_url t_line::get_remote_target_uri(void) const {\n\tif (!active_dialog) return t_url();\n\treturn active_dialog->get_remote_target_uri();\n}\n\nt_url t_line::get_remote_target_uri_pending(void) const {\n\tif (pending_dialogs.empty()) return t_url();\n\treturn pending_dialogs.front()->get_remote_target_uri();\n}\n\nstring t_line::get_remote_target_display(void) const {\n\tif (!active_dialog) return \"\";\n\treturn active_dialog->get_remote_target_display();\n}\n\nstring t_line::get_remote_target_display_pending(void) const {\n\tif (pending_dialogs.empty()) return \"\";\n\treturn pending_dialogs.front()->get_remote_target_display();\n}\n\nt_url t_line::get_remote_uri(void) const {\n\tif (!active_dialog) return t_url();\n\treturn active_dialog->get_remote_uri();\n}\n\nt_url t_line::get_remote_uri_pending(void) const {\n\tif (pending_dialogs.empty()) return t_url();\n\treturn pending_dialogs.front()->get_remote_uri();\n}\n\nstring t_line::get_remote_display(void) const {\n\tif (!active_dialog) return \"\";\n\treturn active_dialog->get_remote_display();\n}\n\nstring t_line::get_remote_display_pending(void) const {\n\tif (pending_dialogs.empty()) return \"\";\n\treturn pending_dialogs.front()->get_remote_display();\n}\n\nstring t_line::get_call_id(void) const {\n\tif (!active_dialog) return \"\";\n\treturn active_dialog->get_call_id();\n}\n\nstring t_line::get_call_id_pending(void) const {\n\tif (pending_dialogs.empty()) return \"\";\n\treturn pending_dialogs.front()->get_call_id();\n}\n\nstring t_line::get_local_tag(void) const {\n\tif (!active_dialog) return \"\";\n\treturn active_dialog->get_local_tag();\n}\n\nstring t_line::get_local_tag_pending(void) const {\n\tif (pending_dialogs.empty()) return \"\";\n\treturn pending_dialogs.front()->get_local_tag();\n}\n\nstring t_line::get_remote_tag(void) const {\n\tif (!active_dialog) return \"\";\n\treturn active_dialog->get_remote_tag();\n}\n\nstring t_line::get_remote_tag_pending(void) const {\n\tif (pending_dialogs.empty()) return \"\";\n\treturn pending_dialogs.front()->get_remote_tag();\n}\n\nbool t_line::remote_extension_supported(const string &extension) const {\n\tif (!active_dialog) return false;\n\treturn active_dialog->remote_extension_supported(extension);\n}\n\nbool t_line::seize(void) {\n\t// Only an idle line can be seized.\n\tif (substate != LSSUB_IDLE) return false;\n\n\tsubstate = LSSUB_SEIZED;\n\tui->cb_line_state_changed();\n\t\n\treturn true;\n}\n\nvoid t_line::unseize(void) {\n\t// Only a seized line can be unseized.\n\tif (substate != LSSUB_SEIZED) return;\n\n\tsubstate = LSSUB_IDLE;\n\tui->cb_line_state_changed();\n}\n\nt_session *t_line::get_session(void) const {\n\tif (!active_dialog) return NULL;\n\n\treturn active_dialog->get_session();\n}\n\nt_audio_session *t_line::get_audio_session(void) const {\n\tif (!active_dialog) return NULL;\n\n\treturn active_dialog->get_audio_session();\n}\n\nvoid t_line::notify_refer_progress(t_response *r) {\n\tif (active_dialog) active_dialog->notify_refer_progress(r);\n}\n\nvoid t_line::failed_retrieve(void) {\n\t// Call retrieve failed, so line is still on-hold\n\tis_on_hold = true;\n\tui->cb_line_state_changed();\n}\n\nvoid t_line::failed_hold(void) {\n\t// Call hold failed, so line is not on-hold\n\tis_on_hold = false;\n\tui->cb_line_state_changed();\n}\n\nvoid t_line::retry_retrieve_succeeded(void) {\n\t// Retry of retrieve succeeded, so line is not on-hold anymore\n\tis_on_hold = false;\n\tui->cb_line_state_changed();\n}\n\nt_call_info t_line::get_call_info(void) const {\n\treturn call_info;\n}\n\nvoid t_line::ci_set_dtmf_supported(bool supported, bool inband, bool info) {\n\tcall_info.dtmf_supported = supported;\n\tcall_info.dtmf_inband = inband;\n\tcall_info.dtmf_info = info;\n}\n\nvoid t_line::ci_set_last_provisional_reason(const string &reason) {\n\tcall_info.last_provisional_reason = reason;\n}\n\nvoid t_line::ci_set_send_codec(t_audio_codec codec) {\n\tcall_info.send_codec = codec;\n}\n\nvoid t_line::ci_set_recv_codec(t_audio_codec codec) {\n\tcall_info.recv_codec = codec;\n}\n\nvoid t_line::ci_set_refer_supported(bool supported) {\n\tcall_info.refer_supported = supported;\n}\n\nvoid t_line::init_rtp_port(void) {\n\trtp_port = sys_config->get_rtp_port() + line_number * 2;\n}\n\nunsigned short t_line::get_rtp_port(void) const {\n\treturn rtp_port;\n}\n\nt_user *t_line::get_user(void) const {\n\tt_user *user_config = NULL;\n\t\n\tif (phone_user) {\n\t\tuser_config = phone_user->get_user_profile();\n\t}\n\t\n\treturn user_config;\n}\n\nt_phone_user *t_line::get_phone_user(void) const {\n\treturn phone_user;\n}\n\nstring t_line::get_ringtone(void) const {\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (!user_defined_ringtone.empty()) {\n\t\t// Ring tone returned by incoming call script\n\t\treturn user_defined_ringtone;\n\t} else if (!user_config->get_ringtone_file().empty()) {\n\t\t// Ring tone from user profile\n\t\treturn user_config->get_ringtone_file();\n\t} else if (!sys_config->get_ringtone_file().empty()) {\n\t\t// Ring tone from system settings\n\t\treturn sys_config->get_ringtone_file();\n\t} else {\n\t\t// Twinkle default\n\t\treturn FILE_RINGTONE;\n\t}\t\n}\n\nvoid t_line::confirm_zrtp_sas(void) {\n\tt_audio_session *as = get_audio_session();\n\t\n\tif (as && !as->get_zrtp_sas_confirmed()) {\n\t\tas->confirm_zrtp_sas();\n\t\tui->cb_zrtp_sas_confirmed(line_number);\n\t\tui->cb_line_state_changed();\n\t\tlog_file->write_header(\"t_line::confirm_zrtp_sas\");\n\t\tlog_file->write_raw(\"Line \");\n\t\tlog_file->write_raw(line_number + 1);\n\t\tlog_file->write_raw(\": User confirmed ZRTP SAS\\n\");\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_line::reset_zrtp_sas_confirmation(void) {\n\tt_audio_session *as = get_audio_session();\n\t\n\tif (as && as->get_zrtp_sas_confirmed()) {\n\t\tas->reset_zrtp_sas_confirmation();\n\t\tui->cb_zrtp_sas_confirmation_reset(line_number);\n\t\tui->cb_line_state_changed();\n\t\tlog_file->write_header(\"t_line::reset_zrtp_sas_confirmation\");\n\t\tlog_file->write_raw(\"Line \");\n\t\tlog_file->write_raw(line_number + 1);\n\t\tlog_file->write_raw(\": User reset ZRTP SAS confirmation\\n\");\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_line::enable_zrtp(void) {\n\tt_audio_session *as = get_audio_session();\n\tif (as) {\n\t\tas->enable_zrtp();\n\t}\n}\n\nvoid t_line::zrtp_request_go_clear(void) {\n\tt_audio_session *as = get_audio_session();\n\tif (as) {\n\t\tas->zrtp_request_go_clear();\n\t}\n}\n\nvoid t_line::zrtp_go_clear_ok(void) {\n\tt_audio_session *as = get_audio_session();\n\tif (as) {\n\t\tas->zrtp_go_clear_ok();\n\t}\n}\n\nvoid t_line::force_idle(void) {\n\tcleanup_forced();\n}\n\nvoid t_line::set_keep_seized(bool seize) {\n\tkeep_seized = seize;\n\tcleanup();\n}\n\nbool t_line::get_keep_seized(void) const {\n\treturn keep_seized;\n}\n\nt_dialog *t_line::get_dialog_with_active_session(void) const {\n\tif (open_dialog && open_dialog->has_active_session()) {\n\t\treturn open_dialog;\n\t}\n\t\n\tif (active_dialog && active_dialog->has_active_session()) {\n\t\treturn active_dialog;\n\t}\n\t\n\tfor (list<t_dialog *>::const_iterator it = pending_dialogs.begin();\n\t     it != pending_dialogs.end(); ++it)\n\t{\n\t\tif ((*it)->has_active_session()) {\n\t\t\treturn *it;\n\t\t}\n\t}\n\t\n\tfor (list<t_dialog *>::const_iterator it = dying_dialogs.begin();\n\t     it != dying_dialogs.end(); ++it)\n\t{\n\t\tif ((*it)->has_active_session()) {\n\t\t\treturn *it;\n\t\t}\n\t}\n\t\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/line.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _LINE_H\n#define _LINE_H\n\n#include <list>\n#include <string>\n#include \"call_history.h\"\n#include \"dialog.h\"\n#include \"id_object.h\"\n#include \"phone.h\"\n#include \"protocol.h\"\n#include \"user.h\"\n#include \"audio/audio_codecs.h\"\n#include \"sockets/url.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"stun/stun.h\"\n\nusing namespace std;\n\n// Forward declarations\nclass t_dialog;\nclass t_phone;\n\n// Info about the current call.\n// This info can be used by the user interface to render the\n// call state to the user.\nclass t_call_info {\npublic:\n\tmutable t_mutex\tmutex;\n\tt_url\t\t\tfrom_uri;\n\tstring\t\t\tfrom_display;\n\t\n\t// Override of display for presentation to user, e.g. name from\n\t// address book lookup.\n\tstring\t\t\tfrom_display_override;\n\t\n\tstring\t\t\tfrom_organization;\n\tt_url\t\t\tto_uri;\n\tstring\t\t\tto_display;\n\tstring\t\t\tto_organization;\n\tstring\t\t\tsubject;\n\tbool\t\t\tdtmf_supported;\n\tbool\t\t\tdtmf_inband; // DTMF must be sent inband\n\tbool\t\t\tdtmf_info; // DTMF must be sent via SIP INFO\n\tt_hdr_referred_by\thdr_referred_by;\n\n\t// The reason phrase of the last received provisional response\n\t// on an outgoing INVITE.\n\tstring\t\tlast_provisional_reason;\n\n\tt_audio_codec\tsend_codec;\n\tt_audio_codec\trecv_codec;\n\tbool\t\trefer_supported;\n\t\n\tt_call_info();\n\tt_call_info(const t_call_info&);\n\tt_call_info& operator=(const t_call_info&);\n\n\tvoid clear(void);\n\t\n\t// Get the from display name to show to the user.\n\tstring get_from_display_presentation(void) const;\n};\n\nclass t_line : public t_id_object {\n\tfriend class t_phone;\n\t\nprivate:\n\tt_line_state\t\tstate;\n\tt_line_substate\t\tsubstate;\n\tbool\t\t\tis_on_hold;\n\tbool\t\t\tis_muted;\n\tbool\t\t\thide_user; // Anonymous call\n\t\n\t// Indicates if a call is a consultation for a transfer\n\tbool\t\t\tis_transfer_consult;\n\t\n\t// The line about which this consultation handles.\n\tunsigned short\t\tconsult_transfer_from_line;\n\t\n\t// Indicates if this call is to be transferred after consultation.\n\tbool\t\t\tto_be_transferred;\n\t\n\t// After consultation this line should be transferred to the\n\t// transfer_to_line.\n\tunsigned short\t\tconsult_transfer_to_line;\n\t\n\t// Indicates if media encryption should be negotiated.\n\tbool\t\t\ttry_to_encrypt;\n\t\n\t// Indicates if call must be auto answered\n\tbool\t\t\tauto_answer;\n\n\t// Line number (starting from 0)\n\t// The number of a line may change when it moves from the user lines\n\t// to the pool of dying lines. So a line number cannot be used as\n\t// unique line identification over longer times.\n\tunsigned short\t\tline_number;\n\n\t// The phone that owns this line\n\tt_phone\t\t\t*phone;\n\n\t// Dialog for which no response with a to-tag has been received.\n\t// Formally this is not a dialog yet.\n\tt_dialog\t\t*open_dialog;\n\n\t// Dialogs for which a response (1XX/2XX) with a to-tag has\n\t// been received.\n\tlist<t_dialog *>\tpending_dialogs;\n\n\t// Outgoing call: The first dialog for which a 2XX has been received.\n\t// Incoming call: Dialog created by an incoming INVITE\n\tt_dialog\t\t*active_dialog;\n\n\t// Currently not used.\n\tlist<t_dialog *>\tdying_dialogs;\n\n\t// Timers\n\tt_object_id\t\tid_invite_comp;\n\tt_object_id\t\tid_no_answer;\n\n\t// Call info\n\tt_call_info\t\tcall_info;\n\n\t/** RTP port to be used for this line. */\n\tunsigned short\t\trtp_port;\n\t\n\t/**\n\t * Phone user using the line.\n\t * This member is only set when the line is not idle.\n\t * An idle line is not associated with a user.\n\t * @note The line object does not own the phone user.\n\t *       Therefor the line object must never delete the phone user.\n\t */\n\tt_phone_user\t\t*phone_user;\n\t\n\t// The incoming call script can return a specific ring tone\n\t// to be played for an incoming call. This ring tone is\n\t// stored here. If there is no specific ring tone to be played\n\t// then this attribute is empty\n\tstring\t\t\tuser_defined_ringtone;\n\t\n\t// Indicates if the line must go to seized state when it\n\t// becomes idle.\n\tbool\t\t\tkeep_seized;\n\n\t// Find a dialog from the list that matches the response.\n\tt_dialog *match_response(t_response *r,\n\t\t\t\tconst list<t_dialog *> &l) const;\n\tt_dialog *match_response(StunMessage *r, t_tuid tuid,\n\t\t\t\tconst list<t_dialog *> &l) const;\n\tt_dialog *match_call_id_tags(const string &call_id,\n\t\tconst string &to_tag, const string &from_tag,\n\t\tconst list<t_dialog *> &l) const;\n\n\t// Get the dialog with id == did. If dialog does not exist\n\t// then NULL is returned.\n\tt_dialog *get_dialog(t_object_id did) const;\n\n\t// Clean up terminated dialogs\n\tvoid cleanup(void);\n\n\t// Cleanup all open and pending dialogs\n\tvoid cleanup_open_pending(void);\n\t\n\t// Forcefully cleanup all dialogs\n\tvoid cleanup_forced(void);\n\t\n\t// Cleanup state for a transfer with consultation.\n\t// If the call on this line is a consult, then the consult state of \n\t// the line that is to be transferred will be cleaned too.\n\t// If the call on this line is to be transferred, then the consult\n\t// state of the consultation line will be cleared too.\n\tvoid cleanup_transfer_consult_state(void);\n\npublic:\n\t// Call history record\n\tt_call_record\t\tcall_hist_record;\n\t\n\tt_line(t_phone *_phone, unsigned short _line_number);\n\t~t_line();\n\n\tt_line_state get_state(void) const;\n\tt_line_substate get_substate(void) const;\n\tt_refer_state get_refer_state(void) const;\n\n\t// Timer operations\n\tvoid start_timer(t_line_timer timer, t_object_id did = 0);\n\tvoid stop_timer(t_line_timer timer, t_object_id did = 0);\n\n\t/** @name Actions */\n\t//@{\n\t/**\n\t * Send INIVTE request.\n\t * @param pu The phone user making this call.\n\t * @param to_uri The URI to be used a request-URI and To header URI\n\t * @param to_display Display name for To header.\n\t * @param subject If not empty, this string will go into the Subject header.\n\t * @param hdr_referred_by The Reffered-By header to be put in the INVITE.\n\t * @param hdr_replaces The Replaces header to be put in the INVITE.\n\t * @param hdr_require Required extensions to be put in the Require header.\n\t * @param hdr_request_disposition Request-Disposition header to be put in the INVITE.\n\t * @param anonymous Inidicates if the INVITE should be sent anonymous.\n\t *\n\t * @pre The line is idle.\n\t */\n\tvoid invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, const t_hdr_referred_by &hdr_referred_by,\n\t\tconst t_hdr_replaces &hdr_replaces, const t_hdr_require &hdr_require,\n\t\tconst t_hdr_request_disposition &hdr_request_disposition,\n\t\tbool anonymous);\n\t\t\n\t/**\n\t * Send INIVTE request.\n\t * @param pu The phone user making this call.\n\t * @param to_uri The URI to be used a request-URI and To header URI\n\t * @param to_display Display name for To header.\n\t * @param subject If not empty, this string will go into the Subject header.\n\t * @param no_fork If true, put a no-fork request disposition in the outgoing INVITE\n\t * @param anonymous Inidicates if the INVITE should be sent anonymous.\n\t *\n\t * @pre The line is idle.\n\t */\n\tvoid invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, bool no_fork, bool anonymous);\n\t\t\n\tvoid answer(void);\n\tvoid reject(void);\n\tvoid redirect(const list<t_display_url> &destinations, int code, string reason = \"\");\n\tvoid end_call(void);\n\tvoid send_dtmf(char digit, bool inband, bool info);\n\t//@}\n\n\t// OPTIONS inside dialog\n\tvoid options(void);\n\n\tbool hold(bool rtponly = false); // returns false if call cannot be put on hold\n\tvoid retrieve(void);\n\t\n\t// Kill all RTP stream associated with this line\n\tvoid kill_rtp(void);\n\t\n\tvoid refer(const t_url &uri, const string &display);\n\n\t// Mute/unmute a call\n\t// - enable = true -> mute\n\t// - enable = false -> unmute\n\tvoid mute(bool enable);\n\n\t/** @name Handle incoming responses */\n\t//@{\n\tvoid recvd_provisional(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_success(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_redirect(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_client_error(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_server_error(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_global_error(t_response *r, t_tuid tuid, t_tid tid);\n\t//@}\n\n\t/** @name Handle incoming requests */\n\t//@{\n\tvoid recvd_invite(t_phone_user *pu, t_request *r, t_tid tid, const string &ringtone);\n\tvoid recvd_ack(t_request *r, t_tid tid);\n\tvoid recvd_cancel(t_request *r, t_tid cancel_tid, t_tid target_tid);\n\tvoid recvd_bye(t_request *r, t_tid tid);\n\tvoid recvd_options(t_request *r, t_tid tid);\n\tvoid recvd_register(t_request *r, t_tid tid);\n\tvoid recvd_prack(t_request *r, t_tid tid);\n\tvoid recvd_subscribe(t_request *r, t_tid tid);\n\tvoid recvd_notify(t_request *r, t_tid tid);\n\tvoid recvd_info(t_request *r, t_tid tid);\n\tvoid recvd_message(t_request *r, t_tid tid);\n\n\t/**\n\t * Process REFER request.\n\t * @return true, if refer has been accepted sofar. The refer may still\n\t * be rejected by the user.\n\t * @return false, if the refer has been rejected.\n\t */\n\tbool recvd_refer(t_request *r, t_tid tid);\n\t//@}\n\t\n\t// Handle the response from the user on the question for refer\n\t// permission. This response is received on the dialog that received\n\t// the REFER before.\n\t// The request (r) is the REFER request that was received.\n\tvoid recvd_refer_permission(bool permission, t_request *r);\n\t\n\tvoid recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid);\n\n\tvoid failure(t_failure failure, t_tid tid);\n\n\tvoid timeout(t_line_timer timer, t_object_id did);\n\tvoid timeout_sub(t_subscribe_timer timer, t_object_id did,\n\t\tconst string &event_type, const string &event_id);\n\n\t// Return true if the response or request matches a dialog that\n\t// is owned by this line\n\tbool match(t_response *r, t_tuid tuid) const;\n\tbool match(t_request *r) const;\n\tbool match_cancel(t_request *r, t_tid target_tid) const;\n\tbool match(StunMessage *r, t_tuid tuid) const;\n\t\n\t/**\n\t * RFC 3891 Match info from Replaces header\n\t * Match call id, to-tag and from tag like an incoming request.\n\t * @param call_id [in] The Call ID of the Replaces header.\n\t * @param to_tag [in] to-tag of the Replaces header.\n\t * @param from_tag [in] from-tag of the Replaces header.\n\t * @param no_fork_req_disposition [in] Indicates if the incoming request\n\t *\tcontains a no-fork request disposition.\n\t * @param early_matched [out] When a match is found, early_matched \n\t * \tindicates if the match was on an early dialog.\n\t * @return true if a match is found with an associated dialog.\n\t */\n\tbool match_replaces(const string &call_id, const string &to_tag, \n\t\tconst string &from_tag, \n\t\tbool no_fork_req_disposition,\n\t\tbool &early_matched) const;\n\n\t// Check if an incoming INVITE is a retransmission of an INVITE\n\t// that is already being processed by this line\n\tbool is_invite_retrans(t_request *r);\n\n\t// Process a retransmission of an incoming INVITE\n\tvoid process_invite_retrans(void);\n\n\t// Create user uri and contact uri\n\tstring create_user_contact(const string &auto_ip) const;\n\tstring create_user_uri(void) const;\n\n\t// Create a response to an OPTIONS request\n\t// Argument 'in-dialog' indicates if the OPTIONS response is\n\t// sent within a dialog.\n\tt_response *create_options_response(t_request *r,\n\t\t\t\t\tbool in_dialog = false) const;\n\n\t// Send a response/request\n\tvoid send_response(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid send_request(t_request *r, t_tuid tuid);\n\n\tt_phone *get_phone(void) const;\n\tunsigned short get_line_number(void) const;\n\tbool get_is_on_hold(void) const;\n\tbool get_is_muted(void) const;\n\tbool get_hide_user(void) const;\n\t\n\t// If this is a transfer consult, then true will be returned and\n\t// lineno will be set to the line that must be transferred.\n\tbool get_is_transfer_consult(unsigned short &lineno) const;\n\t\n\t// When setting the transfer consult indication to true, the\n\t// line that must be transferred must be passed.\n\tvoid set_is_transfer_consult(bool enable, unsigned short lineno);\n\t\n\t// If this line is to be transferred after consultation, then\n\t// true will be returned and lineno will be set to the line\n\t// where this line should be transferred to.\n\tbool get_to_be_transferred(unsigned short &lineno) const;\n\t\n\t// When setting the to be transferred indication to true, the\n\t// line to which must be transferred must be passed.\n\tvoid set_to_be_transferred(bool enable, unsigned short lineno);\n\t\n\tbool get_is_encrypted(void) const;\n\tbool get_try_to_encrypt(void) const;\n\tbool get_auto_answer(void) const;\n\tvoid set_auto_answer(bool enable);\n\tbool is_refer_succeeded(void) const;\n\tbool has_media(void) const;\n\t\n\t/** @name Remote (target) uri/display */\n\t//@{\n\t/** \n\t * Get the remote target URI of the active dialog.\n\t * @return Remote target URI. If there is no active dialog, then an\n\t *         empty URI is returned.\n\t */\n\tt_url get_remote_target_uri(void) const;\n\t\n\t/** \n\t * Get the remote target URI of the first pending dialog.\n\t * @return Remote target URI. If there is no pending dialog, then an\n\t *         empty URI is returned.\n\t */\n\tt_url get_remote_target_uri_pending(void) const;\n\t\n\t/**\n\t * Get the remote target display name of the active dialog.\n\t * @return Remote target display name. If there is no active dialog,\n\t *         then an empty string is returned.\n\t */\n\tstring get_remote_target_display(void) const;\n\t\n\t/**\n\t * Get the remote target display name of the first pending dialog.\n\t * @return Remote target display name. If there is no pending dialog,\n\t *         then an empty string is returned.\n\t */\n\tstring get_remote_target_display_pending(void) const;\n\t\n\t/** \n\t * Get the remote URI of the active dialog.\n\t * @return Remote URI. If there is no active dialog, then an\n\t *         empty URI is returned.\n\t */\n\tt_url get_remote_uri(void) const;\n\t\n\t/** \n\t * Get the remote URI of the first pending dialog.\n\t * @return Remote URI. If there is no pending dialog, then an\n\t *         empty URI is returned.\n\t */\n\tt_url get_remote_uri_pending(void) const;\n\t\n\t/**\n\t * Get the remote display name of the active dialog.\n\t * @return Remote display name. If there is no active dialog,\n\t *         then an empty string is returned.\n\t */\n\tstring get_remote_display(void) const;\n\t\n\t/**\n\t * Get the remote display name of the first pending dialog.\n\t * @return Remote display name. If there is no pending dialog,\n\t *         then an empty string is returned.\n\t */\n\tstring get_remote_display_pending(void) const;\n\t//@}\n\t\n\t/** @name Call identification */\n\t//@{\n\t/**\n\t * Get the call-id of the active dialog\n\t * @return If there is no active dialog, then an empty string is returned.\n\t */\n\tstring get_call_id(void) const;\n\t\n\t/**\n\t * Get the call-id of the first pending dialog\n\t * @return If there is no pending dialog, then an empty string is returned.\n\t */\n\tstring get_call_id_pending(void) const;\n\t\n\t/**\n\t * Get the local tag of the active dialog\n\t * @return If there is no active dialog, then an empty string is returned.\n\t */\n\tstring get_local_tag(void) const;\n\t\n\t/**\n\t * Get the local tag of the first pending dialog\n\t * @return If there is no pending dialog, then an empty string is returned.\n\t */\n\tstring get_local_tag_pending(void) const;\n\t\n\t/**\n\t * Get the remote tag of the active dialog\n\t * @return If there is no active dialog, then an empty string is returned.\n\t */\n\tstring get_remote_tag(void) const;\n\t\n\t/**\n\t * Get the remote tag of the first pending dialog\n\t * @return If there is no pending dialog, then an empty string is returned.\n\t */\n\tstring get_remote_tag_pending(void) const;\n\t//@}\n\t\n\t// Returns true if the remote party of the active dialog supports\n\t// the extension.\n\t// If there is no active dialog, then false is returned.\n\tbool remote_extension_supported(const string &extension) const;\n\n\t// Seize the line. User wants to make an outgoing call, so\n\t// the line must be marked as busy, such that an incoming call\n\t// cannot take this line.\n\t// Returns false if seizure failed\n\tbool seize(void);\n\n\t// Unseize the line\n\tvoid unseize(void);\n\n\t// Return the (audio) session belonging to this line.\n\t// Returns NULL if there is no (audio) session\n\tt_session *get_session(void) const;\n\tt_audio_session *get_audio_session(void) const;\n\n\tvoid notify_refer_progress(t_response *r);\n\n\t// Called by dialog if retrieve/hold actions failed.\n\tvoid failed_retrieve(void);\n\tvoid failed_hold(void);\n\n\t// Called by dialog if retry of a retrieve after a glare (491 response)\n\t// succeeded.\n\tvoid retry_retrieve_succeeded(void);\n\n\t// Get the call info record\n\tt_call_info get_call_info(void) const;\n\tvoid ci_set_dtmf_supported(bool supported, bool inband = false, bool info = false);\n\tvoid ci_set_last_provisional_reason(const string &reason);\n\tvoid ci_set_send_codec(t_audio_codec codec);\n\tvoid ci_set_recv_codec(t_audio_codec codec);\n\tvoid ci_set_refer_supported(bool supported);\n\n\t// Initialize the RTP port for this line based on the settings\n\t// in the user profile.\n\tvoid init_rtp_port(void);\n\n\t/** Get the RTP port to be used for a call on this line. */\n\tunsigned short get_rtp_port(void) const;\n\t\n\t/**\n\t * Get the user profile of the user using the phone.\n\t * @return a pointer to the user object owned by the line.\n\t * NOT a copy.\n\t */\n\tt_user *get_user(void) const;\n\t\n\t/**\n\t * Get the phone user using the phone.\n\t * @return Pointer to the phone user.\n\t */\n\tt_phone_user *get_phone_user(void) const;\n\t\n\t// Get the ring tone to be played for an incoming call\n\tstring get_ringtone(void) const;\n\t\n\t// ZRTP actions\n\tvoid confirm_zrtp_sas(void);\n\tvoid reset_zrtp_sas_confirmation(void);\n\tvoid enable_zrtp(void);\n\tvoid zrtp_request_go_clear(void);\n\tvoid zrtp_go_clear_ok(void);\n\t\n\t// Force a line to the idle state (during termination of Twinkle)\n\tvoid force_idle(void);\n\t\n\t// Indicate if the line must be seized after releasing\n\tvoid set_keep_seized(bool seize);\n\tbool get_keep_seized(void) const;\n\t\n\t/**\n\t * Get a dialog that has an active session (RTP stream).\n\t * @return The dialog that has an active session.\n\t * @return NULL, if there is no dialog with an active session.\n\t * @note There can be at most 1 dialog with an active session.\n\t */\n\tt_dialog *get_dialog_with_active_session(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/listener.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include \"events.h\"\n#include \"listener.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"im/im_iscomposing_body.h\"\n#include \"sockets/connection_table.h\"\n#include \"sockets/ipaddr.h\"\n#include \"sockets/socket.h\"\n#include \"parser/parse_ctrl.h\"\n#include \"parser/sip_message.h\"\n#include \"sdp/sdp_parse_ctrl.h\"\n#include \"stun/stun.h\"\n#include \"audits/memman.h\"\n#include \"presence/pidf_body.h\"\n\nextern t_phone *phone;\nextern t_socket_udp *sip_socket;\nextern t_socket_tcp *sip_socket_tcp;\nextern t_connection_table *connection_table;\nextern t_event_queue *evq_trans_mgr;\nextern t_event_queue *evq_trans_layer;\n\n// Minimal size of a message. Messages below this size will\n// be silently discarded.\n#define MIN_MESSAGE_SIZE\t10\n\n// Maximum number of pending TCP connections.\n#define TCP_BACKLOG\t\t5\n\nvoid recvd_stun_msg(char *datagram, int datagram_size, \n\tIPaddr src_addr, unsigned short src_port) \n{\n\tStunMessage m;\n\t\n\tif (!stunParseMessage(datagram, datagram_size, m, false)) {\n\t\tlog_file->write_report(\"Received faulty STUN message\", \n\t\t\t\t\"::recvd_stun_msg\", LOG_STUN, LOG_DEBUG);\n\t\treturn;\n\t}\n\t\n\tlog_file->write_header(\"::recvd_stun_msg\", LOG_STUN);\n\tlog_file->write_raw(\"Received from: \");\n\tlog_file->write_raw(h_ip2str(src_addr));\n\tlog_file->write_raw(\":\");\n\tlog_file->write_raw(src_port);\n\tlog_file->write_endl();\n\tlog_file->write_raw(stunMsg2Str(m));\n\tlog_file->write_footer();\n\t\n\tevq_trans_mgr->push_stun_response(&m, 0, 0);\n}\n\nt_sip_body *parse_body(const string &data, const t_sip_message *msg) {\n\tif (!msg->hdr_content_type.is_populated()) {\n\t\t// Content-Type header is missing. Pass body\n\t\t// unparsed. The upper application layer will\n\t\t// decide what to do.\n\t\tt_sip_body_opaque *p = new t_sip_body_opaque(data);\n\t\tMEMMAN_NEW(p);\n\t\treturn p;\n\t}\n\n\tif (msg->hdr_content_type.media.type == \"application\" &&\n\t    msg->hdr_content_type.media.subtype == \"sdp\")\n\t{\n\t\t// Parse SDP body\n\t\treturn t_sdp_parser::parse(data);\n\t} else if (msg->hdr_content_type.media.type == \"message\" &&\n\t           msg->hdr_content_type.media.subtype == \"sipfrag\")\n\t{\n\t\tt_sip_body_sipfrag *b;\n\n\t\t// Parse sipfrag body (RFC 3420)\n\t\ttry {\n\t\t\t// If the sipfrag does not contain a body itself,\n\t\t\t// then the CRLF at the end of the headers is optional!\n\t\t\t// Add an additional CRLF such that the SIP parser will\n\t\t\t// parse a sipfrag if the CRLF is not present. The SIP\n\t\t\t// parser will stop after it finds the double CRLF. So\n\t\t\t// a 3rd CRLF will not be detected by the parser (yuck).\n\t\t\tlist<string> parse_errors;\n\t\t\tt_sip_message *m = t_parser::parse(data + CRLF, parse_errors);\n\t\t\tb = new t_sip_body_sipfrag(m);\n\t\t\tMEMMAN_NEW(b);\n\t\t\tMEMMAN_DELETE(m);\n\t\t\tdelete m;\n\t\t\treturn b;\n\t\t} catch (int) {\n\t\t\t// Parsing failed, maybe because a request or status\n\t\t\t// line is not present, which is not mandatory for a\n\t\t\t// sipfrag body. Add a fake status line and try to parse\n\t\t\t// again.\n\t\t\tstring tmp = \"SIP/2.0 100 Trying\";\n\t\t\ttmp += CRLF;\n\t\t\ttmp += data;\n\t\t\ttmp += CRLF;\n\t\t\tlist<string> parse_errors;\n\t\t\tt_sip_message *resp = t_parser::parse(tmp, parse_errors);\n\n\t\t\t// Parsing succeeded. Now strip the fake header\n\t\t\tt_sip_message *m = new t_sip_message(*resp);\n\t\t\tMEMMAN_NEW(m);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete (resp);\n\t\t\tb = new t_sip_body_sipfrag(m);\n\t\t\tMEMMAN_NEW(b);\n\t\t\tMEMMAN_DELETE(m);\n\t\t\tdelete m;\n\t\t\treturn b;\n\t\t}\n\t} else if (msg->hdr_content_type.media.type == \"application\" &&\n\t           msg->hdr_content_type.media.subtype == \"dtmf-relay\")\n\t{\n\t\tt_sip_body_dtmf_relay *b = new t_sip_body_dtmf_relay();\n\t\tMEMMAN_NEW(b);\n\t\tif (b->parse(data)) return b;\n\t\tMEMMAN_DELETE(b);\n\t\tdelete b;\n\t\tthrow -1;\n\t} else if (msg->hdr_content_type.media.type == \"application\" &&\n\t           msg->hdr_content_type.media.subtype == \"simple-message-summary\")\n\t{\n\t\tt_simple_msg_sum_body *b = new t_simple_msg_sum_body();\n\t\tMEMMAN_NEW(b);\n\t\tif (b->parse(data)) return b;\n\t\tMEMMAN_DELETE(b);\n\t\tdelete b;\n\t\tthrow -1;\n\t} else if (msg->hdr_content_type.media.type == \"text\" &&\n\t           msg->hdr_content_type.media.subtype == \"plain\")\n\t{\n\t\tt_sip_body_plain_text *b = new t_sip_body_plain_text(data);\n\t\tMEMMAN_NEW(b);\n\t\treturn b;\n\t} else if (msg->hdr_content_type.media.type == \"text\" &&\n\t           msg->hdr_content_type.media.subtype == \"html\")\n\t{\n\t\tt_sip_body_html_text *b = new t_sip_body_html_text(data);\n\t\tMEMMAN_NEW(b);\n\t\treturn b;\n\t} else if (msg->hdr_content_type.media.type == \"application\" &&\n\t           msg->hdr_content_type.media.subtype == \"pidf+xml\")\n\t{\n\t\tt_pidf_xml_body *b = new t_pidf_xml_body();\n\t\tMEMMAN_NEW(b);\n\t\tif (b->parse(data)) return b;\n\t\tMEMMAN_DELETE(b);\n\t\tdelete b;\n\t\tthrow -1;\n\t} else if (msg->hdr_content_type.media.type == \"application\" &&\n\t           msg->hdr_content_type.media.subtype == \"im-iscomposing+xml\")\n\t{\n\t\tt_im_iscomposing_xml_body *b = new t_im_iscomposing_xml_body();\n\t\tMEMMAN_NEW(b);\n\t\tif (b->parse(data)) return b;\n\t\tMEMMAN_DELETE(b);\n\t\tdelete b;\n\t\tthrow -1;\n\t} else {\n\t\t// Pass other bodies unparsed. The upper application\n\t\t// layer will decide what to do.\n\t\tt_sip_body_opaque *p = new t_sip_body_opaque(data);\n\t\tMEMMAN_NEW(p);\n\t\treturn p;\n\t}\n}\n\nstatic void process_sip_msg(t_sip_message *msg, const string &raw_headers, const string &raw_body) {\n\tt_event_network\t*ev_network;\n\tstring\t\tlog_msg;\n\t\n\t// SIP message received\n\tlog_msg = \"Received from: \";\n\tlog_msg += msg->src_ip_port.tostring();\n\tlog_msg += \"\\n\";\n\t\n\t// Parse body\n\tif (!raw_body.empty()) {\n\t\t// The body should only be parsed if it is complete.\n\t\t// NOTE: The Content-length header may be absent (UDP)\n\t\tif (!msg->hdr_content_length.is_populated() || \n\t\t    msg->hdr_content_length.length == raw_body.size()) \n\t\t{\n\t\t\ttry {\n\t\t\t\tmsg->body = parse_body(raw_body, msg);\n\t\t\t}\n\t\t\tcatch (int) {\n\t\t\t\tif (msg->get_type() == MSG_RESPONSE) {\n\t\t\t\t\t// Discard a SIP response if the body is malformed.\n\t\t\t\t\tlog_msg += \"Invalid SIP message.\\n\";\n\t\t\t\t\tlog_msg += \"Parse error in body.\\n\";\n\t\t\t\t\tlog_msg += to_printable(raw_headers);\n\t\t\t\t\tlog_msg += to_printable(raw_body);\n\t\t\t\t\tlog_file->write_report(log_msg, \"::process_sip_msg\", \n\t\t\t\t\t\tLOG_SIP, LOG_DEBUG);\n\t\t\t\t\t\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\t// For a SIP request with a malformed body, the\n\t\t\t\t\t// transaction layer will give an error response.\n\t\t\t\t\t// Set the invalid body indication for the transaction\n\t\t\t\t\t// layer.\n\t\t\t\t\tmsg->body = new t_sip_body_opaque();\n\t\t\t\t\tMEMMAN_NEW(msg->body);\n\t\t\t\t\tmsg->body->invalid = true;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog_file->write_report(\"Received incomplete body\", \"::process_sip_msg\", \n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t}\n\t}\n\n\tlog_msg += to_printable(raw_headers);\n\tlog_msg += to_printable(raw_body);\n\tlog_file->write_report(log_msg, \"::process_sip_msg\", LOG_SIP);\n\n\t// If the message does not satisfy the mandatory\n\t// requirements from RFC 3261, then discard.\n\t// If the error is non-fatal, then the transaction layer\n\t// will send a proper error response.\n\t// If the message is an invalid response message then\n\t// discard the message. The transaction layer cannot\n\t// handle an invalid response as it cannot send an\n\t// error message back on an answer.\n\tbool fatal;\n\tstring reason;\n\tif (!msg->is_valid(fatal, reason) &&\n\t\t(fatal || msg->get_type() == MSG_RESPONSE))\n\t{\n\t\tlog_file->write_header(\"::process_sip_msg\", LOG_SIP);\n\t\tlog_file->write_raw(\"Discard invalid message.\\n\");\n\t\tlog_file->write_raw(reason);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\n\t\treturn;\n\t}\n\n\tif (msg->get_type() == MSG_REQUEST) {\n\t\t// RFC 3261 18.2.1\n\t\t// When the server transport receives a request over any transport, it\n\t\t// MUST examine the value of the \"sent-by\" parameter in the top Via\n\t\t// header field value.  If the host portion of the \"sent-by\" parameter\n\t\t// contains a domain name, or if it contains an IP address that differs\n\t\t// from the packet source address, the server MUST add a \"received\"\n\t\t// parameter to that Via header field value.  This parameter MUST\n\t\t// contain the source address from which the packet was received.\n\t\tstring src_ip = h_ip2str(msg->src_ip_port.ipaddr);\n\t\tt_via &top_via = msg->hdr_via.via_list.front();\n\t\tif (top_via.host != src_ip) {\n\t\t\ttop_via.received = src_ip;\n\t\t\tlog_file->write_header(\"::process_sip_msg\", LOG_SIP);\n\t\t\tlog_file->write_raw(\"Added via-parameter received=\");\n\t\t\tlog_file->write_raw(src_ip);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\n\t\t// RFC 3581 4\n\t\t// Add rport value if requested\n\t\t// Add received parameter\n\t\tif (top_via.rport_present && top_via.rport == 0) {\n\t\t\ttop_via.rport = msg->src_ip_port.port;\n\t\t\ttop_via.received = src_ip;\n\t\t}\n\t}\n\n\tev_network = new t_event_network(msg);\n\tMEMMAN_NEW(ev_network);\n\tev_network->src_addr = msg->src_ip_port.ipaddr;\n\tev_network->src_port = msg->src_ip_port.port;\n\tev_network->transport = msg->src_ip_port.transport;\n\tevq_trans_mgr->push(ev_network);\n}\n\nvoid *listen_udp(void *arg) {\n\tchar\t\tbuf[sys_config->get_sip_max_udp_size() + 1];\n\tint\t\tdata_size;\n\tIPaddr\tsrc_addr;\n\tunsigned short\tsrc_port;\n\tt_sip_message\t*msg;\n\tt_event_icmp\t*ev_icmp;\n\tstring::size_type\tpos_body;\t// position of body in msg\n\tstring\t\tlog_msg;\n\n\t// Number of consecutive non-icmp errors received\n\tint num_non_icmp_errors = 0;\n\n\twhile(true) {\n\t\ttry {\n\t\t\tdata_size = sip_socket->recvfrom(src_addr, src_port, buf, \n\t\t\t\tsys_config->get_sip_max_udp_size() + 1);\n\t\t\tnum_non_icmp_errors = 0;\n\t\t} catch (int err) {\n\t\t\t// Check if an ICMP error has been received\n\t\t\tt_icmp_msg icmp;\n\t\t\tif (sip_socket->get_icmp(icmp)) {\n\t\t\t\tlog_msg = \"Received ICMP from: \";\n\t\t\t\tlog_msg += h_ip2str(icmp.icmp_src_ipaddr);\n\t\t\t\tlog_msg += \"\\nICMP type: \";\n\t\t\t\tlog_msg += int2str(icmp.type);\n\t\t\t\tlog_msg += \"\\nICMP code: \";\n\t\t\t\tlog_msg += int2str(icmp.code);\n\t\t\t\tlog_msg += \"\\nDestination of packet causing ICMP: \";\n\t\t\t\tlog_msg += h_ip2str(icmp.ipaddr);\n\t\t\t\tlog_msg += \":\";\n\t\t\t\tlog_msg += int2str(icmp.port);\n\t\t\t\tlog_msg += \"\\nSocket error: \";\n\t\t\t\tlog_msg += int2str(err);\n\t\t\t\tlog_msg += \" \";\n\t\t\t\tlog_msg += get_error_str(err);\n\t\t\t\tlog_file->write_report(log_msg, \"::listen_udp\", LOG_NORMAL);\n\t\t\t\n\t\t\t\tev_icmp = new t_event_icmp(icmp);\n\t\t\t\tMEMMAN_NEW(ev_icmp);\n\t\t\t\tevq_trans_mgr->push(ev_icmp);\n\t\t\t\t\n\t\t\t\tnum_non_icmp_errors = 0;\n\t\t\t} else {\n\t\t\t\t// Even if an ICMP message is received this code can get\n\t\t\t\t//  executed. Sometimes the error is already present on \n\t\t\t\t// the socket, but the ICMP message is not yet queued.\n\t\t\t\tlog_msg = \"Failed to receive from SIP UDP socket.\\n\";\n\t\t\t\tlog_msg += \"Error code: \";\n\t\t\t\tlog_msg += int2str(err);\n\t\t\t\tlog_msg += \"\\n\";\n\t\t\t\tlog_msg += get_error_str(err);\n\t\t\t\tlog_file->write_report(log_msg, \"::listen_udp\");\n\t\t\t\t\n\t\t\t\tnum_non_icmp_errors++;\n\t\t\t\t\n\t\t\t\t/*\n\t\t\t\t * non-ICMP errors occur when a destination on the same\n\t\t\t\t * subnet cannot be reached. So this code seems to be\n\t\t\t\t * harmful.\n\t\t\t\tif (num_non_icmp_errors > 100) {\n\t\t\t\t\tlog_msg = \"Excessive number of socket errors.\";\n\t\t\t\t\tlog_file->write_report(log_msg, \"::listen_udp\", \n\t\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\t\tlog_msg = TRANSLATE(\"Excessive number of socket errors.\");\n\t\t\t\t\tui->cb_show_msg(log_msg, MSG_CRITICAL);\n\t\t\t\t\texit(1);\n\t\t\t\t}\n\t\t\t\t*/\n\t\t\t}\t\t\t\n\t\t\t\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\t// Some SIP proxies send small keep alive packets to keep\n\t\t// NAT bindings open. Discard such small packets as these\n\t\t// are not SIP or STUN messages.\n\t\tif (data_size < MIN_MESSAGE_SIZE) continue;\n\t\t\n\t\t// Check if this is a STUN message\n\t\t// The first byte of a STUN message is 0x00 or 0x01.\n\t\t// A SIP message is ASCII so the first byte for SIP is\n\t\t// never 0x00 or 0x01\n\t\tif (buf[0] <= 1)\n\t\t{\n\t\t\trecvd_stun_msg(buf, data_size, src_addr, src_port);\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\t// A SIP message may contain a NULL character (binary body),\n\t\t// do not handle the buffer as a C string.\n\t\tstring datagram(buf, data_size);\n\n\t\t// Split body from header\n\t\tstring seperator = string(CRLF) + string(CRLF);\n\t\tpos_body = datagram.find(seperator);\n\n\t\t// According to RFC 3261 syntax an empty line at\n\t\t// the end of the headers is mandatory in all SIP messages.\n\t\t// Here a missing empty line is accepted, but maybe\n\t\t// the message should be discarded.\n\t\tif (pos_body != string::npos) {\n\t\t\tpos_body += seperator.size();\n\t\t\tif (pos_body >= datagram.size()) {\n\t\t\t\t// No body is present\n\t\t\t\tpos_body = string::npos;\n\t\t\t}\n\t\t}\n\n\t\t// Parse SIP headers\n\t\tstring raw_headers = datagram.substr(0, pos_body);\n\t\tlist<string> parse_errors;\n\t\ttry {\n\t\t\tmsg = t_parser::parse(raw_headers, parse_errors);\n\t\t\tmsg->src_ip_port.ipaddr = src_addr;\n\t\t\tmsg->src_ip_port.port = src_port;\n\t\t\tmsg->src_ip_port.transport = \"udp\";\n\t\t}\n\t\tcatch (int) {\n\t\t\t// Discard malformed SIP messages.\n\t\t\tlog_msg = \"Invalid SIP message.\\n\";\n\t\t\tlog_msg += \"Fatal parse error in headers.\\n\\n\";\n\t\t\tlog_msg += to_printable(datagram);\n\t\t\tlog_msg += \"\\n\";\n\t\t\tlog_file->write_report(log_msg, \"::listen_udp\", LOG_SIP, LOG_DEBUG);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Log non-fatal parse errors.\n\t\tif (!parse_errors.empty()) {\n\t\t\tlog_msg = \"Parse errors:\\n\";\n\t\t\tlog_msg += \"\\n\";\n\t\t\tfor (list<string>::iterator i = parse_errors.begin(); \n\t\t\t     i != parse_errors.end(); i++) \n\t\t\t{\n\t\t\t\tlog_msg += *i;\n\t\t\t\tlog_msg += \"\\n\";\n\t\t\t}\n\t\t\tlog_msg += \"\\n\";\n\t\t\tlog_file->write_report(log_msg, \"::listen_udp\", LOG_SIP, LOG_DEBUG);\n\t\t}\n\n\t\t// Get raw body\n\t\tstring raw_body;\n\t\tif (pos_body != string::npos) {\n\t\t\traw_body = datagram.substr(pos_body);\n\t\t}\n\t\t\n\t\tprocess_sip_msg(msg, raw_headers, raw_body);\n\n\t\tMEMMAN_DELETE(msg);\n\t\tdelete msg;\n\t}\n\t\n\tlog_file->write_report(\"UDP listener terminated.\", \"::listen_udp\");\n\treturn NULL;\n}\n\nvoid *listen_for_data_tcp(void *arg) {\n\tstring log_msg;\n\tlist<t_connection *> readable_connections;\n\t\n\twhile(true) {\n\t\treadable_connections.clear();\n\t\treadable_connections = connection_table->select_read(NULL);\n\t\t\n\t\tif (readable_connections.empty()) {\n\t\t\t// Another thread cancelled the select command.\n\t\t\t// Stop listening.\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\t// NOTE: The connection table is now locked.\n\t\t\n\t\tfor (list<t_connection *>::iterator it = readable_connections.begin();\n\t\t\tit != readable_connections.end(); ++it)\n\t\t{\n\t\t\tstring raw_headers;\n\t\t\tstring raw_body;\n\t\t\tIPaddr remote_addr;\n\t\t\tunsigned short remote_port;\n\t\t\t\n\t\t\t(*it)->get_remote_address(remote_addr, remote_port);\n\t\t\t\n\t\t\ttry {\n\t\t\t\tbool connection_closed;\n\t\t\t\t(*it)->read(connection_closed);\n\t\t\t\t\n\t\t\t\tif (connection_closed) {\n\t\t\t\t\tlog_msg = \"Connection to \";\n\t\t\t\t\tlog_msg += h_ip2str(remote_addr);\n\t\t\t\t\tlog_msg += \":\";\n\t\t\t\t\tlog_msg += int2str(remote_port);\n\t\t\t\t\tlog_msg += \" closed.\";\n\t\t\t\t\tlog_file->write_report(log_msg, \"::listen_for_data_tcp\", LOG_SIP, LOG_DEBUG);\n\t\t\t\t\n\t\t\t\t\tconnection_table->remove_connection(*it);\n\t\t\t\t\tMEMMAN_DELETE(*it);\n\t\t\t\t\tdelete *it;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} catch (int err) {\n\t\t\t\tif (err == EAGAIN || err == EINTR) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tlog_msg = \"Got error on socket to \";\n\t\t\t\tlog_msg += h_ip2str(remote_addr);\n\t\t\t\tlog_msg += \":\";\n\t\t\t\tlog_msg += int2str(remote_port);\n\t\t\t\tlog_msg += \" - \";\n\t\t\t\tlog_msg += get_error_str(err);\n\t\t\t\tlog_file->write_report(log_msg, \"::listen_for_data_tcp\", LOG_SIP, LOG_WARNING);\n\t\t\t\t\n\t\t\t\t// Connection is broken. \n\t\t\t\t// Signal the transaction layer that the connection is broken for\n\t\t\t\t// all associated registered URI's.\n\t\t\t\tconst list<t_url> &uris = (*it)->get_registered_uri_set();\n\t\t\t\tfor (list<t_url>::const_iterator it_uri = uris.begin(); \n\t\t\t\t     it_uri != uris.end(); ++it_uri)\n\t\t\t\t{\n\t\t\t\t\tevq_trans_layer->push_broken_connection(*it_uri);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Remove the broken connection.\n\t\t\t\tconnection_table->remove_connection(*it);\n\t\t\t\tMEMMAN_DELETE(*it);\n\t\t\t\tdelete *it;\n\t\t\t\t\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t// Multiple messages may have been read in one action.\n\t\t\t// Get all SIP messages from the connection.\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tbool error = false;\n\t\t\t\tbool msg_too_large = false;\n\t\t\t\tt_sip_message *msg = (*it)->get_sip_msg(raw_headers, raw_body, error,\n\t\t\t\t\t\tmsg_too_large);\n\t\t\t\t\n\t\t\t\tif (error) {\n\t\t\t\t\t// The data on the connection could not be interpreted.\n\t\t\t\t\t// Close the connection to a faulty remote end.\n\t\t\t\t\tconnection_table->remove_connection(*it);\n\t\t\t\t\tMEMMAN_DELETE(*it);\n\t\t\t\t\tdelete *it;\n\t\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (msg_too_large) {\n\t\t\t\t\t// Close the connection. The message was too long, we don't want\n\t\t\t\t\t// to receive more data. Now we have an incomplete message,\n\t\t\t\t\t// but as we most likely have all headers we can still\n\t\t\t\t\t// send an error response, so the message is processed.\n\t\t\t\t\tconnection_table->remove_connection(*it);\n\t\t\t\t\tMEMMAN_DELETE(*it);\n\t\t\t\t\tdelete *it;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!msg) {\n\t\t\t\t\t// There are no complete messages on the connection.\n\t\t\t\t\t// Stop reading from this connection.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tprocess_sip_msg(msg, raw_headers, raw_body);\n\t\t\t\t\n\t\t\t\tMEMMAN_DELETE(msg);\n\t\t\t\tdelete msg;\n\t\t\t\t\n\t\t\t\tif (msg_too_large) {\n\t\t\t\t\t// The connection is closed already. Stop reading.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tconnection_table->unlock();\n\t}\n\t\n\tlog_file->write_report(\"TCP data listener terminated.\", \"::listen_for_data_tcp\");\n\t\n\treturn NULL;\n}\n\nvoid *listen_for_conn_requests_tcp(void *arg) {\n\tIPaddr dst_addr;\n\tunsigned short dst_port;\n\tstring log_msg;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tsip_socket_tcp->listen(TCP_BACKLOG);\n\t\t\tt_socket_tcp *tcp = sip_socket_tcp->accept(dst_addr, dst_port);\n\t\t\tt_connection *conn = new t_connection(tcp);\n\t\t\tMEMMAN_NEW(conn);\n\t\t\tconnection_table->add_connection(conn);\n\t\t} catch (int err) {\n\t\t\tif (err == EAGAIN || err == EINTR) continue;\n\t\t\t\n\t\t\tlog_file->write_header(\"::listen_for_conn_requests_tcp\", LOG_SIP, LOG_CRITICAL);\n\t\t\tlog_file->write_raw(\"Error on accept on TCP socket: \");\n\t\t\tlog_file->write_raw(get_error_str(err));\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tlog_msg = TRANSLATE(\"Cannot receive incoming TCP connections.\");\n\t\t\tui->cb_show_msg(log_msg, MSG_CRITICAL);\n\t\t\t\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tlog_file->write_report(\"TCP connection listener terminated.\", \"::listen_for_conn_requests_tcp\");\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/listener.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Network listener threads\n */\n\n#ifndef _H_LISTENER\n#define _H_LISTENER\n\n/** Thread listening on SIP UDP port */\nvoid *listen_udp(void *arg);\n\n/** Thread listening on established TCP connections */\nvoid *listen_for_data_tcp(void *arg);\n\n/** Thread listening for incoming TCP connection requests */\nvoid *listen_for_conn_requests_tcp(void *arg);\n\n#endif\n"
  },
  {
    "path": "src/log.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdio>\n#include <iostream>\n#include <fstream>\n#include <sys/time.h>\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"userintf.h\"\n#include \"user.h\"\n#include \"util.h\"\n\n// Pointer allocations/de-allocations are not checked by MEMMAN as the\n// log file will be deleted after the MEMMAN reports are logged and hence\n// would show false memory leaks.\n\nextern t_userintf cli;\n\n// Main function for log viewer\nvoid *main_logview(void *arg) {\n\twhile (true) {\n\t\tlog_file->wait_for_log();\n\t\t// TODO: handle situation where log file was zapped.\n\t\tif (ui) ui->cb_log_updated(false);\n\t}\n\treturn NULL;\n}\n\nbool t_log::move_current_to_old(void) {\n        string old_log = log_filename + \".old\";\n        if (rename(log_filename.c_str(), old_log.c_str()) != 0) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nt_log::t_log() {\n\tlog_disabled = false;\n\tlog_report_disabled = false;\n\tinform_user = false;\n\tsema_logview = NULL;\n\tthr_logview = NULL;\n\n        log_filename = DIR_HOME;\n        log_filename += \"/\";\n        log_filename += DIR_USER;\n        log_filename += \"/\";\n        log_filename += LOG_FILENAME;\n\n        // If there is a previous log file, then move that to the .old file\n        // before zapping the current log file.\n        (void)move_current_to_old();\n\n\tlog_stream = new ofstream(log_filename.c_str());\n\tif (!*log_stream) {\n\t\tlog_disabled = true;\n\t\tstring err = TRANSLATE(\"Failed to create log file %1 .\");\n\t\terr = replace_first(err, \"%1\", log_filename);\n\t\terr += \"\\nLogging is now disabled.\";\n\t\tif (ui) ui->cb_show_msg(err, MSG_WARNING);\n\t\treturn;\n\t}\n\n\tstring s = PRODUCT_NAME;\n\ts += ' ';\n\ts += PRODUCT_VERSION;\n\ts += \", \";\n\ts += PRODUCT_DATE;\n\twrite_report(s, \"t_log::t_log\");\n\t\n\tstring options_built = sys_config->get_options_built();\n\tif (!options_built.empty()) {\n\t\ts = \"Built with support for: \";\n\t\ts += options_built;\n\t\twrite_report(s, \"t_log::t_log\");\n\t}\n}\n\nt_log::~t_log() {\n\tif (thr_logview) delete thr_logview;\n\tif (sema_logview) delete sema_logview;\n\tdelete log_stream;\n}\n\nvoid t_log::write_report(const string &report, const string &func_name) {\n\twrite_report(report, func_name, LOG_NORMAL, LOG_INFO);\n}\n\nvoid t_log::write_report(const string &report, const string &func_name,\n\t\tt_log_class log_class, t_log_severity severity)\n{\n\tif (log_disabled) return;\n\n\twrite_header(func_name, log_class, severity);\n\twrite_raw(report);\n\twrite_endl();\n\twrite_footer();\n}\n\nvoid t_log::write_header(const string &func_name) {\n\twrite_header(func_name, LOG_NORMAL, LOG_INFO);\n}\n\nvoid t_log::write_header(const string &func_name, t_log_class log_class,\n\t                  t_log_severity severity)\n{\n\tif (log_disabled) return;\n\t\n\tmtx_log.lock();\n\t\n\tif (severity == LOG_DEBUG) {\n\t\t if (!sys_config->get_log_show_debug()) {\n\t\t \tlog_report_disabled = true;\n\t\t \treturn;\n\t\t }\n\t}\n\t\n\tswitch (log_class) {\n\tcase LOG_SIP:\n\t\tif (!sys_config->get_log_show_sip()) {\n\t\t\tlog_report_disabled = true;\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase LOG_STUN:\n\t\tif (!sys_config->get_log_show_stun()) {\n\t\t\tlog_report_disabled = true;\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase LOG_MEMORY:\n\t\tif (!sys_config->get_log_show_memory()) {\n\t\t\tlog_report_disabled = true;\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tstruct timeval t;\n\tstruct tm tm;\n\ttime_t\tdate;\n\n\tgettimeofday(&t, NULL);\n\tdate = t.tv_sec;\n\t::localtime_r(&date, &tm);\n\n\t*log_stream << \"+++ \";\n\t*log_stream << tm.tm_mday;\n\t*log_stream << \"-\";\n\t*log_stream << tm.tm_mon + 1;\n\t*log_stream << \"-\";\n\t*log_stream << tm.tm_year + 1900;\n\t*log_stream << \" \";\n\t*log_stream << int2str(tm.tm_hour, \"%02d\");\n\t*log_stream << \":\";\n\t*log_stream << int2str(tm.tm_min, \"%02d\");\n\t*log_stream << \":\";\n\t*log_stream << int2str(tm.tm_sec, \"%02d\");\n\t*log_stream << \".\";\n\t*log_stream << ulong2str(t.tv_usec, \"%06d\");\n\t*log_stream << \" \";\n\n\t// Severity\n\tswitch (severity) {\n\tcase LOG_INFO:\n\t\t*log_stream << \"INFO\";\n\t\tbreak;\n\tcase LOG_WARNING:\n\t\t*log_stream << \"WARNING\";\n\t\tbreak;\n\tcase LOG_CRITICAL:\n\t\t*log_stream << \"CRITICAL\";\n\t\tbreak;\n\tcase LOG_DEBUG:\n\t\t*log_stream << \"DEBUG\";\n\t\tbreak;\n\tdefault:\n\t\t*log_stream << \"UNNKOWN\";\n\t\tbreak;\n\t}\n\t*log_stream << \" \";\n\n\t// Message class\n\tswitch (log_class) {\n\tcase LOG_NORMAL:\n\t\t*log_stream << \"NORMAL\";\n\t\tbreak;\n\tcase LOG_SIP:\n\t\t*log_stream << \"SIP\";\n\t\tbreak;\n\tcase LOG_STUN:\n\t\t*log_stream << \"STUN\";\n\t\tbreak;\n\tcase LOG_MEMORY:\n\t\t*log_stream << \"MEMORY\";\n\t\tbreak;\n\tdefault:\n\t\t*log_stream << \"UNNKOWN\";\n\t\tbreak;\n\t}\n\t*log_stream << \" \";\n\n\t*log_stream << func_name;\n\t*log_stream << endl;\n}\n\nvoid t_log::write_footer(void) {\n\tif (log_disabled) return;\n\t\n\tif (log_report_disabled) {\n\t\tlog_report_disabled = false;\n\t\tmtx_log.unlock();\n\t\treturn;\n\t}\n\n\t*log_stream << \"---\\n\\n\";\n\tlog_stream->flush();\n\n\t// Check if log file is still in a good state\n\tif (!log_stream->good()) {\n\t\t// Log file is bad, disable logging\n\t\tlog_disabled = true;\n\t\tif (ui) ui->cb_display_msg(\"Writing to log file failed. Logging disabled.\",\n\t\t\tMSG_WARNING);\n\t\tmtx_log.unlock();\n\t\treturn;\n\t}\n\n\tbool log_zapped = false;\n\tif (log_stream->tellp() >= sys_config->get_log_max_size() * 1000000) {\n\t\t*log_stream << \"*** Log full. Rotate to new log file. ***\\n\";\n\t\tlog_stream->flush();\n\t\tlog_stream->close();\n\n\t\tif (!move_current_to_old()) {\n\t\t\t// Failed to move log file. Disable logging\n\t\t\tif (ui) ui->cb_display_msg(\"Renaming log file failed. Logging disabled.\",\n\t\t\t\tMSG_WARNING);\n\t\t\tlog_disabled = true;\n\t\t\tmtx_log.unlock();\n\t\t\treturn;\n\t\t}\n\n\t\tdelete log_stream;\n\n\t\tlog_stream = new ofstream(log_filename.c_str());\n\t\tif (!*log_stream) {\n\t\t\t// Failed to create a new log file. Disable logging\n\t\t\tif (ui) ui->cb_display_msg(\"Creating log file failed. Logging disabled.\",\n\t\t\t\tMSG_WARNING);\n\t\t\tlog_disabled = true;\n\t\t\tmtx_log.unlock();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tlog_zapped = true;\n\t}\n\n\tmtx_log.unlock();\n\t\n\t// Inform user about log update.\n\t// This code must be outside the locked region, otherwise it causes\n\t// a deadlock between the GUI and log mutexes.\n\tif (inform_user && sema_logview) sema_logview->up();\n}\n\nvoid t_log::write_raw(const string &raw) {\n\tif (log_disabled || log_report_disabled) return;\n\t\n\tif (raw.size() < MAX_LEN_LOG_STRING) {\n\t\t*log_stream << to_printable(raw);\n\t} else {\n\t\t*log_stream << to_printable(raw.substr(0, MAX_LEN_LOG_STRING));\n\t\t*log_stream << \"\\n\\n\";\n\t\t*log_stream << \"<cut off>\\n\";\n\t}\n}\n\nvoid t_log::write_raw(int raw) {\n\tif (log_disabled || log_report_disabled) return;\n\n\t*log_stream << raw;\n}\n\nvoid t_log::write_raw(unsigned int raw) {\n\tif (log_disabled || log_report_disabled) return;\n\n\t*log_stream << raw;\n}\n\nvoid t_log::write_raw(unsigned short raw) {\n\tif (log_disabled || log_report_disabled) return;\n\n\t*log_stream << raw;\n}\n\nvoid t_log::write_raw(unsigned long raw) {\n\tif (log_disabled || log_report_disabled) return;\n\n\t*log_stream << raw;\n}\n\nvoid t_log::write_raw(long raw) {\n\tif (log_disabled || log_report_disabled) return;\n\n\t*log_stream << raw;\n}\n\nvoid t_log::write_bool(bool raw) {\n\tif (log_disabled || log_report_disabled) return;\n\t\n\t*log_stream << (raw ? \"yes\" : \"no\");\n}\n\nvoid t_log::write_endl(void) {\n\tif (log_disabled || log_report_disabled) return;\n\n\t*log_stream << endl;\n}\n\nstring t_log::get_filename(void) const {\n\treturn log_filename;\n}\n\nvoid t_log::enable_inform_user(bool on) {\n\tif (on) {\n\t\tif (!sema_logview) {\n\t\t\tsema_logview = new t_semaphore(0);\n\t\t}\n\t\t\n\t\tif (!thr_logview) {\n\t\t\tthr_logview = new t_thread(main_logview, NULL);\n\t\t}\n\t} else {\n\t\tif (thr_logview) {\n\t\t\tthr_logview->cancel();\n\t\t\tthr_logview->join();\n\t\t\tdelete thr_logview;\n\t\t\tthr_logview = NULL;\n\t\t}\n\t\t\n\t\tif (sema_logview) {\n\t\t\tdelete sema_logview;\n\t\t\tsema_logview = NULL;\n\t\t}\n\t}\n\n\tinform_user = on;\n}\n\nvoid t_log::wait_for_log(void) {\n\tif (sema_logview) sema_logview->down();\n}\n"
  },
  {
    "path": "src/log.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _LOG_H\n#define _LOG_H\n\n#include <string>\n#include <fstream>\n#include \"threads/mutex.h\"\n#include \"threads/sema.h\"\n#include \"threads/thread.h\"\n\nusing namespace std;\n\n#define LOG_FILENAME    \"twinkle.log\"\n\n// Severity of a log message\nenum t_log_severity {\n\tLOG_INFO,\n\tLOG_WARNING,\n\tLOG_CRITICAL,\n\tLOG_DEBUG\n};\n\n// Message class\nenum t_log_class {\n\tLOG_NORMAL,\n\tLOG_SIP,\n\tLOG_STUN,\n\tLOG_MEMORY\n};\n\nclass t_log {\nprivate:\n\t/** Maximum length of a logged string (bytes) */\n\tstatic const string::size_type MAX_LEN_LOG_STRING = 4096;\n\n        string          log_filename;\n        ofstream        *log_stream;\n\n\t// Mutex for exclusive acces to the log file\n\tt_mutex\t\tmtx_log;\n\n\t// Indicates if logging is disabled\n\tbool\t\tlog_disabled;\n\tbool\t\tlog_report_disabled;\n\t\n\t// Indicates if the user should be informed about log updates\n\tbool\t\tinform_user;\n\t\n\t// Indicates if new data for the log viewer is available\n\tt_semaphore\t*sema_logview;\n\t\n\t// Thread for updating the log viewer\n\tt_thread\t*thr_logview;\n\n        // Move the current log file to the .old log file\n        bool move_current_to_old(void);\n\npublic:\n        t_log();\n        ~t_log();\n\n        // Write a report with header and footer\n        void write_report(const string &report, const string &func_name); // normal, info\n\tvoid write_report(const string &report, const string &func_name,\n\t\tt_log_class log_class, t_log_severity severity = LOG_INFO);\n\n        // Write header\n\t// This locks the mtx_log. So you must call write footer to release\n\t// the log again!\n        void write_header(const string &func_name); // class normal, severity info\n\tvoid write_header(const string &func_name, t_log_class log_class,\n\t                  t_log_severity severity = LOG_INFO);\n\n        // Write footer\n\t// This unlocks the mtx_log.\n        void write_footer(void);\n\n        // Write raw data\n        void write_raw(const string &raw);\n        void write_raw(int raw);\n        void write_raw(unsigned int raw);\n        void write_raw(unsigned short raw);\n        void write_raw(unsigned long raw);\n\tvoid write_raw(long raw);\n\tvoid write_bool(bool raw);\n\n        // Write end of line\n        void write_endl(void);\n        \n        // Return the full path name of the log file\n        string get_filename(void) const;\n        \n        // Enable/disable user informs on updates\n        void enable_inform_user(bool on);\n        \n        // Block till log information is available for log viewer\n        void wait_for_log(void);\n};\n\nextern t_log *log_file;\n\n#endif\n"
  },
  {
    "path": "src/main.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include <string>\n#include <cstdlib>\n#include <ctime>\n#include \"address_book.h\"\n#include \"call_history.h\"\n#include \"events.h\"\n#include \"line.h\"\n#include \"listener.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"protocol.h\"\n#include \"sender.h\"\n#include \"sys_settings.h\"\n#include \"transaction_mgr.h\"\n#include \"translator.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"sockets/connection_table.h\"\n#include \"sockets/interfaces.h\"\n#include \"sockets/socket.h\"\n#include \"threads/thread.h\"\n#include \"utils/mime_database.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\nusing namespace utils;\n\n// Class to initialize the random generator before objects of\n// other classes are created. Initializing just from the main function\n// is too late.\nclass t_init_rand {\npublic:\n\tt_init_rand();\n};\n\nt_init_rand::t_init_rand() { srand(time(NULL)); }\n\n// Memory manager for memory leak tracing\nt_memman \t\t*memman;\n\n// Initialize random generator\nt_init_rand init_rand;\n\n// Indicates if application is ending (because user pressed Quit)\nbool end_app;\n\n// Language translator\nt_translator *translator = NULL;\n\n// IP address on which the phone is running\nstring user_host;\n\n// Local host name\nstring local_hostname;\n\n// SIP UDP socket for sending and receiving signaling\nt_socket_udp *sip_socket;\n\n// SIP TCP socket for sending and receiving signaling\nt_socket_tcp *sip_socket_tcp;\n\n// SIP connection table for connection oriented transport\nt_connection_table *connection_table;\n\n// Event queue that is handled by the transaction manager thread\n// The following threads write to this queue\n// - UDP listener\n// - transaction layer\n// - timekeeper\nt_event_queue\t\t*evq_trans_mgr;\n\n// Event queue that is handled by the sender thread\n// The following threads write to this queue:\n// - phone UAS\n// - phone UAC\n// - transaction manager\nt_event_queue\t\t*evq_sender;\n\n// Event queue that is handled by the transaction layer thread\n// The following threads write to this queue\n// - transaction manager\n// - timekeeper\nt_event_queue\t\t*evq_trans_layer;\n\n// Event queue that is handled by the phone timekeeper thread\n// The following threads write into this queue\n// - phone UAS\n// - phone UAC\n// - transaction manager\nt_event_queue\t\t*evq_timekeeper;\n\n// The timekeeper\nt_timekeeper\t\t*timekeeper;\n\n// The transaction manager\nt_transaction_mgr\t*transaction_mgr;\n\n// The phone\nt_phone\t\t\t*phone;\n\n// User interface\nt_userintf\t\t*ui;\n\n// Log file\nt_log\t\t\t*log_file;\n\n// System config\nt_sys_settings\t\t*sys_config;\n\n// Call history\nt_call_history\t\t*call_history;\n\n// Local address book\nt_address_book\t\t*ab_local;\n\n// Mime database\nt_mime_database\t\t*mime_database;\n\n// If a port number is passed by the user on the command line, then\n// that port number overrides the port from the system settings.\nunsigned short\t\tg_override_sip_port = 0;\nunsigned short\t\tg_override_rtp_port = 0;\n\n// Indicates if LinuxThreads or NPTL is active.\nbool\t\t\tthreading_is_LinuxThreads;\n\n\nint main(int argc, char *argv[]) {\n\tstring error_msg;\n\t\n\tend_app = false;\n\n\tmemman = new t_memman();\n\tMEMMAN_NEW(memman);\n\ttranslator = new t_translator();\n\tMEMMAN_NEW(translator);\n\tconnection_table = new t_connection_table();\n\tMEMMAN_NEW(connection_table);\n\tevq_trans_mgr = new t_event_queue();\n\tMEMMAN_NEW(evq_trans_mgr);\n\tevq_sender = new t_event_queue();\n\tMEMMAN_NEW(evq_sender);\n\tevq_trans_layer = new t_event_queue();\n\tMEMMAN_NEW(evq_trans_layer);\n\tevq_timekeeper = new t_event_queue();\n\tMEMMAN_NEW(evq_timekeeper);\n\ttimekeeper = new t_timekeeper();\n\tMEMMAN_NEW(timekeeper);\n\ttransaction_mgr = new t_transaction_mgr();\n\tMEMMAN_NEW(transaction_mgr);\n\tphone = new t_phone();\n\tMEMMAN_NEW(phone);\n\n\tsys_config = new t_sys_settings();\n\tMEMMAN_NEW(sys_config);\n\tui = new t_userintf(phone);\n\tMEMMAN_NEW(ui);\n\n\t// Check requirements on environment\n\tif (!sys_config->check_environment(error_msg)) {\n\t\t// Environment is not good\n\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\texit(1);\n\t}\n\t\n\t// Read system configuration\n\tif (!sys_config->read_config(error_msg)) {\n\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\texit(1);\n\t}\n\t\n\t// Get default values from system configuration\n\tlist<string> config_files;\n\tlist<string> start_user_profiles = sys_config->get_start_user_profiles();\n\tfor (list<string>::iterator i = start_user_profiles.begin();\n\t     i != start_user_profiles.end(); i++)\n\t{\n\t\tstring config_file = *i;\n\t\tconfig_file += USER_FILE_EXT;\n\t\tconfig_files.push_back(config_file);\n\t}\n\n#if 0\n\t// DEPRECATED\n\tif (user_host.empty()) {\n                string ip;\n\t\tif (exists_interface(sys_config->get_start_user_host())) {\n\t\t\tuser_host = sys_config->get_start_user_host();\n\t\t}\n\t\telse if (exists_interface_dev(sys_config->get_start_user_nic(), ip)) {\n\t\t\tuser_host = ip;\n\t\t}\n\t}\n#endif\n\tuser_host = AUTO_IP4_ADDRESS;\n\tlocal_hostname = get_local_hostname();\n\n\t// Create a lock file to guarantee that the application\n\t// runs only once.\n\tbool already_running;\n\tif (!sys_config->create_lock_file(false, error_msg, already_running)) {\n\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\texit(1);\n\t}\n\n\tlog_file = new t_log();\n\tMEMMAN_NEW(log_file);\n\t\n\tcall_history = new t_call_history();\n\tMEMMAN_NEW(call_history);\n\n\t// Determine threading implementation\n\tthreading_is_LinuxThreads = t_thread::is_LinuxThreads();\n\tif (threading_is_LinuxThreads) {\n\t\tlog_file->write_report(\"Threading implementation is LinuxThreads.\",\n\t\t\t\"::main\", LOG_NORMAL, LOG_INFO);\n\t} else {\n\t\tlog_file->write_report(\"Threading implementation is NPTL.\",\n\t\t\t\"::main\", LOG_NORMAL, LOG_INFO);\n\t}\n\n\t// Take default user profile if there are is no default is sys settings\n\tif (config_files.empty()) config_files.push_back(USER_CONFIG_FILE);\n\n\tif (argc >= 2) {\n\t\tconfig_files.clear();\n\t\tfor (int i = 1; i < argc && i < 2; i++) {\n\t\t\tconfig_files.push_back(argv[i]);\n\t\t}\n\t}\n\n\tfor (int i = 1; i < argc; i++) {\n\tif (strcmp(argv[i], \"--sip-port\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\tsys_config->set_override_sip_port(atoi(argv[i]));\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Port missing for option '--sip-port'\\n\";\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--rtp-port\") == 0) {\n\t\t\tif (i < argc - 1) {\n\t\t\t\ti++;\n\t\t\t\tsys_config->set_override_rtp_port(atoi(argv[i]));\n\t\t\t} else {\n\t\t\t\tcout << argv[0] << \": \";\n\t\t\t\tcout << \"Port missing for option '--rtp-port'\\n\";\n\t\t\t}\n\t\t}\n\t}\n\n\t// Activate users\n\tfor (list<string>::iterator i = config_files.begin();\n\t\ti != config_files.end(); i++)\n\t{\t\t\n\t\tt_user *user_config = new t_user();\n\t\tMEMMAN_NEW(user_config);\n\t\tif (!user_config->read_config(*i, error_msg)) {\n\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t\tsys_config->delete_lock_file();\n\t\t\texit(1);\n\t\t}\n\t\t\n\t\tt_user *dup_user;\n\t\tif(!phone->add_phone_user(*user_config, &dup_user)) {\n\t\t\terror_msg = \"The following profiles are both for user \";\n\t\t\terror_msg += user_config->get_name();\n\t\t\terror_msg += '@';\n\t\t\terror_msg += user_config->get_domain();\n\t\t\terror_msg += \":\\n\\n\";\n\t\t\terror_msg += user_config->get_profile_name();\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += dup_user->get_profile_name();\n\t\t\terror_msg += \"\\n\\n\";\n\t\t\terror_msg += \"You can only run multiple profiles \";\n\t\t\terror_msg += \"for different users.\";\n\t\t\tui->cb_show_msg(error_msg, MSG_CRITICAL);\n\t\t\tsys_config->delete_lock_file();\n\t\t\texit(1);\n\t\t}\n\t\t\t\n\t\tMEMMAN_DELETE(user_config);\n\t\tdelete user_config;\n\t}\n\t\n\t// Read call history\n\tif (!call_history->load(error_msg)) {\n\t\tlog_file->write_report(error_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t}\n\t\n\t// Create local address book\n\tab_local = new t_address_book();\n\tMEMMAN_NEW(ab_local);\n\t\n\t// Read local address book\n\tif (!ab_local->load(error_msg)) {\n\t\tlog_file->write_report(error_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t\tui->cb_show_msg(error_msg, MSG_WARNING);\n\t}\n\t\n\t// Create mime database\n\tmime_database = new t_mime_database();\n\tMEMMAN_NEW(mime_database);\n\tif (!mime_database->load(error_msg)) {\n\t\tlog_file->write_report(error_msg, \"::main\", LOG_NORMAL, LOG_WARNING);\n\t}\n\n\t// Initialize RTP port settings.\n\tphone->init_rtp_ports();\n\n\t// Open UDP socket for SIP signaling\n\ttry {\n\t\tsip_socket = new t_socket_udp(sys_config->get_sip_port());\n\t\tMEMMAN_NEW(sip_socket);\n\t\tif (sip_socket->enable_icmp()) {\n\t\t\tlog_file->write_report(\"ICMP processing enabled.\", \"::main\");\n\t\t} else {\n\t\t\tlog_file->write_report(\"ICMP processing disabled.\", \"::main\");\n\t\t}\n\t} catch (int err) {\n\t\tstring msg(\"Failed to create a UDP socket (SIP) on port \");\n\t\tmsg += int2str(sys_config->get_sip_port());\n\t\tmsg += \"\\n\";\n\t\tmsg += get_error_str(err);\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t}\n\t\n\t// Open TCP socket for SIP signaling\n\ttry {\n\t\tsip_socket_tcp = new t_socket_tcp(sys_config->get_sip_port());\n\t\tMEMMAN_NEW(sip_socket_tcp);\t\t\n\t} catch (int err) {\n\t\tstring msg(\"Failed to create a TCP socket (SIP) on port \");\n\t\tmsg += int2str(sys_config->get_sip_port());\n\t\tmsg += \"\\n\";\n\t\tmsg += get_error_str(err);\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t}\n\t\n#if 0\n\t// DEPRECATED\n\t// Pick network interface\n\tif (user_host.empty()) {\n\t\tuser_host = ui->select_network_intf();\n\t\tif (user_host.empty()) {\n\t\t\tsys_config->delete_lock_file();\n\t\t\texit(1);\n\t\t}\n\t}\n#endif\n\t\n\t// Discover NAT type if STUN is enabled\n\tlist<string> msg_list;\n\tif (!phone->stun_discover_nat(msg_list)) {\n\t\tfor (list<string>::iterator i = msg_list.begin();\n\t\t     i != msg_list.end(); i++)\n\t\t{\n\t\t\tui->cb_show_msg(*i, MSG_WARNING);\n\t\t}\n\t}\n\t\n\t// Dedicated thread will catch SIGALRM, SIGINT, SIGTERM, SIGCHLD signals, \n\t// therefore all threads must block these signals. Block now, then all\n\t// created threads will inherit the signal mask.\n\t// In LinuxThreads the sigwait does not work very well, so\n\t// in LinuxThreads a signal handler is used instead.\n\tif (!threading_is_LinuxThreads) {\n\t\tsigset_t sigset;\n\t\tsigemptyset(&sigset);\n\t\tsigaddset(&sigset, SIGALRM);\n\t\tsigaddset(&sigset, SIGINT);\n\t\tsigaddset(&sigset, SIGTERM);\n\t\tsigaddset(&sigset, SIGCHLD);\n\t\tsigprocmask(SIG_BLOCK, &sigset, NULL);\n\t} else {\n\t\tif (!phone->set_sighandler()) {\n\t\t\tstring msg = \"Failed to register signal handler.\";\n\t\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\tsys_config->delete_lock_file();\n\t\t\texit(1);\n\t\t}\n\t}\n\n\t// Ignore SIGPIPE so read from broken sockets will not cause\n\t// the process to terminate.\n\t(void)signal(SIGPIPE, SIG_IGN);\n\n\t// Create threads\n\tt_thread *thr_sender;\n\tt_thread *thr_tcp_sender;\n\tt_thread *thr_listen_udp;\n\tt_thread *thr_listen_data_tcp;\n\tt_thread *thr_listen_conn_tcp;\n\tt_thread *thr_conn_timeout_handler;\n\tt_thread *thr_timekeeper;\n\tt_thread *thr_alarm_catcher = NULL;\n\tt_thread *thr_sig_catcher = NULL;\n\tt_thread *thr_trans_mgr;\n\tt_thread *thr_phone_uas;\n\n\ttry {\n\t\t// SIP sender thread\n\t\tthr_sender = new t_thread(sender_loop, NULL);\n\t\tMEMMAN_NEW(thr_sender);\n\t\t\n\t\t// SIP TCP sender thread\n\t\tthr_tcp_sender = new t_thread(tcp_sender_loop, NULL);\n\t\tMEMMAN_NEW(thr_tcp_sender);\n\n\t\t// UDP listener thread\n\t\tthr_listen_udp = new t_thread(listen_udp, NULL);\n\t\tMEMMAN_NEW(thr_listen_udp);\n\t\t\n\t\t// TCP data listener thread\n\t\tthr_listen_data_tcp = new t_thread(listen_for_data_tcp, NULL);\n\t\tMEMMAN_NEW(thr_listen_data_tcp);\n\t\t\n\t\t// TCP connection listener thread\n\t\tthr_listen_conn_tcp = new t_thread(listen_for_conn_requests_tcp, NULL);\n\t\tMEMMAN_NEW(thr_listen_conn_tcp);\n\t\t\n\t\t// Connection timeout handler thread\n\t\tthr_conn_timeout_handler = new t_thread(connection_timeout_main, NULL);\n\t\tMEMMAN_NEW(thr_conn_timeout_handler);\n\n\t\t// Timekeeper thread\n\t\tthr_timekeeper = new t_thread(timekeeper_main, NULL);\n\t\tMEMMAN_NEW(thr_timekeeper);\n\t\t\n\t\tif (!threading_is_LinuxThreads) {\n\t\t\t// Alarm catcher thread\n\t\t\tthr_alarm_catcher = new t_thread(timekeeper_sigwait, NULL);\n\t\t\tMEMMAN_NEW(thr_alarm_catcher);\n\n\t\t\t// Signal catcher thread\n\t\t\tthr_sig_catcher = new t_thread(phone_sigwait, NULL);\n\t\t\tMEMMAN_NEW(thr_sig_catcher);\n\t\t}\n\n\t\t// Transaction manager thread\n\t\tthr_trans_mgr = new t_thread(transaction_mgr_main, NULL);\n\t\tMEMMAN_NEW(thr_trans_mgr);\n\n\t\t// Phone thread (UAS)\n\t\tthr_phone_uas = new t_thread(phone_uas_main, NULL);\n\t\tMEMMAN_NEW(thr_phone_uas);\n\t} catch (int) {\n\t\tstring msg = \"Failed to create threads.\";\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t}\n\t\n\t// Validate sound devices\n\tif (!sys_config->exec_audio_validation(true, true, true, error_msg)) {\n\t\tui->cb_show_msg(error_msg, MSG_WARNING);\n\t}\n\n\ttry {\n\t\tui->run();\n\t} catch (string e) {\n\t\tstring msg = \"Exception: \";\n\t\tmsg += e;\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t} catch (...) {\n\t\tstring msg = \"Unknown exception\";\n\t\tlog_file->write_report(msg, \"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\tsys_config->delete_lock_file();\n\t\texit(1);\n\t}\n\t\n\t// Application is ending\n\tend_app = true;\n\t\n\t// Kill the threads getting receiving input from the outside world first,\n\t// so no new inputs come in during termination.\n\tthr_listen_udp->cancel();\n\tthr_listen_udp->join();\n\t\n\tthr_listen_conn_tcp->cancel();\n\tthr_listen_conn_tcp->join();\n\t\n\tconnection_table->cancel_select();\n\tthr_listen_data_tcp->join();\n\tthr_conn_timeout_handler->join();\n\tthr_tcp_sender->join();\n\t\n\tevq_trans_layer->push_quit();\n\tthr_phone_uas->join();\n\t\n\tevq_trans_mgr->push_quit();\n\tthr_trans_mgr->join();\n\t\n\tif (!threading_is_LinuxThreads) {\n\t\ttry {\n\t\t\tthr_sig_catcher->cancel();\n\t\t} catch (int) {\n\t\t\t// Thread terminated already by itself\n\t\t}\n\t\tthr_sig_catcher->join();\n\t\t\n\t\tthr_alarm_catcher->cancel();\n\t\tthr_alarm_catcher->join();\n\t}\n\t\n\tevq_timekeeper->push_quit();\n\tthr_timekeeper->join();\n\t\n\tevq_sender->push_quit();\n\tthr_sender->join();\n\t\n\tsys_config->remove_all_tmp_files();\n\n\tMEMMAN_DELETE(thr_phone_uas);\n\tdelete thr_phone_uas;\n\tMEMMAN_DELETE(thr_trans_mgr);\n\tdelete thr_trans_mgr;\n\tMEMMAN_DELETE(thr_timekeeper);\n\tdelete thr_timekeeper;\n\tMEMMAN_DELETE(thr_conn_timeout_handler);\n\tdelete thr_conn_timeout_handler;\n\n\tif (!threading_is_LinuxThreads) {\n\t\tMEMMAN_DELETE(thr_sig_catcher);\n\t\tdelete thr_sig_catcher;\n\t\tMEMMAN_DELETE(thr_alarm_catcher);\n\t\tdelete thr_alarm_catcher;\n\t}\n\n\tMEMMAN_DELETE(thr_listen_udp);\n\tdelete thr_listen_udp;\n\tMEMMAN_DELETE(thr_sender);\n\tdelete thr_sender;\n\tMEMMAN_DELETE(thr_tcp_sender);\n\tdelete thr_tcp_sender;\n\t\n\t\n\tMEMMAN_DELETE(thr_listen_data_tcp);\n\tdelete thr_listen_data_tcp;\n\tMEMMAN_DELETE(thr_listen_conn_tcp);\n\tdelete thr_listen_conn_tcp;\n\n\tMEMMAN_DELETE(mime_database);\n\tdelete mime_database;\n\tMEMMAN_DELETE(ab_local);\n\tdelete ab_local;\n\tMEMMAN_DELETE(call_history);\n\tdelete call_history;\n\n\tMEMMAN_DELETE(ui);\n\tdelete ui;\n\tui = NULL;\n\t\n\tMEMMAN_DELETE(connection_table);\n\tdelete connection_table;\n\tMEMMAN_DELETE(sip_socket_tcp);\n\tdelete sip_socket_tcp;\n\tMEMMAN_DELETE(sip_socket);\n\tdelete sip_socket;\n\n\tMEMMAN_DELETE(phone);\n\tdelete phone;\n\tMEMMAN_DELETE(transaction_mgr);\n\tdelete transaction_mgr;\n\tMEMMAN_DELETE(timekeeper);\n\tdelete timekeeper;\n\tMEMMAN_DELETE(evq_trans_mgr);\n\tdelete evq_trans_mgr;\n\tMEMMAN_DELETE(evq_sender);\n\tdelete evq_sender;\n\tMEMMAN_DELETE(evq_trans_layer);\n\tdelete evq_trans_layer;\n\tMEMMAN_DELETE(evq_timekeeper);\n\tdelete evq_timekeeper;\n\t\n\tMEMMAN_DELETE(translator);\n\tdelete translator;\n\ttranslator = NULL;\n\n\t// Report memory leaks\n\t// Report deletion of log_file and sys_config already to get\n\t// a correct report.\n\tMEMMAN_DELETE(sys_config);\n\tMEMMAN_DELETE(log_file);\n\tMEMMAN_DELETE(memman);\n\tMEMMAN_REPORT;\n\n\tdelete log_file;\n\tdelete memman;\n\n\tsys_config->delete_lock_file();\n\tdelete sys_config;\n}\n"
  },
  {
    "path": "src/mwi/CMakeLists.txt",
    "content": "project(libtwinkle-mwi)\n\nset(LIBTWINKLE_MWI-SRCS\n\tmwi.cpp\n\tmwi_dialog.cpp\n\tmwi_subscription.cpp\n\tsimple_msg_sum_body.cpp\n)\n\nadd_library(libtwinkle-mwi OBJECT ${LIBTWINKLE_MWI-SRCS})\n"
  },
  {
    "path": "src/mwi/mwi.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"mwi.h\"\n\nt_mwi::t_mwi() :\n\tstatus(MWI_UNKNOWN)\n{}\n\nt_mwi::t_status t_mwi::get_status(void) const {\n\tt_status result;\n\tmtx_mwi.lock();\n\tresult = status;\n\tmtx_mwi.unlock();\n\t\n\treturn result;\n}\n\nbool t_mwi::get_msg_waiting(void) const {\n\tbool result;\n\tmtx_mwi.lock();\n\tresult = msg_waiting;\n\tmtx_mwi.unlock();\n\t\n\treturn result;\n}\n\nt_msg_summary t_mwi::get_voice_msg_summary(void) const {\n\tt_msg_summary result;\n\tmtx_mwi.lock();\n\tresult = voice_msg_summary;\n\tmtx_mwi.unlock();\n\t\n\treturn result;\n}\n\nvoid t_mwi::set_status(t_status _status) {\n\tmtx_mwi.lock();\n\tstatus = _status;\n\tmtx_mwi.unlock();\n}\n\nvoid t_mwi::set_msg_waiting(bool _msg_waiting) {\n\tmtx_mwi.lock();\n\tmsg_waiting = _msg_waiting;\n\tmtx_mwi.unlock();\n}\n\nvoid t_mwi::set_voice_msg_summary(const t_msg_summary &summary) {\n\tmtx_mwi.lock();\n\tvoice_msg_summary = summary;\n\tmtx_mwi.unlock();\n}\n\nvoid t_mwi::clear_voice_msg_summary(void) {\n\tmtx_mwi.lock();\n\tvoice_msg_summary.clear();\n\tmtx_mwi.unlock();\n}\n"
  },
  {
    "path": "src/mwi/mwi.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Message Waiting Indication information.\n */\n\n#ifndef _MWI_HH\n#define _MWI_HH\n\n#include \"simple_msg_sum_body.h\"\n#include \"threads/mutex.h\"\n\n/** MWI information */\nclass t_mwi {\npublic:\n\t/** Status of MWI information */\n\tenum t_status {\n\t\tMWI_UNKNOWN, \t/**< The status is unknown */\n\t\tMWI_KNOWN,   \t/**< MWI properly received */\n\t\tMWI_FAILED  \t/**< MWI subscription failed */ \n\t};\n\t\t\nprivate:\n\t/** Mutex for exclusive access to MWI information */\n\tmutable t_mutex\tmtx_mwi;\n\n\t/** MWI status */\n\tt_status\tstatus;\n\n\t/** Indication if messages are waiting */\n\tbool\t\tmsg_waiting;\n\n\t/** Summary of voice messages waiting */\n\tt_msg_summary\tvoice_msg_summary;\n\t\npublic:\n\tt_mwi();\n\t\n\tt_status get_status(void) const;\n\tbool get_msg_waiting(void) const;\n\tt_msg_summary get_voice_msg_summary(void) const;\n\t\n\tvoid set_status(t_status _status);\n\tvoid set_msg_waiting(bool _msg_waiting);\n\tvoid set_voice_msg_summary(const t_msg_summary &summary);\n\n\t/** Set all counters to zero */\n\tvoid clear_voice_msg_summary(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/mwi/mwi_dialog.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"mwi_dialog.h\"\n\n#include \"mwi_subscription.h\"\n#include \"phone_user.h\"\n#include \"audits/memman.h\"\n\nt_mwi_dialog::t_mwi_dialog(t_phone_user *_phone_user) :\n\tt_subscription_dialog(_phone_user)\n{\n\tsubscription = new t_mwi_subscription(this, &(phone_user->mwi));\n\tMEMMAN_NEW(subscription);\n}\n\nt_mwi_dialog *t_mwi_dialog::copy(void) {\n\t// Copy is not needed.\n\tassert(false);\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/mwi/mwi_dialog.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _MWI_DIALOG_H\n#define _MWI_DIALOG_H\n\n#include \"mwi.h\"\n#include \"subscription_dialog.h\"\n\n// Forward declaration\nclass t_phone_user;\n\nclass t_mwi_dialog : public t_subscription_dialog {\npublic:\n\tt_mwi_dialog(t_phone_user *_phone_user);\n\t\n\tvirtual t_mwi_dialog *copy(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/mwi/mwi_subscription.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"mwi_subscription.h\"\n\n#include <cassert>\n\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n#include \"parser/hdr_event.h\"\n\nt_request *t_mwi_subscription::create_subscribe(unsigned long expires) const {\n\tt_request *r = t_subscription::create_subscribe(expires);\n\tSET_MWI_HDR_ACCEPT(r->hdr_accept);\n\t\n\treturn r;\n}\n\nt_mwi_subscription::t_mwi_subscription(t_mwi_dialog *_dialog, t_mwi *_mwi) :\n\tt_subscription(_dialog, SR_SUBSCRIBER, SIP_EVENT_MSG_SUMMARY),\n\tmwi(_mwi)\n{}\n\nbool t_mwi_subscription::recv_notify(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (t_subscription::recv_notify(r, tuid, tid)) return true;\n\t\n\tbool unsupported_body = false;\n\t\n\t// NOTE: if the subscription is still pending (RFC 3265 3.2.4), then the\n\t//       information in the body has no meaning.\n\tif (r->body && r->body->get_type() == BODY_SIMPLE_MSG_SUM &&\n\t    !is_pending()) \n\t{\n\t\tt_simple_msg_sum_body *body = dynamic_cast<t_simple_msg_sum_body *>(r->body);\n\t\tassert(body);\n\t\tmwi->set_msg_waiting(body->get_msg_waiting());\n\t\t\n\t\tt_msg_summary summary;\n\t\tif (body->get_msg_summary(MSG_CONTEXT_VOICE, summary)) {\n\t\t\tmwi->set_voice_msg_summary(summary);\n\t\t} else {\n\t\t\tmwi->clear_voice_msg_summary();\n\t\t}\n\t\t\n\t\tmwi->set_status(t_mwi::MWI_KNOWN);\n\t}\n\t\n\t// Verify if there is an usupported body.\n\tif (r->body && r->body->get_type() != BODY_SIMPLE_MSG_SUM) {\n\t\tunsupported_body = true;\n\t}\n\t\n\tif (state == SS_TERMINATED && !may_resubscribe) {\n\t\t// The MWI server ended the subscription and indicated\n\t\t// that resubscription is not possible. So no MWI status\n\t\t// can be retrieved anymore. This should not happen, so\n\t\t// present it as a failure to the user.\n\t\tmwi->set_status(t_mwi::MWI_FAILED);\n\t\tui->cb_mwi_terminated(user_config, get_reason_termination());\n\t}\n\t\n\tt_response *resp;\n\tif (unsupported_body) {\n\t\tresp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);\n\t\tSET_MWI_HDR_ACCEPT(r->hdr_accept);\n\t} else {\n\t\tresp = r->create_response(R_200_OK);\n\t}\n\tsend_response(user_config, resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\t\n\tui->cb_update_mwi();\n\treturn true;\n}\n\nbool t_mwi_subscription::recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid) {\n\t// Parent handles the SUBSCRIBE response\n\t(void)t_subscription::recv_subscribe_response(r, tuid, tid);\n\t\n\t// If the subscription is terminated after the SUBSCRIBE response, it means\n\t// that subscription failed.\n\tif (state == SS_TERMINATED) {\n\t\tui->cb_mwi_subscribe_failed(user_config, r, mwi->get_status() != t_mwi::MWI_FAILED);\n\t\tmwi->set_status(t_mwi::MWI_FAILED);\n\t}\n\t\n\tui->cb_update_mwi();\n\treturn true;\n}\n"
  },
  {
    "path": "src/mwi/mwi_subscription.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3842\n// message-summary subscription\n\n#ifndef _MWI_SUBSCRIPTION_H\n#define _MWI_SUBSCRIPTION_H\n\n#include \"mwi.h\"\n#include \"mwi_dialog.h\"\n#include \"subscription.h\"\n\nclass t_mwi_subscription : public t_subscription {\nprivate:\n\tt_mwi *mwi;\n\t\nprotected:\n\tvirtual t_request *create_subscribe(unsigned long expires) const;\n\npublic:\n\tt_mwi_subscription(t_mwi_dialog *_dialog, t_mwi *_mwi);\n\t\n\tvirtual bool recv_notify(t_request *r, t_tuid tuid, t_tid tid);\n\tvirtual bool recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid);\n};\n\n#endif\n"
  },
  {
    "path": "src/mwi/simple_msg_sum_body.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"simple_msg_sum_body.h\"\n\n#include <iostream>\n#include <cstdlib>\n#include <regex>\n\n#include \"protocol.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nt_msg_summary::t_msg_summary() :\n\tnewmsgs(0),\n\tnewmsgs_urgent(0),\n\toldmsgs(0),\n\toldmsgs_urgent(0)\n{}\n\nbool t_msg_summary::parse(const string &s) {\n\tnewmsgs = 0;\n\toldmsgs = 0;\n\tnewmsgs_urgent = 0;\n\toldmsgs_urgent = 0;\n\t\n\t// RFC 3842 5.2\n\t// msg-summary-line = message-context-class HCOLON newmsgs SLASH oldmsgs\n        //                    [ LPAREN new-urgentmsgs SLASH old-urgentmsgs RPAREN ]\n        // This regex matches the part after HCOLON\n    std::regex re(\"(\\\\d+)\\\\s*/\\\\s*(\\\\d+)(?:\\\\s*\\\\((\\\\d+)\\\\s*/\\\\s*(\\\\d+)\\\\s*\\\\))?\");\n\t\n    std::smatch m;\n    if (!std::regex_match(s, m, re)) return false;\n\t\n\tif (m.size() == 3) {\n        newmsgs = std::stoul(m.str(1), NULL, 10);\n        oldmsgs = std::stoul(m.str(2), NULL, 10);\n\t\treturn true;\n\t} else if (m.size() == 5) {\n        newmsgs = std::stoul(m.str(1), NULL, 10);\n        oldmsgs = std::stoul(m.str(2), NULL, 10);\n\n        if (!m.str(3).empty())\n            newmsgs_urgent = std::stoul(m.str(3), NULL, 10);\n        if (!m.str(4).empty())\n            oldmsgs_urgent = std::stoul(m.str(4), NULL, 10);\n\n\t\treturn true;\n\t}\n\t\n\treturn false;\t\n}\n\nvoid t_msg_summary::clear(void) {\n\tnewmsgs = 0;\n\tnewmsgs_urgent = 0;\n\toldmsgs = 0;\n\toldmsgs_urgent = 0;\n}\n\nbool t_simple_msg_sum_body::is_context(const string &s) {\n\treturn ( s == MSG_CONTEXT_VOICE ||\n\t         s == MSG_CONTEXT_FAX ||\n\t         s == MSG_CONTEXT_MULTIMEDIA ||\n\t         s == MSG_CONTEXT_TEXT ||\n\t         s == MSG_CONTEXT_NONE);\n}\n\nt_simple_msg_sum_body::t_simple_msg_sum_body() : t_sip_body()\n{}\n\nstring t_simple_msg_sum_body::encode(void) const {\n\tstring s = \"Messages-Waiting: \";\n\ts += (msg_waiting ? \"yes\" : \"no\");\n\ts += CRLF;\n\t\n\tif (msg_account.is_valid()) {\n\t\ts += \"Message-Account: \";\n\t\ts += msg_account.encode();\n\t\ts += CRLF;\n\t}\n\t\n\tfor (t_msg_sum_const_iter i = msg_summary.begin(); i != msg_summary.end(); ++i) {\n\t\tconst t_msg_summary &summary = i->second;\n\t\ts += i->first;\n\t\ts += \": \";\n\t\ts += ulong2str(summary.newmsgs);\n\t\ts += \"/\";\n\t\ts += ulong2str(summary.oldmsgs);\n\t\t\n\t\tif (summary.newmsgs_urgent > 0 || summary.oldmsgs_urgent > 0) {\n\t\t\ts += \" (\";\n\t\t\ts += ulong2str(summary.newmsgs_urgent);\n\t\t\ts += \"/\";\n\t\t\ts += ulong2str(summary.oldmsgs_urgent);\n\t\t\ts += \")\";\n\t\t}\n\t\t\n\t\ts += CRLF;\n\t}\n\t\n\treturn s;\n}\n\nt_sip_body *t_simple_msg_sum_body::copy(void) const {\n\tt_simple_msg_sum_body *body = new t_simple_msg_sum_body(*this);\n\tMEMMAN_NEW(body);\n\treturn body;\n}\n\nt_body_type t_simple_msg_sum_body::get_type(void) const {\n\treturn BODY_SIMPLE_MSG_SUM;\n}\n\nt_media t_simple_msg_sum_body::get_media(void) const {\n\treturn t_media(\"application\", \"simple-message-summary\");\n}\n\nvoid t_simple_msg_sum_body::add_msg_summary(const string &context, const t_msg_summary summary) {\n\tmsg_summary.insert(make_pair(context, summary));\n}\n\nbool t_simple_msg_sum_body::get_msg_waiting(void) const {\n\treturn msg_waiting;\n}\n\nt_url t_simple_msg_sum_body::get_msg_account(void) const {\n\treturn msg_account;\n}\n\nbool t_simple_msg_sum_body::get_msg_summary(const string &context, t_msg_summary &summary) const {\n\tt_msg_sum_const_iter it = msg_summary.find(context);\n\tif (it == msg_summary.end()) return false;\n\tsummary = it->second;\n\treturn true;\n}\n\nbool t_simple_msg_sum_body::parse(const string &s) {\n\tbool valid = false;\n\tvector<string> lines = split_linebreak(s);\n\t\n\tfor (vector<string>::iterator i = lines.begin(); i != lines.end(); ++i) {\n\t\tstring line = trim(*i);\n\t\tif (line.empty()) continue;\n\t\t\n\t\tvector<string> l = split_on_first(line, ':');\n\t\tif (l.size() != 2) continue;\n\t\t\n\t\tstring header = tolower(trim(l[0]));\n\t\tstring value = tolower(trim(l[1]));\n\t\t\n\t\tif (value.empty()) continue;\n\t\t\n\t\tif (header == \"messages-waiting\") {\n\t\t\tif (value == \"yes\") {\n\t\t\t\tmsg_waiting = true;\n\t\t\t\tvalid = true;\n\t\t\t} else if (value == \"no\") {\n\t\t\t\tmsg_waiting = false;\n\t\t\t\tvalid = true;\n\t\t\t}\n\t\t} else if (header == \"message-account\") {\n\t\t\tmsg_account.set_url(value);\n\t\t} else if (is_context(header)) {\n\t\t\tt_msg_summary summary;\n\t\t\tif (summary.parse(value)) {\n\t\t\t\tadd_msg_summary(header, summary);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tinvalid = !valid;\n\treturn valid;\n}\n"
  },
  {
    "path": "src/mwi/simple_msg_sum_body.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * RFC 3842 simple-message-summary body\n */\n\n#ifndef SIMPLE_MSG_SUM_BODY_HH\n#define SIMPLE_MSG_SUM_BODY_HH\n\n#include <string>\n#include <map>\n#include \"parser/sip_body.h\"\n#include \"sockets/url.h\"\n\n// RFC 3458 6.2\n// Message contexts\n#define MSG_CONTEXT_VOICE\t\"voice-message\"\n#define MSG_CONTEXT_FAX\t\t\"fax-message\"\n#define MSG_CONTEXT_MULTIMEDIA\t\"multimedia-message\"\n#define MSG_CONTEXT_TEXT\t\"text-message\"\n#define MSG_CONTEXT_NONE\t\"none\"\n\nusing namespace std;\n\n/** Message summary counters */\nstruct t_msg_summary {\n\tuint32\t\tnewmsgs;\n\tuint32\t\tnewmsgs_urgent;\n\tuint32\t\toldmsgs;\n\tuint32\t\toldmsgs_urgent;\n\n\tt_msg_summary();\n\t\n\t/**\n\t * Parse a text representation of a message summary.\n\t * @param s [in] The text to parse.\n\t * @return false if parsing fails, true if it succeeds.\n\t */\n\tbool parse(const string &s);\n\n\t/** Set all counters to zero */\n\tvoid clear(void);\n};\n\ntypedef string t_msg_context;\ntypedef map<t_msg_context, t_msg_summary>::const_iterator t_msg_sum_const_iter;\n\nclass t_simple_msg_sum_body : public t_sip_body {\nprivate:\n\tbool\t\t\tmsg_waiting;\n\tt_url\t\t\tmsg_account;\n\tmap<t_msg_context, t_msg_summary> msg_summary;\n\t\n\t// Returns true if string is a valid message context\n\tbool is_context(const string &s);\n\t\t\npublic:\n\tt_simple_msg_sum_body();\n\n\t// Return text encoded body\n\tvirtual string encode(void) const;\n\n\t// Create a copy of the body\n\tvirtual t_sip_body *copy(void) const;\n\n\t// Get type of body\n\tvirtual t_body_type get_type(void) const;\n\t\n\tvirtual t_media get_media(void) const;\n\t\n\t// Add a message summary\n\tvoid add_msg_summary(const string &context, const t_msg_summary summary);\n\t\n\tbool get_msg_waiting(void) const;\n\tt_url get_msg_account(void) const;\n\t\n\t// Get the message summary for a particular context\n\t// If the context is not present, then false is returned\n\tbool get_msg_summary(const string &context, t_msg_summary &summary) const;\n\t\n\t// Parse a text representation of the body.\n\tbool parse(const string &s);\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/CMakeLists.txt",
    "content": "project(libtwinkle-parser)\n\nBISON_TARGET(MyParser parser.yxx ${CMAKE_CURRENT_BINARY_DIR}/parser.cxx)\nFLEX_TARGET(MyScanner scanner.lxx ${CMAKE_CURRENT_BINARY_DIR}/scanner.cxx)\nADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\n\nset(LIBTWINKLE_PARSER-SRCS\n\tchallenge.cpp\n\tcoding.cpp\n\tcredentials.cpp\n\tdefinitions.cpp\n\thdr_accept.cpp\n\thdr_accept_encoding.cpp\n\thdr_accept_language.cpp\n\thdr_alert_info.cpp\n\thdr_allow.cpp\n\thdr_allow_events.cpp\n\thdr_auth_info.cpp\n\thdr_authorization.cpp\n\thdr_call_id.cpp\n\thdr_call_info.cpp\n\thdr_contact.cpp\n\thdr_content_disp.cpp\n\thdr_content_encoding.cpp\n\thdr_content_language.cpp\n\thdr_content_length.cpp\n\thdr_content_type.cpp\n\thdr_cseq.cpp\n\thdr_date.cpp\n\thdr_error_info.cpp\n\thdr_event.cpp\n\thdr_expires.cpp\n\thdr_from.cpp\n\thdr_in_reply_to.cpp\n\thdr_max_forwards.cpp\n\thdr_min_expires.cpp\n\thdr_min_se.cpp\n\thdr_mime_version.cpp\n\thdr_organization.cpp\n\thdr_priority.cpp\n\thdr_privacy.cpp\n\thdr_p_asserted_identity.cpp\n\thdr_p_preferred_identity.cpp\n\thdr_proxy_authenticate.cpp\n\thdr_proxy_authorization.cpp\n\thdr_proxy_require.cpp\n\thdr_rack.cpp\n\thdr_reason.cpp\n\thdr_record_route.cpp\n\thdr_refer_sub.cpp\n\thdr_refer_to.cpp\n\thdr_referred_by.cpp\n\thdr_replaces.cpp\n\thdr_reply_to.cpp\n\thdr_require.cpp\n\thdr_request_disposition.cpp\n\thdr_retry_after.cpp\n\thdr_route.cpp\n\thdr_rseq.cpp\n\thdr_server.cpp\n\thdr_service_route.cpp\n\thdr_session_expires.cpp\n\thdr_sip_etag.cpp\n\thdr_sip_if_match.cpp\n\thdr_subject.cpp\n\thdr_subscription_state.cpp\n\thdr_supported.cpp\n\thdr_timestamp.cpp\n\thdr_to.cpp\n\thdr_unsupported.cpp\n\thdr_user_agent.cpp\n\thdr_via.cpp\n\thdr_warning.cpp\n\thdr_www_authenticate.cpp\n\theader.cpp\n\tidentity.cpp\n\tmedia_type.cpp\n\tmilenage.cpp\n\tparameter.cpp\n\tparse_ctrl.cpp\n\t${CMAKE_CURRENT_BINARY_DIR}/parser.cxx\n\trequest.cpp\n\tresponse.cpp\n\trijndael.cpp\n\troute.cpp\n\t${CMAKE_CURRENT_BINARY_DIR}/scanner.cxx\n\tsip_body.cpp\n\tsip_message.cpp\n)\n\nadd_library(libtwinkle-parser OBJECT ${LIBTWINKLE_PARSER-SRCS})\n"
  },
  {
    "path": "src/parser/challenge.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include \"challenge.h\"\n#include \"definitions.h\"\n#include \"log.h\"\n#include \"util.h\"\n\nt_digest_challenge::t_digest_challenge() {\n\tstale = false;\n\n\t// The default algorithm is MD5.\n\talgorithm = ALG_MD5;\n}\n\nstring t_digest_challenge::encode(void) const {\n\tstring s;\n\n\tif (realm.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"realm=\";\n\t\ts += '\"';\n\t\ts += realm;\n\t\ts += '\"';\n\t}\n\n\tif (domain.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"domain=\";\n\t\ts += '\"';\n\n\t\tfor (list<t_url>::const_iterator i = domain.begin();\n\t     \t     i != domain.end(); i++)\n\t\t{\n\t\t\tif (i != domain.begin()) s += ' ';\n\t\t\ts += i->encode();\n\t\t}\n\n\t\ts += '\"';\n\t}\n\n\tif (nonce.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"nonce=\";\n\t\ts += '\"';\n\t\ts += nonce;\n\t\ts += '\"';\n\t}\n\n\tif (opaque.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"opaque=\";\n\t\ts += '\"';\n\t\ts += opaque;\n\t\ts += '\"';\n\t}\n\n\t// RFC 2617 3.2.1\n\t// If the stale flag is absent it means stale=false.\n\tif (stale) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"stale=true\";\n\t}\n\n\tif (algorithm.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"algorithm=\";\n\t\ts += algorithm;\n\t}\n\n\tif (qop_options.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"qop=\";\n\t\ts += '\"';\n\n\t\tfor (list<string>::const_iterator i = qop_options.begin();\n\t     \t     i != qop_options.end(); i++)\n\t\t{\n\t\t\tif (i != qop_options.begin()) s += ',';\n\t\t\ts += *i;\n\t\t}\n\n\t\ts += '\"';\n\t}\n\n\tfor (list<t_parameter>::const_iterator i = auth_params.begin();\n\t     i != auth_params.end(); i++)\n\t{\n\t\tif (s.size() > 0) s += ',';\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nbool t_digest_challenge::set_attr(const t_parameter &p) {\n\tif (p.name == \"realm\")\n\t\trealm = p.value;\n\telse if (p.name == \"nonce\")\n\t\tnonce = p.value;\n\telse if (p.name == \"opaque\")\n\t\topaque = p.value;\n\telse if (p.name == \"algorithm\")\n\t\talgorithm = p.value;\n\telse if (p.name == \"domain\") {\n\t\tvector<string> l = split_ws(p.value);\n\t\tfor (vector<string>::iterator i = l.begin();\n\t\t     i != l.end(); i++)\n\t\t{\n\t\t\tt_url u(*i);\n\t\t\tif (u.is_valid()) {\n\t\t\t\tdomain.push_back(u);\n\t\t\t} else {\n\t\t\t\tlog_file->write_header(\"t_digest_challenge::set_attr\",\n\t\t\t\t\tLOG_SIP, LOG_WARNING);\n\t\t\t\tlog_file->write_raw(\"Invalid domain in digest challenge: \");\n\t\t\t\tlog_file->write_raw(*i);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t}\n\t\t}\n\t}\n\telse if (p.name == \"qop\") {\n\t\tvector<string> l = split(p.value, ',');\n\t\tfor (vector<string>::iterator i = l.begin();\n\t\t     i != l.end(); i++)\n\t\t{\n\t\t\tqop_options.push_back(trim(*i));\n\t\t}\n\t}\n\telse if (p.name == \"stale\") {\n\t\tif (cmp_nocase(p.value, \"true\") == 0)\n\t\t\tstale = true;\n\t\telse\n\t\t\t// RFC 2617 3.2.1\n\t\t\t// Any value other than false should be interpreted\n\t\t\t// as false.\n\t\t\tstale = false;\n\t}\n\telse\n\t\tauth_params.push_back(p);\n\n\treturn true;\n}\n\nstring t_challenge::encode(void) const {\n\tstring s = auth_scheme;\n\ts += ' ';\n\n\tif (cmp_nocase(auth_scheme,AUTH_DIGEST) == 0) {\n\t\ts += digest_challenge.encode();\n\t} else {\n\t\tfor (list<t_parameter>::const_iterator i = auth_params.begin();\n\t     \t\ti != auth_params.end(); i++)\n\t\t{\n\t\t\tif (i != auth_params.begin()) s += ',';\n\t\t\ts += i->encode();\n\t\t}\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/challenge.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Challenges are used in Proxy-Authenticate and\n// WWW-Authenticate headers\n\n#ifndef _CHALLENGE_H\n#define _CHALLENGE_H\n\n#include <list>\n#include <string>\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_digest_challenge {\npublic:\n\tstring\t\t\trealm;\n\tlist<t_url>\t\tdomain;\n\tstring\t\t\tnonce;\n\tstring\t\t\topaque;\n\tbool\t\t\tstale;\n\tstring\t\t\talgorithm;\n\tlist<string>\t\tqop_options;\n\tlist<t_parameter>\tauth_params;\n\n\tt_digest_challenge();\n\n\t// Set one of the attributes to a value. The parameter p\n\t// indicated wich attribute (p.name) should be set to\n\t// which value (p.value).\n\t// Returns false if p does not contain a valid attribute\n\t// setting.\n\tbool set_attr(const t_parameter &p);\n\n\tstring encode(void) const;\n};\n\nclass t_challenge {\npublic:\n\tstring\t\t\tauth_scheme;\n\tt_digest_challenge\tdigest_challenge;\n\n\t// auth_params is used when auth_scheme is not Digest.\n\tlist<t_parameter>\tauth_params;\n\n\tstring encode(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/coding.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"coding.h\"\n#include \"util.h\"\n\nt_coding::t_coding() {\n\tq = 1.0;\n}\n\nt_coding::t_coding(const string &s) {\n\tq = 1.0;\n\tcontent_coding = s;\n}\n\nstring t_coding::encode(void) const {\n\tstring s;\n\n\ts = content_coding;\n\t\n\tif (q != 1.0) {\n\t\ts += \";q=\";\n\t\ts += float2str(q, 1);\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/coding.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Content coding type\n\n#ifndef _CODING_H\n#define _CODING_H\n\n#include <string>\n\nusing namespace std;\n\nclass t_coding {\npublic:\n\tstring content_coding;\n\tfloat q; // quality factor;\n\n\tt_coding();\n\tt_coding(const string &s);\n\n\tstring encode(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/credentials.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"credentials.h\"\n#include \"definitions.h\"\n#include \"util.h\"\n\nt_digest_response::t_digest_response() {\n\tnonce_count = 0;\n}\n\nstring t_digest_response::encode(void) const {\n\tstring s;\n\n\tif (username.size() > 0) {\n\t\ts += \"username=\";\n\t\ts += '\"';\n\t\ts += username;\n\t\ts += '\"';\n\t}\n\n\tif (realm.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"realm=\";\n\t\ts += '\"';\n\t\ts += realm;\n\t\ts += '\"';\n\t}\n\n\tif (nonce.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"nonce=\";\n\t\ts += '\"';\n\t\ts += nonce;\n\t\ts += '\"';\n\t}\n\n\tif (digest_uri.is_valid()) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"uri=\";\n\t\ts += '\"';\n\t\ts += digest_uri.encode();\n\t\ts += '\"';\n\t}\n\n\tif (dresponse.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"response=\";\n\t\ts += '\"';\n\t\ts += dresponse;\n\t\ts += '\"';\n\t}\n\n\tif (algorithm.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"algorithm=\";\n\t\ts += algorithm;\n\t}\n\n\tif (cnonce.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"cnonce=\";\n\t\ts += '\"';\n\t\ts += cnonce;\n\t\ts += '\"';\n\t}\n\n\tif (opaque.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"opaque=\";\n\t\ts += '\"';\n\t\ts += opaque;\n\t\ts += '\"';\n\t}\n\n\tif (message_qop.size() > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"qop=\";\n\t\ts += message_qop;\n\t}\n\n\tif (nonce_count > 0) {\n\t\tif (s.size() > 0) s += ',';\n\t\ts += \"nc=\";\n\t\ts += ulong2str(nonce_count, \"%08x\");\n\t}\n\n\tfor (list<t_parameter>::const_iterator i = auth_params.begin();\n\t     i != auth_params.end(); i++)\n\t{\n\t\tif (s.size() > 0) s += ',';\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nbool t_digest_response::set_attr(const t_parameter &p) {\n\tif (p.name == \"username\")\n\t\tusername = p.value;\n\telse if (p.name == \"realm\")\n\t\trealm = p.value;\n\telse if (p.name == \"nonce\")\n\t\tnonce = p.value;\n\telse if (p.name == \"digest_uri\") {\n\t\tdigest_uri.set_url(p.value);\n\t\tif (!digest_uri.is_valid()) return false;\n\t}\n\telse if (p.name == \"response\")\n\t\tdresponse = p.value;\n\telse if (p.name == \"cnonce\")\n\t\tcnonce = p.value;\n\telse if (p.name == \"opaque\")\n\t\topaque = p.value;\n\telse if (p.name == \"algorithm\")\n\t\talgorithm = p.value;\n\telse if (p.name == \"qop\")\n\t\tmessage_qop = p.value;\n\telse if (p.name == \"nc\")\n\t\tnonce_count = hex2int(p.value);\n\telse\n\t\tauth_params.push_back(p);\n\n\treturn true;\n}\n\nstring t_credentials::encode(void) const {\n\tstring s = auth_scheme;\n\ts += ' ';\n\n\tif (cmp_nocase(auth_scheme,AUTH_DIGEST) == 0) {\n\t\ts += digest_response.encode();\n\t} else {\n\t\tfor (list<t_parameter>::const_iterator i = auth_params.begin();\n\t     \t\ti != auth_params.end(); i++)\n\t\t{\n\t\t\tif (i != auth_params.begin()) s += ',';\n\t\t\ts += i->encode();\n\t\t}\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/credentials.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Credentials are used in Proxy-Authorization and\n// Authorization headers\n\n#ifndef _CREDENTIALS_H\n#define _CREDENTIALS_H\n\n#include <list>\n#include <string>\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_digest_response {\npublic:\n\tstring\t\t\tusername;\n\tstring\t\t\trealm;\n\tstring\t\t\tnonce;\n\tt_url\t\t\tdigest_uri;\n\tstring\t\t\tdresponse;\n\tstring\t\t\talgorithm;\n\tstring\t\t\tcnonce;\n\tstring\t\t\topaque;\n\tstring\t\t\tmessage_qop;\n\tunsigned long\t\tnonce_count;\n\tlist<t_parameter>\tauth_params;\n\n\tt_digest_response();\n\n\t// Set one of the attributes to a value. The parameter p\n\t// indicated wich attribute (p.name) should be set to\n\t// which value (p.value).\n\t// Returns false if p does not contain a valid attribute\n\t// setting.\n\tbool set_attr(const t_parameter &p);\n\n\tstring encode(void) const;\n};\n\nclass t_credentials {\npublic:\n\tstring\t\t\tauth_scheme;\n\tt_digest_response\tdigest_response;\n\n\t// auth_params is used when auth_scheme is not Digest.\n\tlist<t_parameter>\tauth_params;\n\n\tstring encode(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/definitions.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n\nstring method2str(const t_method &m, const string &unknown) {\n\tswitch (m) {\n\tcase INVITE:\t\treturn \"INVITE\";\n\tcase ACK:\t\treturn \"ACK\";\n\tcase OPTIONS:\t\treturn \"OPTIONS\";\n\tcase BYE:\t\treturn \"BYE\";\n\tcase CANCEL:\t\treturn \"CANCEL\";\n\tcase REGISTER:\t\treturn \"REGISTER\";\n\tcase PRACK:\t\treturn \"PRACK\";\n\tcase SUBSCRIBE:\t\treturn \"SUBSCRIBE\";\n\tcase NOTIFY:\t\treturn \"NOTIFY\";\n\tcase REFER:\t\treturn \"REFER\";\n\tcase INFO:\t\treturn \"INFO\";\n\tcase MESSAGE:\t\treturn \"MESSAGE\";\n\tcase PUBLISH:\t\treturn \"PUBLISH\";\n\tcase METHOD_UNKNOWN:\treturn unknown;\n\tdefault:\t\tassert(false);\n\t}\n\treturn unknown;\n}\n\nt_method str2method(const string &s) {\n\tif (s == \"INVITE\") return INVITE;\n\tif (s == \"ACK\") return ACK;\n\tif (s == \"OPTIONS\") return OPTIONS;\n\tif (s == \"BYE\") return BYE;\n\tif (s == \"CANCEL\") return CANCEL;\n\tif (s == \"REGISTER\") return REGISTER;\n\tif (s == \"PRACK\") return PRACK;\n\tif (s == \"SUBSCRIBE\") return SUBSCRIBE;\n\tif (s == \"NOTIFY\") return NOTIFY;\n\tif (s == \"REFER\") return REFER;\n\tif (s == \"INFO\") return INFO;\n\tif (s == \"MESSAGE\") return MESSAGE;\n\tif (s == \"PUBLISH\") return PUBLISH;\n\n\treturn METHOD_UNKNOWN;\n}\n"
  },
  {
    "path": "src/parser/definitions.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _DEFINITIONS_H\n#define _DEFINITIONS_H\n\n#include <string>\n#include \"protocol.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\n#define SIP_VERSION\t\"2.0\"\n\n// RFC 3261\n#ifndef RFC3261_COOKIE\n#define RFC3261_COOKIE\t\"z9hG4bK\"\n#endif\n\n// Authentication schemes\n#define AUTH_DIGEST\t\"Digest\"\n\n// Authentication algorithms\n#define ALG_MD5\t\t\"MD5\"\n#define ALG_AKAV1_MD5\t\"AKAV1-MD5\"\n#define ALG_MD5_SESS\t\"MD5-sess\"\n#define ALG_SHA256\t\"SHA-256\"\n\n// Authentication QOP\n#define QOP_AUTH\t\"auth\"\n#define QOP_AUTH_INT\t\"auth-int\"\n\n/** SIP request methods. */\nenum t_method {\n\tINVITE,\n\tACK,\n\tOPTIONS,\n\tBYE,\n\tCANCEL,\n\tREGISTER,\n\tPRACK,\n\tSUBSCRIBE,\n\tNOTIFY,\n\tREFER,\n\tINFO,\n\tMESSAGE,\n\tPUBLISH,\n\tMETHOD_UNKNOWN\n};\n\n/**\n * Convert a method to a string.\n * @param m The method.\n * @param unknown Method name if m is @ref METHOD_UNKNOWN.\n * @return The name of the method.\n */\nstring method2str(const t_method &m, const string &unknown = \"\");\n\n/**\n * Convert a string to a method.\n * @param s The string.\n * @return The method having s as name. If s is an unknown name,\n * then @ref METHOD_UNKNOWN is returned.\n */\nt_method str2method(const string &s);\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_accept.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_accept.h\"\n#include \"definitions.h\"\n\nt_hdr_accept::t_hdr_accept() : t_header(\"Accept\") {};\n\nvoid t_hdr_accept::add_media(const t_media &media) {\n\tpopulated = true;\n\tmedia_list.push_back(media);\n}\n\nvoid t_hdr_accept::set_empty(void) {\n\tpopulated = true;\n\tmedia_list.clear();\n}\n\nstring t_hdr_accept::encode_value(void) const {\n\tstring s;\n\t\n\tif (!populated) return s;\n\t\n\tfor (list<t_media>::const_iterator i = media_list.begin();\n\t     i != media_list.end(); i++)\n\t{\n\t\tif (i != media_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_accept.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Accept header\n\n#ifndef _HDR_ACCEPT_H\n#define _HDR_ACCEPT_H\n\n#include <list>\n#include \"header.h\"\n#include \"media_type.h\"\n\nclass t_hdr_accept : public t_header {\npublic:\n\tlist<t_media>\tmedia_list;\t// list of accepted media\n\n\tt_hdr_accept();\n\n\t// Add a media to the list of accepted media\n\tvoid add_media(const t_media &media);\n\n\t// Clear the list of features, but make the header 'populated'.\n\t// An empty header will be in the message.\n\tvoid set_empty(void);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_accept_encoding.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_accept_encoding.h\"\n#include \"definitions.h\"\n\nt_hdr_accept_encoding::t_hdr_accept_encoding() : t_header(\"Accept-Encoding\") {};\n\nvoid t_hdr_accept_encoding::add_coding(const t_coding &coding) {\n\tpopulated = true;\n\tcoding_list.push_back(coding);\n}\n\nstring t_hdr_accept_encoding::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_coding>::const_iterator i = coding_list.begin();\n\t     i != coding_list.end(); i++)\n\t{\n\t\tif (i != coding_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_accept_encoding.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Accept-Encoding header\n\n#ifndef _HDR_ACCEPT_ENCODING_H\n#define _HDR_ACCEPT_ENCODING_H\n\n#include <list>\n#include <string>\n#include \"coding.h\"\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_accept_encoding : public t_header {\npublic:\n\tlist<t_coding> coding_list; // list of content codings;\n\n\tt_hdr_accept_encoding();\n\n\t// Add a coding to the list of content codings\n\tvoid add_coding(const t_coding &coding);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_accept_language.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_accept_language.h\"\n#include \"util.h\"\n\nt_language::t_language() {\n\tlanguage = \"en\";\n\tq = 1.0;\n}\n\nt_language::t_language(const string &l) {\n\tlanguage = l;\n\tq = 1.0;\n}\n\nstring t_language::encode(void) const {\n\tstring s;\n\n\ts = language;\n\tif (q != 1.0) {\n\t\ts += \";q=\";\n\t\ts += float2str(q, 1);\n\t}\n\n\treturn s;\n}\n\n\nt_hdr_accept_language::t_hdr_accept_language() : t_header(\"Accept-Language\") {};\n\nvoid t_hdr_accept_language::add_language(const t_language &language) {\n\tpopulated = true;\n\tlanguage_list.push_back(language);\n}\n\nstring t_hdr_accept_language::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\t\n\tfor (list<t_language>::const_iterator i = language_list.begin();\n\t     i != language_list.end(); i++)\n\t{\n\t\tif (i != language_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_accept_language.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Accept_Language header\n\n#ifndef _HDR_ACCEPT_LANGUAGE_H\n#define _HDR_ACCEPT_LANGUAGE_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_language {\npublic:\n\tstring language;\n\tfloat q; // quality factor\n\n\tt_language();\n\tt_language(const string &l);\n\tstring encode(void) const;\n};\n\nclass t_hdr_accept_language : public t_header {\npublic:\n\tlist<t_language> language_list;\t// list of accepted languages\n\n\tt_hdr_accept_language();\n\n\t// Add a language to the list of accepted media\n\tvoid add_language(const t_language &language);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_alert_info.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_alert_info.h\"\n\nvoid t_alert_param::add_param(const t_parameter &p) {\n\tparameter_list.push_back(p);\n}\n\nstring t_alert_param::encode(void) const {\n\tstring s;\n\n\ts = '<' + uri.encode() + '>';\n\ts += param_list2str(parameter_list);\n\n\treturn s;\n}\n\n\nt_hdr_alert_info::t_hdr_alert_info() : t_header(\"Alert-Info\") {};\n\nvoid t_hdr_alert_info::add_param(const t_alert_param &p) {\n\tpopulated = true;\n\talert_param_list.push_back(p);\n}\n\nstring t_hdr_alert_info::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_alert_param>::const_iterator i = alert_param_list.begin();\n\t     i != alert_param_list.end(); i++)\n\t{\n\t\tif (i != alert_param_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_alert_info.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Alert-Info header\n\n#ifndef _HDR_ALERT_INFO_H\n#define _HDR_ALERT_INFO_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_alert_param {\npublic:\n\tt_url uri;\n\tlist<t_parameter> parameter_list;\n\n\tvoid add_param(const t_parameter &p);\n\tstring encode(void) const;\n};\n\nclass t_hdr_alert_info : public t_header {\npublic:\n\tlist<t_alert_param> alert_param_list;\n\n\tt_hdr_alert_info();\n\n\t// Add a paramter to the list of alert parameters\n\tvoid add_param(const t_alert_param &p);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_allow.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <algorithm>\n\n#include \"definitions.h\"\n#include \"hdr_allow.h\"\n\nusing namespace std;\n\nt_hdr_allow::t_hdr_allow() : t_header(\"Allow\") {}\n\nvoid t_hdr_allow::add_method(const t_method &m, const string &unknown) {\n\tpopulated = true;\n\n\tif (m != METHOD_UNKNOWN) {\n\t\tmethod_list.push_back(m);\n\t} else {\n\t\tunknown_methods.push_back(unknown);\n\t}\n}\n\nvoid t_hdr_allow::add_method(const string &s) {\n\tpopulated = true;\n\n\tt_method m = str2method(s);\n\tif (m != METHOD_UNKNOWN) {\n\t\tmethod_list.push_back(m);\n\t} else {\n\t\tunknown_methods.push_back(s);\n\t}\n}\n\nbool t_hdr_allow::contains_method(const t_method &m) const {\n\treturn (find(method_list.begin(), method_list.end(), m) != method_list.end());\n}\n\nstring t_hdr_allow::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_method>::const_iterator i = method_list.begin();\n\t     i != method_list.end(); i++)\n\t{\n\t\tif (i != method_list.begin()) s += \",\";\n\t\ts += method2str(*i);\n\t}\n\n\tfor (list<string>::const_iterator i = unknown_methods.begin();\n\t     i != unknown_methods.end(); i++)\n\t{\n\t\tif (i != unknown_methods.begin() || method_list.size() != 0) {\n\t\t\ts += \",\";\n\t\t}\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_allow.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Allow header\n\n#ifndef _HDR_ALLOW_H\n#define _HDR_ALLOW_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"definitions.h\"\n\nusing namespace std;\n\nclass t_hdr_allow : public t_header {\npublic:\n\tlist<t_method> method_list;\n\n\t// Unknown methods are represented as strings\n\tlist<string> unknown_methods;\n\n\tt_hdr_allow();\n\tvoid add_method(const t_method &m, const string &unknown = \"\");\n\tvoid add_method(const string &s);\n\tbool contains_method(const t_method &m) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_allow_events.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_allow_events.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_allow_events::t_hdr_allow_events() : t_header(\"Allow-Events\", \"u\") {}\n\nvoid t_hdr_allow_events::add_event_type(const string &t) {\n\tpopulated = true;\n\tevent_types.push_back(t);\n}\n\nstring t_hdr_allow_events::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = event_types.begin();\n\t     i != event_types.end(); i++)\n\t{\n\t\tif (i != event_types.begin()) s += \",\";\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_allow_events.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Allow-Events header\n// RFC 3265\n\n#ifndef _HDR_ALLOW_EVENTS\n#define _HDR_ALLOW_EVENTS\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_allow_events : public t_header {\npublic:\n\tlist<string>\tevent_types;\n\n\tt_hdr_allow_events();\n\tvoid add_event_type(const string &t);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_auth_info.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_auth_info.h\"\n#include \"definitions.h\"\n#include \"util.h\"\n\nt_hdr_auth_info::t_hdr_auth_info() : t_header(\"Authentication-Info\") {\n\tnonce_count = 0;\n}\n\nvoid t_hdr_auth_info::set_next_nonce(const string &nn) {\n\tpopulated = true;\n\tnext_nonce = nn;\n}\n\nvoid t_hdr_auth_info::set_message_qop(const string &mq) {\n\tpopulated = true;\n\tmessage_qop = mq;\n}\n\nvoid t_hdr_auth_info::set_response_auth(const string &ra) {\n\tpopulated = true;\n\tresponse_auth = ra;\n}\n\nvoid t_hdr_auth_info::set_cnonce(const string &cn) {\n\tpopulated = true;\n\tcnonce = cn;\n}\n\nvoid t_hdr_auth_info::set_nonce_count(const unsigned long &nc) {\n\tpopulated = true;\n\tnonce_count = nc;\n}\n\nstring t_hdr_auth_info::encode_value(void) const {\n\tstring s;\n\tbool add_comma = false;\n\n\tif (!populated) return s;\n\n\tif (next_nonce.size() > 0) {\n\t\ts += \"nextnonce=\";\n\t\ts += '\"';\n\t\ts += next_nonce;\n\t\ts += '\"';\n\t\tadd_comma = true;\n\t}\n\n\tif (message_qop.size() > 0) {\n\t\tif (add_comma) s += ',';\n\t\ts += \"qop=\";\n\t\ts += message_qop;\n\t\tadd_comma = true;\n\t}\n\n\tif (response_auth.size() > 0) {\n\t\tif (add_comma) s += ',';\n\t\ts += \"rspauth=\";\n\t\ts += '\"';\n\t\ts += response_auth;\n\t\ts += '\"';\n\t\tadd_comma = true;\n\t}\n\n\tif (cnonce.size() > 0) {\n\t\tif (add_comma) s += ',';\n\t\ts += \"cnonce=\";\n\t\ts += '\"';\n\t\ts += cnonce;\n\t\ts += '\"';\n\t\tadd_comma = true;\n\t}\n\n\tif (nonce_count > 0) {\n\t\tif (add_comma) s += ',';\n\t\ts += \"nc=\";\n\t\ts += ulong2str(nonce_count, \"%08x\");\n\t\tadd_comma = true;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_auth_info.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Authentication-Info header\n\n#ifndef _HDR_AUTH_INFO_H\n#define _HDR_AUTH_INFO_H\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_auth_info : public t_header {\npublic:\n\tstring\t\t\tnext_nonce;\n\tstring\t\t\tmessage_qop;\n\tstring\t\t\tresponse_auth;\n\tstring\t\t\tcnonce;\n\tunsigned long\t\tnonce_count;\n\n\tt_hdr_auth_info();\n\n\tvoid set_next_nonce(const string &nn);\n\tvoid set_message_qop(const string &mq);\n\tvoid set_response_auth(const string &ra);\n\tvoid set_cnonce(const string &cn);\n\tvoid set_nonce_count(const unsigned long &nc);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_authorization.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_authorization.h\"\n#include \"definitions.h\"\n\nt_hdr_authorization::t_hdr_authorization() : t_header(\"Authorization\") {}\n\nvoid t_hdr_authorization::add_credentials(const t_credentials &c) {\n\tpopulated = true;\n\tcredentials_list.push_back(c);\n}\n\nstring t_hdr_authorization::encode(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\t// RFC 3261 20.7\n\t// Each authorization should appear as a separate header\n\tfor (list<t_credentials>::const_iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\ts += header_name;\n\t\ts += \": \";\n\t\ts += i->encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstring t_hdr_authorization::encode_value(void) const {\n\tstring s;\n\t\n\tif (!populated) return s;\n\t\n\tfor (list<t_credentials>::const_iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\tif (i != credentials_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n\nbool t_hdr_authorization::contains(const string &realm,\n\tconst t_url &uri) const\n{\n\tfor (list<t_credentials>::const_iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\tif (i->digest_response.realm == realm &&\n\t\t    i->digest_response.digest_uri == uri)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid t_hdr_authorization::remove_credentials(const string &realm,\n\tconst t_url &uri)\n{\n\tfor (list<t_credentials>::iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\tif (i->digest_response.realm == realm &&\n\t\t    i->digest_response.digest_uri == uri)\n\t\t{\n\t\t\tcredentials_list.erase(i);\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/parser/hdr_authorization.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Authorization header\n\n#ifndef _HDR_AUTHORIZATION_H\n#define _HDR_AUTHORIZATION_H\n\n#include <list>\n#include <string>\n#include \"credentials.h\"\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_authorization : public t_header {\npublic:\n\tlist<t_credentials>\tcredentials_list;\n\n\tt_hdr_authorization();\n\n\tvoid add_credentials(const t_credentials &c);\n\tstring encode(void) const;\n\tstring encode_value(void) const;\n\n\t// Return true if the header contains credentials for a realm/dest\n\tbool contains(const string &realm, const t_url &uri) const;\n\n\t// Remove credentials for a realm/dest\n\tvoid remove_credentials(const string &realm, const t_url &uri);\n};\n\n#endif\n\n"
  },
  {
    "path": "src/parser/hdr_call_id.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_call_id.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_call_id::t_hdr_call_id() : t_header(\"Call-ID\", \"i\") {};\n\nvoid t_hdr_call_id::set_call_id(const string &id) {\n\tpopulated = true;\n\tcall_id = id;\n}\n\nstring t_hdr_call_id::encode_value(void) const {\n\tif (!populated) return \"\";\n\treturn call_id;\n}\n"
  },
  {
    "path": "src/parser/hdr_call_id.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Call-ID header\n\n#ifndef _HDR_CALL_ID_H\n#define _HDR_CALL_ID_H\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_call_id : public t_header {\npublic:\n\tstring\tcall_id;\n\n\tt_hdr_call_id();\n\tvoid set_call_id(const string &id);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_call_info.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_call_info.h\"\n\nvoid t_info_param::add_param(const t_parameter &p) {\n\tparameter_list.push_back(p);\n}\n\nstring t_info_param::encode(void) const {\n\tstring s;\n\n\ts = '<' + uri.encode() + '>';\n\ts += param_list2str(parameter_list);\n\n\treturn s;\n}\n\n\nt_hdr_call_info::t_hdr_call_info() : t_header(\"Call-Info\") {};\n\nvoid t_hdr_call_info::add_param(const t_info_param &p) {\n\tpopulated = true;\n\tinfo_param_list.push_back(p);\n}\n\nstring t_hdr_call_info::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_info_param>::const_iterator i = info_param_list.begin();\n\t     i != info_param_list.end(); i++)\n\t{\n\t\tif (i != info_param_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_call_info.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Call-Info header\n\n#ifndef _HDR_CALL_INFO_H\n#define _HDR_CALL_INFO_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_info_param {\npublic:\n\tt_url uri;\n\tlist<t_parameter> parameter_list;\n\n\tvoid add_param(const t_parameter &p);\n\tstring encode(void) const;\n};\n\nclass t_hdr_call_info : public t_header {\npublic:\n\tlist<t_info_param> info_param_list;\n\n\tt_hdr_call_info();\n\n\t// Add a paramter to the list of alert parameters\n\tvoid add_param(const t_info_param &p);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_contact.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_contact.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_contact_param::t_contact_param() {\n\tqvalue = 1.0;\n\tqvalue_present = false;\n\texpires = 0;\n\texpires_present = false;\n}\n\nvoid t_contact_param::add_extension(const t_parameter &p) {\n\textensions.push_back(p);\n}\n\nstring t_contact_param::encode(void) const {\n\tstring s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\t\n\tif (qvalue_present) {\n\t\ts += \";q=\";\n\t\ts += float2str(qvalue, 3);\n\t}\n\t\n\tif (expires_present) s += ulong2str(expires, \";expires=%u\");\n\ts += param_list2str(extensions);\n\n\treturn s;\n}\n\nbool t_contact_param::operator<(const t_contact_param &c) const {\n\treturn (qvalue > c.qvalue);\n}\n\n\nt_hdr_contact::t_hdr_contact() : t_header(\"Contact\", \"m\") {\n\tany_flag = false;\n}\n\nvoid t_hdr_contact::add_contact(const t_contact_param &contact) {\n\tpopulated = true;\n\tcontact_list.push_back(contact);\n}\n\nvoid t_hdr_contact::add_contacts(const list<t_contact_param> &l) {\n\tpopulated = true;\n\n\tfor (list<t_contact_param>::const_iterator i = l.begin(); i != l.end();\n\t     i++)\n\t{\n\t\tcontact_list.push_back(*i);\n\t}\n}\n\nvoid t_hdr_contact::set_contacts(const list<t_contact_param> &l) {\n\tpopulated = true;\n\tcontact_list = l;\n}\n\nvoid t_hdr_contact::set_contacts(const list<t_url> &l) {\n\tt_contact_param c;\n\tfloat q = 0.9;\n\n\tpopulated = true;\n\n\tcontact_list.clear();\n\tfor (list<t_url>::const_iterator i = l.begin(); i != l.end(); i++) {\n\t\tc.uri = *i;\n\t\tc.set_qvalue(q);\n\t\tcontact_list.push_back(c);\n\t\tq = q - 0.1;\n\t\tif (q < 0.1) q = 0.1;\n\t}\n}\n\nvoid t_hdr_contact::set_contacts(const list<t_display_url> &l) {\n\tt_contact_param c;\n\tfloat q = 0.9;\n\n\tpopulated = true;\n\n\tcontact_list.clear();\n\tfor (list<t_display_url>::const_iterator i = l.begin(); i != l.end(); i++) {\n\t\tc.uri = i->url;\n\t\tc.display = i->display;\n\t\tc.set_qvalue(q);\n\t\tcontact_list.push_back(c);\n\t\tq = q - 0.1;\n\t\tif (q < 0.1) q = 0.1;\n\t}\n}\n\nvoid t_hdr_contact::set_any(void) {\n\tpopulated = true;\n\tany_flag = true;\n\tcontact_list.clear();\n}\n\nt_contact_param *t_hdr_contact::find_contact(const t_url &u) {\n\tfor (list<t_contact_param>::iterator i = contact_list.begin();\n\t     i != contact_list.end(); i++)\n\t{\n\t\tif (u.sip_match(i->uri)) return &(*i);\n\t}\n\n\treturn NULL;\n}\n\nbool t_contact_param::is_expires_present(void) const {\n\treturn expires_present;\n}\n\nunsigned long t_contact_param::get_expires(void) const {\n\treturn expires;\n}\n\nvoid t_contact_param::set_expires(unsigned long e) {\n\texpires_present = true;\n\texpires = e;\n}\n\nfloat t_contact_param::get_qvalue(void) const {\n\treturn qvalue;\n}\n\nvoid t_contact_param::set_qvalue(float q) {\n\tqvalue_present = true;\n\tqvalue = q;\n}\n\nstring t_hdr_contact::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tif (any_flag) {\n\t\ts += '*';\n\t\treturn s;\n\t}\n\n\tfor (list<t_contact_param>::const_iterator i = contact_list.begin();\n\t     i != contact_list.end(); i++)\n\t{\n\t\tif (i != contact_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_contact.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Contact header\n\n#ifndef _HDR_CONTACT\n#define _HDR_CONTACT\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_contact_param {\nprivate:\n\tbool\t\t\texpires_present;\n\tunsigned long\t\texpires;\n\tbool\t\t\tqvalue_present;\n\tfloat\t\t\tqvalue;\n\t\npublic:\n\tstring\t\t\tdisplay; // display name\n\tt_url\t\t\turi;\n\tlist<t_parameter>\textensions;\n\n\tt_contact_param();\n\tvoid add_extension(const t_parameter &p);\n\tstring encode(void) const;\n\tbool is_expires_present(void) const;\n\tunsigned long get_expires(void) const;\n\tvoid set_expires(unsigned long e);\n\tfloat get_qvalue(void) const;\n\tvoid set_qvalue(float q);\n\n\t// Compare contacts on q-value.\n\t// The contacts with the highest q-value comes first in the order\n\tbool operator<(const t_contact_param &c) const;\n};\n\nclass t_hdr_contact : public t_header {\npublic:\n\tbool\t\t\tany_flag; // true if Contact: *\n\tlist<t_contact_param>\tcontact_list;\n\n\tt_hdr_contact();\n\tvoid add_contact(const t_contact_param &contact);\n\tvoid add_contacts(const list<t_contact_param> &l);\n\tvoid set_contacts(const list<t_contact_param> &l);\n\t\n\t/**\n\t * Set the contact list to a sequence of URI's with display names.\n\t * The URI's are give a descending q-value starting at 0.9\n\t * Each subsequent URI gets a q-value 0.1 less than the previous\n\t * URI. If more than 9 URI's are passed then the tail of URI's all\n\t * get a q-value of 0.1.\n\t *\n\t * @param l [in] The list of URI's to be put in the contact list.\n\t */\t\n\tvoid set_contacts(const list<t_url> &l);\n\t\n\t/**\n\t * Set the contact list to a sequence of URI's with display names.\n\t * The URI's are give a descending q-value starting at 0.9\n\t * Each subsequent URI gets a q-value 0.1 less than the previous\n\t * URI. If more than 9 URI's are passed then the tail of URI's all\n\t * get a q-value of 0.1.\n\t *\n\t * @param l [in] The list of URI's to be put in the contact list.\n\t */\n\tvoid set_contacts(const list<t_display_url> &l);\n\n\t// Set contact to any, eg. Contact: *\n\tvoid set_any(void);\n\n\t// Find contact with uri u. If no contact is found, then\n\t// NULL is returned.\n\tt_contact_param *find_contact(const t_url &u);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_content_disp.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_content_disp.h\"\n\nt_hdr_content_disp::t_hdr_content_disp() : t_header(\"Content-Disposition\") {};\n\nvoid t_hdr_content_disp::set_type(const string &t) {\n\tpopulated = true;\n\ttype = t;\n}\n\nvoid t_hdr_content_disp::set_filename(const string &name) {\n\tpopulated = true;\n\tfilename = name;\n}\n\nvoid t_hdr_content_disp::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nvoid t_hdr_content_disp::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nstring t_hdr_content_disp::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = type;\n\t\n\tif (!filename.empty()) {\n\t\ts += \";filename=\\\"\";\n\t\ts += filename;\n\t\ts += \"\\\"\";\n\t}\n\t\n\ts += param_list2str(params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_content_disp.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Content-Disposition header\n\n#ifndef _HDR_CONTENT_DISP\n#define _HDR_CONTENT_DISP\n\n#include <string>\n#include <list>\n#include \"header.h\"\n#include \"parameter.h\"\n\nusing namespace std;\n\n//@{\n/** @name Disposition types */\n#define DISPOSITION_ATTACHMENT\t\"attachment\"\n//@}\n\nclass t_hdr_content_disp : public t_header {\npublic:\n\tstring\t\t\ttype;\n\tstring\t\t\tfilename;\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_content_disp();\n\n\tvoid set_type(const string &t);\n\tvoid set_filename(const string &name);\n\tvoid add_param(const t_parameter &p);\n\tvoid set_params(const list<t_parameter> &l);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_content_encoding.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_content_encoding.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_content_encoding::t_hdr_content_encoding() : t_header(\"Content-Encoding\", \"e\") {};\n\nvoid t_hdr_content_encoding::add_coding(const t_coding &coding) {\n\tpopulated = true;\n\tcoding_list.push_back(coding);\n}\n\nstring t_hdr_content_encoding::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_coding>::const_iterator i = coding_list.begin();\n\t     i != coding_list.end(); i++)\n\t{\n\t\tif (i != coding_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_content_encoding.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Content-Encoding header\n\n#ifndef _HDR_CONTENT_ENCODING_H\n#define _HDR_CONTENT_ENCODING_H\n\n#include <list>\n#include <string>\n#include \"coding.h\"\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_content_encoding : public t_header {\npublic:\n\tlist<t_coding> coding_list; // list of content codings;\n\n\tt_hdr_content_encoding();\n\n\t// Add a coding to the list of content codings\n\tvoid add_coding(const t_coding &coding);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_content_language.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_content_language.h\"\n#include \"util.h\"\n\nt_hdr_content_language::t_hdr_content_language() : t_header(\"Content-Language\") {};\n\nvoid t_hdr_content_language::add_language(const t_language &language) {\n\tpopulated = true;\n\tlanguage_list.push_back(language);\n}\n\nstring t_hdr_content_language::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_language>::const_iterator i = language_list.begin();\n\t     i != language_list.end(); i++)\n\t{\n\t\tif (i != language_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_content_language.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Content_Language header\n\n#ifndef _HDR_CONTENT_LANGUAGE_H\n#define _HDR_CONTENT_LANGUAGE_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"hdr_accept_language.h\"\n\nusing namespace std;\n\nclass t_hdr_content_language : public t_header {\npublic:\n\tlist<t_language> language_list;\t// list of languages\n\n\tt_hdr_content_language();\n\n\t// Add a language to the list of languages\n\tvoid add_language(const t_language &language);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_content_length.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_content_length.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_content_length::t_hdr_content_length() : t_header(\"Content-Length\", \"l\") {\n\tlength = 0;\n}\n\nvoid t_hdr_content_length::set_length(unsigned long l) {\n\tpopulated = true;\n\tlength = l;\n}\n\nstring t_hdr_content_length::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = ulong2str(length);\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_content_length.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Content-Length header\n\n#ifndef _HDR_CONTENT_LENGTH\n#define _HDR_CONTENT_LENGTH\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_content_length : public t_header {\npublic:\n\tunsigned long length;\n\n\tt_hdr_content_length();\n\tvoid set_length(unsigned long l);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_content_type.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_content_type.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_content_type::t_hdr_content_type() : t_header(\"Content-Type\", \"c\") {};\n\nvoid t_hdr_content_type::set_media(const t_media &m) {\n\tpopulated = true;\n\tmedia = m;\n}\n\nstring t_hdr_content_type::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = media.encode();\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_content_type.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Content-Type header\n\n#ifndef _HDR_CONTENT_TYPE_H\n#define _HDR_CONTENT_TYPE_H\n\n#include \"header.h\"\n#include \"media_type.h\"\n\nclass t_hdr_content_type : public t_header {\npublic:\n\tt_media\tmedia;\n\n\tt_hdr_content_type();\n\tvoid set_media(const t_media &m);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_cseq.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_cseq.h\"\n#include \"util.h\"\n\nt_hdr_cseq::t_hdr_cseq() : t_header(\"CSeq\") {\n\tseqnr = 0;\n\tmethod = INVITE;\n}\n\nvoid t_hdr_cseq::set_seqnr(unsigned long l) {\n\tpopulated = true;\n\tseqnr = l;\n}\n\nvoid t_hdr_cseq::set_method(t_method m, const string &unknown) {\n\tpopulated = true;\n\tmethod = m;\n\tunknown_method = unknown;\n}\n\nvoid t_hdr_cseq::set_method(const string &s) {\n\tpopulated = true;\n\tmethod = str2method(s);\n\tif (method == METHOD_UNKNOWN) {\n\t\tunknown_method = s;\n\t}\n}\n\nstring t_hdr_cseq::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = ulong2str(seqnr) + ' ';\n\ts += method2str(method, unknown_method);\n\n\treturn s;\n}\n\nbool t_hdr_cseq::operator==(const t_hdr_cseq &h) const {\n\tif (method != METHOD_UNKNOWN) {\n\t\treturn (seqnr == h.seqnr && method == h.method);\n\t}\n\n\treturn (seqnr == h.seqnr && unknown_method == h.unknown_method);\n}\n"
  },
  {
    "path": "src/parser/hdr_cseq.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// CSeq header\n\n#ifndef _HDR_CSEQ\n#define _HDR_CSEQ\n\n#include <string>\n#include \"header.h\"\n#include \"definitions.h\"\n\nusing namespace std;\n\nclass t_hdr_cseq : public t_header {\npublic:\n\tunsigned long\tseqnr;\n\tt_method\tmethod;\n\tstring\t\tunknown_method; // set if method is UNKNOWN\n\n\tt_hdr_cseq();\n\tvoid set_seqnr(unsigned long l);\n\tvoid set_method(t_method m, const string &unknown = \"\");\n\tvoid set_method(const string &s);\n\n\tstring encode_value(void) const;\n\n\tbool operator==(const t_hdr_cseq &h) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_date.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// NOTE: the date functions are not thread safe\n\n#include <sys/time.h>\n#include \"hdr_date.h\"\n#include \"definitions.h\"\n#include \"util.h\"\n\nt_hdr_date::t_hdr_date() : t_header(\"Date\") {}\n\nvoid t_hdr_date::set_date_gm(struct tm *tm) {\n\tpopulated = true;\n\tdate = timegm(tm);\n}\n\nvoid t_hdr_date::set_now(void) {\n\tstruct timeval t;\n\n\tpopulated = true;\n\tgettimeofday(&t, NULL);\n\tdate = t.tv_sec;\n}\n\nstring t_hdr_date::encode_value(void) const {\n\tstring s;\n\tstruct tm tm;\n\n\tif (!populated) return s;\n\n\tgmtime_r(&date, &tm);\n\ts = weekday2str(tm.tm_wday);\n\ts += \", \";\n\ts += int2str(tm.tm_mday, \"%02d\");\n\ts += ' ';\n\ts += month2str(tm.tm_mon);\n\ts += ' ';\n\ts += int2str(tm.tm_year + 1900, \"%04d\");\n\ts += ' ';\n\ts += int2str(tm.tm_hour, \"%02d\");\n\ts += ':';\n\ts += int2str(tm.tm_min, \"%02d\");\n\ts += ':';\n\ts += int2str(tm.tm_sec, \"%02d\");\n\ts += \" GMT\";\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_date.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Date header\n\n#ifndef _HDR_DATE_H\n#define _HDR_DATE_H\n\n#include <ctime>\n#include <string>\n#include \"header.h\"\n\nclass t_hdr_date : public t_header {\npublic:\n\ttime_t\tdate;\n\n\tt_hdr_date();\n\tvoid set_date_gm(struct tm *tm); // set date, tm is GMT\n\tvoid set_now(void); // Set date/time to current date/time\n\tstring encode_value(void) const;\n};\n\nusing namespace std;\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_error_info.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_error_info.h\"\n\nvoid t_error_param::add_param(const t_parameter &p) {\n\tparameter_list.push_back(p);\n}\n\nstring t_error_param::encode(void) const {\n\tstring s;\n\n\ts = '<' + uri.encode() + '>';\n\ts += param_list2str(parameter_list);\n\n\treturn s;\n}\n\n\nt_hdr_error_info::t_hdr_error_info() : t_header(\"Error-Info\") {};\n\nvoid t_hdr_error_info::add_param(const t_error_param &p) {\n\tpopulated = true;\n\terror_param_list.push_back(p);\n}\n\nstring t_hdr_error_info::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_error_param>::const_iterator i = error_param_list.begin();\n\t     i != error_param_list.end(); i++)\n\t{\n\t\tif (i != error_param_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_error_info.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Error-Info header\n\n#ifndef _HDR_ERROR_INFO_H\n#define _HDR_ERROR_INFO_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_error_param {\npublic:\n\tt_url uri;\n\tlist<t_parameter> parameter_list;\n\n\tvoid add_param(const t_parameter &p);\n\tstring encode(void) const;\n};\n\nclass t_hdr_error_info : public t_header {\npublic:\n\tlist<t_error_param> error_param_list;\n\n\tt_hdr_error_info();\n\n\t// Add a paramter to the list of error parameters\n\tvoid add_param(const t_error_param &p);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_event.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_event.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_event::t_hdr_event() : t_header(\"Event\", \"o\") {}\n\nvoid t_hdr_event::set_event_type(const string &t) {\n\tpopulated = true;\n\tevent_type = t;\n}\n\nvoid t_hdr_event::set_id(const string &s) {\n\tpopulated = true;\n\tid = s;\n}\n\nvoid t_hdr_event::add_event_param(const t_parameter &p) {\n\tpopulated = true;\n\tevent_params.push_back(p);\n}\n\nstring t_hdr_event::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts += event_type;\n\n\tif (id.size() > 0) {\n\t\ts += \";id=\";\n\t\ts += id;\n\t}\n\n\ts += param_list2str(event_params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_event.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Event header\n// RFC 3265\n\n#ifndef _HDR_EVENT\n#define _HDR_EVENT\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n\n#define SIP_EVENT_REFER\t\t\"refer\"\t\t\t// RFC 3515\n#define SIP_EVENT_MSG_SUMMARY\t\"message-summary\"\t// RFC 3842\n#define SIP_EVENT_PRESENCE\t\"presence\"\t\t// RFC 3856\n\nusing namespace std;\n\nclass t_hdr_event : public t_header {\npublic:\n\t// The event_type attribute contains the event-template as well\n\t// if present, e.g. event.template\n\tstring\t\t\tevent_type;\n\tstring\t\t\tid;\n\tlist<t_parameter>\tevent_params;\n\n\tt_hdr_event();\n\tvoid set_event_type(const string &t);\n\tvoid set_id(const string &s);\n\tvoid add_event_param(const t_parameter &p);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_expires.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_expires.h\"\n#include \"util.h\"\n\nt_hdr_expires::t_hdr_expires() : t_header(\"Expires\") {\n\ttime = 0;\n}\n\nvoid t_hdr_expires::set_time(unsigned long t) {\n\tpopulated = true;\n\ttime = t;\n}\n\nstring t_hdr_expires::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn ulong2str(time);\n}\n"
  },
  {
    "path": "src/parser/hdr_expires.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Expires header\n\n#ifndef _HDR_EXPIRES_LENGTH\n#define _HDR_EXPIRES_LENGTH\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_expires : public t_header {\npublic:\n\tunsigned long time; // expiry time in seconds\n\n\tt_hdr_expires();\n\tvoid set_time(unsigned long t);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_from.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_from.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_from::t_hdr_from() : t_header(\"From\", \"f\") {}\n\nvoid t_hdr_from::set_display(const string &d) {\n\tpopulated = true;\n\tdisplay = d;\n}\n\nvoid t_hdr_from::set_uri(const string &u) {\n\tpopulated = true;\n\turi.set_url(u);\n}\n\nvoid t_hdr_from::set_uri(const t_url &u) {\n\tpopulated = true;\n\turi = u;\n}\n\nvoid t_hdr_from::set_tag(const string &t) {\n\tpopulated = true;\n\ttag = t;\n}\n\nvoid t_hdr_from::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nvoid t_hdr_from::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_from::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\tif (tag != \"\") {\n\t\ts += \";tag=\";\n\t\ts += tag;\n\t}\n\n\ts += param_list2str(params);\n\n\treturn s;\n}\n\nstring t_hdr_from::get_display_presentation(void) const {\n\tif (display_override.empty()) {\n\t\treturn display;\n\t} else {\n\t\treturn display_override;\n\t}\n}\n"
  },
  {
    "path": "src/parser/hdr_from.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// From header\n\n#ifndef _H_HDR_FROM\n#define _H_HDR_FROM\n\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_from : public t_header {\npublic:\n\tstring\t\t\tdisplay; // display name\n\t\n\t// The display_override may be set by the UA to display another\n\t// name to the user, then the display name received in the\n\t// signalling, e.g. a lookup from an address book. This value\n\t// does NOT appear in the SIP message.\n\tstring\t\t\tdisplay_override;\n\t\n\tt_url\t\t\turi;\n\tstring\t\t\ttag;\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_from();\n\tvoid set_display(const string &d);\n\tvoid set_uri(const string &u);\n\tvoid set_uri(const t_url &u);\n\tvoid set_tag(const string &t);\n\tvoid set_params(const list<t_parameter> &l);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n\t\n\t// Get the display name to show to the user.\n\tstring get_display_presentation(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_in_reply_to.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_in_reply_to.h\"\n#include \"definitions.h\"\n\nt_hdr_in_reply_to::t_hdr_in_reply_to() : t_header(\"In-Reply-To\") {};\n\nvoid t_hdr_in_reply_to::add_call_id(const string &id) {\n\tpopulated = true;\n\tcall_ids.push_back(id);\n}\n\nstring t_hdr_in_reply_to::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = call_ids.begin();\n\t     i != call_ids.end(); i++)\n\t{\n\t\tif (i != call_ids.begin()) s += \", \";\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_in_reply_to.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// In-Reply-To header\n\n#ifndef _H_HDR_IN_REPLY_TO\n#define _H_HDR_IN_REPLY_TO\n\n#include <string>\n#include <list>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_in_reply_to : public t_header {\npublic:\n\tlist<string>\tcall_ids;\n\n\tt_hdr_in_reply_to();\n\tvoid add_call_id(const string &id);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_max_forwards.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_max_forwards.h\"\n#include \"util.h\"\n\nt_hdr_max_forwards::t_hdr_max_forwards() : t_header(\"Max-Forwards\") {\n\tmax_forwards = 0;\n}\n\nvoid t_hdr_max_forwards::set_max_forwards(int m) {\n\tpopulated = true;\n\tmax_forwards = m;\n}\n\nstring t_hdr_max_forwards::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn int2str(max_forwards);\n}\n"
  },
  {
    "path": "src/parser/hdr_max_forwards.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Max-Forwards header\n\n#ifndef _HDR_MAX_FORWARDS_LENGTH\n#define _HDR_MAX_FORWARDS_LENGTH\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_max_forwards : public t_header {\npublic:\n\tint max_forwards;\n\n\tt_hdr_max_forwards();\n\tvoid set_max_forwards(int m);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_mime_version.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_mime_version.h\"\n\nt_hdr_mime_version::t_hdr_mime_version() : t_header(\"MIME-Version\") {};\n\nvoid t_hdr_mime_version::set_version(const string &v) {\n\tpopulated = true;\n\tversion = v;\n}\n\nstring t_hdr_mime_version::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn version;\n}\n"
  },
  {
    "path": "src/parser/hdr_mime_version.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// MIME-Version header\n#ifndef _H_HDR_MIME_VERSION\n#define _H_HDR_MIME_VERSION\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_mime_version : public t_header {\npublic:\n\tstring\tversion;\n\n\tt_hdr_mime_version();\n\tvoid set_version(const string &v);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_min_expires.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_min_expires.h\"\n#include \"util.h\"\n\nt_hdr_min_expires::t_hdr_min_expires() : t_header(\"Min-Expires\") {\n\ttime = 0;\n}\n\nvoid t_hdr_min_expires::set_time(unsigned long t) {\n\tpopulated = true;\n\ttime = t;\n}\n\nstring t_hdr_min_expires::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn ulong2str(time);\n}\n"
  },
  {
    "path": "src/parser/hdr_min_expires.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Expires header\n\n#ifndef _HDR_MIN_EXPIRES_LENGTH\n#define _HDR_MIN_EXPIRES_LENGTH\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_min_expires : public t_header {\npublic:\n\tunsigned long time; // expiry time in seconds\n\n\tt_hdr_min_expires();\n\tvoid set_time(unsigned long t);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_min_se.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    Copyright (C) 2022       Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_min_se.h\"\n#include \"util.h\"\n\nt_hdr_min_se::t_hdr_min_se() :\n\tt_header(\"Min-SE\"),\n\ttime(90)\n{\n}\n\nvoid t_hdr_min_se::set_time(unsigned long t) {\n\tpopulated = true;\n\ttime = t;\n}\n\nvoid t_hdr_min_se::add_param(const t_parameter &p) {\n\tparams.push_back(p);\n}\n\nvoid t_hdr_min_se::set_params(const std::list<t_parameter> &l) {\n\tparams = l;\n}\n\nstring t_hdr_min_se::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\tstring s;\n\ts += ulong2str(time);\n\ts += param_list2str(params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_min_se.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    Copyright (C) 2022       Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Min-SE header (RFC 4028)\n\n#ifndef _HDR_MIN_SE_H\n#define _HDR_MIN_SE_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n\nclass t_hdr_min_se : public t_header {\npublic:\n\tunsigned long time; // expiry time in seconds\n\tlist<t_parameter> params;\n\n\tt_hdr_min_se();\n\n\tvoid set_time(unsigned long t);\n\n\tvoid add_param(const t_parameter &p);\n\tvoid set_params(const std::list<t_parameter> &l);\n\n\tstd::string encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_organization.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_organization.h\"\n\nt_hdr_organization::t_hdr_organization() : t_header(\"Organization\") {};\n\nvoid t_hdr_organization::set_name(const string &n) {\n\tpopulated = true;\n\tname = n;\n}\n\nstring t_hdr_organization::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn name;\n}\n"
  },
  {
    "path": "src/parser/hdr_organization.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Organization header\n#ifndef _H_HDR_ORGANIZATION\n#define _H_HDR_ORGANIZATION\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_organization : public t_header {\npublic:\n\tstring\tname;\n\n\tt_hdr_organization();\n\tvoid set_name(const string &n);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_p_asserted_identity.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_p_asserted_identity.h\"\n\nt_hdr_p_asserted_identity::t_hdr_p_asserted_identity() : \n\tt_header(\"P-Asserted-Identity\") \n{}\n\nvoid t_hdr_p_asserted_identity::add_identity(const t_identity &identity) {\n\tpopulated = true;\n\tidentity_list.push_back(identity);\n}\n\nstring t_hdr_p_asserted_identity::encode_value(void) const {\n\tstring s;\n\t\n\tif (!populated) return s;\n\t\n\tfor (list<t_identity>::const_iterator i = identity_list.begin();\n\t     i != identity_list.end(); i++)\n\t{\n\t\tif (i != identity_list.begin()) s += ',';\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_p_asserted_identity.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3325 9.1\n// P-Asserted-Identity header\n\n#ifndef _H_HDR_P_ASSERTED_IDENTITY\n#define _H_HDR_P_ASSERTED_IDENTITY\n\n#include <string>\n#include \"header.h\"\n#include \"identity.h\"\n\nusing namespace std;\n\nclass t_hdr_p_asserted_identity : public t_header {\npublic:\t\n\tlist<t_identity> identity_list;\n\n\tt_hdr_p_asserted_identity();\n\tvoid add_identity(const t_identity &identity);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_p_preferred_identity.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_p_preferred_identity.h\"\n\nt_hdr_p_preferred_identity::t_hdr_p_preferred_identity() : \n\tt_header(\"P-Preferred-Identity\") \n{}\n\nvoid t_hdr_p_preferred_identity::add_identity(const t_identity &identity) {\n\tpopulated = true;\n\tidentity_list.push_back(identity);\n}\n\nstring t_hdr_p_preferred_identity::encode_value(void) const {\n\tstring s;\n\t\n\tif (!populated) return s;\n\t\n\tfor (list<t_identity>::const_iterator i = identity_list.begin();\n\t     i != identity_list.end(); i++)\n\t{\n\t\tif (i != identity_list.begin()) s += ',';\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_p_preferred_identity.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3325 9.2\n// P-Preferred-Identity header\n\n#ifndef _H_HDR_P_PREFERRED_IDENTITY\n#define _H_HDR_P_PREFERRED_IDENTITY\n\n#include <string>\n#include \"header.h\"\n#include \"identity.h\"\n\nusing namespace std;\n\nclass t_hdr_p_preferred_identity : public t_header {\npublic:\t\n\tlist<t_identity> identity_list;\n\n\tt_hdr_p_preferred_identity();\n\tvoid add_identity(const t_identity &identity);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_priority.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_priority.h\"\n\nt_hdr_priority::t_hdr_priority() : t_header(\"Priority\") {};\n\nvoid t_hdr_priority::set_priority(const string &p) {\n\tpopulated = true;\n\tpriority = p;\n}\n\nstring t_hdr_priority::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn priority;\n}\n"
  },
  {
    "path": "src/parser/hdr_priority.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Priority header\n#ifndef _H_HDR_PRIORITY_VERSION\n#define _H_HDR_PRIORITY_VERSION\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_priority : public t_header {\npublic:\n\tstring\tpriority;\n\n\tt_hdr_priority();\n\tvoid set_priority(const string &p);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_privacy.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <algorithm>\n\n#include \"definitions.h\"\n#include \"hdr_privacy.h\"\n\nusing namespace std;\n\nt_hdr_privacy::t_hdr_privacy() : t_header(\"Privacy\") {};\n\nvoid t_hdr_privacy::add_privacy(const string &privacy) {\n\tpopulated = true;\n\tprivacy_list.push_back(privacy);\n}\n\nbool t_hdr_privacy::contains_privacy(const string &privacy) const {\n\treturn (find(privacy_list.begin(), privacy_list.end(), privacy) !=\n\t\t\tprivacy_list.end());\n}\n\nstring t_hdr_privacy::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = privacy_list.begin();\n\t     i != privacy_list.end(); i++)\n\t{\n\t\tif (i != privacy_list.begin()) s += \";\";\n\t\ts += *i;\n\t}\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_privacy.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3323\n// Privacy header\n\n#ifndef _H_HDR_PRIVACY\n#define _H_HDR_PRIVACY\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\n#define PRIVACY_HEADER\t\t\"header\"\n#define PRIVACY_SESSION\t\t\"session\"\n#define PRIVACY_USER\t\t\"user\"\n#define PRIVACY_NONE\t\t\"none\"\n#define PRIVACY_CRITICAL\t\"critical\"\n\n// RFC 3325 9.3 defines id privacy\n#define PRIVACY_ID\t\t\"id\"\n\nclass t_hdr_privacy : public t_header {\npublic:\n\tlist<string>\tprivacy_list;\n\t\n\tt_hdr_privacy();\n\tvoid add_privacy(const string &privacy);\n\tbool contains_privacy(const string &privacy) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_proxy_authenticate.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_proxy_authenticate.h\"\n#include \"definitions.h\"\n\nt_hdr_proxy_authenticate::t_hdr_proxy_authenticate() : t_header(\"Proxy-Authenticate\") {}\n\nvoid t_hdr_proxy_authenticate::set_challenge(const t_challenge &c) {\n\tpopulated = true;\n\tchallenge = c;\n}\n\nstring t_hdr_proxy_authenticate::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn challenge.encode();\n}\n"
  },
  {
    "path": "src/parser/hdr_proxy_authenticate.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Proxy-Authenticate header\n\n#ifndef _HDR_PROXY_AUTHENTICATE_H\n#define _HDR_PROXY_AUTHENTICATE_H\n\n#include <list>\n#include <string>\n#include \"challenge.h\"\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_proxy_authenticate : public t_header {\npublic:\n\tt_challenge\t\tchallenge;\n\n\tt_hdr_proxy_authenticate();\n\n\tvoid set_challenge(const t_challenge &c);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_proxy_authorization.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_proxy_authorization.h\"\n#include \"definitions.h\"\n\nt_hdr_proxy_authorization::t_hdr_proxy_authorization() : t_header(\"Proxy-Authorization\") {}\n\nvoid t_hdr_proxy_authorization::add_credentials(const t_credentials &c) {\n\tpopulated = true;\n\tcredentials_list.push_back(c);\n}\n\nstring t_hdr_proxy_authorization::encode(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\t// RFC 3261 20.28\n\t// Each authorization should appear as a separate header\n\tfor (list<t_credentials>::const_iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\ts += header_name;\n\t\ts += \": \";\n\t\ts += i->encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstring t_hdr_proxy_authorization::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_credentials>::const_iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\tif (i != credentials_list.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nbool t_hdr_proxy_authorization::contains(const string &realm,\n\tconst t_url &uri) const\n{\n\tfor (list<t_credentials>::const_iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\tif (i->digest_response.realm == realm &&\n\t\t    i->digest_response.digest_uri == uri)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid t_hdr_proxy_authorization::remove_credentials(const string &realm,\n\tconst t_url &uri)\n{\n\tfor (list<t_credentials>::iterator i = credentials_list.begin();\n\t     i != credentials_list.end(); i++)\n\t{\n\t\tif (i->digest_response.realm == realm &&\n\t\t    i->digest_response.digest_uri == uri)\n\t\t{\n\t\t\tcredentials_list.erase(i);\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/parser/hdr_proxy_authorization.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Proxy-Authorization header\n\n#ifndef _HDR_PROXY_AUTHORIZATION\n#define _HDR_PROXY_AUTHORIZATION\n\n#include <list>\n#include <string>\n#include \"credentials.h\"\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_proxy_authorization : public t_header {\npublic:\n\tlist<t_credentials>\tcredentials_list;\n\n\tt_hdr_proxy_authorization();\n\n\tvoid add_credentials(const t_credentials &c);\n\tstring encode(void) const;\n\tstring encode_value(void) const;\n\n\t// Return true if the header contains credentials for a realm/dest\n\tbool contains(const string &realm, const t_url &uri) const;\n\n\t// Remove credentials for a realm/dest\n\tvoid remove_credentials(const string &realm, const t_url &uri);\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_proxy_require.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_proxy_require.h\"\n\nt_hdr_proxy_require::t_hdr_proxy_require() : t_header(\"Proxy-Require\") {};\n\nvoid t_hdr_proxy_require::add_feature(const string &f) {\n\tpopulated = true;\n\tfeatures.push_back(f);\n}\n\nstring t_hdr_proxy_require::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (i != features.begin()) s += \", \";\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_proxy_require.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Proxy-Require header\n\n#ifndef _H_HDR_PROXY_REQUIRE\n#define _H_HDR_PROXY_REQUIRE\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\nclass t_hdr_proxy_require : public t_header {\npublic:\n\tlist<string>\tfeatures;\n\n\tt_hdr_proxy_require();\n\tvoid add_feature(const string &f);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_rack.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_rack.h\"\n#include \"util.h\"\n\nt_hdr_rack::t_hdr_rack() : t_header(\"RAck\") {\n\tcseq_nr = 0;\n\tresp_nr = 0;\n\tmethod = INVITE;\n}\n\nvoid t_hdr_rack::set_cseq_nr(unsigned long l) {\n\tpopulated = true;\n\tcseq_nr = l;\n}\n\nvoid t_hdr_rack::set_resp_nr(unsigned long l) {\n\tpopulated = true;\n\tresp_nr = l;\n}\n\nvoid t_hdr_rack::set_method(t_method m, const string &unknown) {\n\tpopulated = true;\n\tmethod = m;\n\tunknown_method = unknown;\n}\n\nvoid t_hdr_rack::set_method(const string &s) {\n\tpopulated = true;\n\tmethod = str2method(s);\n\tif (method == METHOD_UNKNOWN) {\n\t\tunknown_method = s;\n\t}\n}\n\nstring t_hdr_rack::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = ulong2str(resp_nr) + ' ';\n\ts += ulong2str(cseq_nr);\n\ts += ' ';\n\ts += method2str(method, unknown_method);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_rack.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RAck header\n// RFC 3262\n\n#ifndef _HDR_RACK\n#define _HDR_RACK\n\n#include <string>\n#include \"header.h\"\n#include \"definitions.h\"\n\nusing namespace std;\n\nclass t_hdr_rack : public t_header {\npublic:\n\tunsigned long\tcseq_nr;\n\tunsigned long\tresp_nr;\n\tt_method\tmethod;\n\tstring\t\tunknown_method; // set if method is UNKNOWN\n\n\tt_hdr_rack();\n\tvoid set_cseq_nr(unsigned long l);\n\tvoid set_resp_nr(unsigned long l);\n\tvoid set_method(t_method m, const string &unknown = \"\");\n\tvoid set_method(const string &s);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_reason.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    Copyright (C) 2022       Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_reason.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\n\nt_reason::t_reason(const std::string &_protocol) :\n\tprotocol(_protocol)\n{\n}\n\nvoid t_reason::set_cause(const std::string &_cause) {\n\tcause = _cause;\n}\n\nvoid t_reason::set_text(const std::string &_text) {\n\ttext = _text;\n}\n\nvoid t_reason::add_extension(const t_parameter &p) {\n\textensions.push_back(p);\n}\n\nstd::string t_reason::encode(void) const {\n\tstd::string s;\n\n\ts = protocol;\n\n\tif (!cause.empty()) {\n\t\ts += \";cause=\";\n\t\ts += cause;\n\t}\n\n\tif (!text.empty()) {\n\t\ts += \";text=\\\"\";\n\t\ts += text;\n\t\ts += \"\\\"\";\n\t}\n\n\ts += param_list2str(extensions);\n\n\treturn s;\n}\n\n\nt_hdr_reason::t_hdr_reason() : t_header(\"Reason\") {}\n\nvoid t_hdr_reason::add_reason(const t_reason &reason) {\n\tpopulated = true;\n\treason_list.push_back(reason);\n}\n\nstd::string t_hdr_reason::get_display_text() const {\n\tfor (const auto &r : reason_list) {\n\t\tif (r.protocol == \"SIP\") {\n\t\t\tstd::vector<std::string> elems;\n\n\t\t\tif (!r.cause.empty()) {\n\t\t\t\telems.push_back(r.cause);\n\t\t\t}\n\t\t\tif (!r.text.empty()) {\n\t\t\t\telems.push_back(r.text);\n\t\t\t}\n\n\t\t\tif (!elems.empty()) {\n\t\t\t\treturn join_strings(elems, \" \");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {};\n}\n\nstd::string t_hdr_reason::encode(void) const {\n\treturn (t_parser::multi_values_as_list ?\n\t\t\tt_header::encode() : encode_multi_header());\n}\n\nstd::string t_hdr_reason::encode_multi_header(void) const {\n\tstd::string s;\n\n\tif (!populated) return s;\n\n\tfor (const auto &r : reason_list) {\n\t\ts += header_name;\n\t\ts += \": \";\n\t\ts += r.encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstd::string t_hdr_reason::encode_value(void) const {\n\tif (!populated) return {};\n\n\tstd::vector<std::string> encoded_values;\n\n\tfor (const auto &r : reason_list) {\n\t\tencoded_values.push_back(r.encode());\n\t}\n\n\treturn join_strings(encoded_values, \",\");\n}\n"
  },
  {
    "path": "src/parser/hdr_reason.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    Copyright (C) 2022       Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Reason header (RFC 3326)\n\n#ifndef _HDR_REASON_H\n#define _HDR_REASON_H\n\n#include <list>\n#include <string>\n\n#include \"header.h\"\n#include \"parameter.h\"\n\n\nclass t_reason {\npublic:\n\tstd::string       \tprotocol;\t// \"SIP\", \"Q.850\" or other\n\tstd::string       \tcause;\t\t// For SIP, status code\n\tstd::string       \ttext;\t\t// Reason text\n\tstd::list<t_parameter>\textensions;\n\n\tt_reason(const std::string &_protocol);\n\n\tvoid set_cause(const std::string &_cause);\n\tvoid set_text(const std::string &_text);\n\tvoid add_extension(const t_parameter &p);\n\n\tstring encode(void) const;\n};\n\nclass t_hdr_reason : public t_header {\npublic:\n\tstd::list<t_reason> reason_list;\n\n\tt_hdr_reason();\n\n\tvoid add_reason(const t_reason &reason);\n\n\t// Returns a (possibly empty) string that can be displayed as an\n\t// explanation to the user\n\tstd::string get_display_text() const;\n\n\tstring encode(void) const;\n\tstring encode_multi_header(void) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_record_route.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_record_route.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_record_route::t_hdr_record_route() : t_header(\"Record-Route\") {}\n\nvoid t_hdr_record_route::add_route(const t_route &r) {\n\tpopulated = true;\n\troute_list.push_back(r);\n}\n\nstring t_hdr_record_route::encode(void) const {\n\treturn (t_parser::multi_values_as_list ? \n\t\t\tt_header::encode() : encode_multi_header());\n}\n\nstring t_hdr_record_route::encode_multi_header(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_route>::const_iterator i = route_list.begin();\n\t     i != route_list.end(); i++)\n\t{\n\t\ts += header_name;\n\t\ts += \": \";\n\t\ts += i->encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstring t_hdr_record_route::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_route>::const_iterator i = route_list.begin();\n\t     i != route_list.end(); i++)\n\t{\n\t\tif (i != route_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_record_route.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Record-Route header\n\n#ifndef _H_HDR_RECORD_ROUTE\n#define _H_HDR_RECORD_ROUTE\n\n#include <list>\n#include <string>\n#include \"route.h\"\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_record_route : public t_header {\npublic:\n\tlist<t_route>\troute_list;\n\n\tt_hdr_record_route();\n\tvoid add_route(const t_route &r);\n\tstring encode(void) const;\n\tstring encode_multi_header(void) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_refer_sub.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_refer_sub.h\"\n\nt_hdr_refer_sub::t_hdr_refer_sub() : t_header(\"Refer-Sub\"),\n\tcreate_refer_sub(true)\n{}\n\nvoid t_hdr_refer_sub::set_create_refer_sub(bool on) {\n\tpopulated = true;\n\tcreate_refer_sub = on;\n}\n\nvoid t_hdr_refer_sub::add_extension(const t_parameter &p) {\n\tpopulated = true;\n\textensions.push_back(p);\n}\n\nvoid t_hdr_refer_sub::set_extensions(const list<t_parameter> &l) {\n\tpopulated = true;\n\textensions = l;\n}\n\nstring t_hdr_refer_sub::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\t\n\ts = (create_refer_sub ? \"true\" : \"false\");\n\ts += param_list2str(extensions);\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_refer_sub.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Refer-Sub header\n// RFC 4488\n\n#ifndef _H_HDR_REFER_SUB\n#define _H_HDR_REFER_SUB\n\n#include <list>\n#include <string>\n\n#include \"header.h\"\n#include \"parameter.h\"\n\nusing namespace std;\n\nclass t_hdr_refer_sub : public t_header {\npublic:\n\tbool\t\t\tcreate_refer_sub;\n\tlist<t_parameter>\textensions;\n\t\n\tt_hdr_refer_sub();\n\tvoid set_create_refer_sub(bool on);\n\tvoid add_extension(const t_parameter &p);\n\tvoid set_extensions(const list<t_parameter> &l);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_refer_to.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_refer_to.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_refer_to::t_hdr_refer_to() : t_header(\"Refer-To\", \"r\") {}\n\nvoid t_hdr_refer_to::set_display(const string &d) {\n\tpopulated = true;\n\tdisplay = d;\n}\n\nvoid t_hdr_refer_to::set_uri(const string &u) {\n\tpopulated = true;\n\turi.set_url(u);\n}\n\nvoid t_hdr_refer_to::set_uri(const t_url &u) {\n\tpopulated = true;\n\turi = u;\n}\n\nvoid t_hdr_refer_to::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nvoid t_hdr_refer_to::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_refer_to::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\ts += param_list2str(params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_refer_to.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3515\n// Refer-To header\n\n#ifndef _H_HDR_REFER_TO\n#define _H_HDR_REFER_TO\n\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_refer_to : public t_header {\npublic:\n\tstring\t\t\tdisplay; // display name\n\tt_url\t\t\turi;\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_refer_to();\n\tvoid set_display(const string &d);\n\tvoid set_uri(const string &u);\n\tvoid set_uri(const t_url &u);\n\tvoid set_params(const list<t_parameter> &l);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_referred_by.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_referred_by.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_referred_by::t_hdr_referred_by() : t_header(\"Referred-By\", \"b\") {}\n\nvoid t_hdr_referred_by::set_display(const string &d) {\n\tpopulated = true;\n\tdisplay = d;\n}\n\nvoid t_hdr_referred_by::set_uri(const string &u) {\n\tpopulated = true;\n\turi.set_url(u);\n}\n\nvoid t_hdr_referred_by::set_uri(const t_url &u) {\n\tpopulated = true;\n\turi = u;\n}\n\nvoid t_hdr_referred_by::set_cid(const string &c) {\n\tpopulated = true;\n\tcid = c;\n}\n\nvoid t_hdr_referred_by::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nvoid t_hdr_referred_by::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_referred_by::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\tif (cid.size() > 0) {\n\t\ts += \";cid=\";\n\t\ts += cid;\n\t}\n\n\ts += param_list2str(params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_referred_by.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3892\n// Referred-By header\n\n#ifndef _H_HDR_REFERRED_BY\n#define _H_HDR_REFERRED_BY\n\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_referred_by : public t_header {\npublic:\n\tstring\t\t\tdisplay; // display name\n\tt_url\t\t\turi;\n\tstring\t\t\tcid;\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_referred_by();\n\tvoid set_display(const string &d);\n\tvoid set_uri(const string &u);\n\tvoid set_uri(const t_url &u);\n\tvoid set_cid(const string &c);\n\tvoid set_params(const list<t_parameter> &l);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_replaces.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_replaces.h\"\n\nt_hdr_replaces::t_hdr_replaces() : t_header(\"Replaces\"),\n\tearly_only(false)\n{}\n\nvoid t_hdr_replaces::set_call_id(const string &id) {\n\tpopulated = true;\n\tcall_id = id;\n}\n\nvoid t_hdr_replaces::set_to_tag(const string &tag) {\n\tpopulated = true;\n\tto_tag = tag;\n}\n\nvoid t_hdr_replaces::set_from_tag(const string &tag) {\n\tpopulated = true;\n\tfrom_tag = tag;\n}\n\nvoid t_hdr_replaces::set_early_only(const bool on) {\n\tpopulated = true;\n\tearly_only = on;\n}\n\nvoid t_hdr_replaces::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nvoid t_hdr_replaces::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_replaces::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\t\n\ts += call_id;\n\ts += \";to-tag=\";\n\ts += to_tag;\n\ts += \";from-tag=\";\n\ts += from_tag;\n\t\n\tif (early_only) {\n\t\ts += \";early-only\";\n\t}\n\t\n\ts += param_list2str(params);\n\t\n\treturn s;\n}\n\nbool t_hdr_replaces::is_valid(void) const {\n\treturn !(call_id.empty() || to_tag.empty() || from_tag.empty());\n}\n"
  },
  {
    "path": "src/parser/hdr_replaces.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Replaces header\n// RFC 3891\n\n#ifndef _H_HDR_REPLACES\n#define _H_HDR_REPLACES\n\n#include <string>\n#include <list>\n#include \"header.h\"\n#include \"parameter.h\"\n\nusing namespace std;\n\nclass t_hdr_replaces : public t_header {\npublic:\n\tstring\t\t\tcall_id;\n\tstring\t\t\tto_tag;\n\tstring\t\t\tfrom_tag;\n\tbool\t\t\tearly_only;\n\tlist<t_parameter>\tparams;\n\t\n\tt_hdr_replaces();\n\tvoid set_call_id(const string &id);\n\tvoid set_to_tag(const string &tag);\n\tvoid set_from_tag(const string &tag);\n\tvoid set_early_only(const bool on);\n\tvoid set_params(const list<t_parameter> &l);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n\tbool is_valid(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_reply_to.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_reply_to.h\"\n#include \"definitions.h\"\n\nt_hdr_reply_to::t_hdr_reply_to() : t_header(\"Reply-To\") {}\n\nvoid t_hdr_reply_to::set_display(const string &d) {\n\tpopulated = true;\n\tdisplay = d;\n}\n\nvoid t_hdr_reply_to::set_uri(const string &u) {\n\tpopulated = true;\n\turi.set_url(u);\n}\n\nvoid t_hdr_reply_to::set_uri(const t_url &u) {\n\tpopulated = true;\n\turi = u;\n}\n\nvoid t_hdr_reply_to::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nvoid t_hdr_reply_to::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_reply_to::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += display;\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\ts += param_list2str(params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_reply_to.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Reply-To header\n\n#ifndef _H_HDR_REPLY_TO\n#define _H_HDR_REPLY_TO\n\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_reply_to : public t_header {\npublic:\n\tstring\t\t\tdisplay; // display name\n\tt_url\t\t\turi;\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_reply_to();\n\tvoid set_display(const string &d);\n\tvoid set_uri(const string &u);\n\tvoid set_uri(const t_url &u);\n\tvoid set_params(const list<t_parameter> &l);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_request_disposition.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_request_disposition.h\"\n\n#include <vector>\n#include \"util.h\"\n\nt_hdr_request_disposition::t_hdr_request_disposition() : \n\tt_header(\"Request-Disposition\", \"d\"),\n\tproxy_directive(PROXY_NULL),\n\tcancel_directive(CANCEL_NULL),\n\tfork_directive(FORK_NULL),\n\trecurse_directive(RECURSE_NULL),\n\tparallel_directive(PARALLEL_NULL),\n\tqueue_directive(QUEUE_NULL)\n{}\n\nvoid t_hdr_request_disposition::set_proxy_directive(t_proxy_directive directive) {\n\tpopulated = true;\n\tproxy_directive = directive;\n}\n\nvoid t_hdr_request_disposition::set_cancel_directive(t_cancel_directive directive) {\n\tpopulated = true;\n\tcancel_directive = directive;\n}\n\nvoid t_hdr_request_disposition::set_fork_directive(t_fork_directive directive) {\n\tpopulated = true;\n\tfork_directive = directive;\n}\n\nvoid t_hdr_request_disposition::set_recurse_directive(t_recurse_directive directive) {\n\tpopulated = true;\n\trecurse_directive = directive;\n}\n\nvoid t_hdr_request_disposition::set_parallel_directive(t_parallel_directive directive) {\n\tpopulated = true;\n\tparallel_directive = directive;\n}\n\nvoid t_hdr_request_disposition::set_queue_directive(t_queue_directive directive) {\n\tpopulated = true;\n\tqueue_directive = directive;\n}\n\nbool t_hdr_request_disposition::set_directive(const string &s) {\n\tif (s == REQDIS_PROXY) {\n\t\tif (proxy_directive == REDIRECT) return false;\n\t\tset_proxy_directive(PROXY);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_REDIRECT) {\n\t\tif (proxy_directive == PROXY) return false;\n\t\tset_proxy_directive(REDIRECT);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_CANCEL) {\n\t\tif (cancel_directive == NO_CANCEL) return false;\n\t\tset_cancel_directive(CANCEL);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_NO_CANCEL) {\n\t\tif (cancel_directive == CANCEL) return false;\n\t\tset_cancel_directive(NO_CANCEL);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_FORK) {\n\t\tif (fork_directive == NO_FORK) return false;\n\t\tset_fork_directive(FORK);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_NO_FORK) {\n\t\tif (fork_directive == FORK) return false;\n\t\tset_fork_directive(NO_FORK);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_RECURSE) {\n\t\tif (recurse_directive == NO_RECURSE) return false;\n\t\tset_recurse_directive(RECURSE);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_NO_RECURSE) {\n\t\tif (recurse_directive == RECURSE) return false;\n\t\tset_recurse_directive(NO_RECURSE);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_PARALLEL) {\n\t\tif (parallel_directive == SEQUENTIAL) return false;\n\t\tset_parallel_directive(PARALLEL);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_SEQUENTIAL) {\n\t\tif (parallel_directive == PARALLEL) return false;\n\t\tset_parallel_directive(SEQUENTIAL);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_QUEUE) {\n\t\tif (queue_directive == NO_QUEUE) return false;\n\t\tset_queue_directive(QUEUE);\n\t\treturn true;\n\t}\n\t\n\tif (s == REQDIS_NO_QUEUE) {\n\t\tif (queue_directive == QUEUE) return false;\n\t\tset_queue_directive(NO_QUEUE);\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nstring t_hdr_request_disposition::encode_value(void) const {\n\tif (!populated) return \"\";\n\t\n\tvector<string> v;\n\t\n\tswitch (proxy_directive) {\n\tcase PROXY:\n\t\tv.push_back(REQDIS_PROXY);\n\t\tbreak;\n\tcase REDIRECT:\n\t\tv.push_back(REQDIS_REDIRECT);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tswitch (cancel_directive) {\n\tcase CANCEL:\n\t\tv.push_back(REQDIS_CANCEL);\n\t\tbreak;\n\tcase NO_CANCEL:\n\t\tv.push_back(REQDIS_NO_CANCEL);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tswitch (fork_directive) {\n\tcase FORK:\n\t\tv.push_back(REQDIS_FORK);\n\t\tbreak;\n\tcase NO_FORK:\n\t\tv.push_back(REQDIS_NO_FORK);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tswitch (recurse_directive) {\n\tcase RECURSE:\n\t\tv.push_back(REQDIS_RECURSE);\n\t\tbreak;\n\tcase NO_RECURSE:\n\t\tv.push_back(REQDIS_NO_RECURSE);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tswitch (parallel_directive) {\n\tcase PARALLEL:\n\t\tv.push_back(REQDIS_PARALLEL);\n\t\tbreak;\n\tcase SEQUENTIAL:\n\t\tv.push_back(REQDIS_SEQUENTIAL);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tswitch (queue_directive) {\n\tcase QUEUE:\n\t\tv.push_back(REQDIS_QUEUE);\n\t\tbreak;\n\tcase NO_QUEUE:\n\t\tv.push_back(REQDIS_NO_QUEUE);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tstring s = join_strings(v, \",\");\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_request_disposition.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Request-Disposition header (RFC 3841)\n */\n \n#ifndef _H_HDR_REQUEST_DISPOSITION\n#define _H_HDR_REQUEST_DISPOSITION\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\n#define REQDIS_PROXY\t\t\"proxy\"\n#define REQDIS_REDIRECT\t\t\"redirect\"\n#define REQDIS_CANCEL\t\t\"cancel\"\n#define REQDIS_NO_CANCEL\t\"no-cancel\"\n#define REQDIS_FORK\t\t\"fork\"\n#define REQDIS_NO_FORK\t\t\"no-fork\"\n#define REQDIS_RECURSE\t\t\"recurse\"\n#define REQDIS_NO_RECURSE\t\"no-recurse\"\n#define REQDIS_PARALLEL\t\t\"parallel\"\n#define REQDIS_SEQUENTIAL\t\"sequential\"\n#define REQDIS_QUEUE\t\t\"queue\"\n#define REQDIS_NO_QUEUE\t\t\"no-queue\"\n\n/** Request-Disposition header (RFC 3841) */\nclass t_hdr_request_disposition : public t_header {\npublic:\n\tenum t_proxy_directive {\n\t\tPROXY_NULL,\n\t\tPROXY,\n\t\tREDIRECT\n\t};\n\t\n\tenum t_cancel_directive {\n\t\tCANCEL_NULL,\n\t\tCANCEL,\n\t\tNO_CANCEL\n\t};\n\t\n\tenum t_fork_directive {\n\t\tFORK_NULL,\n\t\tFORK,\n\t\tNO_FORK\n\t};\n\t\n\tenum t_recurse_directive {\n\t\tRECURSE_NULL,\n\t\tRECURSE,\n\t\tNO_RECURSE\n\t};\n\t\n\tenum t_parallel_directive {\n\t\tPARALLEL_NULL,\n\t\tPARALLEL,\n\t\tSEQUENTIAL\n\t};\n\t\n\tenum t_queue_directive {\n\t\tQUEUE_NULL,\n\t\tQUEUE,\n\t\tNO_QUEUE\n\t};\n\t\n\tt_proxy_directive proxy_directive;\n\tt_cancel_directive cancel_directive;\n\tt_fork_directive fork_directive;\n\tt_recurse_directive recurse_directive;\n\tt_parallel_directive parallel_directive;\n\tt_queue_directive queue_directive;\n\t\n\tt_hdr_request_disposition();\n\t\n\tvoid set_proxy_directive(t_proxy_directive directive);\n\tvoid set_cancel_directive(t_cancel_directive directive);\n\tvoid set_fork_directive(t_fork_directive directive);\n\tvoid set_recurse_directive(t_recurse_directive directive);\n\tvoid set_parallel_directive(t_parallel_directive directive);\n\tvoid set_queue_directive(t_queue_directive directive);\n\t\n\t/**\n\t * Set a directive using one of the tokens define in RFC 3841\n\t * @param s [in] Directive token.\n\t * @return True if directive set. False if directive is invalid or\n\t *         conflicts with exisiting directives.\n\t */\n\tbool set_directive(const string &s);\n\t\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_require.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_require.h\"\n\nt_hdr_require::t_hdr_require() : t_header(\"Require\") {};\n\nvoid t_hdr_require::add_feature(const string &f) {\n\tpopulated = true;\n\tif (!contains(f)) {\n\t\tfeatures.push_back(f);\n\t}\n}\n\nvoid t_hdr_require::add_features(const list<string> &l) {\n\tif (l.empty()) return;\n\t\n\tfor (list<string>::const_iterator i = l.begin(); i != l.end(); i++)\n\t{\n\t\tadd_feature(*i);\n\t}\n\tpopulated = true;\n}\n\nvoid t_hdr_require::del_feature(const string &f) {\n\tfeatures.remove(f);\n}\n\nbool t_hdr_require::contains(const string &f) const {\n\tif (!populated) return false;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (*i == f) return true;\n\t}\n\n\treturn false;\n}\n\nstring t_hdr_require::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (i != features.begin()) s += \", \";\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n\nvoid t_hdr_require::unpopulate(void) {\n\tpopulated = false;\n\tfeatures.clear();\n}\n"
  },
  {
    "path": "src/parser/hdr_require.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Require header\n\n#ifndef _H_HDR_REQUIRE\n#define _H_HDR_REQUIRE\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\nclass t_hdr_require : public t_header {\npublic:\n\tlist<string>\tfeatures;\n\n\tt_hdr_require();\n\tvoid add_feature(const string &f);\n\tvoid add_features(const list<string> &l);\n\tvoid del_feature(const string &f);\n\tbool contains(const string &f) const;\n\tstring encode_value(void) const;\n\tvoid unpopulate(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_retry_after.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_retry_after.h\"\n#include \"util.h\"\n\nt_hdr_retry_after::t_hdr_retry_after() : t_header(\"Retry-After\") {\n\ttime = 0;\n\tduration = 0;\n}\n\nvoid t_hdr_retry_after::set_time(unsigned long t) {\n\tpopulated = true;\n\ttime = t;\n}\n\nvoid t_hdr_retry_after::set_comment(const string &c) {\n\tpopulated = true;\n\tcomment = c;\n}\n\nvoid t_hdr_retry_after::set_duration(unsigned long d) {\n\tpopulated = true;\n\tduration = d;\n}\n\nvoid t_hdr_retry_after::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_retry_after::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = ulong2str(time);\n\n\tif (comment.size() > 0) {\n\t\ts += \" (\";\n\t\ts += comment;\n\t\ts += ')';\n\t}\n\n\tif (duration > 0) {\n\t\ts += \";duration=\";\n\t\ts += ulong2str(duration);\n\t}\n\n\ts += param_list2str(params);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_retry_after.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Retry-After header\n\n#ifndef _H_HDR_RETRY_AFTER\n#define _H_HDR_RETRY_AFTER\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n\nusing namespace std;\n\nclass t_hdr_retry_after : public t_header {\npublic:\n\tunsigned long\t\ttime; // in seconds\n\tstring\t\t\tcomment;\n\tunsigned long\t\tduration; // in seconds\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_retry_after();\n\tvoid set_time(unsigned long t);\n\tvoid set_comment(const string &c);\n\tvoid set_duration(unsigned long d);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_route.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_route.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_route::t_hdr_route() : t_header(\"Route\") {\n\troute_to_first_route = false;\n}\n\nvoid t_hdr_route::add_route(const t_route &r) {\n\tpopulated = true;\n\troute_list.push_back(r);\n}\n\nstring t_hdr_route::encode(void) const {\n\treturn (t_parser::multi_values_as_list ? \n\t\t\tt_header::encode() : encode_multi_header());\n}\n\nstring t_hdr_route::encode_multi_header(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_route>::const_iterator i = route_list.begin();\n\t     i != route_list.end(); i++)\n\t{\n\t\ts += header_name;\n\t\ts += \": \";\n\t\ts += i->encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstring t_hdr_route::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_route>::const_iterator i = route_list.begin();\n\t     i != route_list.end(); i++)\n\t{\n\t\tif (i != route_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_route.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Route header\n\n#ifndef _H_HDR_ROUTE\n#define _H_HDR_ROUTE\n\n#include <list>\n#include <string>\n#include \"route.h\"\n#include \"header.h\"\n#include \"parameter.h\"\n\nusing namespace std;\n\nclass t_hdr_route : public t_header {\npublic:\n\tlist<t_route>\troute_list;\n\n\t// If route_to_first_route == true, then the request must be routed\n\t// to the first route in the list. Otherwise to the request URI.\n\tbool\t\troute_to_first_route;\n\n\tt_hdr_route();\n\tvoid add_route(const t_route &r);\n\tstring encode(void) const;\n\tstring encode_multi_header(void) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_rseq.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_rseq.h\"\n#include \"util.h\"\n\nt_hdr_rseq::t_hdr_rseq() : t_header(\"RSeq\") {\n\tresp_nr = 0;\n}\n\nvoid t_hdr_rseq::set_resp_nr(unsigned long l) {\n\tpopulated = true;\n\tresp_nr = l;\n}\n\nstring t_hdr_rseq::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn ulong2str(resp_nr);\n}\n\nbool t_hdr_rseq::operator==(const t_hdr_rseq &h) const {\n\treturn (resp_nr == h.resp_nr);\n}\n"
  },
  {
    "path": "src/parser/hdr_rseq.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RSeq header\n// RFC 3262\n\n#ifndef _HDR_RSEQ\n#define _HDR_RSEQ\n\n#include <string>\n#include \"header.h\"\n#include \"definitions.h\"\n\nusing namespace std;\n\nclass t_hdr_rseq : public t_header {\npublic:\n\tunsigned long \t\tresp_nr;\n\n\tt_hdr_rseq();\n\tvoid set_resp_nr(unsigned long l);\n\n\tstring encode_value(void) const;\n\n\tbool operator==(const t_hdr_rseq &h) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_server.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_server.h\"\n#include \"util.h\"\n\nt_server::t_server() {}\n\nt_server::t_server(const string &_product, const string &_version,\n\t\t const string &_comment)\n{\n\tproduct = _product;\n\tversion = _version;\n\tcomment = _comment;\n}\n\nstring t_server::encode(void) const {\n\tstring s;\n\n\ts = product;\n\n\tif (version.size() > 0) {\n\t\ts += '/';\n\t\ts += version;\n\t}\n\n\tif (comment.size() > 0) {\n\t\tif (s.size() > 0) s += ' ';\n\t\ts += \"(\";\n\t\ts += comment;\n\t\ts += ')';\n\t}\n\n\treturn s;\n}\n\nt_hdr_server::t_hdr_server() : t_header(\"Server\") {};\n\nvoid t_hdr_server::add_server(const t_server &s) {\n\tpopulated = true;\n\tserver_info.push_back(s);\n}\n\nstring t_hdr_server::get_server_info(void) const {\n\tstring s;\n\t\n\tfor (list<t_server>::const_iterator i = server_info.begin();\n\t     i != server_info.end(); i++ )\n\t{\n\t\tif (i != server_info.begin()) s += ' ';\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\n}\n\nstring t_hdr_server::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn get_server_info();\n}\n"
  },
  {
    "path": "src/parser/hdr_server.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Server header\n\n#ifndef _H_HDR_SERVER\n#define _H_HDR_SERVER\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_server {\npublic:\n\tstring\tproduct;\n\tstring\tversion;\n\tstring\tcomment;\n\n\tt_server();\n\tt_server(const string &_product, const string &_version,\n\t\t const string &_comment = \"\");\n\tstring encode(void) const;\n};\n\nclass t_hdr_server : public t_header {\npublic:\n\tlist<t_server>\tserver_info;\n\n\tt_hdr_server();\n\tvoid add_server(const t_server &s);\n\t\n\t// Get a string representation of server_info\n\tstring get_server_info(void) const;\n\t\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_service_route.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_service_route.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_service_route::t_hdr_service_route() : t_header(\"Service-Route\") {}\n\nvoid t_hdr_service_route::add_route(const t_route &r) {\n\tpopulated = true;\n\troute_list.push_back(r);\n}\n\nstring t_hdr_service_route::encode(void) const {\n\treturn (t_parser::multi_values_as_list ? \n\t\t\tt_header::encode() : encode_multi_header());\n}\n\nstring t_hdr_service_route::encode_multi_header(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_route>::const_iterator i = route_list.begin();\n\t     i != route_list.end(); i++)\n\t{\n\t\ts += header_name;\n\t\ts += \": \";\n\t\ts += i->encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstring t_hdr_service_route::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_route>::const_iterator i = route_list.begin();\n\t     i != route_list.end(); i++)\n\t{\n\t\tif (i != route_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_service_route.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Service-Route header\n\n#ifndef _H_HDR_SERVICE_ROUTE\n#define _H_HDR_SERVICE_ROUTE\n\n#include <list>\n#include <string>\n#include \"route.h\"\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_service_route : public t_header {\npublic:\n\tlist<t_route>\troute_list;\n\n\tt_hdr_service_route();\n\tvoid add_route(const t_route &r);\n\tstring encode(void) const;\n\tstring encode_multi_header(void) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_session_expires.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    Copyright (C) 2022       Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_session_expires.h\"\n#include \"util.h\"\n\nt_hdr_session_expires::t_hdr_session_expires() :\n\tt_header(\"Session-Expires\", \"x\"),\n\ttime(0),\n\trefresher(REFRESHER_NONE)\n{\n}\n\nvoid t_hdr_session_expires::set_time(unsigned long t) {\n\tpopulated = true;\n\ttime = t;\n}\n\nvoid t_hdr_session_expires::set_refresher(t_refresher r) {\n\trefresher = r;\n}\n\nbool t_hdr_session_expires::set_refresher(const std::string &r) {\n\tif (r == SE_REFRESHER_UAS) {\n\t\tif (refresher == REFRESHER_UAC) return false;\n\t\tset_refresher(REFRESHER_UAS);\n\t\treturn true;\n\t}\n\n\tif (r == SE_REFRESHER_UAC) {\n\t\tif (refresher == REFRESHER_UAS) return false;\n\t\tset_refresher(REFRESHER_UAC);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid t_hdr_session_expires::add_param(const t_parameter &p) {\n\tparams.push_back(p);\n}\n\nvoid t_hdr_session_expires::set_params(const std::list<t_parameter> &l) {\n\tparams = l;\n}\n\nstring t_hdr_session_expires::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\tstring s;\n\n\tstd::list<t_parameter> p = params;\n\n\tstd::string refresher_str;\n\tswitch (refresher) {\n\t\tcase REFRESHER_UAS:\n\t\t\trefresher_str = SE_REFRESHER_UAS;\n\t\t\tbreak;\n\t\tcase REFRESHER_UAC:\n\t\t\trefresher_str = SE_REFRESHER_UAC;\n\t\t\tbreak;\n\t}\n\tif (!refresher_str.empty()) {\n\t\tt_parameter r(\"refresher\", refresher_str);\n\t\tp.push_front(r);\n\t}\n\n\ts += ulong2str(time);\n\ts += param_list2str(p);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_session_expires.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n    Copyright (C) 2022       Frédéric Brière <fbriere@fbriere.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Session-Expires header (RFC 4028)\n\n#ifndef _HDR_SESSION_EXPIRES_H\n#define _HDR_SESSION_EXPIRES_H\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n\n#define SE_REFRESHER_UAS\t\"uas\"\n#define SE_REFRESHER_UAC\t\"uac\"\n\nclass t_hdr_session_expires : public t_header {\npublic:\n\tenum t_refresher {\n\t\tREFRESHER_NONE,\n\t\tREFRESHER_UAS,\n\t\tREFRESHER_UAC\n\t};\n\n\tunsigned long time; // expiry time in seconds\n\tt_refresher refresher;\n\tlist<t_parameter> params;\n\n\tt_hdr_session_expires();\n\n\tvoid set_time(unsigned long t);\n\tvoid set_refresher(t_refresher r);\n\tbool set_refresher(const std::string &r);\n\n\tvoid add_param(const t_parameter &p);\n\tvoid set_params(const std::list<t_parameter> &l);\n\n\tstd::string encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_sip_etag.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_sip_etag.h\"\n\nt_hdr_sip_etag::t_hdr_sip_etag() : t_header(\"SIP-ETag\") {};\n\nvoid t_hdr_sip_etag::set_etag(const string &_etag) {\n\tpopulated = true;\n\tetag = _etag;\n}\n\nstring t_hdr_sip_etag::encode_value(void) const {\n\tif (!populated) return \"\";\n\treturn etag;\n}\n"
  },
  {
    "path": "src/parser/hdr_sip_etag.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * SIP-ETag header (RFC 3903)\n */\n \n#ifndef _HDR_SIP_ETAG_H\n#define _HDR_SIP_ETAG_H\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\n/** SIP-ETag header (RFC 3903) */\nclass t_hdr_sip_etag : public t_header {\npublic:\n\tstring\tetag;\t/**< Entity tag. */\n\t\n\t/** Constructor. */\n\tt_hdr_sip_etag();\n\t\n\t/**\n\t * Set entity tag.\n\t * @param _etag [in] Entity tag to set.\n\t */\n\tvoid set_etag(const string &_etag);\n\t\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_sip_if_match.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_sip_if_match.h\"\n\nt_hdr_sip_if_match::t_hdr_sip_if_match() : t_header(\"SIP-If-Match\") {};\n\nvoid t_hdr_sip_if_match::set_etag(const string &_etag) {\n\tpopulated = true;\n\tetag = _etag;\n}\n\nstring t_hdr_sip_if_match::encode_value(void) const {\n\tif (!populated) return \"\";\n\treturn etag;\n}\n\nvoid t_hdr_sip_if_match::clear(void) {\n\tetag.clear();\n\tpopulated = false;\n}\n"
  },
  {
    "path": "src/parser/hdr_sip_if_match.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * SIP-If-Match header (RFC 3903)\n */\n \n#ifndef _HDR_SIP_IF_MATCH_H\n#define _HDR_SIP_IF_MATCH_H\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\n/** SIP-If-Match header (RFC 3903) */\nclass t_hdr_sip_if_match : public t_header {\npublic:\n\tstring\tetag;\t/**< Entity tag. */\n\t\n\t/** Constructor. */\n\tt_hdr_sip_if_match();\n\t\n\t/**\n\t * Set entity tag.\n\t * @param _etag [in] Entity tag to set.\n\t */\n\tvoid set_etag(const string &_etag);\n\t\n\t/** Clear the header. */\n\tvoid clear(void);\n\t\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_subject.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_subject.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_subject::t_hdr_subject() : t_header(\"Subject\", \"s\") {};\n\nvoid t_hdr_subject::set_subject(const string &s) {\n\tpopulated = true;\n\tsubject = s;\n}\n\nstring t_hdr_subject::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn subject;\n}\n"
  },
  {
    "path": "src/parser/hdr_subject.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Subject header\n#ifndef _H_HDR_SUBJECT\n#define _H_HDR_SUBJECT\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_subject : public t_header {\npublic:\n\tstring\tsubject;\n\n\tt_hdr_subject();\n\tvoid set_subject(const string &s);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_subscription_state.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_subscription_state.h\"\n#include \"util.h\"\n\nt_hdr_subscription_state::t_hdr_subscription_state() : t_header(\"Subscription-State\") {\n\texpires = 0;\n\tretry_after = 0;\n}\n\nvoid t_hdr_subscription_state::set_substate(const string &s) {\n\tpopulated = true;\n\tsubstate = s;\n}\n\nvoid t_hdr_subscription_state::set_reason(const string &s) {\n\tpopulated = true;\n\treason = s;\n}\n\nvoid t_hdr_subscription_state::set_expires(unsigned long e) {\n\tpopulated = true;\n\texpires = e;\n}\n\nvoid t_hdr_subscription_state::set_retry_after(unsigned long r) {\n\tpopulated = true;\n\tretry_after = r;\n}\n\nvoid t_hdr_subscription_state::add_extension(const t_parameter &p) {\n\tpopulated = true;\n\textensions.push_back(p);\n}\n\nstring t_hdr_subscription_state::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = substate;\n\n\tif (reason.size() > 0) {\n\t\ts += \";reason=\";\n\t\ts += reason;\n\t}\n\n\tif (expires > 0) {\n\t\ts += \";expires=\";\n\t\ts += ulong2str(expires);\n\t}\n\n\tif (retry_after > 0) {\n\t\ts += \";retry-after=\";\n\t\ts += ulong2str(retry_after);\n\t}\n\n\ts += param_list2str(extensions);\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_subscription_state.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Subscription-State header\n// RFC 3265\n\n#ifndef _HDR_SUBSCRIPTION_STATE\n#define _HDR_SUBSCRIPTION_STATE\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n\n// Subscription states\n#define SUBSTATE_ACTIVE\t\t\"active\"\n#define SUBSTATE_PENDING\t\"pending\"\n#define SUBSTATE_TERMINATED\t\"terminated\"\n\n// Event reasons\n#define EV_REASON_DEACTIVATED\t\"deactivated\"\n#define EV_REASON_PROBATION\t\"probation\"\n#define EV_REASON_REJECTED\t\"rejected\"\n#define EV_REASON_TIMEOUT\t\"timeout\"\n#define EV_REASON_GIVEUP\t\"giveup\"\n#define EV_REASON_NORESOURCE\t\"noresource\"\n\nusing namespace std;\n\nclass t_hdr_subscription_state : public t_header {\npublic:\n\tstring\t\t\tsubstate;\n\tstring\t\t\treason;\n\tunsigned long\t\texpires;\n\tunsigned long\t\tretry_after;\n\tlist<t_parameter>\textensions;\n\n\tt_hdr_subscription_state();\n\tvoid set_substate(const string &s);\n\tvoid set_reason(const string &s);\n\tvoid set_expires(unsigned long e);\n\tvoid set_retry_after(unsigned long r);\n\tvoid add_extension(const t_parameter &p);\n\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_supported.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_supported.h\"\n#include \"parse_ctrl.h\"\n\nt_hdr_supported::t_hdr_supported() : t_header(\"Supported\", \"k\") {};\n\nvoid t_hdr_supported::add_feature(const string &f) {\n\tpopulated = true;\n\tif (!contains(f)) {\n\t\tfeatures.push_back(f);\n\t}\n}\n\nvoid t_hdr_supported::add_features(const list<string> &l) {\n\tif (l.empty()) return;\n\t\n\tfor (list<string>::const_iterator i = l.begin(); i != l.end(); i++)\n\t{\n\t\tadd_feature(*i);\n\t}\n\tpopulated = true;\n}\n\nvoid t_hdr_supported::set_empty(void) {\n\tpopulated = true;\n\tfeatures.clear();\n}\n\nbool t_hdr_supported::contains(const string &f) const {\n\tif (!populated) return false;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (*i == f) return true;\n\t}\n\n\treturn false;\n}\n\nstring t_hdr_supported::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (i != features.begin()) s += \",\";\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_supported.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Supported header\n\n#ifndef _H_HDR_SUPPORTED\n#define _H_HDR_SUPPORTED\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\n#define EXT_100REL\t\"100rel\"\t// RFC 3262\n#define EXT_REPLACES\t\"replaces\"\t// RFC 3891\n#define EXT_TIMER\t\"timer\"\t\t// RFC 4028\n#define EXT_NOREFERSUB\t\"norefersub\"\t// RFC 4488\n\nclass t_hdr_supported : public t_header {\npublic:\n\tlist<string>\tfeatures;\n\n\tt_hdr_supported();\n\tvoid add_feature(const string &f);\n\tvoid add_features(const list<string> &l);\n\n\t// Clear the list of features, but make the header 'populated'.\n\t// An empty header will be in the message.\n\tvoid set_empty(void);\n\n\tbool contains(const string &f) const;\n\t\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_timestamp.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_timestamp.h\"\n#include \"util.h\"\n\nt_hdr_timestamp::t_hdr_timestamp() : t_header(\"Timestamp\") {\n\ttimestamp = 0;\n\tdelay = 0;\n}\n\nvoid t_hdr_timestamp::set_timestamp(float t) {\n\tpopulated = true;\n\ttimestamp = t;\n}\n\nvoid t_hdr_timestamp::set_delay(float d) {\n\tpopulated = true;\n\tdelay = d;\n}\n\nstring t_hdr_timestamp::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts += float2str(timestamp, 3);\n\n\tif (delay != 0) {\n\t\ts += \" \";\n\t\ts += float2str(delay, 3);\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_timestamp.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Timestamp header\n\n#ifndef _H_HDR_TIMESTAMP\n#define _H_HDR_TIMESTAMP\n\n#include <string>\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_timestamp : public t_header {\npublic:\n\tfloat\ttimestamp;\n\tfloat\tdelay;\n\n\tt_hdr_timestamp();\n\tvoid set_timestamp(float t);\n\tvoid set_delay(float d);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_to.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_to.h\"\n#include \"definitions.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nt_hdr_to::t_hdr_to() : t_header(\"To\", \"t\") {}\n\nvoid t_hdr_to::set_display(const string &d) {\n\tpopulated = true;\n\tdisplay = d;\n}\n\nvoid t_hdr_to::set_uri(const string &u) {\n\tpopulated = true;\n\turi.set_url(u);\n}\n\nvoid t_hdr_to::set_uri(const t_url &u) {\n\tpopulated = true;\n\turi = u;\n}\n\nvoid t_hdr_to::set_tag(const string &t) {\n\tpopulated = true;\n\ttag = t;\n}\n\nvoid t_hdr_to::set_params(const list<t_parameter> &l) {\n\tpopulated = true;\n\tparams = l;\n}\n\nvoid t_hdr_to::add_param(const t_parameter &p) {\n\tpopulated = true;\n\tparams.push_back(p);\n}\n\nstring t_hdr_to::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\tif (tag != \"\") {\n\t\ts += \";tag=\";\n\t\ts += tag;\n\t}\n\n\ts += param_list2str(params);\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_to.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// To header\n\n#ifndef _H_HDR_TO\n#define _H_HDR_TO\n\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_hdr_to : public t_header {\npublic:\n\tstring\t\t\tdisplay; // display name\n\tt_url\t\t\turi;\n\tstring\t\t\ttag;\n\tlist<t_parameter>\tparams;\n\n\tt_hdr_to();\n\tvoid set_display(const string &d);\n\tvoid set_uri(const string &u);\n\tvoid set_uri(const t_url &u);\n\tvoid set_tag(const string &t);\n\tvoid set_params(const list<t_parameter> &l);\n\tvoid add_param(const t_parameter &p);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_unsupported.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_unsupported.h\"\n\nt_hdr_unsupported::t_hdr_unsupported() : t_header(\"Unsupported\") {};\n\nvoid t_hdr_unsupported::add_feature(const string &f) {\n\tpopulated = true;\n\tfeatures.push_back(f);\n}\n\nvoid t_hdr_unsupported::set_features(const list<string> &_features) {\n\tpopulated = true;\n\tfeatures = _features;\n}\n\nbool t_hdr_unsupported::contains(const string &f) const {\n\tif (!populated) return false;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (*i == f) return true;\n\t}\n\n\treturn false;\n}\n\nstring t_hdr_unsupported::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<string>::const_iterator i = features.begin();\n\t     i != features.end(); i++)\n\t{\n\t\tif (i != features.begin()) s += \",\";\n\t\ts += *i;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_unsupported.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Unsupported header\n\n#ifndef _H_HDR_UNSUPPORTED\n#define _H_HDR_UNSUPPORTED\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\nclass t_hdr_unsupported : public t_header {\npublic:\n\tlist<string>\tfeatures;\n\n\tt_hdr_unsupported();\n\tvoid add_feature(const string &f);\n\tvoid set_features(const list<string> &_features);\n\tbool contains(const string &f) const;\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_user_agent.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_user_agent.h\"\n#include \"util.h\"\n\nt_hdr_user_agent::t_hdr_user_agent() : t_header(\"User-Agent\") {};\n\nvoid t_hdr_user_agent::add_server(const t_server &s) {\n\tpopulated = true;\n\tua_info.push_back(s);\n}\n\nstring t_hdr_user_agent::get_ua_info(void) const {\n\tstring s;\n\t\n\tfor (list<t_server>::const_iterator i = ua_info.begin();\n\t     i != ua_info.end(); i++ )\n\t{\n\t\tif (i != ua_info.begin()) s += ' ';\n\t\ts += i->encode();\n\t}\n\t\n\treturn s;\t\n}\n\nstring t_hdr_user_agent::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn get_ua_info();\n}\n"
  },
  {
    "path": "src/parser/hdr_user_agent.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// User-Agent header\n\n#ifndef _H_HDR_USER_AGENT\n#define _H_HDR_USER_AGENT\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"hdr_server.h\"\n\nusing namespace std;\n\nclass t_hdr_user_agent : public t_header {\npublic:\n\tlist<t_server>\tua_info;\n\n\tt_hdr_user_agent();\n\tvoid add_server(const t_server &s);\n\t\n\t// Get string representation of ua_info;\n\tstring get_ua_info(void) const;\n\t\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_via.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include \"definitions.h\"\n#include \"hdr_via.h\"\n#include \"util.h\"\n#include \"parse_ctrl.h\"\n#include \"protocol.h\"\n#include \"sockets/url.h\"\n\nt_via::t_via() {\n\tport = 0;\n\tttl = 0;\n\trport_present = false;\n\trport = 0;\n}\n\nt_via::t_via(const string &_host, const int _port, bool add_rport) {\n\tprotocol_name = \"SIP\";\n\tprotocol_version = SIP_VERSION;\n\ttransport = \"UDP\";\n\thost = _host;\n\tbranch = RFC3261_COOKIE + random_token(8);\n\n\tif (_port != get_default_port(\"sip\")) {\n\t\tport = _port;\n\t} else {\n\t\tport = 0;\n\t}\n\n\tttl = 0;\n\trport_present = add_rport;\n\trport = 0;\n}\n\nvoid t_via::add_extension(const t_parameter &p) {\n\textensions.push_back(p);\n}\n\nstring t_via::encode(void) const {\n\tstring s;\n\n\ts = protocol_name + '/' + protocol_version + '/' + transport;\n\ts += ' ';\n\ts += host;\n\n\tif (port > 0) {\n\t\ts += ':';\n\t\ts += int2str(port);\n\t}\n\n\tif (ttl > 0) s += int2str(ttl, \";ttl=%d\");\n\n\tif (maddr.size() > 0) {\n\t\ts += \";maddr=\";\n\t\ts += maddr;\n\t}\n\n\tif (received.size() > 0) {\n\t\ts += \";received=\";\n\t\ts += received;\n\t}\n\n\tif (rport_present) {\n\t\ts += \";rport\";\n\t\tif (rport > 0) {\n\t\t\ts += \"=\";\n\t\t\ts += int2str(rport);\n\t\t}\n\t}\n\n\tif (branch.size() > 0) {\n\t\ts += \";branch=\";\n\t\ts += branch;\n\t}\n\n\ts += param_list2str(extensions);\n\treturn s;\n}\n\nvoid t_via::get_response_dst(t_ip_port &ip_port) const {\n\tstring url_str(\"sip:\");\n\n\t// RFC 3261 18.2.2\n\t// Determine the address to send a repsonse to\n\t// NOTE: the received-parameter will be added by the listener if needed.\n\t\n\tif (tolower(transport) == \"tcp\") {\n\t\t// NOTE: The response must be sent over the connection on which\n\t\t// the request was received. The address returned here is an\n\t\t// alternative if that connection is closed already.\n\t\tif (received.size() > 0) {\n\t\t\turl_str += received;\n\t\t} else {\n\t\t\turl_str += host;\n\t\t}\n\t\t\t\n\t\turl_str += \":\";\n\t\t\n\t\t// NOTE: The rport parameter is not processed here as it only\n\t\t// applies to unreliable transports (RFC 3581 4)\n\t\turl_str += int2str(port);\n\t\t\n\t\tt_url u(url_str);\n\t\tlist<t_ip_port> ip_list = u.get_h_ip_srv(\"tcp\");\n\t\tip_port = ip_list.front();\n\t} else {\n\t\tif (maddr.size() > 0) {\n\t\t\turl_str += maddr;\n\t\t} else if (received.size() > 0) {\n\t\t\turl_str += received;\n\t\t} else {\n\t\t\turl_str += host;\n\t\t}\n\t\n\t\t// RFC 3581 4\n\t\tif (rport_present && rport > 0) {\n\t\t\t// NOTE: the rport value will be added by the UDP listener\n\t\t\t// if the rport parameter without value was present.\n\t\t\turl_str += ':';\n\t\t\turl_str += int2str(rport);\n\t\t} else if (port != 0) {\n\t\t\turl_str += ':';\n\t\t\turl_str += int2str(port);\n\t\t}\n\t\t\n\t\t// If there was no maddr parameter, then the URL will always point to\n\t\t// an IP address; either the host was an IP address or a received parameter\n\t\t// containing an IP address was added (see RFC 3261 18.2.1)\n\t\t// If there was an maddr, then the URL can be a domain that could have\n\t\t// multiple SRV records. RFC 3263 section 5 does not specify what to do in\n\t\t// this case. So just send the response to the first destination.\n\t\tt_url u(url_str);\n\t\tlist<t_ip_port> ip_list = u.get_h_ip_srv(\"udp\");\n\t\tip_port = ip_list.front();\n\t}\n}\n\nbool t_via::rfc3261_compliant(void) const {\n\treturn (branch.find(RFC3261_COOKIE) == 0);\n}\n\n\nt_hdr_via::t_hdr_via() : t_header(\"Via\", \"v\") {}\n\nvoid t_hdr_via::add_via(const t_via &v) {\n\tpopulated = true;\n\tvia_list.push_back(v);\n}\n\nstring t_hdr_via::encode(void) const {\n\treturn (t_parser::multi_values_as_list ? \n\t\t\tt_header::encode() : encode_multi_header());\n}\n\nstring t_hdr_via::encode_multi_header(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_via>::const_iterator i = via_list.begin();\n\t     i != via_list.end(); i++)\n\t{\n\t\ts += (t_parser::compact_headers ? compact_name : header_name);\n\t\ts += \": \";\n\t\ts += i->encode();\n\t\ts += CRLF;\n\t}\n\n\treturn s;\n}\n\nstring t_hdr_via::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_via>::const_iterator i = via_list.begin();\n\t     i != via_list.end(); i++)\n\t{\n\t\tif (i != via_list.begin()) s += \",\";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nvoid t_hdr_via::get_response_dst(t_ip_port &ip_port) const {\n\tif (!populated) {\n\t\tip_port.clear();\n\t\treturn;\n\t}\n\n\tvia_list.front().get_response_dst(ip_port);\n}\n"
  },
  {
    "path": "src/parser/hdr_via.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Via header\n\n#ifndef _H_HDR_VIA\n#define _H_HDR_VIA\n\n#include <list>\n#include <string>\n#include \"header.h\"\n#include \"parameter.h\"\n\nclass t_via {\npublic:\n\tstring\t\t\tprotocol_name;\n\tstring\t\t\tprotocol_version;\n\tstring\t\t\ttransport;\n\tstring\t\t\thost;\n\tint\t\t\tport;\n\tint\t\t\tttl;\n\tstring\t\t\tmaddr;\n\tstring\t\t\treceived;\n\tstring\t\t\tbranch;\n\n\t// RFC 3581: symetric response routing\n\tbool\t\t\trport_present;\n\tint\t\t\trport;\n\n\tlist <t_parameter>\textensions;\n\n\tt_via();\n\tt_via(const string &_host, const int _port, bool add_rport = true);\n\tvoid add_extension(const t_parameter &p);\n\tstring encode(void) const;\n\n\t// Get the response destination\n\tvoid get_response_dst(t_ip_port &ip_port) const;\n\n\t// Returns true if branch starts with RFC 3261 magic cookie\n\tbool rfc3261_compliant(void) const;\n};\n\nclass t_hdr_via : public t_header {\npublic:\n\tlist<t_via>\tvia_list;\n\n\tt_hdr_via();\n\tvoid add_via(const t_via &v);\n\tstring encode(void) const;\n\tstring encode_multi_header(void) const;\n\tstring encode_value(void) const;\n\n\t// Get the response destination\n\tvoid get_response_dst(t_ip_port &ip_port) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_warning.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"hdr_warning.h\"\n#include \"util.h\"\n\nt_warning::t_warning() {\n\tcode = 0;\n\tport = 0;\n}\n\nt_warning::t_warning(const string &_host, int _port, int _code, string _text) {\n\thost = _host;\n\tport = _port;\n\tcode = _code;\n\n\tswitch(code) {\n\tcase 300: text = WARNING_300; break;\n\tcase 301: text = WARNING_301; break;\n\tcase 302: text = WARNING_302; break;\n\tcase 303: text = WARNING_303; break;\n\tcase 304: text = WARNING_304; break;\n\tcase 305: text = WARNING_305; break;\n\tcase 306: text = WARNING_306; break;\n\tcase 307: text = WARNING_307; break;\n\tcase 330: text = WARNING_330; break;\n\tcase 331: text = WARNING_331; break;\n\tcase 370: text = WARNING_370; break;\n\tcase 399: text = WARNING_399; break;\n\tdefault: text = \"Warning\";\n\t}\n\n\tif (_text != \"\") {\n\t\ttext += \": \";\n\t\ttext += _text;\n\t}\n}\n\nstring t_warning::encode(void) const {\n\tstring s;\n\n\ts = int2str(code, \"%3d\");\n\ts += ' ';\n\ts += host;\n\tif (port > 0) s += int2str(port, \":%d\");\n\ts += ' ';\n\ts += '\"';\n\ts += text;\n\ts += '\"';\n\treturn s;\n}\n\nt_hdr_warning::t_hdr_warning() : t_header(\"Warning\") {}\n\nvoid t_hdr_warning::add_warning(const t_warning &w) {\n\tpopulated = true;\n\twarnings.push_back(w);\n}\n\nstring t_hdr_warning::encode_value(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\tfor (list<t_warning>::const_iterator i = warnings.begin();\n\t     i != warnings.end(); i++)\n\t{\n\t\tif (i != warnings.begin()) s += \", \";\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/hdr_warning.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Warning header\n\n#ifndef _H_HDR_WARNING\n#define _H_HDR_WARNING\n\n#include <list>\n#include <string>\n#include \"header.h\"\n\n// Warning codes\n#define W_300_INCOMPATIBLE_NWK_PROT\t300\n#define W_301_INCOMPATIBLE_ADDR_FORMAT\t301\n#define W_302_INCOMPATIBLE_TRANS_PROT\t302\n#define W_303_INCOMPATIBLE_BW_UNITS\t303\n#define W_304_MEDIA_TYPE_NOT_AVAILABLE\t304\n#define W_305_INCOMPATIBLE_MEDIA_FORMAT\t305\n#define W_306_ATTRIBUTE_NOT_UNDERSTOOD\t306\n#define W_307_PARAMETER_NOT_UNDERSTOOD\t307\n#define W_330_MULTICAST_NOT_AVAILABLE\t330\n#define W_331_UNICAST_NOT_AVAILABLE\t331\n#define W_370_INSUFFICIENT_BANDWITH\t370\n#define W_399_MISCELLANEOUS\t\t399\n\n// Warning texts\n#define WARNING_300\t\"Incompatible network protocol\"\n#define WARNING_301\t\"Incompatible network address formats\"\n#define WARNING_302\t\"Incompatible transport protocol\"\n#define WARNING_303\t\"Incompatible bandwidth units\"\n#define WARNING_304\t\"Media type not available\"\n#define WARNING_305\t\"Incompatible media format\"\n#define WARNING_306\t\"Attribute not understood\"\n#define WARNING_307\t\"Session description parameter not understood\"\n#define WARNING_330\t\"Multicast not available\"\n#define WARNING_331\t\"Unicast not available\"\n#define WARNING_370\t\"Insufficient bandwidth\"\n#define WARNING_399\t\"Miscellaneous warning\"\n\nusing namespace std;\n\nclass t_warning {\npublic:\n\tint\t\tcode;\n\tstring\t\thost;\n\tint\t\tport;\n\tstring\t\ttext;\n\n\tt_warning();\n\n\t// The default text will be used as warning appended with passed\n\t// text if present\n\tt_warning(const string &_host, int _port, int _code, string _text);\n\n\tstring encode(void) const;\n};\n\nclass t_hdr_warning : public t_header {\npublic:\n\tlist<t_warning>\twarnings;\n\n\tt_hdr_warning();\n\tvoid add_warning(const t_warning &w);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/hdr_www_authenticate.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"hdr_www_authenticate.h\"\n#include \"definitions.h\"\n#include \"util.h\"\n\nt_hdr_www_authenticate::t_hdr_www_authenticate() : t_header(\"WWW-Authenticate\") {}\n\nvoid t_hdr_www_authenticate::set_challenge(const t_challenge &c) {\n\t// The server may send multiple WWW-Authenticate/Proxy-Authenticate\n\t// headers, with different digest algorithms, in decreasing order of\n\t// preference.  We must therefore avoid overwriting any supported\n\t// challenge once we've got a hold of one.  (We don't simply ignore\n\t// all unsupported challenges, however, just in case the server forgot\n\t// to include a Digest challenge.)\n\tif (populated) {\n\t\t// Don't overwrite the previous challenge if it was supported\n\t\tif (cmp_nocase(challenge.auth_scheme, AUTH_DIGEST) == 0) {\n\t\t\treturn;\n\t\t}\n\t}\n\tpopulated = true;\n\tchallenge = c;\n}\n\nstring t_hdr_www_authenticate::encode_value(void) const {\n\tif (!populated) return \"\";\n\n\treturn challenge.encode();\n}\n"
  },
  {
    "path": "src/parser/hdr_www_authenticate.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// WWW-Authenticate header\n\n#ifndef _HDR_WWW_AUTHENTICATE_H\n#define _HDR_WWW_AUTHENTICATE_H\n\n#include <list>\n#include <string>\n#include \"challenge.h\"\n#include \"header.h\"\n\nusing namespace std;\n\nclass t_hdr_www_authenticate : public t_header {\npublic:\n\tt_challenge\t\tchallenge;\n\n\tt_hdr_www_authenticate();\n\n\tvoid set_challenge(const t_challenge &c);\n\tstring encode_value(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/header.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"header.h\"\n#include \"parse_ctrl.h\"\n#include \"protocol.h\"\n#include \"util.h\"\n\nt_header::t_header() :\n\tpopulated(false)\n{}\n\nt_header::t_header(const string &_header_name, const string &_compact_name) :\n\tpopulated(false),\n\theader_name(_header_name),\n\tcompact_name(_compact_name)\n{}\n\nstring t_header::encode(void) const {\n\tstring s;\n\n\tif (!populated) return s;\n\n\ts = (t_parser::compact_headers && !compact_name.empty() ? \n\t\t\tcompact_name : header_name);\n\ts += \": \";\n\ts += encode_value();\n\ts += CRLF;\n\t\n\treturn s;\n}\n\nstring t_header::encode_env(void) const {\n\tstring s(\"SIP_\");\n\ts += toupper(replace_char(header_name, '-', '_'));\n\ts += '=';\n\ts += encode_value();\n\t\n\treturn s;\n}\n\nbool t_header::is_populated() const {\n\treturn populated;\n}\n\nstring t_header::get_name(void) const {\n\treturn header_name;\n}\n\nstring t_header::get_value(void) const {\n\tstring s;\n\tstring::size_type i;\n\n\tif (!populated) return s;\n\n\ts = encode();\n\ti = s.find(':');\n\n\t// The colon cannot be the first or last character\n\tif (i == string::npos || i == s.size()-1) return \"\";\n\n\ts = s.substr(i+1);\n\ti = s.find(CRLF);\n\ts = s.substr(0, i);\n\n\treturn (trim(s));\n}\n"
  },
  {
    "path": "src/parser/header.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Base class for message and response headers\n\n#ifndef _HEADER_H\n#define _HEADER_H\n\n#include <string>\n\nusing namespace std;\n\nclass t_header {\nprivate:\n\tt_header();\n\nprotected:\n\tbool\tpopulated;\t// true = header is populated\n\tstring\theader_name;\t// Full name of header in SIP messages\n\tstring\tcompact_name;\t// Compact name of header in SIP messages\n\npublic:\n\tvirtual ~t_header() {}\n\tt_header(const string &_header_name, const string &_compact_name = \"\");\n\n\t// Return the text encoded header (CRLF at end of string)\n\tvirtual string encode(void) const;\n\t\n\t// Return the text encoded value part (no CRLF at end of string)\n\tvirtual string encode_value(void) const = 0;\n\t\n\t// Return a environemnt variable setting\n\t// The format of the setting is:\n\t//\n\t// SIP_<header name>=<value>\n\t//\n\t// The header name is in capitals. Dashes are replaced by underscores.\n\tvirtual string encode_env(void) const;\n\t\n\t// Get the header name\n\tstring get_name(void) const;\n\n\t// Get text encoding of the header value only.\n\t// I.e. without header name and no trailing CRLF\n\tstring get_value(void) const;\n\n\t// Return true if the header is populated\n\tbool is_populated(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/identity.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"identity.h\"\n#include \"util.h\"\n\nt_identity::t_identity() : display(), uri() {}\n\nvoid t_identity::set_display(const string &d) {\n\tdisplay = d;\n}\n\nvoid t_identity::set_uri(const string &u) {\n\turi.set_url(u);\n}\n\nvoid t_identity::set_uri(const t_url &u) {\n\turi = u;\n}\n\nstring t_identity::encode(void) const {\n\tstring s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/identity.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _IDENTITY_H\n#define _IDENTITY_H\n\n#include <string>\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_identity {\npublic:\n\tstring\tdisplay; // display name\t\n\tt_url\turi;\n\t\n\tt_identity();\n\tvoid set_display(const string &d);\n\tvoid set_uri(const string &u);\n\tvoid set_uri(const t_url &u);\n\t\n\tstring encode(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/media_type.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <cstdlib>\n\n#include \"media_type.h\"\n#include \"util.h\"\n#include \"utils/mime_database.h\"\n\nusing namespace std;\nusing namespace utils;\n\nt_media::t_media() : q(1.0) {}\n\nt_media::t_media(const string &t, const string &s) :\n\ttype(t),\n\tsubtype(s),\n\tq(1.0)\n{}\n\nt_media::t_media(const string &mime_type) : q(1.0) \n{\n\tvector<string> v = split(mime_type, '/');\n\t\n\tif (v.size() == 2) {\n\t\ttype = v[0];\n\t\tsubtype = v[1];\n\t}\n}\n\nvoid t_media::add_params(const list<t_parameter> &l) {\n\tlist<t_parameter>::const_iterator i = l.begin();\n\n\tmedia_param_list.clear();\n\taccept_extension_list.clear();\n\n\t// Add media parameters\n\twhile (i != l.end() && i->name != \"q\") {\n\t\tif (i->name == \"charset\") {\n\t\t\tcharset = i->value;\n\t\t} else {\n\t\t\tmedia_param_list.push_back(*i);\n\t\t}\n\t\t++i;\n\t}\n\n\t// Set the quality factor\n\tif (i != l.end()) {\n\t\tq = atof(i->value.c_str());\n\t\ti++;\n\t}\n\n\t// Add accept extension parameters\n\twhile (i != l.end()) {\n\t\taccept_extension_list.push_back(*i);\n\t\ti++;\n\t}\n}\n\n\nstring t_media::encode(void) const {\n\tstring s;\n\n\ts = type + '/' + subtype;\n\tif (!charset.empty()) {\n\t\ts += \";charset=\";\n\t\ts += charset;\n\t}\n\ts += param_list2str(media_param_list);\n\t\n\tif (q != 1) {\n\t\ts += \";q=\";\n\t\ts += float2str(q, 1);\n\t}\n\t\n\ts += param_list2str(accept_extension_list);\n\n\treturn s;\n}\n\nstring t_media::get_file_glob(void) const {\n\tstring file_glob = mime_database->get_glob(type + '/' + subtype);\n\treturn file_glob;\n}\n"
  },
  {
    "path": "src/parser/media_type.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Media MIME type definition.\n */\n\n#ifndef _MEDIA_TYPE_H\n#define _MEDIA_TYPE_H\n\n#include <list>\n#include <string>\n#include \"parameter.h\"\n\nusing namespace std;\n\n/** Media MIME type definition. */\nclass t_media {\npublic:\n\tstring\ttype;\t\t/**< main type */\n\tstring\tsubtype;\t/**< subtype */\n\tstring\tcharset;\t/**< Character set */\n\tfloat\tq;\t\t/**< quality factor */\n\tlist<t_parameter> media_param_list; \t /**< media paramters */\n\tlist<t_parameter> accept_extension_list; /**< accept parameters */\n\n\t/** Constructor */\n\tt_media();\n\n\t/** \n\t * Constructor. \n\t * Construct object with a specic type and subtype.\n\t * @param t [in] type\n\t * @param s [in] subtype\n\t */\n\tt_media(const string &t, const string &s);\n\t\n\t/**\n\t * Constructor.\n\t * Construct a media object from a mime type name\n\t * @param mime_type [in] The mime type name, e.g. \"text/plain\"\n\t */\n\tt_media(const string &mime_type);\n\n\t/**\n\t * Add a parameter list.\n\t * Method for parser to add the parsed parameter list l.\n\t * l should start with optional media parameters followed\n\t * by the q-paramter followed by accept parameters.\n\t * @param l [in] The parameter list.\n\t */\n\tvoid add_params(const list<t_parameter> &l);\n\n\t/**\n\t * Encode as string.\n\t * @return The encoded media type.\n\t */\n\tstring encode(void) const;\n\t\n\t/**\n\t * Get the glob for a file name containing this MIME type.\n\t * E.g. <wildcard>.txt for text/plain\n\t * @return The file name extension.\n\t */\n\tstring get_file_glob(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/milenage.cpp",
    "content": "/*-------------------------------------------------------------------\n *          Example algorithms f1, f1*, f2, f3, f4, f5, f5*\n *-------------------------------------------------------------------\n *\n *  A sample implementation of the example 3GPP authentication and\n *  key agreement functions f1, f1*, f2, f3, f4, f5 and f5*.  This is\n *  a byte-oriented implementation of the functions, and of the block\n *  cipher kernel function Rijndael.\n *\n *  This has been coded for clarity, not necessarily for efficiency.\n *\n *  The functions f2, f3, f4 and f5 share the same inputs and have\n *  been coded together as a single function.  f1, f1* and f5* are\n *  all coded separately.\n *\n *-----------------------------------------------------------------*/\n\n#include \"milenage.h\"\n#include \"rijndael.h\"\n\n/*--------------------------- prototypes --------------------------*/\n\n\n\n/*-------------------------------------------------------------------\n *                            Algorithm f1\n *-------------------------------------------------------------------\n *\n *  Computes network authentication code MAC-A from key K, random\n *  challenge RAND, sequence number SQN and authentication management\n *  field AMF.\n *\n *-----------------------------------------------------------------*/\n\nvoid f1    ( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2], \n             u8 mac_a[8], u8 op[16] )\n{\n  u8 op_c[16];\n  u8 temp[16];\n  u8 in1[16];\n  u8 out1[16];\n  u8 rijndaelInput[16];\n  u8 i;\n\n  RijndaelKeySchedule( k );\n\n  ComputeOPc( op_c, op );\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] = rand[i] ^ op_c[i];\n  RijndaelEncrypt( rijndaelInput, temp );\n\n  for (i=0; i<6; i++)\n  {\n    in1[i]    = sqn[i];\n    in1[i+8]  = sqn[i];\n  }\n  for (i=0; i<2; i++)\n  {\n    in1[i+6]  = amf[i];\n    in1[i+14] = amf[i];\n  }\n\n  /* XOR op_c and in1, rotate by r1=64, and XOR *\n   * on the constant c1 (which is all zeroes)   */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i];\n\n  /* XOR on the value temp computed before */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] ^= temp[i];\n  \n  RijndaelEncrypt( rijndaelInput, out1 );\n  for (i=0; i<16; i++)\n    out1[i] ^= op_c[i];\n\n  for (i=0; i<8; i++)\n    mac_a[i] = out1[i];\n\n  return;\n} /* end of function f1 */\n\n\n  \n/*-------------------------------------------------------------------\n *                            Algorithms f2-f5\n *-------------------------------------------------------------------\n *\n *  Takes key K and random challenge RAND, and returns response RES,\n *  confidentiality key CK, integrity key IK and anonymity key AK.\n *\n *-----------------------------------------------------------------*/\n\nvoid f2345 ( u8 k[16], u8 rand[16],\n             u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6], u8 op[16] )\n{\n  u8 op_c[16];\n  u8 temp[16];\n  u8 out[16];\n  u8 rijndaelInput[16];\n  u8 i;\n\n  RijndaelKeySchedule( k );\n\n  ComputeOPc( op_c, op );\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] = rand[i] ^ op_c[i];\n  RijndaelEncrypt( rijndaelInput, temp );\n\n  /* To obtain output block OUT2: XOR OPc and TEMP,    *\n   * rotate by r2=0, and XOR on the constant c2 (which *\n   * is all zeroes except that the last bit is 1).     */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] = temp[i] ^ op_c[i];\n  rijndaelInput[15] ^= 1;\n\n  RijndaelEncrypt( rijndaelInput, out );\n  for (i=0; i<16; i++)\n    out[i] ^= op_c[i];\n\n  for (i=0; i<8; i++)\n    res[i] = out[i+8];\n  for (i=0; i<6; i++)\n    ak[i]  = out[i];\n\n  /* To obtain output block OUT3: XOR OPc and TEMP,        *\n   * rotate by r3=32, and XOR on the constant c3 (which    *\n   * is all zeroes except that the next to last bit is 1). */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[(i+12) % 16] = temp[i] ^ op_c[i];\n  rijndaelInput[15] ^= 2;\n\n  RijndaelEncrypt( rijndaelInput, out );\n  for (i=0; i<16; i++)\n    out[i] ^= op_c[i];\n\n  for (i=0; i<16; i++)\n    ck[i] = out[i];\n\n  /* To obtain output block OUT4: XOR OPc and TEMP,         *\n   * rotate by r4=64, and XOR on the constant c4 (which     *\n   * is all zeroes except that the 2nd from last bit is 1). */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[(i+8) % 16] = temp[i] ^ op_c[i];\n  rijndaelInput[15] ^= 4;\n\n  RijndaelEncrypt( rijndaelInput, out );\n  for (i=0; i<16; i++)\n    out[i] ^= op_c[i];\n\n  for (i=0; i<16; i++)\n    ik[i] = out[i];\n\n  return;\n} /* end of function f2345 */\n\n  \n/*-------------------------------------------------------------------\n *                            Algorithm f1*\n *-------------------------------------------------------------------\n *\n *  Computes resynch authentication code MAC-S from key K, random\n *  challenge RAND, sequence number SQN and authentication management\n *  field AMF.\n *\n *-----------------------------------------------------------------*/\n\nvoid f1star( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2], \n             u8 mac_s[8], u8 op[16] )\n{\n  u8 op_c[16];\n  u8 temp[16];\n  u8 in1[16];\n  u8 out1[16];\n  u8 rijndaelInput[16];\n  u8 i;\n\n  RijndaelKeySchedule( k );\n\n  ComputeOPc( op_c, op );\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] = rand[i] ^ op_c[i];\n  RijndaelEncrypt( rijndaelInput, temp );\n\n  for (i=0; i<6; i++)\n  {\n    in1[i]    = sqn[i];\n    in1[i+8]  = sqn[i];\n  }\n  for (i=0; i<2; i++)\n  {\n    in1[i+6]  = amf[i];\n    in1[i+14] = amf[i];\n  }\n\n  /* XOR op_c and in1, rotate by r1=64, and XOR *\n   * on the constant c1 (which is all zeroes)   */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i];\n\n  /* XOR on the value temp computed before */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] ^= temp[i];\n  \n  RijndaelEncrypt( rijndaelInput, out1 );\n  for (i=0; i<16; i++)\n    out1[i] ^= op_c[i];\n\n  for (i=0; i<8; i++)\n    mac_s[i] = out1[i+8];\n\n  return;\n} /* end of function f1star */\n\n  \n/*-------------------------------------------------------------------\n *                            Algorithm f5*\n *-------------------------------------------------------------------\n *\n *  Takes key K and random challenge RAND, and returns resynch\n *  anonymity key AK.\n *\n *-----------------------------------------------------------------*/\n\nvoid f5star( u8 k[16], u8 rand[16],\n             u8 ak[6], u8 op[16] )\n{\n  u8 op_c[16];\n  u8 temp[16];\n  u8 out[16];\n  u8 rijndaelInput[16];\n  u8 i;\n\n  RijndaelKeySchedule( k );\n\n  ComputeOPc( op_c, op );\n\n  for (i=0; i<16; i++)\n    rijndaelInput[i] = rand[i] ^ op_c[i];\n  RijndaelEncrypt( rijndaelInput, temp );\n\n  /* To obtain output block OUT5: XOR OPc and TEMP,         *\n   * rotate by r5=96, and XOR on the constant c5 (which     *\n   * is all zeroes except that the 3rd from last bit is 1). */\n\n  for (i=0; i<16; i++)\n    rijndaelInput[(i+4) % 16] = temp[i] ^ op_c[i];\n  rijndaelInput[15] ^= 8;\n\n  RijndaelEncrypt( rijndaelInput, out );\n  for (i=0; i<16; i++)\n    out[i] ^= op_c[i];\n\n  for (i=0; i<6; i++)\n    ak[i] = out[i];\n\n  return;\n} /* end of function f5star */\n\n  \n/*-------------------------------------------------------------------\n *  Function to compute OPc from OP and K.  Assumes key schedule has\n    already been performed.\n *-----------------------------------------------------------------*/\n\nvoid ComputeOPc( u8 op_c[16], u8 op[16] )\n{\n  u8 i;\n  \n  RijndaelEncrypt( op, op_c );\n  for (i=0; i<16; i++)\n    op_c[i] ^= op[i];\n\n  return;\n} /* end of function ComputeOPc */\n"
  },
  {
    "path": "src/parser/milenage.h",
    "content": "/*-------------------------------------------------------------------\n *          Example algorithms f1, f1*, f2, f3, f4, f5, f5*\n *-------------------------------------------------------------------\n *\n *  A sample implementation of the example 3GPP authentication and\n *  key agreement functions f1, f1*, f2, f3, f4, f5 and f5*.  This is\n *  a byte-oriented implementation of the functions, and of the block\n *  cipher kernel function Rijndael.\n *\n *  This has been coded for clarity, not necessarily for efficiency.\n *\n *  The functions f2, f3, f4 and f5 share the same inputs and have\n *  been coded together as a single function.  f1, f1* and f5* are\n *  all coded separately.\n *\n *-----------------------------------------------------------------*/\n\n#ifndef MILENAGE_H\n#define MILENAGE_H\n\ntypedef unsigned char u8;\n\n\nvoid f1    ( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2],\n             u8 mac_a[8], u8 op[16] );\nvoid f2345 ( u8 k[16], u8 rand[16],\n             u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6], u8 op[16] );\nvoid f1star( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2],\n             u8 mac_s[8], u8 op[16] );\nvoid f5star( u8 k[16], u8 rand[16],\n             u8 ak[6], u8 op[16] );\nvoid ComputeOPc( u8 op_c[16], u8 op[16] );\n\n\n#endif\n"
  },
  {
    "path": "src/parser/parameter.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"parameter.h\"\n#include \"util.h\"\n\nt_parameter::t_parameter() {\n\ttype = VALUE;\n}\n\nt_parameter::t_parameter(const string &n) {\n\ttype = NOVALUE;\n\tname = n;\n}\n\nt_parameter::t_parameter(const string &n, const string &v) {\n\ttype = VALUE;\n\tname = n;\n\tvalue = v;\n}\n\nstring t_parameter::encode(void) const {\n\tstring s;\n\n\ts += name;\n\n\tif (type == VALUE) {\n\t\ts += '=';\n\t\tif (must_quote(value)) {\n\t\t\ts += '\\\"' + value + '\\\"';\n\t\t} else {\n\t\t\ts += value;\n\t\t}\n\t}\n\n\treturn s;\n}\n\nbool t_parameter::operator==(const t_parameter &rhs) {\n\treturn (type == rhs.type && name == rhs.name);\n}\n\nt_parameter str2param(const string &s) {\n\tvector<string> l = split_on_first(s, '=');\n\tif (l.size() == 1) {\n\t\treturn t_parameter(s);\n\t} else {\n\t\treturn t_parameter(trim(l[0]), trim(l[1]));\n\t}\n}\n\nstring param_list2str(const list<t_parameter> &l) {\n\tstring s;\n\n\tfor (list<t_parameter>::const_iterator i = l.begin();\n\t     i != l.end(); i++)\n\t{\n\t\ts += ';';\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nlist<t_parameter> str2param_list(const string &s) {\n\tlist<t_parameter> result;\n\t\n\tvector<string> l = split(s, ';');\n\tfor (vector<string>::const_iterator i = l.begin(); i != l.end(); i++) {\n\t\tt_parameter p = str2param(trim(*i));\n\t\tresult.push_back(p);\n\t}\n\t\n\treturn result;\n}\n"
  },
  {
    "path": "src/parser/parameter.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _PARAMETER_H\n#define _PARAMETER_H\n\n#include <string>\n#include <list>\n\nusing namespace std;\n\nclass t_parameter {\npublic:\nenum t_param_type{\n\tNOVALUE,\t// a parameter without a value\n\tVALUE\t\t// parameter having a value (default)\n};\n\n\tt_param_type\ttype;\t// type of parameter\n\tstring\t\tname;\t// name of parameter\n\tstring\t\tvalue;\t// value of parameter if type is VALUE\n\n\tt_parameter();\n\n\t// Construct a NOVALUE parameter with name = n\n\tt_parameter(const string &n);\n\n\t// Construct a VALUE parameter with name = n, value = v\n\tt_parameter(const string &n, const string &v);\n\n\tstring encode(void) const;\n\t\n\tbool operator==(const t_parameter &rhs);\n};\n\n// Decode a parameter\nt_parameter str2param(const string &s);\n\n// Encode a parameter list\nstring param_list2str(const list<t_parameter> &l);\n\n// Decode a parameter list\nlist<t_parameter> str2param_list(const string &s);\n\n#endif\n"
  },
  {
    "path": "src/parser/parse_ctrl.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"parse_ctrl.h\"\n#include \"protocol.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\n// Interface to Bison\nextern int yyparse(void);\n\n// Interface to Flex\nstruct yy_buffer_state;\nextern struct yy_buffer_state *yy_scan_string(const char *);\nextern void yy_delete_buffer(struct yy_buffer_state *);\n\nt_mutex t_parser::mtx_parser;\nbool t_parser::check_max_forwards = true;\nbool t_parser::compact_headers = false;\nbool t_parser::multi_values_as_list = true;\nint t_parser::comment_level = 0;\nlist<string> t_parser::parse_errors;\n\nstring t_parser::unfold(const string &h) {\n\tstring::size_type i;\n\tstring s = h;\n\n\twhile ((i = s.find(\"\\r\\n \")) != string::npos) {\n\t\ts.replace(i, 3, \" \");\n\t}\n\n\twhile ((i = s.find(\"\\r\\n\\t\")) != string::npos) {\n\t\ts.replace(i, 3, \" \");\n\t}\n\n\t// This is only for easy testing of hand edited messages\n\t// in Linux where the end of line character is \\n only.\n\twhile ((i = s.find(\"\\n \")) != string::npos) {\n\t\ts.replace(i, 2, \" \");\n\t}\n\n\twhile ((i = s.find(\"\\n\\t\")) != string::npos) {\n\t\ts.replace(i, 2, \" \");\n\t}\n\n\treturn s;\n}\n\nt_parser::t_context t_parser::context = t_parser::X_INITIAL;\nt_sip_message *t_parser::msg = NULL;\n\nt_sip_message *t_parser::parse(const string &s, list<string> &parse_errors_) {\n\tt_mutex_guard guard(mtx_parser);\n\t\n\tint ret;\n\tstruct yy_buffer_state *b;\n\tmsg = NULL;\n\n\tparse_errors.clear();\n\n\tstring x = unfold(s);\n\n\tb = yy_scan_string(x.c_str());\n\tret = yyparse();\n\tyy_delete_buffer(b);\n\n\tif (ret != 0) {\n\t\tif (msg) {\n\t\t\tMEMMAN_DELETE(msg);\n\t\t\tdelete msg;\n\t\t\tmsg = NULL;\n\t\t}\n\t\tthrow ret;\n\t}\n\n\tparse_errors_ = parse_errors;\n\treturn msg;\n}\n\nt_sip_message *t_parser::parse_headers(const string &s, list<string> &parse_errors_) {\n\tstring msg(\"INVITE sip:fake@fake.invalid SIP/2.0\");\n\tmsg += CRLF;\n\t\n\tlist<t_parameter> hdr_list = str2param_list(s);\n\tfor (list<t_parameter>::iterator i = hdr_list.begin();\n\t     i != hdr_list.end(); i++)\n\t{\n\t\tmsg += unescape_hex(i->name);\n\t\tmsg += \": \";\n\t\tmsg += unescape_hex(i->value);\n\t\tmsg += CRLF;\n\t}\n\t\n\tmsg += CRLF;\n\t\n\treturn parse(msg, parse_errors_);\n}\n\nvoid t_parser::enter_ctx_comment(void) {\n\tcomment_level = 0;\n\tcontext = t_parser::X_COMMENT;\n}\n\nvoid t_parser::inc_comment_level(void) {\n\tcomment_level++;\n}\n\nbool t_parser::dec_comment_level(void) {\n\tif (comment_level == 0) return false;\n\tcomment_level--;\n\treturn true;\n}\n\nvoid t_parser::add_header_error(const string &header_name) {\n\tstring s = \"Parse error in header: \" + header_name;\n\tparse_errors.push_back(s);\n}\n\nt_syntax_error::t_syntax_error(const string &e) {\n\terror = e;\n}\n"
  },
  {
    "path": "src/parser/parse_ctrl.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Parser control\n\n#ifndef _PARSE_CTRL_H\n#define _PARSE_CTRL_H\n\n#include \"sip_message.h\"\n#include \"threads/mutex.h\"\n\n#define MSG\t\tt_parser::msg\n\n#define CTXT_INITIAL\t(t_parser::context = t_parser::X_INITIAL)\n#define CTXT_URI\t(t_parser::context = t_parser::X_URI)\n#define CTXT_URI_SPECIAL (t_parser::context = t_parser::X_URI_SPECIAL)\n#define CTXT_LANG\t(t_parser::context = t_parser::X_LANG)\n#define CTXT_WORD\t(t_parser::context = t_parser::X_WORD)\n#define CTXT_NUM\t(t_parser::context = t_parser::X_NUM)\n#define CTXT_DATE\t(t_parser::context = t_parser::X_DATE)\n#define CTXT_LINE\t(t_parser::context = t_parser::X_LINE)\n#define CTXT_COMMENT\t(t_parser::enter_ctx_comment())\n#define CTXT_NEW\t(t_parser::context = t_parser::X_NEW)\n#define CTXT_AUTH_SCHEME (t_parser::context = t_parser::X_AUTH_SCHEME)\n#define CTXT_IPV6ADDR\t(t_parser::context = t_parser::X_IPV6ADDR)\n#define CTXT_PARAMVAL\t(t_parser::context = t_parser::X_PARAMVAL)\n\n#define PARSE_ERROR(h)\t{ t_parser::add_header_error(h); CTXT_INITIAL; }\n\n// The t_parser controls the direction of the scanner/parser\n// process and it stores the results from the parser.\nclass t_parser {\nprivate:\n\t/** Mutex to synchronize parse operations */\n\tstatic t_mutex\tmtx_parser;\n\n\t// Level for nested comments\n\tstatic int comment_level;\n\n\t// Non-fatal parse errors generated during parsing.\n\tstatic list<string> parse_errors;\n\n\t// Unfold SIP headers\n\tstatic string unfold(const string &h);\n\npublic:\nenum t_context {\n\tX_INITIAL,\t// Initial context\n\tX_URI,\t\t// URI context where parameters belong to URI\n\tX_URI_SPECIAL,\t// URI context where parameters belong to SIP header\n\t\t\t// if URI is not enclosed by < and >\n\tX_LANG,\t\t// Language tag context\n\tX_WORD,\t\t// Word context\n\tX_NUM,\t\t// Number context\n\tX_DATE,\t\t// Date context\n\tX_LINE,\t\t// Whole line context\n\tX_COMMENT,\t// Comment context\n\tX_NEW,\t\t// Start of a new SIP message to distinguish\n\t\t\t// request from responses\n\tX_AUTH_SCHEME,\t// Authorization scheme context\n\tX_IPV6ADDR,\t// IPv6 address context\n\tX_PARAMVAL,\t// Generic parameter value context\n};\n\n\t// Parser options\n\t// According to RFC3261 the Max-Forwards header is mandatory, but\n\t// many implementations do not send this header.\n\tstatic bool\t\tcheck_max_forwards;\n\n\t// Encode headers in compact forom\n\tstatic bool\t\tcompact_headers;\n\t\n\t// Encode multiple values as comma separated list or multiple headers\n\tstatic bool\t\tmulti_values_as_list;\n\n\tstatic t_context\tcontext;    // Scan context\n\tstatic t_sip_message\t*msg;       // Message that has been parsed\n\n\t/** \n\t * Parse a string representing a SIP message.\n\t * @param s [in] String to parse.\n\t * @param parse_errors_ [out] List of non-fatal parse errors.\n\t * @return The parsed SIP message.\n\t * @throw int exception when parsing fails.\n\t */\n\tstatic t_sip_message *parse(const string &s, list<string> &parse_errors_);\n\t\n\t/**\n\t * Parse a string of headers (hdr1=val1;hdr=val2;...)\n\t * The resulting SIP message is a SIP request with a fake request line.\n\t * @param s [in] String to parse.\n\t * @param parse_errors_ [out] List of non-fatal parse errors.\n\t * @return The parsed SIP message.\n\t * @throw int exception when parsing fails.\n\t */\n\tstatic t_sip_message *parse_headers(const string &s, list<string> &parse_errors_);\n\n\tstatic void enter_ctx_comment(void);\n\n\t// Increment and decrement levels for nested comments\n\t// dec_comment_level returns false if the level cannot be decremented.\n\tstatic void inc_comment_level(void);\n\tstatic bool dec_comment_level(void);\n\n\t// Add parsing error for a header to the list of parse errors\n\tstatic void add_header_error(const string &header_name);\n};\n\n// Error that can be thrown as exception\nclass t_syntax_error {\npublic:\n\tstring error;\n\tt_syntax_error(const string &e);\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/parser.yxx",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n%{\n#include <cstdio>\n#include <cstdlib>\n#include <string>\n#include \"media_type.h\"\n#include \"parameter.h\"\n#include \"parse_ctrl.h\"\n#include \"request.h\"\n#include \"response.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\n\nextern int yylex(void);\nvoid yyerror(const char *s);\n%}\n\n// The %debug option causes a problem with the %destructor options later on.\n// The bison compilor generates undefined symbols:\n//\n//   parser.y: In function `void yysymprint(FILE*, int, yystype)':\n//   parser.y:0: error: `null' undeclared (first use this function)\n//\n// So if you need to debug, then outcomment the %destructor first. This will\n// do no harm to your debugging, it only will cause memory leaks during\n// error handling.\n//\n// %debug\n\n%expect 2\n/* See below for the expected shift/reduce conflicts. */\n\n%union {\n\tint\t\t\tyyt_int;\n\tunsigned long\t\tyyt_ulong;\n\tfloat\t\t\tyyt_float;\n\tstring\t\t\t*yyt_str;\n\tt_parameter\t\t*yyt_param;\n\tlist<t_parameter>\t*yyt_params;\n\tt_media\t\t\t*yyt_media;\n\tt_coding\t\t*yyt_coding;\n\tt_language\t\t*yyt_language;\n\tt_alert_param\t\t*yyt_alert_param;\n\tt_info_param\t\t*yyt_info_param;\n\tlist<t_contact_param>\t*yyt_contacts;\n\tt_contact_param\t\t*yyt_contact;\n\tt_error_param\t\t*yyt_error_param;\n\tt_identity\t\t*yyt_from_addr;\n\tt_reason\t\t*yyt_reason;\n\tt_route\t\t\t*yyt_route;\n\tt_server\t\t*yyt_server;\n\tt_via\t\t\t*yyt_via;\n\tt_warning\t\t*yyt_warning;\n\tt_digest_response\t*yyt_dig_resp;\n\tt_credentials\t\t*yyt_credentials;\n\tt_digest_challenge\t*yyt_dig_chlg;\n\tt_challenge\t\t*yyt_challenge;\n}\n\n%token <yyt_ulong>\tT_NUM\n%token <yyt_str>\tT_TOKEN\n%token <yyt_str>\tT_QSTRING\n%token <yyt_str>\tT_COMMENT\n%token <yyt_str>\tT_LINE\n%token <yyt_str>\tT_URI\n%token\t\t\tT_URI_WILDCARD\n%token <yyt_str>\tT_DISPLAY\n%token <yyt_str>\tT_LANG\n%token <yyt_str>\tT_WORD\n%token <yyt_int>\tT_WKDAY\n%token <yyt_int>\tT_MONTH\n%token\t\t\tT_GMT\n%token\t\t\tT_SIP\n%token <yyt_str>\tT_METHOD\n%token\t\t\tT_AUTH_DIGEST\n%token <yyt_str>\tT_AUTH_OTHER\n%token <yyt_str>\tT_IPV6ADDR\n%token <yyt_str>\tT_PARAMVAL\n\n%token\t\t\tT_HDR_ACCEPT\n%token\t\t\tT_HDR_ACCEPT_ENCODING\n%token\t\t\tT_HDR_ACCEPT_LANGUAGE\n%token\t\t\tT_HDR_ALERT_INFO\n%token\t\t\tT_HDR_ALLOW\n%token\t\t\tT_HDR_ALLOW_EVENTS\n%token\t\t\tT_HDR_AUTHENTICATION_INFO\n%token\t\t\tT_HDR_AUTHORIZATION\n%token\t\t\tT_HDR_CALL_ID\n%token\t\t\tT_HDR_CALL_INFO\n%token\t\t\tT_HDR_CONTACT\n%token\t\t\tT_HDR_CONTENT_DISP\n%token\t\t\tT_HDR_CONTENT_ENCODING\n%token\t\t\tT_HDR_CONTENT_LANGUAGE\n%token\t\t\tT_HDR_CONTENT_LENGTH\n%token\t\t\tT_HDR_CONTENT_TYPE\n%token\t\t\tT_HDR_CSEQ\n%token\t\t\tT_HDR_DATE\n%token\t\t\tT_HDR_ERROR_INFO\n%token\t\t\tT_HDR_EVENT\n%token\t\t\tT_HDR_EXPIRES\n%token\t\t\tT_HDR_FROM\n%token\t\t\tT_HDR_IN_REPLY_TO\n%token\t\t\tT_HDR_MAX_FORWARDS\n%token\t\t\tT_HDR_MIN_EXPIRES\n%token\t\t\tT_HDR_MIN_SE\n%token\t\t\tT_HDR_MIME_VERSION\n%token\t\t\tT_HDR_ORGANIZATION\n%token\t\t\tT_HDR_P_ASSERTED_IDENTITY\n%token\t\t\tT_HDR_P_PREFERRED_IDENTITY\n%token\t\t\tT_HDR_PRIORITY\n%token\t\t\tT_HDR_PRIVACY\n%token\t\t\tT_HDR_PROXY_AUTHENTICATE\n%token\t\t\tT_HDR_PROXY_AUTHORIZATION\n%token\t\t\tT_HDR_PROXY_REQUIRE\n%token\t\t\tT_HDR_RACK\n%token\t\t\tT_HDR_RECORD_ROUTE\n%token\t\t\tT_HDR_SERVICE_ROUTE\n%token\t\t\tT_HDR_REASON\n%token\t\t\tT_HDR_REFER_SUB\n%token\t\t\tT_HDR_REFER_TO\n%token\t\t\tT_HDR_REFERRED_BY\n%token\t\t\tT_HDR_REPLACES\n%token\t\t\tT_HDR_REPLY_TO\n%token\t\t\tT_HDR_REQUIRE\n%token\t\t\tT_HDR_REQUEST_DISPOSITION\n%token\t\t\tT_HDR_RETRY_AFTER\n%token\t\t\tT_HDR_ROUTE\n%token\t\t\tT_HDR_RSEQ\n%token\t\t\tT_HDR_SERVER\n%token\t\t\tT_HDR_SESSION_EXPIRES\n%token\t\t\tT_HDR_SIP_ETAG\n%token\t\t\tT_HDR_SIP_IF_MATCH\n%token\t\t\tT_HDR_SUBJECT\n%token\t\t\tT_HDR_SUBSCRIPTION_STATE\n%token\t\t\tT_HDR_SUPPORTED\n%token\t\t\tT_HDR_TIMESTAMP\n%token\t\t\tT_HDR_TO\n%token\t\t\tT_HDR_UNSUPPORTED\n%token\t\t\tT_HDR_USER_AGENT\n%token\t\t\tT_HDR_VIA\n%token\t\t\tT_HDR_WARNING\n%token\t\t\tT_HDR_WWW_AUTHENTICATE\n%token <yyt_str>\tT_HDR_UNKNOWN\n\n%token\t\t\tT_CRLF\n\n%token\t\t\tT_ERROR\n\n// The token T_NULL is never returned by the scanner.\n%token\t\t\tT_NULL\n\n%destructor { MEMMAN_DELETE($$); delete $$; } \tT_TOKEN;\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_QSTRING\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_COMMENT\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_LINE\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_URI\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_DISPLAY\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_LANG\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_WORD\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_METHOD\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_AUTH_OTHER\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_IPV6ADDR\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_PARAMVAL\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_HDR_UNKNOWN\n\n%type <yyt_alert_param>\talert_param\n%type <yyt_params>\tauth_params\n%type <yyt_str>\t\tcall_id\n%type <yyt_challenge>\tchallenge\n%type <yyt_str>\t\tcomment\n%type <yyt_str>\t\tcomment_opt\n%type <yyt_contact>\tcontact_addr\n%type <yyt_contact>\tcontact_param\n%type <yyt_contacts>\tcontacts\n%type <yyt_coding>\tcontent_coding\n%type <yyt_credentials>\tcredentials\n%type <yyt_float>\tdelay\n%type <yyt_dig_chlg>\tdigest_challenge\n%type <yyt_dig_resp>\tdigest_response\n%type <yyt_str>\t\tdisplay_name\n%type <yyt_error_param>\terror_param\n%type <yyt_from_addr>\tfrom_addr\n%type <yyt_str>\t\thdr_unknown\n%type <yyt_via>\t\thost\n%type <yyt_str>\t\tipv6reference\n%type <yyt_info_param>\tinfo_param\n%type <yyt_language>\tlanguage\n%type <yyt_media>\tmedia_range\n%type <yyt_param>\tparameter\n%type <yyt_str>\t\tparameter_val\n%type <yyt_params>\tparameters\n%type <yyt_float>\tq_factor\n%type <yyt_reason>\treason_param\n%type <yyt_route>\trec_route\n%type <yyt_via>\t\tsent_protocol\n%type <yyt_server>\tserver\n%type <yyt_str>\t\tsip_version\n%type <yyt_float>\ttimestamp\n%type <yyt_via>\t\tvia_parm\n%type <yyt_warning>\twarning\n\n%destructor { MEMMAN_DELETE($$); delete $$; }\talert_param\n%destructor { MEMMAN_DELETE($$); delete $$; }\tauth_params\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcall_id\n%destructor { MEMMAN_DELETE($$); delete $$; }\tchallenge\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcomment\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcomment_opt\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcontact_addr\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcontact_param\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcontacts\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcontent_coding\n%destructor { MEMMAN_DELETE($$); delete $$; }\tcredentials\n%destructor { MEMMAN_DELETE($$); delete $$; }\tdigest_challenge\n%destructor { MEMMAN_DELETE($$); delete $$; }\tdigest_response\n%destructor { MEMMAN_DELETE($$); delete $$; }\tdisplay_name\n%destructor { MEMMAN_DELETE($$); delete $$; }\terror_param\n%destructor { MEMMAN_DELETE($$); delete $$; }\tfrom_addr\n%destructor { MEMMAN_DELETE($$); delete $$; }\thdr_unknown\n%destructor { MEMMAN_DELETE($$); delete $$; }\thost\n%destructor { MEMMAN_DELETE($$); delete $$; }\tipv6reference\n%destructor { MEMMAN_DELETE($$); delete $$; }\tinfo_param\n%destructor { MEMMAN_DELETE($$); delete $$; }\tlanguage\n%destructor { MEMMAN_DELETE($$); delete $$; }\tmedia_range\n%destructor { MEMMAN_DELETE($$); delete $$; }\tparameter\n%destructor { MEMMAN_DELETE($$); delete $$; }\tparameter_val\n%destructor { MEMMAN_DELETE($$); delete $$; }\tparameters\n%destructor { MEMMAN_DELETE($$); delete $$; }\treason_param\n%destructor { MEMMAN_DELETE($$); delete $$; }\trec_route\n%destructor { MEMMAN_DELETE($$); delete $$; }\tsent_protocol\n%destructor { MEMMAN_DELETE($$); delete $$; }\tserver\n%destructor { MEMMAN_DELETE($$); delete $$; }\tsip_version\n%destructor { MEMMAN_DELETE($$); delete $$; }\tvia_parm\n%destructor { MEMMAN_DELETE($$); delete $$; }\twarning\n\n%%\nsip_message:\t{ CTXT_NEW; } sip_message2\n;\n\nsip_message2:\t  request\n\t\t| response\n\t\t| error T_NULL {\n\t\t\t/* KLUDGE to work around a memory leak in bison.\n\t\t\t * T_NULL does never match, so the parser never\n\t\t\t * gets here. The error keyword causes bison\n\t\t\t * to eat all input and destroy all tokens returned\n\t\t\t * by the parser.\n\t\t\t * Without this workaround the following input causes\n\t\t\t * the parser to leak:\n\t\t\t *\n\t\t\t *   INVITE INVITE ....\n\t\t\t *\n\t\t\t * In request_line a T_METHOD is returned as look ahead\n\t\t\t * token when bison tries to match sip_version.\n\t\t\t * This does not match, but the look ahead token is\n\t\t\t * never destructed by Bison.\n\t\t\t */\n\t\t\tYYABORT;\n\t\t}\n;\n\nrequest:\t  request_line headers T_CRLF {\n\t\t  \t/* Parsing stops here. Remaining text is\n\t\t\t * not parsed.\n\t\t\t */\n\t\t  \tYYACCEPT; }\n;\n\n// KLUDGE: The use of CTXT_NEW is a kludge, to get the T_SIP symbol\n//         for the SIP version\nrequest_line:\t  T_METHOD { CTXT_URI; } T_URI { CTXT_NEW; }\n\t\t  sip_version T_CRLF {\n\t\t  \tMSG = new t_request();\n\t\t\tMEMMAN_NEW(MSG);\n\t\t\t((t_request *)MSG)->set_method(*$1);\n\t\t\t((t_request *)MSG)->uri.set_url(*$3);\n\t\t\tMSG->version = *$5;\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($5); delete $5;\n\n\t\t\tif (!((t_request *)MSG)->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE(MSG); delete MSG;\n\t\t\t\tMSG = NULL;\n\t\t\t\tYYABORT;\n\t\t\t} }\n;\n\nsip_version:\t  T_SIP { CTXT_INITIAL; } '/' T_TOKEN {\n\t\t\t$$ = $4; }\n;\n\nresponse:\t  status_line headers T_CRLF {\n\t\t  \t/* Parsing stops here. Remaining text is\n\t\t\t * not parsed.\n\t\t\t */\n\t\t  \tYYACCEPT; }\n;\n\nstatus_line:\t  sip_version { CTXT_NUM; } T_NUM { CTXT_LINE; } T_LINE\n\t\t  { CTXT_INITIAL; } T_CRLF {\n\t\t\tMSG = new t_response();\n\t\t\tMEMMAN_NEW(MSG);\n\t\t  \tMSG->version = *$1;\n\t\t\t((t_response *)MSG)->code = $3;\n\t\t\t((t_response *)MSG)->reason = trim(*$5);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($5); delete $5; }\n;\n\nheaders:\t  /* empty */\n\t\t| headers header\n;\n\nheader:\t\t  hd_accept hdr_accept T_CRLF\n\t\t| hd_accept_encoding hdr_accept_encoding T_CRLF\n\t\t| hd_accept_language hdr_accept_language T_CRLF\n\t\t| hd_alert_info hdr_alert_info T_CRLF\n\t\t| hd_allow hdr_allow T_CRLF\n\t\t| hd_allow_events hdr_allow_events T_CRLF\n\t\t| hd_authentication_info hdr_authentication_info T_CRLF\n\t\t| hd_authorization hdr_authorization T_CRLF\n\t\t| hd_call_id hdr_call_id T_CRLF\n\t\t| hd_call_info hdr_call_info T_CRLF\n\t\t| hd_contact hdr_contact T_CRLF\n\t\t| hd_content_disp hdr_content_disp T_CRLF\n\t\t| hd_content_encoding hdr_content_encoding T_CRLF\n\t\t| hd_content_language hdr_content_language T_CRLF\n\t\t| hd_content_length hdr_content_length T_CRLF\n\t\t| hd_content_type hdr_content_type T_CRLF\n\t\t| hd_cseq hdr_cseq T_CRLF\n\t\t| hd_date hdr_date T_CRLF\n\t\t| hd_event hdr_event T_CRLF\n\t\t| hd_error_info hdr_error_info T_CRLF\n\t\t| hd_expires hdr_expires T_CRLF\n\t\t| hd_from hdr_from T_CRLF\n\t\t| hd_in_reply_to hdr_in_reply_to T_CRLF\n\t\t| hd_max_forwards hdr_max_forwards T_CRLF\n\t\t| hd_min_expires hdr_min_expires T_CRLF\n\t\t| hd_min_se hdr_min_se T_CRLF\n\t\t| hd_mime_version hdr_mime_version T_CRLF\n\t\t| hd_organization hdr_organization T_CRLF\n\t\t| hd_p_asserted_identity hdr_p_asserted_identity T_CRLF\n\t\t| hd_p_preferred_identity hdr_p_preferred_identity T_CRLF\n\t\t| hd_priority hdr_priority T_CRLF\n\t\t| hd_privacy hdr_privacy T_CRLF\n\t\t| hd_proxy_authenticate hdr_proxy_authenticate T_CRLF\n\t\t| hd_proxy_authorization hdr_proxy_authorization T_CRLF\n\t\t| hd_proxy_require hdr_proxy_require T_CRLF\n\t\t| hd_rack hdr_rack T_CRLF\n\t\t| hd_reason hdr_reason T_CRLF\n\t\t| hd_record_route hdr_record_route T_CRLF\n\t\t| hd_refer_sub hdr_refer_sub T_CRLF\n\t\t| hd_refer_to hdr_refer_to T_CRLF\n\t\t| hd_referred_by hdr_referred_by T_CRLF\n\t\t| hd_replaces hdr_replaces T_CRLF\n\t\t| hd_reply_to hdr_reply_to T_CRLF\n\t\t| hd_require hdr_require T_CRLF\n\t\t| hd_request_disposition hdr_request_disposition T_CRLF\n\t\t| hd_retry_after hdr_retry_after T_CRLF\n\t\t| hd_route hdr_route T_CRLF\n\t\t| hd_rseq hdr_rseq T_CRLF\n\t\t| hd_server hdr_server T_CRLF\n\t\t| hd_service_route hdr_service_route T_CRLF\n\t\t| hd_session_expires hdr_session_expires T_CRLF\n\t\t| hd_sip_etag hdr_sip_etag T_CRLF\n\t\t| hd_sip_if_match hdr_sip_if_match T_CRLF\n\t\t| hd_subject hdr_subject T_CRLF\n\t\t| hd_subscription_state hdr_subscription_state T_CRLF\n\t\t| hd_supported hdr_supported T_CRLF\n\t\t| hd_timestamp hdr_timestamp T_CRLF\n\t\t| hd_to hdr_to T_CRLF\n\t\t| hd_unsupported hdr_unsupported T_CRLF\n\t\t| hd_user_agent hdr_user_agent T_CRLF\n\t\t| hd_via hdr_via T_CRLF\n\t\t| hd_warning hdr_warning T_CRLF\n\t\t| hd_www_authenticate hdr_www_authenticate T_CRLF\n\t\t| T_HDR_UNKNOWN ':' hdr_unknown T_CRLF {\n\t\t\tMSG->add_unknown_header(*$1, trim(*$3));\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n\t\t| hd_accept error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Accept\"); }\n\t\t| hd_accept_encoding error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Accept-Encoding\"); }\n\t\t| hd_accept_language error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Accept-Language\"); }\n\t\t| hd_alert_info error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Alert-Info\"); }\n\t\t| hd_allow error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Allow\"); }\n\t\t| hd_allow_events error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Allow-Events\"); }\n\t\t| hd_authentication_info error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Authentication-Info\"); }\n\t\t| hd_authorization error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Authorization\"); }\n\t\t| hd_call_id error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Call-ID\"); }\n\t\t| hd_call_info error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Call-Info\"); }\n\t\t| hd_contact error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Contact\"); }\n\t\t| hd_content_disp error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Content-Disposition\"); }\n\t\t| hd_content_encoding error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Content-Encoding\"); }\n\t\t| hd_content_language error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Content-Language\"); }\n\t\t| hd_content_length \terror T_CRLF\n\t\t\t{ PARSE_ERROR(\"Content-Length\"); }\n\t\t| hd_content_type error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Content-Type\"); }\n\t\t| hd_cseq error T_CRLF\n\t\t\t{ PARSE_ERROR(\"CSeq\"); }\n\t\t| hd_date error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Date\"); }\n\t\t| hd_error_info error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Error-Info\"); }\n\t\t| hd_event error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Event\"); }\n\t\t| hd_expires error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Expires\"); }\n\t\t| hd_from error T_CRLF\n\t\t\t{ PARSE_ERROR(\"From\"); }\n\t\t| hd_in_reply_to error T_CRLF\n\t\t\t{ PARSE_ERROR(\"In-Reply-To\"); }\n\t\t| hd_max_forwards error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Max-Forwards\"); }\n\t\t| hd_min_expires error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Min-Expires\"); }\n\t\t| hd_min_se error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Min-SE\"); }\n\t\t| hd_mime_version error T_CRLF\n\t\t\t{ PARSE_ERROR(\"MIME-Version\"); }\n\t\t| hd_organization error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Organization\"); }\n\t\t| hd_p_asserted_identity error T_CRLF\n\t\t\t{ PARSE_ERROR(\"P-Asserted-Identity\"); }\n\t\t| hd_p_preferred_identity error T_CRLF\n\t\t\t{ PARSE_ERROR(\"P-Preferred-Identity\"); }\n\t\t| hd_priority error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Priority\"); }\n\t\t| hd_privacy error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Privacy\"); }\n\t\t| hd_proxy_authenticate error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Proxy-Authenticate\"); }\n\t\t| hd_proxy_authorization error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Proxy-Authorization\"); }\n\t\t| hd_proxy_require error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Proxy-Require\"); }\n\t\t| hd_rack error T_CRLF\n\t\t\t{ PARSE_ERROR(\"RAck\"); }\n\t\t| hd_reason error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Reason\"); }\n\t\t| hd_record_route error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Record-Route\"); }\n\t\t| hd_refer_sub error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Refer-Sub\"); }\n\t\t| hd_refer_to error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Refer-To\"); }\n\t\t| hd_referred_by error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Referred-By\"); }\n\t\t| hd_replaces error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Replaces\"); }\n\t\t| hd_reply_to error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Reply-To\"); }\n\t\t| hd_require error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Require\"); }\n\t\t| hd_request_disposition error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Request-Disposition\"); }\n\t\t| hd_retry_after error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Retry-After\"); }\n\t\t| hd_route error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Route\"); }\n\t\t| hd_rseq error T_CRLF\n\t\t\t{ PARSE_ERROR(\"RSeq\"); }\n\t\t| hd_server error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Server\"); }\n\t\t| hd_service_route error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Service-Route\"); }\n\t\t| hd_session_expires error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Session-Expires\"); }\n\t\t| hd_sip_etag error T_CRLF\n\t\t\t{ PARSE_ERROR(\"SIP-ETag\"); }\n\t\t| hd_sip_if_match error T_CRLF\n\t\t\t{ PARSE_ERROR(\"SIP-If-Match\"); }\n\t\t| hd_subject error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Subject\"); }\n\t\t| hd_subscription_state error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Subscription-State\"); }\n\t\t| hd_supported error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Supported\"); }\n\t\t| hd_timestamp error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Timestamp\"); }\n\t\t| hd_to error T_CRLF\n\t\t\t{ PARSE_ERROR(\"To\"); }\n\t\t| hd_unsupported error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Unsupported\"); }\n\t\t| hd_user_agent error T_CRLF\n\t\t\t{ PARSE_ERROR(\"User-Agent\"); }\n\t\t| hd_via error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Via\"); }\n\t\t| hd_warning error T_CRLF\n\t\t\t{ PARSE_ERROR(\"Warning\"); }\n\t\t| hd_www_authenticate error T_CRLF\n\t\t\t{ PARSE_ERROR(\"WWW-Authenticate\"); }\n;\n\n\n// KLUDGE\n// These rules are needed to get the error recovery working.\n// Many header rules start with a context change. As Bison uses\n// one token look ahead to determine a matching rule, the context\n// change must be done before Bison tries to match the error rule.\n// Changing the context header and once again at the header rule\n// does no harm as the context change operator is idem-potent.\n\nhd_accept:\t\tT_HDR_ACCEPT ':'\n;\nhd_accept_encoding:\tT_HDR_ACCEPT_ENCODING ':'\n;\nhd_accept_language:\tT_HDR_ACCEPT_LANGUAGE ':' { CTXT_LANG; }\n;\nhd_alert_info:\t\tT_HDR_ALERT_INFO ':'\n;\nhd_allow:\t\tT_HDR_ALLOW ':'\n;\nhd_allow_events:\tT_HDR_ALLOW_EVENTS ':'\n;\nhd_authentication_info:\tT_HDR_AUTHENTICATION_INFO ':'\n;\nhd_authorization:\tT_HDR_AUTHORIZATION ':' { CTXT_AUTH_SCHEME; }\n;\nhd_call_id:\t\tT_HDR_CALL_ID ':' { CTXT_WORD; }\n;\nhd_call_info:\t\tT_HDR_CALL_INFO ':'\n;\nhd_contact:\t\tT_HDR_CONTACT ':' { CTXT_URI_SPECIAL; }\n;\nhd_content_disp:\tT_HDR_CONTENT_DISP ':'\n;\nhd_content_encoding:\tT_HDR_CONTENT_ENCODING ':'\n;\nhd_content_language:\tT_HDR_CONTENT_LANGUAGE ':' { CTXT_LANG; }\n;\nhd_content_length:\tT_HDR_CONTENT_LENGTH ':' { CTXT_NUM; }\n;\nhd_content_type:\tT_HDR_CONTENT_TYPE ':'\n;\nhd_cseq:\t\tT_HDR_CSEQ ':' { CTXT_NUM; }\n;\nhd_date:\t\tT_HDR_DATE ':' { CTXT_DATE;}\n;\nhd_error_info:\t\tT_HDR_ERROR_INFO ':'\n;\nhd_event:\t\tT_HDR_EVENT ':'\n;\nhd_expires:\t\tT_HDR_EXPIRES ':' { CTXT_NUM; }\n;\nhd_from:\t\tT_HDR_FROM ':' { CTXT_URI_SPECIAL; }\n;\nhd_in_reply_to:\t\tT_HDR_IN_REPLY_TO ':' { CTXT_WORD; }\n;\nhd_max_forwards:\tT_HDR_MAX_FORWARDS ':' { CTXT_NUM; }\n;\nhd_min_expires:\t\tT_HDR_MIN_EXPIRES ':' { CTXT_NUM; }\n;\nhd_min_se:\t\tT_HDR_MIN_SE ':' { CTXT_NUM; }\n;\nhd_mime_version:\tT_HDR_MIME_VERSION ':'\n;\nhd_organization:\tT_HDR_ORGANIZATION ':' { CTXT_LINE; }\n;\nhd_p_asserted_identity:\tT_HDR_P_ASSERTED_IDENTITY ':' { CTXT_URI_SPECIAL; }\n;\nhd_p_preferred_identity: T_HDR_P_PREFERRED_IDENTITY ':' { CTXT_URI_SPECIAL; }\n;\nhd_priority:\t\tT_HDR_PRIORITY ':'\n;\nhd_privacy:\t\tT_HDR_PRIVACY ':'\n;\nhd_proxy_authenticate:\tT_HDR_PROXY_AUTHENTICATE ':' { CTXT_AUTH_SCHEME; }\n;\nhd_proxy_authorization:\tT_HDR_PROXY_AUTHORIZATION ':' { CTXT_AUTH_SCHEME; }\n;\nhd_proxy_require:\tT_HDR_PROXY_REQUIRE ':'\n;\nhd_rack:\t\tT_HDR_RACK ':' { CTXT_NUM; }\n;\nhd_reason:\t\tT_HDR_REASON ':'\n;\nhd_record_route:\tT_HDR_RECORD_ROUTE ':' { CTXT_URI; }\n;\nhd_refer_sub:\t\tT_HDR_REFER_SUB ':'\n;\nhd_refer_to:\t\tT_HDR_REFER_TO ':' { CTXT_URI_SPECIAL; }\n;\nhd_referred_by:\t\tT_HDR_REFERRED_BY ':' { CTXT_URI_SPECIAL; }\n;\nhd_replaces:\t\tT_HDR_REPLACES ':' { CTXT_WORD; }\n;\nhd_reply_to:\t\tT_HDR_REPLY_TO ':' { CTXT_URI_SPECIAL; }\n;\nhd_require:\t\tT_HDR_REQUIRE ':'\n;\nhd_request_disposition:\tT_HDR_REQUEST_DISPOSITION ':'\n;\nhd_retry_after:\t\tT_HDR_RETRY_AFTER ':' { CTXT_NUM; }\n;\nhd_route:\t\tT_HDR_ROUTE ':' { CTXT_URI; }\n;\nhd_rseq:\t\tT_HDR_RSEQ ':' { CTXT_NUM; }\n;\nhd_server:\t\tT_HDR_SERVER ':'\n;\nhd_service_route:\tT_HDR_SERVICE_ROUTE ':' { CTXT_URI; }\n;\nhd_session_expires:\tT_HDR_SESSION_EXPIRES ':' { CTXT_NUM; }\n;\nhd_sip_etag:\t\tT_HDR_SIP_ETAG ':'\n;\nhd_sip_if_match:\tT_HDR_SIP_IF_MATCH ':'\n;\nhd_subject:\t\tT_HDR_SUBJECT ':' { CTXT_LINE; }\n;\nhd_subscription_state:\tT_HDR_SUBSCRIPTION_STATE ':'\n;\nhd_supported:\t\tT_HDR_SUPPORTED ':'\n;\nhd_timestamp:\t\tT_HDR_TIMESTAMP ':' { CTXT_NUM; }\n;\nhd_to:\t\t\tT_HDR_TO ':' { CTXT_URI_SPECIAL; }\n;\nhd_unsupported:\t\tT_HDR_UNSUPPORTED ':'\n;\nhd_user_agent:\t\tT_HDR_USER_AGENT ':'\n;\nhd_via:\t\t\tT_HDR_VIA ':'\n;\nhd_warning:\t\tT_HDR_WARNING ':' { CTXT_NUM; }\n;\nhd_www_authenticate:\tT_HDR_WWW_AUTHENTICATE ':' { CTXT_AUTH_SCHEME; }\n;\n\nhdr_accept:\t  /* empty */\n\t\t| media_range parameters {\n\t\t\t$1->add_params(*$2);\n\t\t\tMSG->hdr_accept.add_media(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n\t\t| hdr_accept ',' media_range parameters {\n\t\t\t$3->add_params(*$4);\n\t\t\tMSG->hdr_accept.add_media(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nmedia_range:\t  T_TOKEN '/' T_TOKEN { $$ = new t_media(tolower(*$1), tolower(*$3));\n\t\t\t\t\tMEMMAN_NEW($$);\n\t\t\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nparameters:\t  /* empty */\t{ $$ = new list<t_parameter>; MEMMAN_NEW($$); }\n\t\t| parameters ';' parameter {\n\t\t\t$1->push_back(*$3);\n\t\t\t$$ = $1;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nparameter:\t  T_TOKEN {\n\t\t\t$$ = new t_parameter(tolower(*$1));\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_TOKEN '=' { CTXT_PARAMVAL; } parameter_val { CTXT_INITIAL; } {\n\t\t\t$$ = new t_parameter(tolower(*$1), *$4);\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nparameter_val:\t  T_PARAMVAL {\n\t\t\t$$ = $1; }\n\t\t| T_QSTRING {\n\t\t\t$$ = $1; }\n;\n\nhdr_accept_encoding: content_coding {\n\t\t\tMSG->hdr_accept_encoding.add_coding(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t   | hdr_accept_encoding ',' content_coding {\n\t\t\tMSG->hdr_accept_encoding.add_coding(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\ncontent_coding:\t  T_TOKEN {\n\t\t\t$$ = new t_coding(tolower(*$1));\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_TOKEN q_factor {\n\t\t\t$$ = new t_coding(tolower(*$1));\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->q = $2;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nq_factor:\t  ';' parameter {\n\t\t\tif ($2->name != \"q\") YYERROR;\n\t\t\t$$ = atof($2->value.c_str());\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\t}\n;\n\nhdr_accept_language: { CTXT_LANG; } language {\n\t\t\tMSG->hdr_accept_language.add_language(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n\t\t   | hdr_accept_language ',' { CTXT_LANG; } language {\n\t\t\tMSG->hdr_accept_language.add_language(*$4);\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nlanguage:\t  T_LANG {\n\t\t\tCTXT_INITIAL;\n\t\t  \t$$ = new t_language(tolower(*$1));\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_LANG { CTXT_INITIAL; } q_factor {\n\t\t\t$$ = new t_language(tolower(*$1));\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->q = $3;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nhdr_alert_info:\t  alert_param {\n\t\t\tMSG->hdr_alert_info.add_param(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_alert_info ',' alert_param {\n\t\t\tMSG->hdr_alert_info.add_param(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nalert_param:\t  '<' { CTXT_URI; } T_URI { CTXT_INITIAL; } '>' parameters {\n\t\t  \t$$ = new t_alert_param();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->uri.set_url(*$3);\n\t\t\t$$->parameter_list = *$6;\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t \n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($6); delete $6; }\n;\n\nhdr_allow:\t  T_TOKEN {\n\t\t\tMSG->hdr_allow.add_method(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_allow ',' T_TOKEN {\n\t\t\tMSG->hdr_allow.add_method(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_call_id:\t  { CTXT_WORD; } call_id { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_call_id.set_call_id(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\ncall_id:\t  T_WORD { $$ = $1; }\n\t\t| T_WORD '@' T_WORD {\n\t\t\t$$ = new string(*$1 + '@' + *$3);\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_call_info:\t  info_param {\n\t\t\tMSG->hdr_call_info.add_param(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_call_info ',' info_param {\n\t\t\tMSG->hdr_call_info.add_param(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\ninfo_param:\t  '<' { CTXT_URI; } T_URI { CTXT_INITIAL; } '>' parameters {\n\t\t  \t$$ = new t_info_param();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->uri.set_url(*$3);\n\t\t\t$$->parameter_list = *$6;\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($6); delete $6; }\n;\n\nhdr_contact:\t  { CTXT_URI_SPECIAL; } T_URI_WILDCARD { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_contact.set_any(); }\n\t\t| contacts {\n\t\t\tMSG->hdr_contact.add_contacts(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\ncontacts:\t  contact_param {\n\t\t\t$$ = new list<t_contact_param>;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->push_back(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| contacts ',' contact_param {\n\t\t\t$1->push_back(*$3);\n\t\t\t$$ = $1;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\ncontact_param:\t  contact_addr parameters {\n\t\t\t$$ = $1;\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $2->begin(); i != $2->end(); i++) {\n\t\t\t\tif (i->name == \"q\") {\n\t\t\t\t\t$$->set_qvalue(atof(i->value.c_str()));\n\t\t\t\t} else if (i->name == \"expires\") {\n\t\t\t\t\t$$->set_expires(strtoul(\n\t\t\t\t\t\ti->value.c_str(), NULL, 10));\n\t\t\t\t} else {\n\t\t\t\t\t$$->add_extension(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\ncontact_addr:\t  { CTXT_URI_SPECIAL; } T_URI { CTXT_INITIAL; } {\n\t\t\t$$ = new t_contact_param();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->uri.set_url(*$2);\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n\t\t| { CTXT_URI_SPECIAL; } display_name '<' { CTXT_URI; } T_URI { CTXT_INITIAL; } '>' {\n\t\t\t$$ = new t_contact_param();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->display = *$2;\n\t\t\t$$->uri.set_url(*$5);\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t \n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($5); delete $5; }\n;\n\ndisplay_name:\t  /* empty */ { $$ = new string(); MEMMAN_NEW($$); }\n\t\t| T_DISPLAY {\n\t\t\t$$ = new string(rtrim(*$1));\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_QSTRING { $$ = $1; }\n;\n\nhdr_content_disp: T_TOKEN parameters {\n\t\t\tMSG->hdr_content_disp.set_type(tolower(*$1));\n\t\t\t\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $2->begin(); i != $2->end(); i++) {\n\t\t\t\tif (i->name == \"filename\") {\n\t\t\t\t\tMSG->hdr_content_disp.set_filename(i->value);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_content_disp.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_content_encoding: content_coding {\n\t\t\tMSG->hdr_content_encoding.add_coding(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t    | hdr_content_encoding ',' content_coding {\n\t\t\tMSG->hdr_content_encoding.add_coding(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_content_language: { CTXT_LANG; } language {\n\t\t\tMSG->hdr_content_language.add_language(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n\t\t    | hdr_content_language ',' { CTXT_LANG; } language {\n\t\t\tMSG->hdr_content_language.add_language(*$4);\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_content_length: { CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_content_length.set_length($2); }\n;\n\nhdr_content_type:  media_range parameters {\n\t\t\t$1->add_params(*$2);\n\t\t\tMSG->hdr_content_type.set_media(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_cseq: \t{ CTXT_NUM; } T_NUM { CTXT_INITIAL; } T_TOKEN {\n\t\t\tMSG->hdr_cseq.set_seqnr($2);\n\t\t\tMSG->hdr_cseq.set_method(*$4);\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_date:\t{ CTXT_DATE;}\n\t\t  T_WKDAY ',' T_NUM T_MONTH T_NUM\n\t\t  T_NUM ':' T_NUM ':' T_NUM T_GMT\n\t\t{ CTXT_INITIAL; } {\n\t\t\tstruct tm t;\n\t\t\tt.tm_mday = $4;\n\t\t\tt.tm_mon = $5;\n\t\t\tt.tm_year = $6 - 1900;\n\t\t\tt.tm_hour = $7;\n\t\t\tt.tm_min = $9;\n\t\t\tt.tm_sec = $11;\n\t\t\tMSG->hdr_date.set_date_gm(&t); }\n;\n\nhdr_error_info:\t  error_param {\n\t\t\tMSG->hdr_error_info.add_param(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_error_info ',' error_param {\n\t\t\tMSG->hdr_error_info.add_param(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nerror_param:\t  '<' { CTXT_URI; } T_URI { CTXT_INITIAL; } '>' parameters {\n\t\t  \t$$ = new t_error_param();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->uri.set_url(*$3);\n\t\t\t$$->parameter_list = *$6;\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($6); delete $6; }\n;\n\nhdr_expires:\t{ CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_expires.set_time($2); }\n;\n\nhdr_from:\t  { CTXT_URI_SPECIAL; } from_addr parameters {\n\t\t\tMSG->hdr_from.set_display($2->display);\n\t\t\tMSG->hdr_from.set_uri($2->uri);\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $3->begin(); i != $3->end(); i++) {\n\t\t\t\tif (i->name == \"tag\") {\n\t\t\t\t\tMSG->hdr_from.set_tag(i->value);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_from.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nfrom_addr:\t  T_URI { CTXT_INITIAL; } {\n\t\t\t$$ = new t_identity();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->set_uri(*$1);\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| display_name '<' { CTXT_URI; } T_URI { CTXT_INITIAL; } '>' {\n\t\t\t$$ = new t_identity();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->set_display(*$1);\n\t\t\t$$->set_uri(*$4);\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_in_reply_to:  { CTXT_WORD; } call_id { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_in_reply_to.add_call_id(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n\t\t| hdr_in_reply_to ',' { CTXT_WORD; } call_id { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_in_reply_to.add_call_id(*$4);\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_max_forwards: { CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_max_forwards.set_max_forwards($2); }\n;\n\nhdr_min_expires:  { CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_min_expires.set_time($2); }\n;\n\nhdr_min_se:\t{ CTXT_NUM; } T_NUM { CTXT_INITIAL; } parameters {\n\t\t\tMSG->hdr_min_se.set_time($2);\n\t\t\tMSG->hdr_min_se.set_params(*$4);\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_mime_version: T_TOKEN {\n\t\t\tMSG->hdr_mime_version.set_version(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nhdr_organization: { CTXT_LINE; } T_LINE { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_organization.set_name(trim(*$2));\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_p_asserted_identity:  { CTXT_URI_SPECIAL; } from_addr {\n\t\t\t\tMSG->hdr_p_asserted_identity.add_identity(*$2);\n\t\t\t\tMEMMAN_DELETE($2); delete $2; }\t\n\t\t\t| hdr_p_asserted_identity ',' from_addr {\n\t\t\t\tMSG->hdr_p_asserted_identity.add_identity(*$3);\n\t\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_p_preferred_identity: { CTXT_URI_SPECIAL; } from_addr {\n\t\t\t\tMSG->hdr_p_preferred_identity.add_identity(*$2);\n\t\t\t\tMEMMAN_DELETE($2); delete $2; }\t\n\t\t\t| hdr_p_preferred_identity ',' from_addr {\n\t\t\t\tMSG->hdr_p_preferred_identity.add_identity(*$3);\n\t\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_priority:\t  T_TOKEN {\n\t\t\tMSG->hdr_priority.set_priority(tolower(*$1));\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nhdr_privacy:\t  T_TOKEN {\n\t\t\tMSG->hdr_privacy.add_privacy(tolower(*$1));\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_privacy ';' T_TOKEN {\n\t\t\tMSG->hdr_privacy.add_privacy(tolower(*$3));\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_proxy_require:   T_TOKEN {\n\t\t\tMSG->hdr_proxy_require.add_feature(tolower(*$1));\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t   | hdr_proxy_require ',' T_TOKEN {\n\t\t\tMSG->hdr_proxy_require.add_feature(tolower(*$3));\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_reason:\t  reason_param {\n\t\t\tMSG->hdr_reason.add_reason(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_reason ',' reason_param {\n\t\t\tMSG->hdr_reason.add_reason(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nreason_param:\t  T_TOKEN parameters {\n\t\t\t$$ = new t_reason(*$1);\n\t\t\tMEMMAN_NEW($$);\n\t\t\tfor (const auto &param : *$2) {\n\t\t\t\tif (param.name == \"cause\") {\n\t\t\t\t\t$$->set_cause(param.value);\n\t\t\t\t} else if (param.name == \"text\") {\n\t\t\t\t\t$$->set_text(param.value);\n\t\t\t\t} else {\n\t\t\t\t\t$$->add_extension(param);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_record_route:   rec_route {\n\t\t\tMSG->hdr_record_route.add_route(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t  | hdr_record_route ',' rec_route {\n\t\t  \tMSG->hdr_record_route.add_route(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nrec_route:\t{ CTXT_URI; } display_name '<' T_URI { CTXT_INITIAL; } '>'\n\t\tparameters {\n\t\t\t$$ = new t_route;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->display = *$2;\n\t\t\t$$->uri.set_url(*$4);\n\t\t\t$$->set_params(*$7);\n\n\t\t\tif (!$$->uri.is_valid()) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\t \n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($4); delete $4;\n\t\t\tMEMMAN_DELETE($7); delete $7; }\n;\n\nhdr_service_route:  rec_route {\n\t\t\tMSG->hdr_service_route.add_route(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t  | hdr_service_route ',' rec_route {\n\t\t  \tMSG->hdr_service_route.add_route(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_replaces:\t  { CTXT_WORD; } call_id { CTXT_INITIAL; } parameters {\n\t\t\tMSG->hdr_replaces.set_call_id(*$2);\n\t\t\t\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $4->begin(); i != $4->end(); i++) {\n\t\t\t\tif (i->name == \"to-tag\") {\n\t\t\t\t\tMSG->hdr_replaces.set_to_tag(i->value);\n\t\t\t\t} else if (i->name == \"from-tag\") {\n\t\t\t\t\tMSG->hdr_replaces.set_from_tag(i->value);\n\t\t\t\t} else if (i->name == \"early-only\") {\n\t\t\t\t\tMSG->hdr_replaces.set_early_only(true);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_replaces.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!MSG->hdr_replaces.is_valid()) YYERROR;\n\t\t\t\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_reply_to:  { CTXT_URI_SPECIAL; } from_addr parameters {\n\t\t\tMSG->hdr_reply_to.set_display($2->display);\n\t\t\tMSG->hdr_reply_to.set_uri($2->uri);\n\t\t\tMSG->hdr_reply_to.set_params(*$3);\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_require:\t  T_TOKEN {\n\t\t\tMSG->hdr_require.add_feature(tolower(*$1));\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_proxy_require ',' T_TOKEN {\n\t\t\tMSG->hdr_require.add_feature(tolower(*$3));\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_retry_after:  { CTXT_NUM; } T_NUM { CTXT_INITIAL; } comment_opt parameters {\n\t\t\tMSG->hdr_retry_after.set_time($2);\n\t\t\tMSG->hdr_retry_after.set_comment(*$4);\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $5->begin(); i != $5->end(); i++) {\n\t\t\t\tif (i->name == \"duration\") {\n\t\t\t\t\tint d = strtoul(i->value.c_str(), NULL, 10);\n\t\t\t\t\tMSG->hdr_retry_after.set_duration(d);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_retry_after.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($4); delete $4;\n\t\t\tMEMMAN_DELETE($5); delete $5; }\n;\n\n/* shift/reduce conflict: `T_TOKEN comment` can be parsed as 1 or 2 servers */\ncomment_opt:\t  /* empty */ { $$ = new string(); MEMMAN_NEW($$); }\n\t\t| comment { $$ = $1; }\n;\n\ncomment:\t  '(' { CTXT_COMMENT; } T_COMMENT { CTXT_INITIAL; } ')' {\n\t\t\t$$ = $3; }\n;\n\nhdr_route:\t  rec_route {\n\t\t\tMSG->hdr_route.add_route(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_route ',' rec_route {\n\t\t  \tMSG->hdr_route.add_route(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_server:\t  server {\n\t\t\tMSG->hdr_server.add_server(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_server server {\n\t\t\tMSG->hdr_server.add_server(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nserver:\t\t  comment {\n\t\t\t$$ = new t_server();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->comment = *$1;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_TOKEN comment_opt {\n\t\t\t$$ = new t_server();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->product = *$1;\n\t\t\t$$->comment = *$2;\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n\t\t| T_TOKEN '/' T_TOKEN comment_opt {\n\t\t\t$$ = new t_server();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->product = *$1;\n\t\t\t$$->version = *$3;\n\t\t\t$$->comment = *$4;\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_session_expires:\t{ CTXT_NUM; } T_NUM { CTXT_INITIAL; } parameters {\n\t\t\tMSG->hdr_session_expires.set_time($2);\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $4->begin(); i != $4->end(); i++) {\n\t\t\t\tif (i->name == \"refresher\") {\n\t\t\t\t\tMSG->hdr_session_expires.set_refresher(i->value);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_session_expires.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nhdr_subject:\t{ CTXT_LINE; } T_LINE { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_subject.set_subject(trim(*$2));\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_supported:\t  /* empty */ {\n\t\t\tMSG->hdr_supported.set_empty(); }\n\t\t| T_TOKEN {\n\t\t\tMSG->hdr_supported.add_feature(tolower(*$1));\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_supported ',' T_TOKEN {\n\t\t\tMSG->hdr_supported.add_feature(tolower(*$3));\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_timestamp:\t  { CTXT_NUM; } hdr_timestamp1 { CTXT_INITIAL; }\n;\n\nhdr_timestamp1:\t  timestamp {\n\t\t\tMSG->hdr_timestamp.set_timestamp($1); }\n\t\t| timestamp delay {\n\t\t\tMSG->hdr_timestamp.set_timestamp($1);\n\t\t\tMSG->hdr_timestamp.set_delay($2); }\n;\n\ntimestamp:\t  T_NUM { $$ = $1; }\n\t\t| T_NUM '.' T_NUM {\n\t\t\tstring s = int2str($1) + '.' + int2str($3);\n\t\t\t$$ = atof(s.c_str()); }\n;\n\ndelay:\t  \t  T_NUM { $$ = $1; }\n\t\t| T_NUM '.' T_NUM {\n\t\t\tstring s = int2str($1) + '.' + int2str($3);\n\t\t\t$$ = atof(s.c_str()); }\n;\n\nhdr_to:\t\t  { CTXT_URI_SPECIAL; } from_addr parameters {\n\t\t\tMSG->hdr_to.set_display($2->display);\n\t\t\tMSG->hdr_to.set_uri($2->uri);\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $3->begin(); i != $3->end(); i++) {\n\t\t\t\tif (i->name == \"tag\") {\n\t\t\t\t\tMSG->hdr_to.set_tag(i->value);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_to.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_unsupported:  T_TOKEN {\n\t\t\tMSG->hdr_unsupported.add_feature(tolower(*$1));\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_unsupported ',' T_TOKEN {\n\t\t\tMSG->hdr_unsupported.add_feature(tolower(*$3));\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_user_agent:\t  server {\n\t\t\tMSG->hdr_user_agent.add_server(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_user_agent server {\n\t\t\tMSG->hdr_user_agent.add_server(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_via:\t  via_parm {\n\t\t\tMSG->hdr_via.add_via(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_via ',' via_parm {\n\t\t\tMSG->hdr_via.add_via(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nvia_parm:\t  sent_protocol host parameters {\n\t\t\t$$ = $1;\n\t\t\t$$->host = $2->host;\n\t\t\t$$->port = $2->port;\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $3->begin(); i != $3->end(); i++) {\n\t\t\t\tif (i->name == \"ttl\") {\n\t\t\t\t\t$$->ttl = atoi(i->value.c_str());\n\t\t\t\t} else if (i->name == \"maddr\") {\n\t\t\t\t\t$$->maddr = i->value;\n\t\t\t\t} else if (i->name == \"received\") {\n\t\t\t\t\t$$->received = i->value;\n\t\t\t\t} else if (i->name == \"branch\") {\n\t\t\t\t\t$$->branch = i->value;\n\t\t\t\t} else if (i->name == \"rport\") {\n\t\t\t\t\t$$->rport_present = true;\n\t\t\t\t\tif (i->type == t_parameter::VALUE) {\n\t\t\t\t\t\t$$->rport =\n\t\t\t\t\t\t\tatoi(i->value.c_str());\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$$->add_extension(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nsent_protocol:\t  T_TOKEN '/' T_TOKEN '/' T_TOKEN {\n\t\t\t$$ = new t_via();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->protocol_name = toupper(*$1);\n\t\t\t$$->protocol_version = *$3;\n\t\t\t$$->transport = toupper(*$5);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($5); delete $5; }\n;\n\nhost:\t\t  T_TOKEN {\n\t\t\t$$ = new t_via();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->host = *$1;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_TOKEN ':' { CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\tif ($4 > 65535) YYERROR;\n\t\t\t\n\t\t\t$$ = new t_via();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->host = *$1;\n\t\t\t$$->port = $4;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| ipv6reference {\n\t\t\t$$ = new t_via();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->host = *$1;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| ipv6reference ':' { CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\t$$ = new t_via();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->host = *$1;\n\t\t\t$$->port = $4;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nipv6reference:\t '[' { CTXT_IPV6ADDR; } T_IPV6ADDR { CTXT_INITIAL; } ']' {\n\t\t\t// TODO: check correct format of IPv6 address\n\t\t\t$$ = new string('[' + *$3 + ']');\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($3); }\n;\n\nhdr_warning:\t  warning {\n\t\t\tMSG->hdr_warning.add_warning(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| hdr_warning ',' warning {\n\t\t\tMSG->hdr_warning.add_warning(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nwarning:\t  { CTXT_NUM; } T_NUM { CTXT_INITIAL; } host T_QSTRING {\n\t\t\t$$ = new t_warning();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->code = $2;\n\t\t\t$$->host = $4->host;\n\t\t\t$$->port = $4->port;\n\t\t\t$$->text = *$5;\n\t\t\tMEMMAN_DELETE($4); delete $4;\n\t\t\tMEMMAN_DELETE($5); delete $5; }\n;\n\nhdr_unknown:\t  { CTXT_LINE; } T_LINE { CTXT_INITIAL; } { $$ = $2; }\n;\n\nainfo:\t\t  parameter {\n\t\t\tif ($1->name == \"nextnonce\")\n\t\t\t\tMSG->hdr_auth_info.set_next_nonce($1->value);\n\t\t \telse if ($1->name == \"qop\")\n\t\t\t\tMSG->hdr_auth_info.set_message_qop($1->value);\n\t\t\telse if ($1->name == \"rspauth\")\n\t\t\t\tMSG->hdr_auth_info.set_response_auth($1->value);\n\t\t\telse if ($1->name == \"cnonce\")\n\t\t\t\tMSG->hdr_auth_info.set_cnonce($1->value);\n\t\t\telse if ($1->name == \"nc\") {\n\t\t\t\tMSG->hdr_auth_info.set_nonce_count(\n\t\t\t\t\t\t\thex2int($1->value));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tYYERROR;\n\t\t\t}\n\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nhdr_authentication_info:  ainfo\n\t\t\t| hdr_authentication_info ',' ainfo\n;\n\ndigest_response:  parameter {\n\t\t\t$$ = new t_digest_response();\n\t\t\tMEMMAN_NEW($$);\n\t\t\tif (!$$->set_attr(*$1)) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| digest_response ',' parameter {\n\t\t\t$$ = $1;\n\t\t\tif (!$$->set_attr(*$3)) {\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nauth_params:\t  parameter {\n\t\t\t$$ = new list<t_parameter>;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->push_back(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| auth_params ',' parameter {\n\t\t\t$$ = $1;\n\t\t\t$$->push_back(*$3);\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\ncredentials:\t  T_AUTH_DIGEST { CTXT_INITIAL; } digest_response {\n\t\t\t$$ = new t_credentials;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->auth_scheme = AUTH_DIGEST;\n\t\t\t$$->digest_response = *$3;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n\t\t| T_AUTH_OTHER { CTXT_INITIAL; } auth_params {\n\t\t\t$$ = new t_credentials;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->auth_scheme = *$1;\n\t\t\t$$->auth_params = *$3;\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_authorization:  { CTXT_AUTH_SCHEME; } credentials {\n\t\t\tMSG->hdr_authorization.add_credentials(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\ndigest_challenge: parameter {\n\t\t\t$$ = new t_digest_challenge();\n\t\t\tMEMMAN_NEW($$);\n\t\t\tif (!$$->set_attr(*$1)) {\n\t\t\t\tMEMMAN_DELETE($$); delete $$;\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| digest_challenge ',' parameter {\n\t\t\t$$ = $1;\n\t\t\tif (!$$->set_attr(*$3)) {\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nchallenge:\t  T_AUTH_DIGEST { CTXT_INITIAL; } digest_challenge {\n\t\t\t$$ = new t_challenge;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->auth_scheme = AUTH_DIGEST;\n\t\t\t$$->digest_challenge = *$3;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n\t\t| T_AUTH_OTHER { CTXT_INITIAL; } auth_params {\n\t\t\t$$ = new t_challenge;\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->auth_scheme = *$1;\n\t\t\t$$->auth_params = *$3;\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_proxy_authenticate:\t{ CTXT_AUTH_SCHEME; } challenge {\n\t\t\t\tMSG->hdr_proxy_authenticate.set_challenge(*$2);\n\t\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_proxy_authorization: { CTXT_AUTH_SCHEME; } credentials {\n\t\t\t\tMSG->hdr_proxy_authorization.\n\t\t\t\t\t\t\tadd_credentials(*$2);\n\t\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_www_authenticate:\t{ CTXT_AUTH_SCHEME; } challenge {\n\t\t\t\tMSG->hdr_www_authenticate.set_challenge(*$2);\n\t\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_rseq: \t{ CTXT_NUM; } T_NUM { CTXT_INITIAL; } {\n\t\t\tMSG->hdr_rseq.set_resp_nr($2); }\n;\n\nhdr_rack: \t{ CTXT_NUM; } T_NUM T_NUM { CTXT_INITIAL; } T_TOKEN {\n\t\t\tMSG->hdr_rack.set_resp_nr($2);\n\t\t\tMSG->hdr_rack.set_cseq_nr($3);\n\t\t\tMSG->hdr_rack.set_method(*$5);\n\t\t\tMEMMAN_DELETE($5); delete $5; }\n;\n\nhdr_event:\tT_TOKEN parameters {\n\t\t\tMSG->hdr_event.set_event_type(tolower(*$1));\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $2->begin(); i != $2->end(); i++) {\n\t\t\t\tif (i->name == \"id\") {\n\t\t\t\t\tMSG->hdr_event.set_id(i->value);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_event.add_event_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_allow_events:\tT_TOKEN {\n\t\t\t\tMSG->hdr_allow_events.add_event_type(tolower(*$1));\n\t\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t      | hdr_allow_events ',' T_TOKEN {\n\t\t      \t\tMSG->hdr_allow_events.add_event_type(tolower(*$3));\n\t\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_subscription_state:\tT_TOKEN parameters {\n\t\t\t\tMSG->hdr_subscription_state.set_substate(tolower(*$1));\n\t\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\t\tfor (i = $2->begin(); i != $2->end(); i++) {\n\t\t\t\t\tif (i->name == \"reason\") {\n\t\t\t\t\t\tMSG->hdr_subscription_state.\n\t\t\t\t\t\t\tset_reason(tolower(i->value));\n\t\t\t\t\t} else if (i->name == \"expires\") {\n\t\t\t\t\t\tMSG->hdr_subscription_state.\n\t\t\t\t\t\t\tset_expires(strtoul(\n\t\t\t\t\t\t\t  i->value.c_str(),\n\t\t\t\t\t\t\t  NULL, 10));\n\t\t\t\t\t} else if (i->name == \"retry-after\") {\n\t\t\t\t\t\tMSG->hdr_subscription_state.\n\t\t\t\t\t\t\tset_retry_after(strtoul(\n\t\t\t\t\t\t\t  i->value.c_str(),\n\t\t\t\t\t\t\t  NULL, 10));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tMSG->hdr_subscription_state.\n\t\t\t\t\t\t\tadd_extension(*i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_refer_to:\t  { CTXT_URI_SPECIAL; } from_addr parameters {\n\t\t\tMSG->hdr_refer_to.set_display($2->display);\n\t\t\tMSG->hdr_refer_to.set_uri($2->uri);\n\t\t\tMSG->hdr_refer_to.set_params(*$3);\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_referred_by:  { CTXT_URI_SPECIAL; } from_addr parameters {\n\t\t\tMSG->hdr_referred_by.set_display($2->display);\n\t\t\tMSG->hdr_referred_by.set_uri($2->uri);\n\t\t\tlist<t_parameter>::const_iterator i;\n\t\t\tfor (i = $3->begin(); i != $3->end(); i++) {\n\t\t\t\tif (i->name == \"cid\") {\n\t\t\t\t\tMSG->hdr_referred_by.set_cid(i->value);\n\t\t\t\t} else {\n\t\t\t\t\tMSG->hdr_referred_by.add_param(*i);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nhdr_refer_sub:\t  T_TOKEN parameters {\n\t\t\tstring value(tolower(*$1));\n\t\t\tif (value != \"true\" && value != \"false\") {\n\t\t\t\tYYERROR;\n\t\t\t}\n\t\t\tMSG->hdr_refer_sub.set_create_refer_sub(value == \"true\");\n\t\t\tMSG->hdr_refer_sub.set_extensions(*$2);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nhdr_sip_etag:\t  T_TOKEN {\n\t\t\tMSG->hdr_sip_etag.set_etag(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\t\t\t\nhdr_sip_if_match: T_TOKEN {\n\t\t\tMSG->hdr_sip_if_match.set_etag(*$1);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nhdr_request_disposition:  T_TOKEN {\n\t\t\t\tbool ret = MSG->hdr_request_disposition.set_directive(*$1);\n\t\t\t\tif (!ret) YYERROR;\n\t\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t\t| hdr_request_disposition ',' T_TOKEN {\n\t\t\t\tbool ret = MSG->hdr_request_disposition.set_directive(*$3);\n\t\t\t\tif (!ret) YYERROR;\n\t\t\t\tMEMMAN_DELETE($3); delete $3; }\n\n%%\n\nvoid\nyyerror (const char *s)  /* Called by yyparse on error */\n{\n  // printf (\"%s\\n\", s);\n}\n"
  },
  {
    "path": "src/parser/request.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"request.h\"\n#include \"util.h\"\n#include \"parse_ctrl.h\"\n#include \"protocol.h\"\n#include \"milenage.h\"\n#include \"audits/memman.h\"\n#include <sstream>\n#include <ucommon/secure.h>\n\nusing namespace UCOMMON_NAMESPACE;\n\n// AKAv1-MD5 algorithm specific helpers\n\n#define B64_ENC_SZ(x) (4 * ((x + 2) / 3))\n#define B64_DEC_SZ(x) (3 * ((x + 3) / 4))\n\nint b64_enc(const u8 * src, u8 * dst, int len)\n{\n\tstatic char tbl[64] = {\n\t\t'A','B','C','D','E','F','G','H',\n\t\t'I','J','K','L','M','N','O','P',\n\t\t'Q','R','S','T','U','V','W','X',\n\t\t'Y','Z','a','b','c','d','e','f',\n\t\t'g','h','i','j','k','l','m','n',\n\t\t'o','p','q','r','s','t','u','v',\n\t\t'w','x','y','z','0','1','2','3',\n\t\t'4','5','6','7','8','9','+','/'\n\t};\n\tu8 * dst0 = dst;\n\tint i, v, div = len / 3, mod = len % 3;\n\n\tfor (i = 0; i < div * 3; i += 3) {\n\t\t\n\t\tv = (src[i+0] & 0xfc) >> 2;\n\t\t*(dst++) = tbl[v];\n\n\t\tv  = (src[i+0] & 0x03) << 4;\n\t\tv |= (src[i+1] & 0xf0) >> 4;\n\t\t*(dst++) = tbl[v];\n\n\t\tv  = (src[i+1] & 0x0f) << 2;\n\t\tv |= (src[i+2] & 0xc0) >> 6;\n\t\t*(dst++) = tbl[v];\n\t\t\n\t\tv = src[i+2] & 0x3f;\n\t\t*(dst++) = tbl[v];\n\t}\n\n\tif (mod == 1) {\n\t\tv = (src[i+0] & 0xfc) >> 2;\n\t\t*(dst++) = tbl[v];\n\t\t\n\t\tv = (src[i+0] & 0x03) << 4;\n\t\t*(dst++) = tbl[v];\n\n\t\t*(dst++) = '=';\n\t\t*(dst++) = '=';\n\t} else if (mod == 2) {\n\t\tv = (src[i+0] & 0xfc) >> 2;\n\t\t*(dst++) = tbl[v];\n\t\t\n\t\tv  = (src[i+0] & 0x03) << 4;\n\t\tv |= (src[i+1] & 0xf0) >> 4;\n\t\t*(dst++) = tbl[v];\n\n\t\tv = (src[i+1] & 0x0f) << 2;\n\t\t*(dst++) = tbl[v];\n\n\t\t*(dst++) = '=';\n\t}\n\t\n\treturn dst - dst0;\n}\n\nstatic int b64_val(u8 x)\n{\n\tif (x >= 'A' && x <= 'Z')\n\t\treturn x - 'A';\n\telse if (x >= 'a' && x <= 'z')\n\t\treturn x - 'a' + 26;\n\telse if (x >= '0' && x <= '9')\n\t\treturn x - '0' + 52;\n\telse if (x == '+')\n\t\treturn 62;\n\telse if (x == '/')\n\t\treturn 63;\n\t//else if (x == '=')\n\t\treturn -1;\n}\n\nint b64_dec(const u8 * src, u8 * dst, int len)\n{\n\tu8 * dst0 = dst;\n\tint i, x1, x2, x3, x4;\n\n\tif (len % 4)\n\t\treturn 0;\n\n\tfor (i=0; i+4 < len; i += 4) {\n\t\tx1 = b64_val(*(src++));\n\t\tx2 = b64_val(*(src++));\n\t\tx3 = b64_val(*(src++));\n\t\tx4 = b64_val(*(src++));\n\t\t\n\t\t*(dst++) = (x1 << 2) | ((x2 & 0x30) >> 4);\n\t\t*(dst++) = ((x2 & 0x0F) << 4) | ((x3 & 0x3C) >> 2);\n\t\t*(dst++) = ((x3 & 0x03) << 6) | (x4 & 0x3F);\n\t}\n\t\n\tif (len) {\n\t\tx1 = b64_val(*(src++));\n\t\tx2 = b64_val(*(src++));\n\t\tx3 = b64_val(*(src++));\n\t\tx4 = b64_val(*(src++));\n\t\t\n\t\t*(dst++) = (x1 << 2) | ((x2 & 0x30) >> 4);\n\t\tif (x3 != -1) {\n\t\t\t*(dst++) = ((x2 & 0x0F) << 4) | ((x3 & 0x3C) >> 2);\n\t\t\tif (x4 != -1)\n\t\t\t\t*(dst++) = ((x3 & 0x03) << 6) | (x4 & 0x3F);\n\t\t}\n\t}\n\n\treturn dst - dst0;\n}\n\n#define HASH_HEX_LEN\t32\n#define HASH_LEN\t\t16\n\n// authentication with AKAv1-MD5 algorithm (RFC 3310)\n\nbool t_request::authorize_akav1_md5(const t_digest_challenge &dchlg,\n\tconst std::string &username, const std::string &passwd, uint8 *op, uint8 *amf,\n\tunsigned long nc,\n\tconst std::string &cnonce, const std::string &qop, std::string &resp,\n\tstd::string &fail_reason) const\n{\n\tu8 nonce64[B64_DEC_SZ(dchlg.nonce.size())];\n\tint len = b64_dec((const u8 *)dchlg.nonce.c_str(), nonce64, dchlg.nonce.size());\n\tu8 rnd[AKA_RANDLEN];\n  \tu8 sqnxoraka[AKA_SQNLEN];\n\tu8 sqn[AKA_SQNLEN];\n\tu8 k[AKA_KLEN];\n\tu8 res[AKA_RESLEN];\n\tu8 ck[AKA_CKLEN];\n\tu8 ik[AKA_IKLEN];\n\tu8 ak[AKA_AKLEN];\n\tint i;\n\n\tif (len < AKA_RANDLEN+AKA_AUTNLEN) {\n\t\tfail_reason = \"nonce base64 data too short (need 32 bytes)\";\n\t\treturn false;\n\t}\n\n\tmemset(rnd, 0, AKA_RANDLEN);\n\tmemset(sqnxoraka, 0, AKA_SQNLEN);\n\tmemset(k, 0, AKA_KLEN);\n\n\tmemcpy(rnd, nonce64, AKA_RANDLEN);\n\tmemcpy(sqnxoraka, nonce64 + AKA_RANDLEN, AKA_SQNLEN);\n\thex2binary(passwd, k);\n\n\tf2345(k, rnd, res, ck, ik, ak, op);\n\t\n\tfor (i=0; i < AKA_SQNLEN; i++)\n    \tsqn[i] = sqnxoraka[i] ^ ak[i];\n\t\n\tstd::string res_str = std::string((char *)res, AKA_RESLEN);\n\t\n\treturn authorize_md5(dchlg, username, res_str, nc, cnonce, qop, \n\t\t\tresp, fail_reason);\n}\n\n// authentication with a given hash algorithm\n\nbool t_request::authorize_generic(const t_digest_challenge &dchlg,\n\tconst std::string &algo,\n\tconst std::string &username, const std::string &passwd, unsigned long nc,\n\tconst std::string &cnonce, const std::string &qop, std::string &resp,\n\tstd::string &fail_reason) const\n{\n\tstd::string A1, A2;\n\t// RFC 2617 3.2.2.2\n\tA1 = username + \":\" + dchlg.realm + \":\" + passwd;\n\n\t// RFC 2617 3.2.2.3\n\tif (cmp_nocase(qop, QOP_AUTH) == 0 || qop == \"\") {\n\t\tA2 = method2str(method, unknown_method) + \":\" + uri.encode();\n\t} else {\n\t\tA2 = method2str(method, unknown_method) + \":\" + uri.encode();\n\t\tA2 += \":\";\n\t\tif (body) {\n\t\t\tdigest_t H_body(algo.c_str());\n\t\t\tH_body.puts(body->encode().c_str());\n\t\t\tA2 += std::string(H_body.str());\n\t\t} else {\n\t\t\tdigest_t H_body(algo.c_str());\n\t\t\tH_body.puts(\"\");\n\t\t\tA2 += std::string(H_body.str());\n\t\t}\n\t}\n\t// RFC 2716 3.2.2.1\n\t// Caculate digest\n\tdigest_t H_A1(algo.c_str());\n\tdigest_t H_A2(algo.c_str());\n\n\tH_A1.puts(A1.c_str());\n\tH_A2.puts(A2.c_str());\n\n\tstd::string x;\n\n\tif (cmp_nocase(qop, QOP_AUTH) == 0 || cmp_nocase(qop, QOP_AUTH_INT) == 0) {\n\t        x = std::string(H_A1.str());\n\t\tx += \":\";\n\t\tx += dchlg.nonce + \":\";\n\t\tx += int2str(nc, \"%08x\") + \":\";\n\t\tx += cnonce + \":\";\n\t\tx += qop + \":\";\n\t\tx += std::string(H_A2.str());\n\t} else {\n\t\tx = std::string(H_A1.str());\n\t\tx += \":\";\n\t\tx += dchlg.nonce + \":\";\n\t\tx += std::string(H_A2.str());\n\t}\n\n\tdigest_t digest(algo.c_str());\n\tdigest.puts(x.c_str());\n\n\tresp = std::string(digest.str());\n\n\treturn true;\n}\n\n// authentication with MD5 algorithm\n\nbool t_request::authorize_md5(const t_digest_challenge &dchlg,\n\tconst std::string &username, const std::string &passwd, unsigned long nc,\n\tconst std::string &cnonce, const std::string &qop, std::string &resp,\n\tstd::string &fail_reason) const\n{\n\tconst std::string algo = \"md5\";\n\treturn authorize_generic(dchlg, algo, username, passwd, nc, cnonce, qop,\n\t\t\tresp, fail_reason);\n}\n\n// authentication with SHA-256 algorithm\n\nbool t_request::authorize_sha256(const t_digest_challenge &dchlg,\n\tconst std::string &username, const std::string &passwd, unsigned long nc,\n\tconst std::string &cnonce, const std::string &qop, std::string &resp,\n\tstd::string &fail_reason) const\n{\n\tconst std::string algo = \"sha256\";\n\treturn authorize_generic(dchlg, algo, username, passwd, nc, cnonce, qop,\n\t\t\tresp, fail_reason);\n}\n\nbool t_request::authorize(const t_challenge &chlg, t_user *user_config,\n\tconst std::string &username, const std::string &passwd, unsigned long nc,\n\tconst std::string &cnonce, t_credentials &cr, std::string &fail_reason) const\n{\n\t// Only Digest authentication is supported\n\tif (cmp_nocase(chlg.auth_scheme, AUTH_DIGEST) != 0) {\n\t\tfail_reason = \"Authentication scheme \" + chlg.auth_scheme;\n\t\tfail_reason += \" not supported.\";\n\t\treturn false;\n\t}\n\n\tconst t_digest_challenge &dchlg = chlg.digest_challenge;\n\t\n\tstd::string qop = \"\";\n\n\t// Determine QOP\n\t// If both auth and auth-int are supported by the server, then\n\t// choose auth to avoid problems with SIP ALGs. A SIP ALG rewrites\n\t// the body of a message, thereby breaking auth-int authentication.\n\tif (!dchlg.qop_options.empty()) {\n\t\tconst list<std::string>::const_iterator i = find(\n\t\t\tdchlg.qop_options.begin(), dchlg.qop_options.end(),\n\t\t\tQOP_AUTH_INT);\n\t\tconst list<std::string>::const_iterator j = find(\n\t\t\tdchlg.qop_options.begin(), dchlg.qop_options.end(),\n\t\t\tQOP_AUTH);\n\t\tif (j != dchlg.qop_options.end())\n\t\t\tqop = QOP_AUTH;\n\t\telse {\n\t\t\tif (i != dchlg.qop_options.end())\n\t\t\t\tqop = QOP_AUTH_INT;\n\t\t\telse {\n\t\t\t\tfail_reason = \"Non of the qop values are supported.\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tbool ret = false;\n\tstd::string resp;\n\n\tif (cmp_nocase(dchlg.algorithm, ALG_MD5) == 0) {\n\t\tret = authorize_md5(dchlg, username, passwd, nc, cnonce, \n\t\t\t\tqop, resp, fail_reason);\n\t} else if (cmp_nocase(dchlg.algorithm, ALG_SHA256) == 0) {\n\t\tret = authorize_sha256(dchlg, username, passwd, nc, cnonce,\n\t\t\t\tqop, resp, fail_reason);\n\t} else if (cmp_nocase(dchlg.algorithm, ALG_AKAV1_MD5) == 0) {\n\t\tuint8 aka_op[AKA_OPLEN];\n\t\tuint8 aka_amf[AKA_AMFLEN];\n\t\tuser_config->get_auth_aka_op(aka_op);\n\t\tuser_config->get_auth_aka_amf(aka_amf);\n\t\t\n\t\tret = authorize_akav1_md5(dchlg, username, passwd, aka_op, aka_amf, nc, cnonce, \n\t\t\t\tqop, resp, fail_reason);\n\t} else {\n\t\tfail_reason = \"Authentication algorithm \" + dchlg.algorithm;\n\t\tfail_reason += \" not supported.\";\n\t\treturn false;\n\t}\n\t\n\tif (!ret) return false;\n\t\n\t// Create credentials\n\tcr.auth_scheme = AUTH_DIGEST;\n\tt_digest_response &dr = cr.digest_response;\n\n\tdr.dresponse = resp;\n\tdr.username = username;\n\tdr.realm = dchlg.realm;\n\tdr.nonce = dchlg.nonce;\n\tdr.digest_uri = uri;\n\tdr.algorithm = dchlg.algorithm;\n\tdr.opaque = dchlg.opaque;\n\n\t// RFC 2617 3.2.2\n\tif (qop != \"\") {\n\t\tdr.message_qop = qop;\n\t\tdr.cnonce = cnonce;\n\t\tdr.nonce_count = nc;\n\t}\n\n\treturn true;\n}\n\nt_request::t_request() : t_sip_message(),\n\ttransport_specified(false),\n\tmethod(METHOD_UNKNOWN)\n{\n}\n\nt_request::t_request(const t_request &r) : t_sip_message(r),\n\t\tdestinations(r.destinations),\n\t\ttransport_specified(r.transport_specified),\n\t\turi(r.uri),\n\t\tmethod(r.method),\n\t\tunknown_method(r.unknown_method)\n{\n}\n\nt_request::t_request(const t_method m) : t_sip_message() {\n\tmethod = m;\n}\n\nvoid t_request::set_method(const std::string &s) {\n\tmethod = str2method(s);\n\tif (method == METHOD_UNKNOWN) {\n\t\tunknown_method = s;\n\t}\n}\n\nstd::string t_request::encode(bool add_content_length) {\n\tstd::string s;\n\n\ts = method2str(method, unknown_method) + ' ' + uri.encode();\n\ts += \" SIP/\";\n\ts += version;\n\ts += CRLF;\n\ts += t_sip_message::encode(add_content_length);\n\treturn s;\n}\n\nlist<std::string> t_request::encode_env(void) {\n\tstd::string s;\n\tlist<std::string> l = t_sip_message::encode_env();\n\t\n\ts = \"SIPREQUEST_METHOD=\";\n\ts += method2str(method, unknown_method);\n\tl.push_back(s);\n\t\n\ts = \"SIPREQUEST_URI=\";\n\ts += uri.encode();\n\tl.push_back(s);\n\t\n\treturn l;\n}\n\nt_sip_message *t_request::copy(void) const {\n\tt_sip_message *m =  new t_request(*this);\n\tMEMMAN_NEW(m);\n\treturn m;\n}\n\nvoid t_request::set_route(const t_url &target_uri, const list<t_route> &route_set) {\n\t// RFC 3261 12.2.1.1\n        if (route_set.empty()) {\n                uri = target_uri;\n        } else {\n                if (route_set.front().uri.get_lr()) {\n\t\t\t// Loose routing\n                        uri = target_uri;\n                        for (list<t_route>::const_iterator i = route_set.begin();\n                             i != route_set.end(); ++i)\n                        {\n                                hdr_route.add_route(*i);\n                        }\n                        hdr_route.route_to_first_route = true;\n                } else {\n\t\t\t// Strict routing\n                        uri = route_set.front().uri;\n                        for (list<t_route>::const_iterator i = route_set.begin();\n                             i != route_set.end(); ++i)\n                        {\n                                if (i != route_set.begin()) {\n                                        hdr_route.add_route(*i);\n                                }\n                        }\n\n                        // Add target uri to the route list\n                        t_route route;\n                        route.uri = target_uri;\n                        hdr_route.add_route(route);\n                }\n        }\n}\n\nt_response *t_request::create_response(int code, std::string reason) const\n{\n\tt_response *r;\n\n\tr = new t_response(code, reason);\n\tMEMMAN_NEW(r);\n\t\n\tr->src_ip_port_request = src_ip_port;\n\t\n\tr->hdr_from = hdr_from;\n\tr->hdr_call_id = hdr_call_id;\n\tr->hdr_cseq = hdr_cseq;\n\tr->hdr_via = hdr_via;\n\tr->hdr_to = hdr_to;\n\n\t// Create a to-tag if none was present in the request\n\t// NOTE: 100 Trying should not get a to-tag\n\tif (hdr_to.tag.size() == 0 && code != R_100_TRYING) {\n\t\tr->hdr_to.set_tag(NEW_TAG);\n\t}\n\n\t// Server\n\tSET_HDR_SERVER(r->hdr_server);\n\n\treturn r;\n}\n\nbool t_request::is_valid(bool &fatal, std::string &reason) const {\n\tif (!t_sip_message::is_valid(fatal, reason)) return false;\n\n\tfatal = false;\n\n\tif (t_parser::check_max_forwards && !hdr_max_forwards.is_populated()) {\n\t\treason = \"Max-Forwards header missing\";\n\t\treturn false;\n\t}\n\n\t// RFC 3261 8.1.1.5\n\t// The CSeq method must match the request method.\n\tif (hdr_cseq.method != method) {\n\t\treason = \"CSeq method does not match request method\";\n\t\treturn false;\n\t}\n\n\tswitch(method) {\n\tcase INVITE:\n\t\tif (!hdr_contact.is_populated()) {\n\t\t\treason = \"Contact header missing\";\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tcase PRACK:\n\t\t// RFC 3262 7.1\n\t\tif (!hdr_rack.is_populated()) {\n\t\t\treason = \"RAck header missing\";\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tcase SUBSCRIBE:\n\t\t// RFC 3265 7.1, 7.2\n\t\tif (!hdr_contact.is_populated()) {\n\t\t\treason = \"Contact header missing\";\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!hdr_event.is_populated()) {\n\t\t\treason = \"Event header missing\";\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tcase NOTIFY:\n\t\t// RFC 3265 7.1, 7.2\n\t\tif (!hdr_contact.is_populated()) {\n\t\t\treason = \"Contact header missing\";\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!hdr_event.is_populated()) {\n\t\t\treason = \"Event header missing\";\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// RFC 3265 7.2\n\t\t// Subscription-State header is mandatory\n\t\t// As an exception Twinkle allows an unsolicited NOTIFY for MWI\n\t\t// without a Subscription-State header. Asterisk sends\n\t\t// unsolicited NOTIFY requests.\n\t\tif (!hdr_to.tag.empty() || \n\t\t    hdr_event.event_type != SIP_EVENT_MSG_SUMMARY)\n\t\t{\n\t\t\tif (!hdr_subscription_state.is_populated()) {\n\t\t\t\treason = \"Subscription-State header missing\";\n\t\t\t \treturn false;\n\t\t\t }\n\t\t}\n\n\t\t// The Subscription-State header is mandatory.\n\t\t// However, Asterisk uses an expired draft for sending\n\t\t// unsolicitied NOTIFY messages without a Subscription-State\n\t\t// header. As Asterisk is popular, Twinkle allows this.\n\t\tbreak;\n\tcase REFER:\n\t\t// RFC 3515 2.4.1\n\t\tif (!hdr_refer_to.is_populated()) {\n\t\t\treason = \"Refer-To header missing\";\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tif (hdr_replaces.is_populated()) {\n\t\t// RFC 3891 3\n\t\tif (method != INVITE) {\n\t\t\treason = \"Replaces header not allowed\";\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid t_request::add_destinations(const t_user &user_profile, const t_url &dst_uri) {\n\tt_url dest;\n\tif (dst_uri.get_scheme() == \"tel\")\n\t{\n\t\t// Send a tel-URI to the configure domain for the user.\n\t\tdest = \"sip:\" + user_profile.get_domain();\n\t}\n\telse\n\t{\n\t\tdest = dst_uri;\n\t}\n\n\tif ((user_profile.get_sip_transport() == SIP_TRANS_AUTO ||\n\t     user_profile.get_sip_transport() == SIP_TRANS_TCP)\n\t    &&\n\t    (dest.get_transport().empty() ||\n\t     cmp_nocase(dest.get_transport(), \"tcp\") == 0))\n\t{\n\t\tlist<t_ip_port> l = dest.get_h_ip_srv(\"tcp\");\n\t\tdestinations.insert(destinations.end(), l.begin(), l.end());\n\t}\n\t\n\t// Add UDP destinations after TCP, so UDP will be used as a fallback\n\t// for large messages, when TCP fails. If the message is not large,\n\t// then the TCP destinations will be removed later.\n\t// NOTE: If a message is larger than 64K, it cannot be sent via UDP\n\tif ((user_profile.get_sip_transport() == SIP_TRANS_AUTO ||\n\t    user_profile.get_sip_transport() == SIP_TRANS_UDP)\n\t   &&\n\t   (dest.get_transport().empty() ||\n\t    cmp_nocase(dest.get_transport(), \"udp\") == 0)\n\t   &&\n\t   (get_encoded_size() < 65536))\n\t{\n\t\tlist<t_ip_port> l = dest.get_h_ip_srv(\"udp\");\n\t\tdestinations.insert(destinations.end(), l.begin(), l.end());\n\t}\n\t\n\ttransport_specified = !dest.get_transport().empty();\n}\n\nvoid t_request::calc_destinations(const t_user &user_profile) {\n\tdestinations.clear();\n\n\t// Send a REGISTER to the registrar if provisioned.\n\tif (method == REGISTER && user_profile.get_use_registrar()) {\n\t\tadd_destinations(user_profile, user_profile.get_registrar());\n\t\treturn;\n\t}\n\t\n\t// Bypass the proxy for an out-of-dialog SUBSCRIBE if provisioned.\n\tif (method == SUBSCRIBE && hdr_to.tag.empty()) {\n\t\tif (hdr_event.event_type == SIP_EVENT_MSG_SUMMARY) {\n\t\t\tif (!user_profile.get_mwi_via_proxy()) {\n\t\t\t\t// Take Request-URI\n\t\t\t\tadd_destinations(user_profile, uri);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!user_profile.get_use_outbound_proxy() ||\n\t    (hdr_to.tag != \"\" && !user_profile.get_all_requests_to_proxy())) {\n\t\t// A mid dialog request will go to the host in the contact\n\t\t// header (put in the request-URI in this request) or route list\n\t\t// specified in the final response of the invite (the Route-header in\n\t\t// this request).\n\t\t// Note that an ACK for a failed INVITE (3XX-6XX) will be\n\t\t// sent by the transaction layer to the ipaddr/port of the\n\t\t// INVITE.\n\t\tif (hdr_route.is_populated() && hdr_route.route_to_first_route) {\n\t\t\t// Take URI from first route-header\n\t\t\tt_url &u = hdr_route.route_list.front().uri;\n\t\t\tadd_destinations(user_profile, u);\n\t\t} else {\n\t\t\t// Take Request-URI\n\t\t\tadd_destinations(user_profile, uri);\n\t\t}\n\t}\n\n\t// Send request to outbound proxy if configured\n\tif (user_profile.get_use_outbound_proxy()) {\n\t\tif (user_profile.get_non_resolvable_to_proxy() && !destinations.empty())\n\t\t{\n\t\t\t// The destination has been resolved, so do not\n\t\t\t// use the outbound proxy in this case.\n\t\t\treturn;\n\t\t}\n\n\t\tif (user_profile.get_all_requests_to_proxy() || hdr_to.tag == \"\") {\n\t\t\t// All requests should go to the proxy.\n\t\t\t// Override destination by the outbound proxy address.\n\t\t\tdestinations.clear();\n\t\t\tadd_destinations(user_profile, user_profile.get_outbound_proxy());\n\t\t}\n\t}\n}\n\nvoid t_request::get_destination(t_ip_port &ip_port, const t_user &user_profile) {\n\tif (destinations.empty()) calc_destinations(user_profile);\n\t\n\t// RFC 3261 18.1.1\n\t// If the message size is larger than 1300 then the message must be\n\t// sent over TCP.\n\t// If the destination URI indicated an explicit transport, then the\n\t// destination calculation picked the possible destinations already.\n\t// The size cannot influence this calculation anymore.\n\tif (user_profile.get_sip_transport() == SIP_TRANS_AUTO &&\n\t    !destinations.empty() &&\n\t    destinations.front().transport == \"tcp\" &&\n\t    get_encoded_size() <= user_profile.get_sip_transport_udp_threshold() &&\n\t    !transport_specified)\n\t{\n\t\t// The message can be sent over UDP. Remove all TCP destinations.\n\t\twhile (!destinations.empty() && destinations.front().transport == \"tcp\") {\n\t\t\tdestinations.pop_front();\n\t\t}\n\t}\n\t\n\tget_current_destination(ip_port);\n}\n\nvoid t_request::get_current_destination(t_ip_port &ip_port) {\n\tif (destinations.empty()) {\n\t\t// No destinations could be found.\n\t\tip_port.transport = \"udp\";\n\t\tip_port.ipaddr = 0;\n\t\tip_port.port = 0;\n\t} else {\n\t\t// Return first destination\n\t\tip_port = destinations.front();\n\t}\n}\n\nbool t_request::next_destination(void) {\t\t\n\tif (destinations.size() <= 1) return false;\n\t\n\t// Remove current destination\n\tdestinations.pop_front();\n\treturn true;\t\n}\n\nvoid t_request::set_destination(const t_ip_port &ip_port) {\n\tdestinations.clear();\n\tdestinations.push_back(ip_port);\n}\n\nbool t_request::www_authorize(const t_challenge &chlg, t_user *user_config, \n\t       const std::string &username, const std::string &passwd, unsigned long nc,\n\t       const std::string &cnonce, t_credentials &cr, std::string &fail_reason)\n{\n\tif (!authorize(chlg, user_config, username, passwd, nc, cnonce, cr, fail_reason)) {\n\t\treturn false;\n\t}\n\n\thdr_authorization.add_credentials(cr);\n\n\treturn true;\n}\n\nbool t_request::proxy_authorize(const t_challenge &chlg, t_user *user_config,\n\t       const std::string &username, const std::string &passwd, unsigned long nc,\n\t       const std::string &cnonce, t_credentials &cr, std::string &fail_reason)\n{\n\tif (!authorize(chlg, user_config, username, passwd, nc, cnonce, cr, fail_reason)) {\n\t\treturn false;\n\t}\n\n\thdr_proxy_authorization.add_credentials(cr);\n\n\treturn true;\n}\n\nvoid t_request::calc_local_ip(void) {\n\tt_ip_port dst;\n\t\n\tget_current_destination(dst);\n\tif (dst.ipaddr != 0) {\n\t\tlocal_ip_ = get_src_ip4_address_for_dst(dst.ipaddr);\n\t}\n}\n\nbool t_request::is_registration_request(void) const {\n\tif (method != REGISTER) return false;\n\t\n\tif (hdr_expires.is_populated() && hdr_expires.time > 0) return true;\n\t\n\tif (hdr_contact.is_populated() && \n\t    !hdr_contact.contact_list.empty() &&\n\t    hdr_contact.contact_list.front().is_expires_present() &&\n\t    hdr_contact.contact_list.front().get_expires() > 0)\n\t{\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nbool t_request::is_de_registration_request(void) const {\n\tif (method != REGISTER) return false;\n\n\tif (hdr_expires.is_populated() && hdr_expires.time == 0) return true;\n\t\n\tif (hdr_contact.is_populated() && \n\t    !hdr_contact.contact_list.empty() &&\n\t    hdr_contact.contact_list.front().is_expires_present() &&\n\t    hdr_contact.contact_list.front().get_expires() == 0)\n\t{\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n"
  },
  {
    "path": "src/parser/request.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Request\n\n#ifndef _REQUEST_H\n#define _REQUEST_H\n\n#include <string>\n#include \"response.h\"\n#include \"sip_message.h\"\n#include \"sockets/url.h\"\n#include \"user.h\"\n\n// Forward declaration\nclass t_user;\n\nusing namespace std;\n\nclass t_request : public t_sip_message {\nprivate:\n\t/**\n\t * A DNS lookup on the request URI (or outbound proxy) might resolve \n\t * into multiple destinations. @ref get_destination() will return the first \n\t * destination. All destinations are stored here.\n\t * @ref next_destination() will remove the first destination of this\n\t * list.\n\t */\n\tlist<t_ip_port>\t\tdestinations;\n\t\n\t/**\n\t * Indicates if the destination specified a transport, i.e. via the\n\t * transport parameter in a URI.\n\t */\n\tbool\t\t\ttransport_specified;\n\t\n\t/**\n\t * Add destinations for a given URI based on transport settings.\n\t * @param user_profile [in] User profile\n\t * @param dst_uri [in] The URI to resolve.\n\t */\n\tvoid add_destinations(const t_user &user_profile, const t_url &dst_uri);\n\t\n\t/**\n\t * Calculate credentials based on the challenge.\n\t * @param chlg [in] The challenge\n\t * @param user_config [in] User configuration for user to be authorized.\n\t * @param username [in] User authentication name\n\t * @param passwd [in] Authentication password.\n\t * @param nc [in] Nonce count\n\t * @param cnonce [in] Client nonce\n\t * @param cr [out] Credentials on successful return.\n\t * @param fail_reason [out] Failure reason on failure return.\n\t * @return false, if authorization fails.\n\t * @return true, if authorization succeeded\n\t */\n\tbool authorize(const t_challenge &chlg, t_user *user_config, \n\t\t       const string &username, const string &passwd, unsigned long nc,\n\t\t       const string &cnonce, t_credentials &cr,\n\t\t       string &fail_reason) const;\n\n\t/**\n\t * Calculate MD5/SHA-256 response based on the challenge.\n\t * @param chlg [in] The challenge\n\t * @param username [in] User authentication name\n\t * @param passwd [in] Authentication password.\n\t * @param nc [in] Nonce count\n\t * @param cnonce [in] Client nonce\n\t * @param qop [in] Quality of protection\n\t * @param resp [out] Response on successful return.\n\t * @param fail_reason [out] Failure reason on failure return.\n\t * @return false, if authorization fails.\n\t * @return true, if authorization succeeded\n\t */\n\tbool authorize_md5(const t_digest_challenge &dchlg,\n\t\t\t\tconst string &username, const string &passwd, unsigned long nc,\n\t\t\t\tconst string &cnonce, const string &qop, string &resp, \n\t\t\t\tstring &fail_reason) const;\n\tbool authorize_sha256(const t_digest_challenge &dchlg,\n\t\t\t\tconst string &username, const string &passwd, unsigned long nc,\n\t\t\t\tconst string &cnonce, const string &qop, string &resp,\n\t\t\t\tstring &fail_reason) const;\n\n\t/**\n\t * Calculate AKAv1-MD5 response based on the challenge.\n\t * @param chlg [in] The challenge\n\t * @param username [in] User authentication name\n\t * @param passwd [in] Authentication password.\n\t * @param op [in] Operator variant key\n\t * @param amf [in] Authentication method field\n\t * @param nc [in] Nonce count\n\t * @param cnonce [in] Client nonce\n\t * @param qop [in] Quality of protection\n\t * @param resp [out] Response on successful return.\n\t * @param fail_reason [out] Failure reason on failure return.\n\t * @return false, if authorization fails.\n\t * @return true, if authorization succeeded\n\t */\n\tbool authorize_akav1_md5(const t_digest_challenge &dchlg,\n\t\t\t\tconst string &username, const string &passwd, \n\t\t\t\tuint8 *op, uint8 *amf,\n\t\t\t\tunsigned long nc,\n\t\t\t\tconst string &cnonce, const string &qop, string &resp, \n\t\t\t\tstring &fail_reason) const;\n\n\t/**\n\t * Calculate response based on the challenge for a given hash algorithm.\n\t * @param chlg [in] The challenge\n\t * @param algo [in] Hash algorithm name\n\t * @param username [in] User authentication name\n\t * @param passwd [in] Authentication password.\n\t * @param nc [in] Nonce count\n\t * @param cnonce [in] Client nonce\n\t * @param qop [in] Quality of protection\n\t * @param resp [out] Response on successful return.\n\t * @param fail_reason [out] Failure reason on failure return.\n\t * @return false, if authorization fails.\n\t * @return true, if authorization succeeded\n\t */\n\tbool authorize_generic(const t_digest_challenge &dchlg,\n\t\t\t\tconst string &algo,\n\t\t\t\tconst string &username, const string &passwd, unsigned long nc,\n\t\t\t\tconst string &cnonce, const string &qop, string &resp,\n\t\t\t\tstring &fail_reason) const;\n\n\npublic:\n\tt_url\t\turi;\n\tt_method\tmethod;\n\tstring\t\tunknown_method; // set if method is UNKNOWN\n\n\tt_request();\n\tt_request(const t_request &r);\n\tt_request(const t_method m);\n\n\tt_msg_type get_type(void) const { return MSG_REQUEST; }\n\tvoid set_method(const string &s);\n\tstring encode(bool add_content_length = true);\n\tlist<string> encode_env(void);\n\tt_sip_message *copy(void) const;\n\t\n\t/**\n\t * Set the Request-URI and the Route header.\n\t * This is done according to the procedures of RFC 3261 12.2.1.1\n\t * @param target_uri [in] The URI of the destination for this request.\n\t * @param route_set [in] The route set for this request (may be empty).\n\t */\n\tvoid set_route(const t_url &target_uri, const list<t_route> &route_set);\n\n\t// Create a response with response code based on the\n\t// request. The response is created following the general\n\t// rules in RFC 3261 8.2.6.2.\n\t// The to-hdr is simply copied from the request to the\n\t// response.\n\t// If the to-tag is missing, then\n\t// a to-tag will be generated and added to the to-header\n\t// of the response.\n\t// A specific reason may be added to the status code.\n\tt_response *create_response(int code, string reason = \"\") const;\n\n\tbool is_valid(bool &fatal, string &reason) const;\n\t\n\t// Calculate the set of possible destinations for this request.\n\tvoid calc_destinations(const t_user &user_profile);\n\n\t// Get destination to send this request to.\n\tvoid get_destination(t_ip_port &ip_port, const t_user &user_profile);\n\tvoid get_current_destination(t_ip_port &ip_port);\n\t\t\n\t// Move to next destination. This method should only be called after\n\t// calc_destination() was called.\n\t// Returns true if there is a next destination, otherwise returns false.\n\tbool next_destination(void);\n\t\n\t// Set a single destination to send this request to.\n\tvoid set_destination(const t_ip_port &ip_port);\n\n\t/** \n\t * Create WWW authorization credentials based on the challenge.\n\t * @param chlg [in] The challenge\n\t * @param user_config [in] User configuration for user to be authorized.\n\t * @param username [in] User authentication name\n\t * @param passwd [in] Authentication password.\n\t * @param nc [in] Nonce count\n\t * @param cnonce [in] Client nonce\n\t * @param cr [out] Credentials on successful return.\n\t * @param fail_reason [out] Failure reason on failure return.\n\t * @return false, if challenge is not supported.\n\t * @return true, if authorization succeeded\n\t */\n\tbool www_authorize(const t_challenge &chlg, t_user *user_config,\n\t       const string &username, const string &passwd, unsigned long nc,\n\t       const string &cnonce, t_credentials &cr, string &fail_reason);\n\n\t/** \n\t * Create proxy authorization credentials based on the challenge.\n\t * @param chlg [in] The challenge\n\t * @param user_config [in] User configuration for user to be authorized.\n\t * @param username [in] User authentication name\n\t * @param passwd [in] Authentication password.\n\t * @param nc [in] Nonce count\n\t * @param cnonce [in] Client nonce\n\t * @param cr [out] Credentials on successful return.\n\t * @param fail_reason [out] Failure reason on failure return.\n\t * @return false, if challenge is not supported.\n\t * @return true, if authorization succeeded\n\t */\n\tbool proxy_authorize(const t_challenge &chlg, t_user *user_config,\n\t       const string &username, const string &passwd, unsigned long nc,\n\t       const string &cnonce, t_credentials &cr, string &fail_reason);\n\t       \n\tvirtual void calc_local_ip(void);\n\t\n\t/**\n\t * Check if the request is a registration request.\n\t * @return True if the request is a registration request, otherwise false.\n\t */\n\tbool is_registration_request(void) const;\n\t\n\t/**\n\t * Check if the request is a de-registration request.\n\t * @return True if the request is a de-registration request, otherwise false.\n\t */\n\tbool is_de_registration_request(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/response.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n\n#include \"response.h\"\n#include \"util.h\"\n#include \"parse_ctrl.h\"\n#include \"audits/memman.h\"\n\nt_response::t_response() : t_sip_message() {}\n\nt_response::t_response(const t_response &r) : t_sip_message(r) ,\n\t\tcode(r.code),\n\t\treason(r.reason),\n\t\tsrc_ip_port_request(r.src_ip_port_request)\n{\n}\n\nt_response::t_response(int _code, string _reason) : t_sip_message() {\n\tcode = _code;\n\n\tif (_reason == \"\") {\n\t\tswitch (code) {\n\t\tcase 100: reason = REASON_100; break;\n\t\tcase 180: reason = REASON_180; break;\n\t\tcase 181: reason = REASON_181; break;\n\t\tcase 182: reason = REASON_182; break;\n\t\tcase 183: reason = REASON_183; break;\n\t\tcase 200: reason = REASON_200; break;\n\t\tcase 202: reason = REASON_202; break;\n\t\tcase 300: reason = REASON_300; break;\n\t\tcase 301: reason = REASON_301; break;\n\t\tcase 302: reason = REASON_302; break;\n\t\tcase 305: reason = REASON_305; break;\n\t\tcase 380: reason = REASON_380; break;\n\t\tcase 400: reason = REASON_400; break;\n\t\tcase 401: reason = REASON_401; break;\n\t\tcase 402: reason = REASON_402; break;\n\t\tcase 403: reason = REASON_403; break;\n\t\tcase 404: reason = REASON_404; break;\n\t\tcase 405: reason = REASON_405; break;\n\t\tcase 406: reason = REASON_406; break;\n\t\tcase 407: reason = REASON_407; break;\n\t\tcase 408: reason = REASON_408; break;\n\t\tcase 410: reason = REASON_410; break;\n\t\tcase 412: reason = REASON_412; break;\n\t\tcase 413: reason = REASON_413; break;\n\t\tcase 414: reason = REASON_414; break;\n\t\tcase 415: reason = REASON_415; break;\n\t\tcase 416: reason = REASON_416; break;\n\t\tcase 420: reason = REASON_420; break;\n\t\tcase 421: reason = REASON_421; break;\n\t\tcase 422: reason = REASON_422; break;\n\t\tcase 423: reason = REASON_423; break;\n\t\tcase 480: reason = REASON_480; break;\n\t\tcase 481: reason = REASON_481; break;\n\t\tcase 482: reason = REASON_482; break;\n\t\tcase 483: reason = REASON_483; break;\n\t\tcase 484: reason = REASON_484; break;\n\t\tcase 485: reason = REASON_485; break;\n\t\tcase 486: reason = REASON_486; break;\n\t\tcase 487: reason = REASON_487; break;\n\t\tcase 488: reason = REASON_488; break;\n\t\tcase 489: reason = REASON_489; break;\n\t\tcase 491: reason = REASON_491; break;\n\t\tcase 493: reason = REASON_493; break;\n\t\tcase 500: reason = REASON_500; break;\n\t\tcase 501: reason = REASON_501; break;\n\t\tcase 502: reason = REASON_502; break;\n\t\tcase 503: reason = REASON_503; break;\n\t\tcase 504: reason = REASON_504; break;\n\t\tcase 505: reason = REASON_505; break;\n\t\tcase 513: reason = REASON_513; break;\n\t\tcase 600: reason = REASON_600; break;\n\t\tcase 603: reason = REASON_603; break;\n\t\tcase 604: reason = REASON_604; break;\n\t\tcase 606: reason = REASON_606; break;\n\t\tdefault:  reason = \"Unknown Error\";\n\t\t}\n\t} else {\n\t\treason = _reason;\n\t}\n}\n\nint t_response::get_class(void) const {\n\treturn code / 100;\n}\n\nbool t_response::is_provisional(void) const {\n\treturn (get_class() == R_1XX);\n}\n\nbool t_response::is_final(void) const {\n\treturn (get_class() != R_1XX);\n}\n\nbool t_response::is_success(void) const {\n\treturn (get_class() == R_2XX);\n}\n\nstring t_response::encode(bool add_content_length) {\n\tstring s;\n\n\ts = \"SIP/\" + version + ' ' + int2str(code, \"%3d\") + ' ' + reason;\n\ts += CRLF;\n\ts += t_sip_message::encode(add_content_length);\n\n\treturn s;\n}\n\nlist<string> t_response::encode_env(void) {\n\tstring s;\n\n\tlist<string> l = t_sip_message::encode_env();\n\t\n\ts = \"SIPSTATUS_CODE=\";\n\ts += int2str(code, \"%3d\");\n\tl.push_back(s);\n\t\n\ts = \"SIPSTATUS_REASON=\";\n\ts += reason;\n\tl.push_back(s);\n\t\n\treturn l;\n}\n\nt_sip_message *t_response::copy(void) const {\n\tt_sip_message *m = new t_response(*this);\n\tMEMMAN_NEW(m);\n\treturn m;\n}\n\nbool t_response::is_valid(bool &fatal, string &reason) const {\n\tif (!t_sip_message::is_valid(fatal, reason)) return false;\n\n\tfatal = false;\n\n\tswitch(hdr_cseq.method) {\n\tcase INVITE:\n\t\tif (get_class() == R_2XX && !hdr_contact.is_populated()) {\n\t\t\treason = \"Contact header missing\";\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tcase SUBSCRIBE:\n\t\t// RFC 3265 7.1, 7.2\n\t\t/*\n\t\tSome SIP servers do not send the mandatory Expires header.\n\t\tFor interoperability this deviation is allowed.\n\t\tif (get_class()== R_2XX && !hdr_expires.is_populated()) {\n\t\t\treason = \"Expires header missing\";\n\t\t\treturn false;\n\t\t}\n\t\t*/\n\n\t\tswitch (code) {\n\t\tcase R_489_BAD_EVENT:\n\t\t\tif (!hdr_allow_events.is_populated()) {\n\t\t\t\treason = \"Allow-Events header missing\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tbreak;\n\tcase NOTIFY:\n\t\t// RFC 3265 7.1, 7.2\n\t\tswitch (code) {\n\t\tcase R_489_BAD_EVENT:\n\t\t\tif (!hdr_allow_events.is_populated()) {\n\t\t\t\treason = \"Allow-Events header is missing\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\tif (hdr_rseq.is_populated()) {\n\t\t// RFC 3262 7.1\n\t\t// The value ranges from 1 to 2**32 - 1\n\t\tif (hdr_rseq.resp_nr == 0) {\n\t\t\treason = \"RSeq is zero\";\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool t_response::must_authenticate(void) const {\n\treturn (code == R_401_UNAUTHORIZED &&\n\t        hdr_www_authenticate.is_populated() ||\n\t        code == R_407_PROXY_AUTH_REQUIRED &&\n\t        hdr_proxy_authenticate.is_populated());\n}\n\nvoid t_response::get_destination(t_ip_port &ip_port) const {\n\tassert(hdr_via.is_populated());\n\t\n\tif (src_ip_port_request.transport == \"tcp\") {\n\t\t// RFC 3261 18.2.2\n\t\t// For TCP the response should be sent on the connection on which\n\t\t// the request was received. So the address returned here is the\n\t\t// alternative destination when the connection is closed already.\n\t\tip_port = src_ip_port_request;\n\t} else {\n\t\thdr_via.get_response_dst(ip_port);\n\t}\n}\n\nvoid t_response::calc_local_ip(void) {\n\tt_ip_port dst;\n\t\n\tget_destination(dst);\n\tif (dst.ipaddr != 0) {\n\t\tlocal_ip_ = get_src_ip4_address_for_dst(dst.ipaddr);\n\t}\n}\n"
  },
  {
    "path": "src/parser/response.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Response\n\n#ifndef _H_RESPONSE\n#define _H_RESPONSE\n\n#include <string>\n#include \"sip_message.h\"\n\nusing namespace std;\n\n// Repsonse codes\n// Informational\n#define\tR_100_TRYING 100\n#define\tR_180_RINGING 180\n#define\tR_181_CALL_IS_BEING_FORWARDED 181\n#define\tR_182_QUEUED 182\n#define\tR_183_SESSION_PROGRESS 183\n\n// Success\n#define\tR_200_OK 200\n#define R_202_ACCEPTED 202\n\n// Redirection\n#define\tR_300_MULTIPLE_CHOICES 300\n#define\tR_301_MOVED_PERMANENTLY 301\n#define\tR_302_MOVED_TEMPORARILY 302\n#define\tR_305_USE_PROXY 305\n#define\tR_380_ALTERNATIVE_SERVICE 380\n\n// Client error\n#define\tR_400_BAD_REQUEST 400\n#define\tR_401_UNAUTHORIZED 401\n#define\tR_402_PAYMENT_REQUIRED 402\n#define\tR_403_FORBIDDEN 403\n#define\tR_404_NOT_FOUND 404\n#define\tR_405_METHOD_NOT_ALLOWED 405\n#define\tR_406_NOT_ACCEPTABLE 406\n#define\tR_407_PROXY_AUTH_REQUIRED 407\n#define\tR_408_REQUEST_TIMEOUT 408\n#define\tR_410_GONE 410\n#define R_412_CONDITIONAL_REQUEST_FAILED 412\n#define\tR_413_REQ_ENTITY_TOO_LARGE 413\n#define\tR_414_REQ_URI_TOO_LARGE 414\n#define\tR_415_UNSUPPORTED_MEDIA_TYPE 415\n#define\tR_416_UNSUPPORTED_URI_SCHEME 416\n#define\tR_420_BAD_EXTENSION 420\n#define\tR_421_EXTENSION_REQUIRED 421\n#define\tR_422_SESSION_INTERVAL_TOO_SMALL 422\n#define\tR_423_INTERVAL_TOO_BRIEF 423\n#define\tR_480_TEMP_NOT_AVAILABLE 480\n#define\tR_481_TRANSACTION_NOT_EXIST 481\n#define\tR_482_LOOP_DETECTED 482\n#define\tR_483_TOO_MANY_HOPS 483\n#define\tR_484_ADDRESS_INCOMPLETE 484\n#define\tR_485_AMBIGUOUS 485\n#define\tR_486_BUSY_HERE 486\n#define\tR_487_REQUEST_TERMINATED 487\n#define\tR_488_NOT_ACCEPTABLE_HERE 488\n#define R_489_BAD_EVENT 489\n#define\tR_491_REQUEST_PENDING 491\n#define\tR_493_UNDECIPHERABLE 493\n\n// Server error\n#define\tR_500_INTERNAL_SERVER_ERROR 500\n#define\tR_501_NOT_IMPLEMENTED 501\n#define\tR_502_BAD_GATEWAY 502\n#define\tR_503_SERVICE_UNAVAILABLE 503\n#define\tR_504_SERVER_TIMEOUT 504\n#define\tR_505_SIP_VERSION_NOT_SUPPORTED 505\n#define\tR_513_MESSAGE_TOO_LARGE 513\n\n// Global failure\n#define\tR_600_BUSY_EVERYWHERE 600\n#define\tR_603_DECLINE 603\n#define\tR_604_NOT_EXIST_ANYWHERE 604\n#define\tR_606_NOT_ACCEPTABLE 606\n\n// Response classes\n#define\tR_1XX\t1\t// Informational\n#define\tR_2XX\t2\t// Success\n#define\tR_3XX\t3\t// Redirection\n#define\tR_4XX\t4\t// Client error\n#define\tR_5XX\t5\t// Server error\n#define\tR_6XX\t6\t// Global failure\n\n// Default reason strings\n#define REASON_100 \"Trying\"\n#define REASON_180 \"Ringing\"\n#define REASON_181 \"Call Is Being Forwarded\"\n#define REASON_182 \"Queued\"\n#define REASON_183 \"Session Progress\"\n\n#define REASON_200 \"OK\"\n#define REASON_202 \"Accepted\"\n\n#define REASON_300 \"Multiple Choices\"\n#define REASON_301 \"Moved Permanently\"\n#define REASON_302 \"Moved Temporarily\"\n#define REASON_305 \"Use Proxy\"\n#define REASON_380 \"Alternative Service\"\n\n#define REASON_400 \"Bad Request\"\n#define REASON_401 \"Unauthorized\"\n#define REASON_402 \"Payment Required\"\n#define REASON_403 \"Forbidden\"\n#define REASON_404 \"Not Found\"\n#define REASON_405 \"Method Not Allowed\"\n#define REASON_406 \"Not Acceptable\"\n#define REASON_407 \"Proxy Authentication Required\"\n#define REASON_408 \"Request Timeout\"\n#define REASON_410 \"Gone\"\n#define REASON_412 \"Conditional Request Failed\"\n#define REASON_413 \"Request Entity Too Large\"\n#define REASON_414 \"Request-URI Too Large\"\n#define REASON_415 \"Unsupported Media Type\"\n#define REASON_416 \"Unsupported URI Scheme\"\n#define REASON_420 \"Bad Extension\"\n#define REASON_421 \"Extension Required\"\n#define REASON_422 \"Session Interval Too Small\"\n#define REASON_423 \"Interval Too Brief\"\n#define REASON_480 \"Temporarily Not Available\"\n#define REASON_481 \"Call Leg/Transaction Does Not Exist\"\n#define REASON_482 \"Loop Detected\"\n#define REASON_483 \"Too Many Hops\"\n#define REASON_484 \"Address Incomplete\"\n#define REASON_485 \"Ambiguous\"\n#define REASON_486 \"Busy Here\"\n#define REASON_487 \"Request Terminated\"\n#define REASON_488 \"Not Acceptable Here\"\n#define REASON_489 \"Bad Event\"\n#define REASON_491 \"Request Pending\"\n#define REASON_493 \"Undecipherable\"\n\n#define REASON_500 \"Internal Server Error\"\n#define REASON_501 \"Not Implemented\"\n#define REASON_502 \"Bad Gateway\"\n#define REASON_503 \"Service Unavailable\"\n#define REASON_504 \"Server Time-out\"\n#define REASON_505 \"SIP Version Not Supported\"\n#define REASON_513 \"Message Too Large\"\n\n#define REASON_600 \"Busy Everywhere\"\n#define REASON_603 \"Decline\"\n#define REASON_604 \"Does Not Exist Anywhere\"\n#define REASON_606 \"Not Acceptable\"\n\n// The protocol allows a SIP response to have a non-default reason\n// phrase that gives a more detailed reason.\n\n// RFC 3261 21.4.18\n// Code 480 should have a specific reason phrase\n#define REASON_480_NO_ANSWER\t\t\t\"User not responding\"\n\n// RFC 3265 3.2.4\n#define REASON_481_SUBSCRIPTION_NOT_EXIST\t\"Subscription does not exist\"\n\n\nclass t_response : public t_sip_message {\npublic:\n\tint\t\tcode;\n\tstring\t\treason;\n\t\n\t/** The source address of the request generating this response. */\n\tt_ip_port\tsrc_ip_port_request;\n\n\tt_response();\n\tt_response(const t_response &r);\n\tt_response(int _code, string _reason = \"\");\n\n\tt_msg_type get_type(void) const { return MSG_RESPONSE; }\n\n\t// Return the response class 1,2,3,4,5,6\n\tint get_class(void) const;\n\n\tbool is_provisional(void) const;\n\tbool is_final(void) const;\n\tbool is_success(void) const;\n\n\tstring encode(bool add_content_length = true);\n\tlist<string> encode_env(void);\n\tt_sip_message *copy(void) const;\n\n\tbool is_valid(bool &fatal, string &reason) const;\n\n\t// Returns true if the response is a 401/407 with\n\t// the proper authenticate header.\n\tbool must_authenticate(void) const;\n\t\n\t/**\n\t * Get the destination address for sending the response.\n\t * @param ip_port [out] The destination address.\n\t */\n\tvoid get_destination(t_ip_port &ip_port) const;\n\t\n\tvirtual void calc_local_ip(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/rijndael.cpp",
    "content": "/*-------------------------------------------------------------------\n *                      Rijndael Implementation\n *-------------------------------------------------------------------\n *\n *  A sample 32-bit orientated implementation of Rijndael, the\n *  suggested kernel for the example 3GPP authentication and key\n *  agreement functions.\n *\n *  This implementation draws on the description in section 5.2 of\n *  the AES proposal and also on the implementation by\n *  Dr B. R. Gladman <brg@gladman.uk.net> 9th October 2000.\n *  It uses a number of large (4k) lookup tables to implement the\n *  algorithm in an efficient manner.\n *\n *  Note: in this implementation the State is stored in four 32-bit\n *  words, one per column of the State, with the top byte of the\n *  column being the _least_ significant byte of the word.\n *\n*-----------------------------------------------------------------*/\n\n#include \"twinkle_config.h\"\n\ntypedef unsigned char   u8;\ntypedef unsigned int\t u32;\n\n/* Circular byte rotates of 32 bit values */\n\n#define rot1(x) ((x <<  8) | (x >> 24))\n#define rot2(x) ((x << 16) | (x >> 16))\n#define rot3(x) ((x << 24) | (x >>  8))\n\n/* Extract a byte from a 32-bit u32 */\n\n#define byte0(x)    ((u8)(x))\n#define byte1(x)    ((u8)(x >>  8))\n#define byte2(x)    ((u8)(x >> 16))\n#define byte3(x)    ((u8)(x >> 24))\n\n\n/* Put or get a 32 bit u32 (v) in machine order from a byte\t*\n * address in (x)                                           */\n\n#ifndef  WORDS_BIGENDIAN\n\n#define u32_in(x)     (*(u32*)(x))\n#define u32_out(x,y)  (*(u32*)(x) = y)\n\n#else\n\n/* Invert byte order in a 32 bit variable */\n\n__inline u32 byte_swap(const u32 x)\n{\n    return rot1(x) & 0x00ff00ff | rot3(x) & 0xff00ff00;\n}\n__inline u32 u32_in(const u8 x[])\n{\n  return byte_swap(*(u32*)x);\n};\n__inline void u32_out(u8 x[], const u32 v) \n{\n  *(u32*)x = byte_swap(v);\n};\n\n#endif\n\n/*--------------- The lookup tables ----------------------------*/\n\nstatic u32 rnd_con[10] = \n{ \n 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36\n};\n\nstatic u32 ft_tab[4][256] = \n{\n {\n 0xA56363C6,0x847C7CF8,0x997777EE,0x8D7B7BF6,0x0DF2F2FF,0xBD6B6BD6,0xB16F6FDE,0x54C5C591,\n 0x50303060,0x03010102,0xA96767CE,0x7D2B2B56,0x19FEFEE7,0x62D7D7B5,0xE6ABAB4D,0x9A7676EC,\n 0x45CACA8F,0x9D82821F,0x40C9C989,0x877D7DFA,0x15FAFAEF,0xEB5959B2,0xC947478E,0x0BF0F0FB,\n 0xECADAD41,0x67D4D4B3,0xFDA2A25F,0xEAAFAF45,0xBF9C9C23,0xF7A4A453,0x967272E4,0x5BC0C09B,\n 0xC2B7B775,0x1CFDFDE1,0xAE93933D,0x6A26264C,0x5A36366C,0x413F3F7E,0x02F7F7F5,0x4FCCCC83,\n 0x5C343468,0xF4A5A551,0x34E5E5D1,0x08F1F1F9,0x937171E2,0x73D8D8AB,0x53313162,0x3F15152A,\n 0x0C040408,0x52C7C795,0x65232346,0x5EC3C39D,0x28181830,0xA1969637,0x0F05050A,0xB59A9A2F,\n 0x0907070E,0x36121224,0x9B80801B,0x3DE2E2DF,0x26EBEBCD,0x6927274E,0xCDB2B27F,0x9F7575EA,\n 0x1B090912,0x9E83831D,0x742C2C58,0x2E1A1A34,0x2D1B1B36,0xB26E6EDC,0xEE5A5AB4,0xFBA0A05B,\n 0xF65252A4,0x4D3B3B76,0x61D6D6B7,0xCEB3B37D,0x7B292952,0x3EE3E3DD,0x712F2F5E,0x97848413,\n 0xF55353A6,0x68D1D1B9,0000000000,0x2CEDEDC1,0x60202040,0x1FFCFCE3,0xC8B1B179,0xED5B5BB6,\n 0xBE6A6AD4,0x46CBCB8D,0xD9BEBE67,0x4B393972,0xDE4A4A94,0xD44C4C98,0xE85858B0,0x4ACFCF85,\n 0x6BD0D0BB,0x2AEFEFC5,0xE5AAAA4F,0x16FBFBED,0xC5434386,0xD74D4D9A,0x55333366,0x94858511,\n 0xCF45458A,0x10F9F9E9,0x06020204,0x817F7FFE,0xF05050A0,0x443C3C78,0xBA9F9F25,0xE3A8A84B,\n 0xF35151A2,0xFEA3A35D,0xC0404080,0x8A8F8F05,0xAD92923F,0xBC9D9D21,0x48383870,0x04F5F5F1,\n 0xDFBCBC63,0xC1B6B677,0x75DADAAF,0x63212142,0x30101020,0x1AFFFFE5,0x0EF3F3FD,0x6DD2D2BF,\n 0x4CCDCD81,0x140C0C18,0x35131326,0x2FECECC3,0xE15F5FBE,0xA2979735,0xCC444488,0x3917172E,\n 0x57C4C493,0xF2A7A755,0x827E7EFC,0x473D3D7A,0xAC6464C8,0xE75D5DBA,0x2B191932,0x957373E6,\n 0xA06060C0,0x98818119,0xD14F4F9E,0x7FDCDCA3,0x66222244,0x7E2A2A54,0xAB90903B,0x8388880B,\n 0xCA46468C,0x29EEEEC7,0xD3B8B86B,0x3C141428,0x79DEDEA7,0xE25E5EBC,0x1D0B0B16,0x76DBDBAD,\n 0x3BE0E0DB,0x56323264,0x4E3A3A74,0x1E0A0A14,0xDB494992,0x0A06060C,0x6C242448,0xE45C5CB8,\n 0x5DC2C29F,0x6ED3D3BD,0xEFACAC43,0xA66262C4,0xA8919139,0xA4959531,0x37E4E4D3,0x8B7979F2,\n 0x32E7E7D5,0x43C8C88B,0x5937376E,0xB76D6DDA,0x8C8D8D01,0x64D5D5B1,0xD24E4E9C,0xE0A9A949,\n 0xB46C6CD8,0xFA5656AC,0x07F4F4F3,0x25EAEACF,0xAF6565CA,0x8E7A7AF4,0xE9AEAE47,0x18080810,\n 0xD5BABA6F,0x887878F0,0x6F25254A,0x722E2E5C,0x241C1C38,0xF1A6A657,0xC7B4B473,0x51C6C697,\n 0x23E8E8CB,0x7CDDDDA1,0x9C7474E8,0x211F1F3E,0xDD4B4B96,0xDCBDBD61,0x868B8B0D,0x858A8A0F,\n 0x907070E0,0x423E3E7C,0xC4B5B571,0xAA6666CC,0xD8484890,0x05030306,0x01F6F6F7,0x120E0E1C,\n 0xA36161C2,0x5F35356A,0xF95757AE,0xD0B9B969,0x91868617,0x58C1C199,0x271D1D3A,0xB99E9E27,\n 0x38E1E1D9,0x13F8F8EB,0xB398982B,0x33111122,0xBB6969D2,0x70D9D9A9,0x898E8E07,0xA7949433,\n 0xB69B9B2D,0x221E1E3C,0x92878715,0x20E9E9C9,0x49CECE87,0xFF5555AA,0x78282850,0x7ADFDFA5,\n 0x8F8C8C03,0xF8A1A159,0x80898909,0x170D0D1A,0xDABFBF65,0x31E6E6D7,0xC6424284,0xB86868D0,\n 0xC3414182,0xB0999929,0x772D2D5A,0x110F0F1E,0xCBB0B07B,0xFC5454A8,0xD6BBBB6D,0x3A16162C \n },\n {\n 0x6363C6A5,0x7C7CF884,0x7777EE99,0x7B7BF68D,0xF2F2FF0D,0x6B6BD6BD,0x6F6FDEB1,0xC5C59154,\n 0x30306050,0x01010203,0x6767CEA9,0x2B2B567D,0xFEFEE719,0xD7D7B562,0xABAB4DE6,0x7676EC9A,\n 0xCACA8F45,0x82821F9D,0xC9C98940,0x7D7DFA87,0xFAFAEF15,0x5959B2EB,0x47478EC9,0xF0F0FB0B,\n 0xADAD41EC,0xD4D4B367,0xA2A25FFD,0xAFAF45EA,0x9C9C23BF,0xA4A453F7,0x7272E496,0xC0C09B5B,\n 0xB7B775C2,0xFDFDE11C,0x93933DAE,0x26264C6A,0x36366C5A,0x3F3F7E41,0xF7F7F502,0xCCCC834F,\n 0x3434685C,0xA5A551F4,0xE5E5D134,0xF1F1F908,0x7171E293,0xD8D8AB73,0x31316253,0x15152A3F,\n 0x0404080C,0xC7C79552,0x23234665,0xC3C39D5E,0x18183028,0x969637A1,0x05050A0F,0x9A9A2FB5,\n 0x07070E09,0x12122436,0x80801B9B,0xE2E2DF3D,0xEBEBCD26,0x27274E69,0xB2B27FCD,0x7575EA9F,\n 0x0909121B,0x83831D9E,0x2C2C5874,0x1A1A342E,0x1B1B362D,0x6E6EDCB2,0x5A5AB4EE,0xA0A05BFB,\n 0x5252A4F6,0x3B3B764D,0xD6D6B761,0xB3B37DCE,0x2929527B,0xE3E3DD3E,0x2F2F5E71,0x84841397,\n 0x5353A6F5,0xD1D1B968,0000000000,0xEDEDC12C,0x20204060,0xFCFCE31F,0xB1B179C8,0x5B5BB6ED,\n 0x6A6AD4BE,0xCBCB8D46,0xBEBE67D9,0x3939724B,0x4A4A94DE,0x4C4C98D4,0x5858B0E8,0xCFCF854A,\n 0xD0D0BB6B,0xEFEFC52A,0xAAAA4FE5,0xFBFBED16,0x434386C5,0x4D4D9AD7,0x33336655,0x85851194,\n 0x45458ACF,0xF9F9E910,0x02020406,0x7F7FFE81,0x5050A0F0,0x3C3C7844,0x9F9F25BA,0xA8A84BE3,\n 0x5151A2F3,0xA3A35DFE,0x404080C0,0x8F8F058A,0x92923FAD,0x9D9D21BC,0x38387048,0xF5F5F104,\n 0xBCBC63DF,0xB6B677C1,0xDADAAF75,0x21214263,0x10102030,0xFFFFE51A,0xF3F3FD0E,0xD2D2BF6D,\n 0xCDCD814C,0x0C0C1814,0x13132635,0xECECC32F,0x5F5FBEE1,0x979735A2,0x444488CC,0x17172E39,\n 0xC4C49357,0xA7A755F2,0x7E7EFC82,0x3D3D7A47,0x6464C8AC,0x5D5DBAE7,0x1919322B,0x7373E695,\n 0x6060C0A0,0x81811998,0x4F4F9ED1,0xDCDCA37F,0x22224466,0x2A2A547E,0x90903BAB,0x88880B83,\n 0x46468CCA,0xEEEEC729,0xB8B86BD3,0x1414283C,0xDEDEA779,0x5E5EBCE2,0x0B0B161D,0xDBDBAD76,\n 0xE0E0DB3B,0x32326456,0x3A3A744E,0x0A0A141E,0x494992DB,0x06060C0A,0x2424486C,0x5C5CB8E4,\n 0xC2C29F5D,0xD3D3BD6E,0xACAC43EF,0x6262C4A6,0x919139A8,0x959531A4,0xE4E4D337,0x7979F28B,\n 0xE7E7D532,0xC8C88B43,0x37376E59,0x6D6DDAB7,0x8D8D018C,0xD5D5B164,0x4E4E9CD2,0xA9A949E0,\n 0x6C6CD8B4,0x5656ACFA,0xF4F4F307,0xEAEACF25,0x6565CAAF,0x7A7AF48E,0xAEAE47E9,0x08081018,\n 0xBABA6FD5,0x7878F088,0x25254A6F,0x2E2E5C72,0x1C1C3824,0xA6A657F1,0xB4B473C7,0xC6C69751,\n 0xE8E8CB23,0xDDDDA17C,0x7474E89C,0x1F1F3E21,0x4B4B96DD,0xBDBD61DC,0x8B8B0D86,0x8A8A0F85,\n 0x7070E090,0x3E3E7C42,0xB5B571C4,0x6666CCAA,0x484890D8,0x03030605,0xF6F6F701,0x0E0E1C12,\n 0x6161C2A3,0x35356A5F,0x5757AEF9,0xB9B969D0,0x86861791,0xC1C19958,0x1D1D3A27,0x9E9E27B9,\n 0xE1E1D938,0xF8F8EB13,0x98982BB3,0x11112233,0x6969D2BB,0xD9D9A970,0x8E8E0789,0x949433A7,\n 0x9B9B2DB6,0x1E1E3C22,0x87871592,0xE9E9C920,0xCECE8749,0x5555AAFF,0x28285078,0xDFDFA57A,\n 0x8C8C038F,0xA1A159F8,0x89890980,0x0D0D1A17,0xBFBF65DA,0xE6E6D731,0x424284C6,0x6868D0B8,\n 0x414182C3,0x999929B0,0x2D2D5A77,0x0F0F1E11,0xB0B07BCB,0x5454A8FC,0xBBBB6DD6,0x16162C3A \n },\n {\n 0x63C6A563,0x7CF8847C,0x77EE9977,0x7BF68D7B,0xF2FF0DF2,0x6BD6BD6B,0x6FDEB16F,0xC59154C5,\n 0x30605030,0x01020301,0x67CEA967,0x2B567D2B,0xFEE719FE,0xD7B562D7,0xAB4DE6AB,0x76EC9A76,\n 0xCA8F45CA,0x821F9D82,0xC98940C9,0x7DFA877D,0xFAEF15FA,0x59B2EB59,0x478EC947,0xF0FB0BF0,\n 0xAD41ECAD,0xD4B367D4,0xA25FFDA2,0xAF45EAAF,0x9C23BF9C,0xA453F7A4,0x72E49672,0xC09B5BC0,\n 0xB775C2B7,0xFDE11CFD,0x933DAE93,0x264C6A26,0x366C5A36,0x3F7E413F,0xF7F502F7,0xCC834FCC,\n 0x34685C34,0xA551F4A5,0xE5D134E5,0xF1F908F1,0x71E29371,0xD8AB73D8,0x31625331,0x152A3F15,\n 0x04080C04,0xC79552C7,0x23466523,0xC39D5EC3,0x18302818,0x9637A196,0x050A0F05,0x9A2FB59A,\n 0x070E0907,0x12243612,0x801B9B80,0xE2DF3DE2,0xEBCD26EB,0x274E6927,0xB27FCDB2,0x75EA9F75,\n 0x09121B09,0x831D9E83,0x2C58742C,0x1A342E1A,0x1B362D1B,0x6EDCB26E,0x5AB4EE5A,0xA05BFBA0,\n 0x52A4F652,0x3B764D3B,0xD6B761D6,0xB37DCEB3,0x29527B29,0xE3DD3EE3,0x2F5E712F,0x84139784,\n 0x53A6F553,0xD1B968D1,0000000000,0xEDC12CED,0x20406020,0xFCE31FFC,0xB179C8B1,0x5BB6ED5B,\n 0x6AD4BE6A,0xCB8D46CB,0xBE67D9BE,0x39724B39,0x4A94DE4A,0x4C98D44C,0x58B0E858,0xCF854ACF,\n 0xD0BB6BD0,0xEFC52AEF,0xAA4FE5AA,0xFBED16FB,0x4386C543,0x4D9AD74D,0x33665533,0x85119485,\n 0x458ACF45,0xF9E910F9,0x02040602,0x7FFE817F,0x50A0F050,0x3C78443C,0x9F25BA9F,0xA84BE3A8,\n 0x51A2F351,0xA35DFEA3,0x4080C040,0x8F058A8F,0x923FAD92,0x9D21BC9D,0x38704838,0xF5F104F5,\n 0xBC63DFBC,0xB677C1B6,0xDAAF75DA,0x21426321,0x10203010,0xFFE51AFF,0xF3FD0EF3,0xD2BF6DD2,\n 0xCD814CCD,0x0C18140C,0x13263513,0xECC32FEC,0x5FBEE15F,0x9735A297,0x4488CC44,0x172E3917,\n 0xC49357C4,0xA755F2A7,0x7EFC827E,0x3D7A473D,0x64C8AC64,0x5DBAE75D,0x19322B19,0x73E69573,\n 0x60C0A060,0x81199881,0x4F9ED14F,0xDCA37FDC,0x22446622,0x2A547E2A,0x903BAB90,0x880B8388,\n 0x468CCA46,0xEEC729EE,0xB86BD3B8,0x14283C14,0xDEA779DE,0x5EBCE25E,0x0B161D0B,0xDBAD76DB,\n 0xE0DB3BE0,0x32645632,0x3A744E3A,0x0A141E0A,0x4992DB49,0x060C0A06,0x24486C24,0x5CB8E45C,\n 0xC29F5DC2,0xD3BD6ED3,0xAC43EFAC,0x62C4A662,0x9139A891,0x9531A495,0xE4D337E4,0x79F28B79,\n 0xE7D532E7,0xC88B43C8,0x376E5937,0x6DDAB76D,0x8D018C8D,0xD5B164D5,0x4E9CD24E,0xA949E0A9,\n 0x6CD8B46C,0x56ACFA56,0xF4F307F4,0xEACF25EA,0x65CAAF65,0x7AF48E7A,0xAE47E9AE,0x08101808,\n 0xBA6FD5BA,0x78F08878,0x254A6F25,0x2E5C722E,0x1C38241C,0xA657F1A6,0xB473C7B4,0xC69751C6,\n 0xE8CB23E8,0xDDA17CDD,0x74E89C74,0x1F3E211F,0x4B96DD4B,0xBD61DCBD,0x8B0D868B,0x8A0F858A,\n 0x70E09070,0x3E7C423E,0xB571C4B5,0x66CCAA66,0x4890D848,0x03060503,0xF6F701F6,0x0E1C120E,\n 0x61C2A361,0x356A5F35,0x57AEF957,0xB969D0B9,0x86179186,0xC19958C1,0x1D3A271D,0x9E27B99E,\n 0xE1D938E1,0xF8EB13F8,0x982BB398,0x11223311,0x69D2BB69,0xD9A970D9,0x8E07898E,0x9433A794,\n 0x9B2DB69B,0x1E3C221E,0x87159287,0xE9C920E9,0xCE8749CE,0x55AAFF55,0x28507828,0xDFA57ADF,\n 0x8C038F8C,0xA159F8A1,0x89098089,0x0D1A170D,0xBF65DABF,0xE6D731E6,0x4284C642,0x68D0B868,\n 0x4182C341,0x9929B099,0x2D5A772D,0x0F1E110F,0xB07BCBB0,0x54A8FC54,0xBB6DD6BB,0x162C3A16 \n },\n {\n 0xC6A56363,0xF8847C7C,0xEE997777,0xF68D7B7B,0xFF0DF2F2,0xD6BD6B6B,0xDEB16F6F,0x9154C5C5,\n 0x60503030,0x02030101,0xCEA96767,0x567D2B2B,0xE719FEFE,0xB562D7D7,0x4DE6ABAB,0xEC9A7676,\n 0x8F45CACA,0x1F9D8282,0x8940C9C9,0xFA877D7D,0xEF15FAFA,0xB2EB5959,0x8EC94747,0xFB0BF0F0,\n 0x41ECADAD,0xB367D4D4,0x5FFDA2A2,0x45EAAFAF,0x23BF9C9C,0x53F7A4A4,0xE4967272,0x9B5BC0C0,\n 0x75C2B7B7,0xE11CFDFD,0x3DAE9393,0x4C6A2626,0x6C5A3636,0x7E413F3F,0xF502F7F7,0x834FCCCC,\n 0x685C3434,0x51F4A5A5,0xD134E5E5,0xF908F1F1,0xE2937171,0xAB73D8D8,0x62533131,0x2A3F1515,\n 0x080C0404,0x9552C7C7,0x46652323,0x9D5EC3C3,0x30281818,0x37A19696,0x0A0F0505,0x2FB59A9A,\n 0x0E090707,0x24361212,0x1B9B8080,0xDF3DE2E2,0xCD26EBEB,0x4E692727,0x7FCDB2B2,0xEA9F7575,\n 0x121B0909,0x1D9E8383,0x58742C2C,0x342E1A1A,0x362D1B1B,0xDCB26E6E,0xB4EE5A5A,0x5BFBA0A0,\n 0xA4F65252,0x764D3B3B,0xB761D6D6,0x7DCEB3B3,0x527B2929,0xDD3EE3E3,0x5E712F2F,0x13978484,\n 0xA6F55353,0xB968D1D1,0000000000,0xC12CEDED,0x40602020,0xE31FFCFC,0x79C8B1B1,0xB6ED5B5B,\n 0xD4BE6A6A,0x8D46CBCB,0x67D9BEBE,0x724B3939,0x94DE4A4A,0x98D44C4C,0xB0E85858,0x854ACFCF,\n 0xBB6BD0D0,0xC52AEFEF,0x4FE5AAAA,0xED16FBFB,0x86C54343,0x9AD74D4D,0x66553333,0x11948585,\n 0x8ACF4545,0xE910F9F9,0x04060202,0xFE817F7F,0xA0F05050,0x78443C3C,0x25BA9F9F,0x4BE3A8A8,\n 0xA2F35151,0x5DFEA3A3,0x80C04040,0x058A8F8F,0x3FAD9292,0x21BC9D9D,0x70483838,0xF104F5F5,\n 0x63DFBCBC,0x77C1B6B6,0xAF75DADA,0x42632121,0x20301010,0xE51AFFFF,0xFD0EF3F3,0xBF6DD2D2,\n 0x814CCDCD,0x18140C0C,0x26351313,0xC32FECEC,0xBEE15F5F,0x35A29797,0x88CC4444,0x2E391717,\n 0x9357C4C4,0x55F2A7A7,0xFC827E7E,0x7A473D3D,0xC8AC6464,0xBAE75D5D,0x322B1919,0xE6957373,\n 0xC0A06060,0x19988181,0x9ED14F4F,0xA37FDCDC,0x44662222,0x547E2A2A,0x3BAB9090,0x0B838888,\n 0x8CCA4646,0xC729EEEE,0x6BD3B8B8,0x283C1414,0xA779DEDE,0xBCE25E5E,0x161D0B0B,0xAD76DBDB,\n 0xDB3BE0E0,0x64563232,0x744E3A3A,0x141E0A0A,0x92DB4949,0x0C0A0606,0x486C2424,0xB8E45C5C,\n 0x9F5DC2C2,0xBD6ED3D3,0x43EFACAC,0xC4A66262,0x39A89191,0x31A49595,0xD337E4E4,0xF28B7979,\n 0xD532E7E7,0x8B43C8C8,0x6E593737,0xDAB76D6D,0x018C8D8D,0xB164D5D5,0x9CD24E4E,0x49E0A9A9,\n 0xD8B46C6C,0xACFA5656,0xF307F4F4,0xCF25EAEA,0xCAAF6565,0xF48E7A7A,0x47E9AEAE,0x10180808,\n 0x6FD5BABA,0xF0887878,0x4A6F2525,0x5C722E2E,0x38241C1C,0x57F1A6A6,0x73C7B4B4,0x9751C6C6,\n 0xCB23E8E8,0xA17CDDDD,0xE89C7474,0x3E211F1F,0x96DD4B4B,0x61DCBDBD,0x0D868B8B,0x0F858A8A,\n 0xE0907070,0x7C423E3E,0x71C4B5B5,0xCCAA6666,0x90D84848,0x06050303,0xF701F6F6,0x1C120E0E,\n 0xC2A36161,0x6A5F3535,0xAEF95757,0x69D0B9B9,0x17918686,0x9958C1C1,0x3A271D1D,0x27B99E9E,\n 0xD938E1E1,0xEB13F8F8,0x2BB39898,0x22331111,0xD2BB6969,0xA970D9D9,0x07898E8E,0x33A79494,\n 0x2DB69B9B,0x3C221E1E,0x15928787,0xC920E9E9,0x8749CECE,0xAAFF5555,0x50782828,0xA57ADFDF,\n 0x038F8C8C,0x59F8A1A1,0x09808989,0x1A170D0D,0x65DABFBF,0xD731E6E6,0x84C64242,0xD0B86868,\n 0x82C34141,0x29B09999,0x5A772D2D,0x1E110F0F,0x7BCBB0B0,0xA8FC5454,0x6DD6BBBB,0x2C3A1616 \n } \n};\n\nstatic u32 fl_tab[4][256] = \n{\n {\n 0x00000063,0x0000007C,0x00000077,0x0000007B,0x000000F2,0x0000006B,0x0000006F,0x000000C5,\n 0x00000030,0x00000001,0x00000067,0x0000002B,0x000000FE,0x000000D7,0x000000AB,0x00000076,\n 0x000000CA,0x00000082,0x000000C9,0x0000007D,0x000000FA,0x00000059,0x00000047,0x000000F0,\n 0x000000AD,0x000000D4,0x000000A2,0x000000AF,0x0000009C,0x000000A4,0x00000072,0x000000C0,\n 0x000000B7,0x000000FD,0x00000093,0x00000026,0x00000036,0x0000003F,0x000000F7,0x000000CC,\n 0x00000034,0x000000A5,0x000000E5,0x000000F1,0x00000071,0x000000D8,0x00000031,0x00000015,\n 0x00000004,0x000000C7,0x00000023,0x000000C3,0x00000018,0x00000096,0x00000005,0x0000009A,\n 0x00000007,0x00000012,0x00000080,0x000000E2,0x000000EB,0x00000027,0x000000B2,0x00000075,\n 0x00000009,0x00000083,0x0000002C,0x0000001A,0x0000001B,0x0000006E,0x0000005A,0x000000A0,\n 0x00000052,0x0000003B,0x000000D6,0x000000B3,0x00000029,0x000000E3,0x0000002F,0x00000084,\n 0x00000053,0x000000D1,0x00000000,0x000000ED,0x00000020,0x000000FC,0x000000B1,0x0000005B,\n 0x0000006A,0x000000CB,0x000000BE,0x00000039,0x0000004A,0x0000004C,0x00000058,0x000000CF,\n 0x000000D0,0x000000EF,0x000000AA,0x000000FB,0x00000043,0x0000004D,0x00000033,0x00000085,\n 0x00000045,0x000000F9,0x00000002,0x0000007F,0x00000050,0x0000003C,0x0000009F,0x000000A8,\n 0x00000051,0x000000A3,0x00000040,0x0000008F,0x00000092,0x0000009D,0x00000038,0x000000F5,\n 0x000000BC,0x000000B6,0x000000DA,0x00000021,0x00000010,0x000000FF,0x000000F3,0x000000D2,\n 0x000000CD,0x0000000C,0x00000013,0x000000EC,0x0000005F,0x00000097,0x00000044,0x00000017,\n 0x000000C4,0x000000A7,0x0000007E,0x0000003D,0x00000064,0x0000005D,0x00000019,0x00000073,\n 0x00000060,0x00000081,0x0000004F,0x000000DC,0x00000022,0x0000002A,0x00000090,0x00000088,\n 0x00000046,0x000000EE,0x000000B8,0x00000014,0x000000DE,0x0000005E,0x0000000B,0x000000DB,\n 0x000000E0,0x00000032,0x0000003A,0x0000000A,0x00000049,0x00000006,0x00000024,0x0000005C,\n 0x000000C2,0x000000D3,0x000000AC,0x00000062,0x00000091,0x00000095,0x000000E4,0x00000079,\n 0x000000E7,0x000000C8,0x00000037,0x0000006D,0x0000008D,0x000000D5,0x0000004E,0x000000A9,\n 0x0000006C,0x00000056,0x000000F4,0x000000EA,0x00000065,0x0000007A,0x000000AE,0x00000008,\n 0x000000BA,0x00000078,0x00000025,0x0000002E,0x0000001C,0x000000A6,0x000000B4,0x000000C6,\n 0x000000E8,0x000000DD,0x00000074,0x0000001F,0x0000004B,0x000000BD,0x0000008B,0x0000008A,\n 0x00000070,0x0000003E,0x000000B5,0x00000066,0x00000048,0x00000003,0x000000F6,0x0000000E,\n 0x00000061,0x00000035,0x00000057,0x000000B9,0x00000086,0x000000C1,0x0000001D,0x0000009E,\n 0x000000E1,0x000000F8,0x00000098,0x00000011,0x00000069,0x000000D9,0x0000008E,0x00000094,\n 0x0000009B,0x0000001E,0x00000087,0x000000E9,0x000000CE,0x00000055,0x00000028,0x000000DF,\n 0x0000008C,0x000000A1,0x00000089,0x0000000D,0x000000BF,0x000000E6,0x00000042,0x00000068,\n 0x00000041,0x00000099,0x0000002D,0x0000000F,0x000000B0,0x00000054,0x000000BB,0x00000016 \n },\n {\n 0x00006300,0x00007C00,0x00007700,0x00007B00,0x0000F200,0x00006B00,0x00006F00,0x0000C500,\n 0x00003000,0x00000100,0x00006700,0x00002B00,0x0000FE00,0x0000D700,0x0000AB00,0x00007600,\n 0x0000CA00,0x00008200,0x0000C900,0x00007D00,0x0000FA00,0x00005900,0x00004700,0x0000F000,\n 0x0000AD00,0x0000D400,0x0000A200,0x0000AF00,0x00009C00,0x0000A400,0x00007200,0x0000C000,\n 0x0000B700,0x0000FD00,0x00009300,0x00002600,0x00003600,0x00003F00,0x0000F700,0x0000CC00,\n 0x00003400,0x0000A500,0x0000E500,0x0000F100,0x00007100,0x0000D800,0x00003100,0x00001500,\n 0x00000400,0x0000C700,0x00002300,0x0000C300,0x00001800,0x00009600,0x00000500,0x00009A00,\n 0x00000700,0x00001200,0x00008000,0x0000E200,0x0000EB00,0x00002700,0x0000B200,0x00007500,\n 0x00000900,0x00008300,0x00002C00,0x00001A00,0x00001B00,0x00006E00,0x00005A00,0x0000A000,\n 0x00005200,0x00003B00,0x0000D600,0x0000B300,0x00002900,0x0000E300,0x00002F00,0x00008400,\n 0x00005300,0x0000D100,0000000000,0x0000ED00,0x00002000,0x0000FC00,0x0000B100,0x00005B00,\n 0x00006A00,0x0000CB00,0x0000BE00,0x00003900,0x00004A00,0x00004C00,0x00005800,0x0000CF00,\n 0x0000D000,0x0000EF00,0x0000AA00,0x0000FB00,0x00004300,0x00004D00,0x00003300,0x00008500,\n 0x00004500,0x0000F900,0x00000200,0x00007F00,0x00005000,0x00003C00,0x00009F00,0x0000A800,\n 0x00005100,0x0000A300,0x00004000,0x00008F00,0x00009200,0x00009D00,0x00003800,0x0000F500,\n 0x0000BC00,0x0000B600,0x0000DA00,0x00002100,0x00001000,0x0000FF00,0x0000F300,0x0000D200,\n 0x0000CD00,0x00000C00,0x00001300,0x0000EC00,0x00005F00,0x00009700,0x00004400,0x00001700,\n 0x0000C400,0x0000A700,0x00007E00,0x00003D00,0x00006400,0x00005D00,0x00001900,0x00007300,\n 0x00006000,0x00008100,0x00004F00,0x0000DC00,0x00002200,0x00002A00,0x00009000,0x00008800,\n 0x00004600,0x0000EE00,0x0000B800,0x00001400,0x0000DE00,0x00005E00,0x00000B00,0x0000DB00,\n 0x0000E000,0x00003200,0x00003A00,0x00000A00,0x00004900,0x00000600,0x00002400,0x00005C00,\n 0x0000C200,0x0000D300,0x0000AC00,0x00006200,0x00009100,0x00009500,0x0000E400,0x00007900,\n 0x0000E700,0x0000C800,0x00003700,0x00006D00,0x00008D00,0x0000D500,0x00004E00,0x0000A900,\n 0x00006C00,0x00005600,0x0000F400,0x0000EA00,0x00006500,0x00007A00,0x0000AE00,0x00000800,\n 0x0000BA00,0x00007800,0x00002500,0x00002E00,0x00001C00,0x0000A600,0x0000B400,0x0000C600,\n 0x0000E800,0x0000DD00,0x00007400,0x00001F00,0x00004B00,0x0000BD00,0x00008B00,0x00008A00,\n 0x00007000,0x00003E00,0x0000B500,0x00006600,0x00004800,0x00000300,0x0000F600,0x00000E00,\n 0x00006100,0x00003500,0x00005700,0x0000B900,0x00008600,0x0000C100,0x00001D00,0x00009E00,\n 0x0000E100,0x0000F800,0x00009800,0x00001100,0x00006900,0x0000D900,0x00008E00,0x00009400,\n 0x00009B00,0x00001E00,0x00008700,0x0000E900,0x0000CE00,0x00005500,0x00002800,0x0000DF00,\n 0x00008C00,0x0000A100,0x00008900,0x00000D00,0x0000BF00,0x0000E600,0x00004200,0x00006800,\n 0x00004100,0x00009900,0x00002D00,0x00000F00,0x0000B000,0x00005400,0x0000BB00,0x00001600 \n },\n {\n 0x00630000,0x007C0000,0x00770000,0x007B0000,0x00F20000,0x006B0000,0x006F0000,0x00C50000,\n 0x00300000,0x00010000,0x00670000,0x002B0000,0x00FE0000,0x00D70000,0x00AB0000,0x00760000,\n 0x00CA0000,0x00820000,0x00C90000,0x007D0000,0x00FA0000,0x00590000,0x00470000,0x00F00000,\n 0x00AD0000,0x00D40000,0x00A20000,0x00AF0000,0x009C0000,0x00A40000,0x00720000,0x00C00000,\n 0x00B70000,0x00FD0000,0x00930000,0x00260000,0x00360000,0x003F0000,0x00F70000,0x00CC0000,\n 0x00340000,0x00A50000,0x00E50000,0x00F10000,0x00710000,0x00D80000,0x00310000,0x00150000,\n 0x00040000,0x00C70000,0x00230000,0x00C30000,0x00180000,0x00960000,0x00050000,0x009A0000,\n 0x00070000,0x00120000,0x00800000,0x00E20000,0x00EB0000,0x00270000,0x00B20000,0x00750000,\n 0x00090000,0x00830000,0x002C0000,0x001A0000,0x001B0000,0x006E0000,0x005A0000,0x00A00000,\n 0x00520000,0x003B0000,0x00D60000,0x00B30000,0x00290000,0x00E30000,0x002F0000,0x00840000,\n 0x00530000,0x00D10000,0000000000,0x00ED0000,0x00200000,0x00FC0000,0x00B10000,0x005B0000,\n 0x006A0000,0x00CB0000,0x00BE0000,0x00390000,0x004A0000,0x004C0000,0x00580000,0x00CF0000,\n 0x00D00000,0x00EF0000,0x00AA0000,0x00FB0000,0x00430000,0x004D0000,0x00330000,0x00850000,\n 0x00450000,0x00F90000,0x00020000,0x007F0000,0x00500000,0x003C0000,0x009F0000,0x00A80000,\n 0x00510000,0x00A30000,0x00400000,0x008F0000,0x00920000,0x009D0000,0x00380000,0x00F50000,\n 0x00BC0000,0x00B60000,0x00DA0000,0x00210000,0x00100000,0x00FF0000,0x00F30000,0x00D20000,\n 0x00CD0000,0x000C0000,0x00130000,0x00EC0000,0x005F0000,0x00970000,0x00440000,0x00170000,\n 0x00C40000,0x00A70000,0x007E0000,0x003D0000,0x00640000,0x005D0000,0x00190000,0x00730000,\n 0x00600000,0x00810000,0x004F0000,0x00DC0000,0x00220000,0x002A0000,0x00900000,0x00880000,\n 0x00460000,0x00EE0000,0x00B80000,0x00140000,0x00DE0000,0x005E0000,0x000B0000,0x00DB0000,\n 0x00E00000,0x00320000,0x003A0000,0x000A0000,0x00490000,0x00060000,0x00240000,0x005C0000,\n 0x00C20000,0x00D30000,0x00AC0000,0x00620000,0x00910000,0x00950000,0x00E40000,0x00790000,\n 0x00E70000,0x00C80000,0x00370000,0x006D0000,0x008D0000,0x00D50000,0x004E0000,0x00A90000,\n 0x006C0000,0x00560000,0x00F40000,0x00EA0000,0x00650000,0x007A0000,0x00AE0000,0x00080000,\n 0x00BA0000,0x00780000,0x00250000,0x002E0000,0x001C0000,0x00A60000,0x00B40000,0x00C60000,\n 0x00E80000,0x00DD0000,0x00740000,0x001F0000,0x004B0000,0x00BD0000,0x008B0000,0x008A0000,\n 0x00700000,0x003E0000,0x00B50000,0x00660000,0x00480000,0x00030000,0x00F60000,0x000E0000,\n 0x00610000,0x00350000,0x00570000,0x00B90000,0x00860000,0x00C10000,0x001D0000,0x009E0000,\n 0x00E10000,0x00F80000,0x00980000,0x00110000,0x00690000,0x00D90000,0x008E0000,0x00940000,\n 0x009B0000,0x001E0000,0x00870000,0x00E90000,0x00CE0000,0x00550000,0x00280000,0x00DF0000,\n 0x008C0000,0x00A10000,0x00890000,0x000D0000,0x00BF0000,0x00E60000,0x00420000,0x00680000,\n 0x00410000,0x00990000,0x002D0000,0x000F0000,0x00B00000,0x00540000,0x00BB0000,0x00160000 \n },\n {\n 0x63000000,0x7C000000,0x77000000,0x7B000000,0xF2000000,0x6B000000,0x6F000000,0xC5000000,\n 0x30000000,0x01000000,0x67000000,0x2B000000,0xFE000000,0xD7000000,0xAB000000,0x76000000,\n 0xCA000000,0x82000000,0xC9000000,0x7D000000,0xFA000000,0x59000000,0x47000000,0xF0000000,\n 0xAD000000,0xD4000000,0xA2000000,0xAF000000,0x9C000000,0xA4000000,0x72000000,0xC0000000,\n 0xB7000000,0xFD000000,0x93000000,0x26000000,0x36000000,0x3F000000,0xF7000000,0xCC000000,\n 0x34000000,0xA5000000,0xE5000000,0xF1000000,0x71000000,0xD8000000,0x31000000,0x15000000,\n 0x04000000,0xC7000000,0x23000000,0xC3000000,0x18000000,0x96000000,0x05000000,0x9A000000,\n 0x07000000,0x12000000,0x80000000,0xE2000000,0xEB000000,0x27000000,0xB2000000,0x75000000,\n 0x09000000,0x83000000,0x2C000000,0x1A000000,0x1B000000,0x6E000000,0x5A000000,0xA0000000,\n 0x52000000,0x3B000000,0xD6000000,0xB3000000,0x29000000,0xE3000000,0x2F000000,0x84000000,\n 0x53000000,0xD1000000,0000000000,0xED000000,0x20000000,0xFC000000,0xB1000000,0x5B000000,\n 0x6A000000,0xCB000000,0xBE000000,0x39000000,0x4A000000,0x4C000000,0x58000000,0xCF000000,\n 0xD0000000,0xEF000000,0xAA000000,0xFB000000,0x43000000,0x4D000000,0x33000000,0x85000000,\n 0x45000000,0xF9000000,0x02000000,0x7F000000,0x50000000,0x3C000000,0x9F000000,0xA8000000,\n 0x51000000,0xA3000000,0x40000000,0x8F000000,0x92000000,0x9D000000,0x38000000,0xF5000000,\n 0xBC000000,0xB6000000,0xDA000000,0x21000000,0x10000000,0xFF000000,0xF3000000,0xD2000000,\n 0xCD000000,0x0C000000,0x13000000,0xEC000000,0x5F000000,0x97000000,0x44000000,0x17000000,\n 0xC4000000,0xA7000000,0x7E000000,0x3D000000,0x64000000,0x5D000000,0x19000000,0x73000000,\n 0x60000000,0x81000000,0x4F000000,0xDC000000,0x22000000,0x2A000000,0x90000000,0x88000000,\n 0x46000000,0xEE000000,0xB8000000,0x14000000,0xDE000000,0x5E000000,0x0B000000,0xDB000000,\n 0xE0000000,0x32000000,0x3A000000,0x0A000000,0x49000000,0x06000000,0x24000000,0x5C000000,\n 0xC2000000,0xD3000000,0xAC000000,0x62000000,0x91000000,0x95000000,0xE4000000,0x79000000,\n 0xE7000000,0xC8000000,0x37000000,0x6D000000,0x8D000000,0xD5000000,0x4E000000,0xA9000000,\n 0x6C000000,0x56000000,0xF4000000,0xEA000000,0x65000000,0x7A000000,0xAE000000,0x08000000,\n 0xBA000000,0x78000000,0x25000000,0x2E000000,0x1C000000,0xA6000000,0xB4000000,0xC6000000,\n 0xE8000000,0xDD000000,0x74000000,0x1F000000,0x4B000000,0xBD000000,0x8B000000,0x8A000000,\n 0x70000000,0x3E000000,0xB5000000,0x66000000,0x48000000,0x03000000,0xF6000000,0x0E000000,\n 0x61000000,0x35000000,0x57000000,0xB9000000,0x86000000,0xC1000000,0x1D000000,0x9E000000,\n 0xE1000000,0xF8000000,0x98000000,0x11000000,0x69000000,0xD9000000,0x8E000000,0x94000000,\n 0x9B000000,0x1E000000,0x87000000,0xE9000000,0xCE000000,0x55000000,0x28000000,0xDF000000,\n 0x8C000000,0xA1000000,0x89000000,0x0D000000,0xBF000000,0xE6000000,0x42000000,0x68000000,\n 0x41000000,0x99000000,0x2D000000,0x0F000000,0xB0000000,0x54000000,0xBB000000,0x16000000 \n } \n};\n\n/*----------------- The workspace ------------------------------*/\n\nstatic u32 Ekey[44];\t/* The expanded key */\n\n/*------ The round Function.  4 table lookups and 4 Exors ------*/\n#define f_rnd(x, n)                     \\\n  ( ft_tab[0][byte0(x[n])]              \\\n  ^ ft_tab[1][byte1(x[(n + 1) & 3])]    \\\n  ^ ft_tab[2][byte2(x[(n + 2) & 3])]    \\\n  ^ ft_tab[3][byte3(x[(n + 3) & 3])] )\n\n#define f_round(bo, bi, k)          \\\n    bo[0] = f_rnd(bi, 0) ^ k[0];    \\\n    bo[1] = f_rnd(bi, 1) ^ k[1];    \\\n    bo[2] = f_rnd(bi, 2) ^ k[2];    \\\n    bo[3] = f_rnd(bi, 3) ^ k[3];    \\\n    k += 4\n\n/*--- The S Box lookup used in constructing the Key schedule ---*/\n#define ls_box(x)       \\\n (  fl_tab[0][byte0(x)] \\\n  ^ fl_tab[1][byte1(x)] \\\n  ^ fl_tab[2][byte2(x)] \\\n  ^ fl_tab[3][byte3(x)] )\n\n/*------------ The last round function (no MixColumn) ----------*/\n#define lf_rnd(x, n)                    \\\n  ( fl_tab[0][byte0(x[n])]              \\\n  ^ fl_tab[1][byte1(x[(n + 1) & 3])]    \\\n  ^ fl_tab[2][byte2(x[(n + 2) & 3])]    \\\n  ^ fl_tab[3][byte3(x[(n + 3) & 3])] )\n\n\n/*-----------------------------------------------------------\n * RijndaelKeySchedule\n *   Initialise the key schedule from a supplied key\n */\nvoid RijndaelKeySchedule(u8 key[16])\n{\n    u32  t;\n    u32  *ek=Ekey,\t    /* pointer to the expanded key   */\n         *rc=rnd_con;   /* pointer to the round constant */\n\n    Ekey[0] = u32_in(key     );\n    Ekey[1] = u32_in(key +  4);\n    Ekey[2] = u32_in(key +  8);\n    Ekey[3] = u32_in(key + 12);\n\n\twhile(ek < Ekey + 40)\n    {\n\t\tt = rot3(ek[3]);\n        ek[4] = ek[0] ^ ls_box(t) ^ *rc++;\n        ek[5] = ek[1] ^ ek[4];\n        ek[6] = ek[2] ^ ek[5];\n        ek[7] = ek[3] ^ ek[6];\n        ek += 4;\n    }\n}\n\n/*-----------------------------------------------------------\n * RijndaelEncrypt\n *   Encrypt an input block\n */\nvoid RijndaelEncrypt(u8 in[16], u8 out[16])\n{\n    u32    b0[4], b1[4], *kp = Ekey;\n\n    b0[0] = u32_in(in     ) ^ *kp++;\n    b0[1] = u32_in(in +  4) ^ *kp++;\n    b0[2] = u32_in(in +  8) ^ *kp++;\n    b0[3] = u32_in(in + 12) ^ *kp++;\n\n    f_round(b1, b0, kp); \n    f_round(b0, b1, kp);\n    f_round(b1, b0, kp); \n    f_round(b0, b1, kp);\n    f_round(b1, b0, kp); \n    f_round(b0, b1, kp);\n    f_round(b1, b0, kp); \n    f_round(b0, b1, kp);\n    f_round(b1, b0, kp); \n\n    u32_out(out,      lf_rnd(b1, 0) ^ kp[0]); \n    u32_out(out +  4, lf_rnd(b1, 1) ^ kp[1]);\n    u32_out(out +  8, lf_rnd(b1, 2) ^ kp[2]); \n    u32_out(out + 12, lf_rnd(b1, 3) ^ kp[3]);\n}\n"
  },
  {
    "path": "src/parser/rijndael.h",
    "content": "/*-------------------------------------------------------------------\n *          Example algorithms f1, f1*, f2, f3, f4, f5, f5*\n *-------------------------------------------------------------------\n *\n *  A sample implementation of the example 3GPP authentication and\n *  key agreement functions f1, f1*, f2, f3, f4, f5 and f5*.  This is\n *  a byte-oriented implementation of the functions, and of the block\n *  cipher kernel function Rijndael.\n *\n *  This has been coded for clarity, not necessarily for efficiency.\n *\n *  The functions f2, f3, f4 and f5 share the same inputs and have\n *  been coded together as a single function.  f1, f1* and f5* are\n *  all coded separately.\n *\n *-----------------------------------------------------------------*/\n\n#ifndef RIJNDAEL_H\n#define RIJNDAEL_H\n\n\nvoid RijndaelKeySchedule( u8 key[16] );\nvoid RijndaelEncrypt( u8 input[16], u8 output[16] );\n\n\n#endif\n"
  },
  {
    "path": "src/parser/route.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"definitions.h\"\n#include \"route.h\"\n#include \"parse_ctrl.h\"\n#include \"util.h\"\n\nvoid t_route::add_param(const t_parameter &p) {\n\tparams.push_back(p);\n}\n\nvoid t_route::set_params(const list<t_parameter> &l) {\n\tparams = l;\n}\n\nstring t_route::encode(void) const {\n\tstring s;\n\n\tif (display.size() > 0) {\n\t\ts += '\"';\n\t\ts += escape(display, '\"');\n\t\ts += '\"';\n\t\ts += ' ';\n\t}\n\n\ts += '<';\n\ts += uri.encode();\n\ts += '>';\n\n\ts += param_list2str(params);\n\treturn s;\n}\n"
  },
  {
    "path": "src/parser/route.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Route item\n\n#ifndef _H_ROUTE\n#define _H_ROUTE\n\n#include <list>\n#include <string>\n#include \"parameter.h\"\n\nusing namespace std;\n\nclass t_route {\npublic:\n\tstring\t\t\tdisplay;\n\tt_url\t\t\turi;\n\tlist<t_parameter>\tparams;\n\n\tvoid add_param(const t_parameter &p);\n\tvoid set_params(const list<t_parameter> &l);\n\tstring encode(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/scanner.lxx",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n%{\n#include <cstdio>\n#include <cstring>\n#include <math.h>\n#include <string>\n#include \"parse_ctrl.h\"\n#include \"parser.hxx\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\n%}\n\n%option noyywrap\n%option stack\n\nDIGIT\t\t[0-9]\nHEXDIG\t\t[0-9a-fA-F]\nALPHA\t\t[a-zA-Z]\nCAPITALS\t[A-Z]\nALNUM\t\t[a-zA-Z0-9]\nTOKEN_SYM\t[[:alnum:]\\-\\.!%\\*_\\+\\`\\'~]\nWORD_SYM\t[[:alnum:]\\-\\.!%\\*_\\+\\`\\'~\\(\\)<>:\\\\\\\"\\/\\[\\]\\?\\{\\}]\n\n%x C_URI\n%x C_URI_SPECIAL\n%x C_QSTRING\n%x C_LANG\n%x C_WORD\n%x C_NUM\n%x C_DATE\n%x C_LINE\n%x C_COMMENT\n%x C_NEW\n%x C_AUTH_SCHEME\n%x C_RPAREN\n%x C_IPV6ADDR\n%x C_PARAMVAL\n\n%%\n\tswitch (t_parser::context) {\n\tcase t_parser::X_URI:\t\tBEGIN(C_URI); break;\n\tcase t_parser::X_URI_SPECIAL:\tBEGIN(C_URI_SPECIAL); break;\n\tcase t_parser::X_LANG:\t\tBEGIN(C_LANG); break;\n\tcase t_parser::X_WORD:\t\tBEGIN(C_WORD); break;\n\tcase t_parser::X_NUM:\t\tBEGIN(C_NUM); break;\n\tcase t_parser::X_DATE:\t\tBEGIN(C_DATE); break;\n\tcase t_parser::X_LINE:\t\tBEGIN(C_LINE); break;\n\tcase t_parser::X_COMMENT:\tBEGIN(C_COMMENT); break;\n\tcase t_parser::X_NEW:\t\tBEGIN(C_NEW); break;\n\tcase t_parser::X_AUTH_SCHEME:\tBEGIN(C_AUTH_SCHEME); break;\n\tcase t_parser::X_IPV6ADDR:\tBEGIN(C_IPV6ADDR); break;\n\tcase t_parser::X_PARAMVAL:\tBEGIN(C_PARAMVAL); break;\n\tdefault:\t\t\tBEGIN(INITIAL);\n\t}\n\n\t/* Headers */\n^(?i:Accept)\t\t\t{ return T_HDR_ACCEPT; }\n^(?i:Accept-Encoding)\t\t{ return T_HDR_ACCEPT_ENCODING; }\n^(?i:Accept-Language)\t\t{ return T_HDR_ACCEPT_LANGUAGE; }\n^(?i:Alert-Info)\t\t{ return T_HDR_ALERT_INFO; }\n^(?i:Allow)\t\t\t{ return T_HDR_ALLOW; }\n^(?i:Allow-Events|u)\t\t{ return T_HDR_ALLOW_EVENTS; }\n^(?i:Authentication-Info)\t{ return T_HDR_AUTHENTICATION_INFO; }\n^(?i:Authorization)\t\t{ return T_HDR_AUTHORIZATION; }\n^(?i:Call-ID|i)\t\t\t{ return T_HDR_CALL_ID; }\n^(?i:Call-Info)\t\t\t{ return T_HDR_CALL_INFO; }\n^(?i:Contact|m)\t\t\t{ return T_HDR_CONTACT; }\n^(?i:Content-Disposition)\t{ return T_HDR_CONTENT_DISP; }\n^(?i:Content-Encoding|e)\t{ return T_HDR_CONTENT_ENCODING; }\n^(?i:Content-Language)\t\t{ return T_HDR_CONTENT_LANGUAGE; }\n^(?i:Content-Length|l)\t\t{ return T_HDR_CONTENT_LENGTH; }\n^(?i:Content-Type|c)\t\t{ return T_HDR_CONTENT_TYPE; }\n^(?i:CSeq)\t\t\t{ return T_HDR_CSEQ; }\n^(?i:Date)\t\t\t{ return T_HDR_DATE; }\n^(?i:Error-Info)\t\t{ return T_HDR_ERROR_INFO; }\n^(?i:Event|o)\t\t\t{ return T_HDR_EVENT; }\n^(?i:Expires)\t\t\t{ return T_HDR_EXPIRES; }\n^(?i:From|f)\t\t\t{ return T_HDR_FROM; }\n^(?i:In-Reply-To)\t\t{ return T_HDR_IN_REPLY_TO; }\n^(?i:Max-Forwards)\t\t{ return T_HDR_MAX_FORWARDS; }\n^(?i:Min-Expires)\t\t{ return T_HDR_MIN_EXPIRES; }\n^(?i:Min-SE)\t\t\t{ return T_HDR_MIN_SE; }\n^(?i:MIME-Version)\t\t{ return T_HDR_MIME_VERSION; }\n^(?i:Organization)\t\t{ return T_HDR_ORGANIZATION; }\n^(?i:P-Asserted-Identity)\t{ return T_HDR_P_ASSERTED_IDENTITY; }\n^(?i:P-Preferred-Identity)\t{ return T_HDR_P_PREFERRED_IDENTITY; }\n^(?i:Priority)\t\t\t{ return T_HDR_PRIORITY; }\n^(?i:Privacy)\t\t\t{ return T_HDR_PRIVACY; }\n^(?i:Proxy-Authenticate)\t{ return T_HDR_PROXY_AUTHENTICATE; }\n^(?i:Proxy-Authorization)\t{ return T_HDR_PROXY_AUTHORIZATION; }\n^(?i:Proxy-Require)\t\t{ return T_HDR_PROXY_REQUIRE; }\n^(?i:RAck)\t\t\t{ return T_HDR_RACK; }\n^(?i:Reason)\t\t\t{ return T_HDR_REASON; }\n^(?i:Record-Route)\t\t{ return T_HDR_RECORD_ROUTE; }\n^(?i:Service-Route)\t\t{ return T_HDR_SERVICE_ROUTE; }\n^(?i:Refer-Sub)\t\t\t{ return T_HDR_REFER_SUB; }\n^(?i:Refer-To|r)\t\t{ return T_HDR_REFER_TO; }\n^(?i:Referred-By|b)\t\t{ return T_HDR_REFERRED_BY; }\n^(?i:Replaces)\t\t\t{ return T_HDR_REPLACES; }\n^(?i:Reply-To)\t\t\t{ return T_HDR_REPLY_TO; }\n^(?i:Require)\t\t\t{ return T_HDR_REQUIRE; }\n^(?i:Request-Disposition|d)\t{ return T_HDR_REQUEST_DISPOSITION; }\n^(?i:Retry-After)\t\t{ return T_HDR_RETRY_AFTER; }\n^(?i:Route)\t\t\t{ return T_HDR_ROUTE; }\n^(?i:RSeq)\t\t\t{ return T_HDR_RSEQ; }\n^(?i:Server)\t\t\t{ return T_HDR_SERVER; }\n^(?i:Session-Expires|x)\t\t{ return T_HDR_SESSION_EXPIRES; }\n^(?i:SIP-ETag)\t\t\t{ return T_HDR_SIP_ETAG; }\n^(?i:SIP-If-Match)\t\t{ return T_HDR_SIP_IF_MATCH; }\n^(?i:Subject|s)\t\t\t{ return T_HDR_SUBJECT; }\n^(?i:Subscription-State)\t{ return T_HDR_SUBSCRIPTION_STATE; }\n^(?i:Supported|k)\t\t{ return T_HDR_SUPPORTED; }\n^(?i:Timestamp)\t\t\t{ return T_HDR_TIMESTAMP; }\n^(?i:To|t)\t\t\t{ return T_HDR_TO; }\n^(?i:unsupported)\t\t{ return T_HDR_UNSUPPORTED; }\n^(?i:User-Agent)\t\t{ return T_HDR_USER_AGENT; }\n^(?i:Via|v)\t\t\t{ return T_HDR_VIA; }\n^(?i:Warning)\t\t\t{ return T_HDR_WARNING; }\n^(?i:WWW-Authenticate)\t\t{ return T_HDR_WWW_AUTHENTICATE; }\n^{TOKEN_SYM}+\t\t{ yylval.yyt_str = new string(yytext);\n\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t  return T_HDR_UNKNOWN; }\n\n\t/* Token as define in RFC 3261 */\n{TOKEN_SYM}+ \t{ yylval.yyt_str = new string(yytext);\n\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t  return T_TOKEN; }\n\n\t/* Switch to quoted string context */\n\\\"\t\t{ yy_push_state(C_QSTRING); }\n\n\t/* End of line */\n\\r\\n\t\t{ return T_CRLF; }\n\\n\t\t{ return T_CRLF; }\n\n[[:blank:]]\t/* Skip white space */\n\n\t/* Single character token */\n.\t\t{ return yytext[0]; }\n\n\t/* URI. \n\t   This context scans a URI including parameters.\n\t   The syntax of a URI will be checked outside the scanner \n\t */\n<C_URI>\\\"\t\t{ yy_push_state(C_QSTRING); }\n<C_URI>{TOKEN_SYM}({TOKEN_SYM}|[[:blank:]])*/< {\n\t\t\tyylval.yyt_str = new string(yytext);\n\t\t\tMEMMAN_NEW(yylval.yyt_str);\n\t\t\treturn T_DISPLAY; }\n<C_URI>[^[:blank:]<>\\r\\n]+/[[:blank:]]*> {\n\t\t\tyylval.yyt_str = new string(yytext);\n\t\t\tMEMMAN_NEW(yylval.yyt_str);\n\t\t\treturn T_URI; }\n<C_URI>\\*\t\t{ return T_URI_WILDCARD; }\n<C_URI>[^[:blank:]<>\\\"\\r\\n]+ {\n\t\t\tyylval.yyt_str = new string(yytext);\n\t\t\tMEMMAN_NEW(yylval.yyt_str);\n\t\t\treturn T_URI; }\n<C_URI>[[:blank:]]\t /* Skip white space */\n<C_URI>.\t\t { return yytext[0]; }\n<C_URI>\\n\t\t { return T_ERROR; }\n\n\t/* URI special case.\n\t   In several headers (eg. From, To, Contact, Reply-To) the URI\n\t   can be enclosed by < and >\n\t   If it is enclosed then parameters belong to the URI, if it\n\t   is not enclosed then parameters belong to the header.\n\t   Parameters are seperated by a semi-colon. \n\t   For the URI special case, parameters belong to the header.\n\t   If the parser receives a < from the scanner, then the parser\n\t   will switch to the normal URI case.\n\t   The syntax of a URI will be checked outside the scanner \n\t */\n<C_URI_SPECIAL>\\\"\t{ yy_push_state(C_QSTRING); }\n<C_URI_SPECIAL>{TOKEN_SYM}({TOKEN_SYM}|[[:blank:]])*/< {\n\t\t\tyylval.yyt_str = new string(yytext);\n\t\t\tMEMMAN_NEW(yylval.yyt_str);\n\t\t\treturn T_DISPLAY; }\n<C_URI_SPECIAL>\\*\t\t{ return T_URI_WILDCARD; }\n<C_URI_SPECIAL>[^[:blank:]<>;\\\"\\r\\n]+ {\n\t\t\tyylval.yyt_str = new string(yytext);\n\t\t\tMEMMAN_NEW(yylval.yyt_str);\n\t\t\treturn T_URI; }\n<C_URI_SPECIAL>[[:blank:]]\t /* Skip white space */\n<C_URI_SPECIAL>.\t\t { return yytext[0]; }\n<C_URI_SPECIAL>\\n\t\t { return T_ERROR; }\n\n\t/* Quoted string (starting after open quote, closing quote\n\t   will be consumed but not returned. */\n<C_QSTRING>\\\\\t\t\t{ yymore(); }\n<C_QSTRING>[^\\\"\\\\\\r\\n]*\\\\\\\"\t{ yymore(); }\n<C_QSTRING>[^\\\"\\\\\\r\\n]*\\\"\t{ yy_pop_state();\n\t\t\t  \t  yytext[strlen(yytext)-1] = '\\0';\n\t\t\t  \t  yylval.yyt_str = new string(unescape(string(yytext)));\n\t\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t  \t  return T_QSTRING; }\n<C_QSTRING>[^\\\"\\\\\\n]*\\n\t\t{ yy_pop_state(); return T_ERROR; }\n<C_QSTRING>.\t\t\t{ yy_pop_state(); return T_ERROR; }\n\n\t/* Comment (starting after LPAREN till RPAREN) */\n<C_COMMENT>\\\\\t\t\t{ yymore(); }\n<C_COMMENT>[^\\(\\)\\\\\\r\\n]*\\\\\\)\t{ yymore(); }\n<C_COMMENT>[^\\(\\)\\\\\\r\\n]*\\\\\\(\t{ yymore(); }\n<C_COMMENT>[^\\(\\)\\\\\\r\\n]*\\(\t{ t_parser::inc_comment_level(); yymore(); }\n<C_COMMENT>[^\\(\\)\\\\\\r\\n]*/\\)\t{ if (t_parser::dec_comment_level()) {\n\t\t\t\t\tBEGIN(C_RPAREN);\n\t\t\t\t\tyymore();\n\t\t\t\t  } else {\n\t\t\t\t  \tyylval.yyt_str = new string(yytext);\n\t\t\t\t\tMEMMAN_NEW(yylval.yyt_str);\n\t\t\t  \t  \treturn T_COMMENT;\n\t\t\t\t  }\n\t\t\t\t}\n<C_COMMENT>[^\\(\\)\\\\\\n]*\\n\t{ return T_ERROR; }\n<C_COMMENT>.\t\t\t{ return T_ERROR; }\n<C_RPAREN>\\)\t\t\t{ BEGIN(C_COMMENT); yymore(); }\n\n\t/* Language tag */\n<C_LANG>{ALPHA}{1,8}(\\-{ALPHA}{1,8})*\t{ yylval.yyt_str = new string(yytext);\n\t\t\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t\t\t  return T_LANG; }\n<C_LANG>[[:blank:]]\t\t\t/* Skip white space */\n<C_LANG>.\t\t\t\t{ return yytext[0]; }\n<C_LANG>\\r\\n\t\t\t\t{ return T_CRLF; }\n<C_LANG>\\n\t\t\t\t{ return T_CRLF; }\n\n\t/* Word */\n<C_WORD>{WORD_SYM}+\t{ yylval.yyt_str = new string(yytext);\n\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t  return T_WORD; }\n<C_WORD>[[:blank:]]\t/* Skip white space */\n<C_WORD>.\t\t{ return yytext[0]; }\n<C_WORD>\\r\\n\t\t\t\t{ return T_CRLF; }\n<C_WORD>\\n\t\t\t\t{ return T_CRLF; }\n\n\t/* Number */\n<C_NUM>{DIGIT}+\t\t{ yylval.yyt_ulong = strtoul(yytext, NULL, 10); return T_NUM; }\n<C_NUM>[[:blank:]]\t/* Skip white space */\n<C_NUM>.\t\t{ return yytext[0]; }\n<C_NUM>\\r\\n\t\t{ return T_CRLF; }\n<C_NUM>\\n\t\t{ return T_CRLF; }\n\n\t/* Date */\n<C_DATE>Mon\t\t{ yylval.yyt_int = 1; return T_WKDAY; }\n<C_DATE>Tue\t\t{ yylval.yyt_int = 2; return T_WKDAY; }\n<C_DATE>Wed\t\t{ yylval.yyt_int = 3; return T_WKDAY; }\n<C_DATE>Thu\t\t{ yylval.yyt_int = 4; return T_WKDAY; }\n<C_DATE>Fri\t\t{ yylval.yyt_int = 5; return T_WKDAY; }\n<C_DATE>Sat\t\t{ yylval.yyt_int = 6; return T_WKDAY; }\n<C_DATE>Sun\t\t{ yylval.yyt_int = 0; return T_WKDAY; }\n<C_DATE>Jan\t\t{ yylval.yyt_int = 0; return T_MONTH; }\n<C_DATE>Feb\t\t{ yylval.yyt_int = 1; return T_MONTH; }\n<C_DATE>Mar\t\t{ yylval.yyt_int = 2; return T_MONTH; }\n<C_DATE>Apr\t\t{ yylval.yyt_int = 3; return T_MONTH; }\n<C_DATE>May\t\t{ yylval.yyt_int = 4; return T_MONTH; }\n<C_DATE>Jun\t\t{ yylval.yyt_int = 5; return T_MONTH; }\n<C_DATE>Jul\t\t{ yylval.yyt_int = 6; return T_MONTH; }\n<C_DATE>Aug\t\t{ yylval.yyt_int = 7; return T_MONTH; }\n<C_DATE>Sep\t\t{ yylval.yyt_int = 8; return T_MONTH; }\n<C_DATE>Oct\t\t{ yylval.yyt_int = 9; return T_MONTH; }\n<C_DATE>Nov\t\t{ yylval.yyt_int = 10; return T_MONTH; }\n<C_DATE>Dec\t\t{ yylval.yyt_int = 11; return T_MONTH; }\n<C_DATE>GMT\t\t{ return T_GMT; }\n<C_DATE>{DIGIT}+\t{ yylval.yyt_ulong = strtoul(yytext, NULL, 10); return T_NUM; }\n<C_DATE>[[:blank:]]\t/* Skip white space */\n<C_DATE>.\t\t{ return yytext[0]; }\n<C_DATE>\\r\\n\t\t{ return T_CRLF; }\n<C_DATE>\\n\t\t{ return T_CRLF; }\n\n\t/* Get all text till end of line */\n<C_LINE>[^\\r\\n]+\t{ yylval.yyt_str = new string(yytext);\n\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t  return T_LINE; }\n<C_LINE>\\r\\n\t\t{ return T_CRLF; }\n<C_LINE>\\n\t\t{ return T_CRLF; }\n<C_LINE>\\r\t\t{ return T_CRLF; }\n\n\t/* Start of a new message */\n<C_NEW>SIP\t\t{ return T_SIP; }\n<C_NEW>{CAPITALS}+\t{ yylval.yyt_str = new string(yytext);\n\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t  return T_METHOD; }\n<C_NEW>[[:blank:]]\t/* Skip white space */\n<C_NEW>.\t\t{ return T_ERROR; }\n<C_NEW>\\r\\n\t\t{ return T_CRLF; }\n<C_NEW>\\n\t\t{ return T_CRLF; }\n\n\t/* Authorization scheme */\n<C_AUTH_SCHEME>(?i:Digest)\t{ return T_AUTH_DIGEST; }\n<C_AUTH_SCHEME>{TOKEN_SYM}+ \t{ yylval.yyt_str = new string(yytext);\n\t\t\t \t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t\t  return T_AUTH_OTHER; }\n<C_AUTH_SCHEME>[[:blank:]]\t/* Skip white space */\n<C_AUTH_SCHEME>.\t\t{ return T_ERROR; }\n<C_AUTH_SCHEME>\\r\\n\t\t{ return T_CRLF; }\n<C_AUTH_SCHEME>\\n\t\t{ return T_CRLF; }\n\n\t/* IPv6 address\n\t * NOTE: the validity of the format is not checked here.\n\t */\n<C_IPV6ADDR>({HEXDIG}|[:\\.])+\t{ yylval.yyt_str = new string(yytext);\n\t\t\t\t  MEMMAN_NEW(yylval.yyt_str);\n\t\t\t\t  return T_IPV6ADDR; }\n<C_IPV6ADDR>[[:blank:]]\t/* Skip white space */\n<C_IPV6ADDR>.\t\t{ return T_ERROR; }\n<C_IPV6ADDR>\\r\\n\t{ return T_CRLF; }\n<C_IPV6ADDR>\\n\t\t{ return T_CRLF; }\n\n\t/* Parameter values may contain an IPv6 address or reference. */\n<C_PARAMVAL>({TOKEN_SYM}|[:\\[\\]])+ { yylval.yyt_str = new string(yytext);\n\t\t\t\t     MEMMAN_NEW(yylval.yyt_str);\n\t\t\t\t     return T_PARAMVAL; }\n<C_PARAMVAL>\\\"\t\t{ yy_push_state(C_QSTRING); }\n<C_PARAMVAL>[[:blank:]]\t/* Skip white space */\n<C_PARAMVAL>.\t\t{ return T_ERROR; }\n<C_PARAMVAL>\\r\\n\t{ return T_CRLF; }\n<C_PARAMVAL>\\n\t\t{ return T_CRLF; }\n"
  },
  {
    "path": "src/parser/sip_body.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"sip_body.h\"\n\n#include <list>\n#include <cassert>\n#include <cstdlib>\n#include \"log.h\"\n#include \"protocol.h\"\n#include \"sip_message.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"audio/rtp_telephone_event.h\"\n\n////////////////////////////////////\n// class t_sip_body\n////////////////////////////////////\n\nt_sip_body::t_sip_body() {\n\tinvalid = false;\n}\n\nbool t_sip_body::local_ip_check(void) const {\n\treturn true;\n}\n\nsize_t t_sip_body::get_encoded_size(void) const {\n\treturn encode().size();\n}\n\n////////////////////////////////////\n// class t_sip_xml_body\n////////////////////////////////////\n\nvoid t_sip_body_xml::create_xml_doc(const string &xml_version, const string &charset) {\n\tclear_xml_doc();\n\t\n\t// XML doc\n\txml_doc = xmlNewDoc(BAD_CAST xml_version.c_str());\n\tMEMMAN_NEW(xml_doc);\n\txml_doc->encoding = xmlCharStrdup(charset.c_str());\n}\n\nvoid t_sip_body_xml::clear_xml_doc(void) {\n\tif (xml_doc) {\n\t\tMEMMAN_DELETE(xml_doc);\n\t\txmlFreeDoc(xml_doc);\n\t\txml_doc = NULL;\n\t}\n}\n\nvoid t_sip_body_xml::copy_xml_doc(t_sip_body_xml *to_body) const {\n\tif (to_body->xml_doc) {\n\t\tto_body->clear_xml_doc();\n\t}\n\t\n\tif (xml_doc) {\n\t\tto_body->xml_doc = xmlCopyDoc(xml_doc, 1);\n\t\tif (!to_body->xml_doc) {\n\t\t\tlog_file->write_report(\"Failed to copy xml document.\",\n\t\t\t\t\"t_sip_body_xml::copy\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t} else {\n\t\t\tMEMMAN_NEW(to_body->xml_doc);\n\t\t}\n\t}\n}\n\nt_sip_body_xml::t_sip_body_xml() : t_sip_body(),\n\txml_doc(NULL)\n{}\n\nt_sip_body_xml::~t_sip_body_xml() {\n\tclear_xml_doc();\n}\n\nstring t_sip_body_xml::encode(void) const {\n\tif (!xml_doc) {\n\t\tt_sip_body_xml *self = const_cast<t_sip_body_xml *>(this);\n\t\tself->create_xml_doc();\n\t}\n\tassert(xml_doc);\n\t\n\txmlChar *buf;\n\tint buf_size;\n\t\n\txmlDocDumpMemory(xml_doc, &buf, &buf_size);\n\tstring result((char*)buf);\n\txmlFree(buf);\n\t\n\treturn result;\n}\n\nbool t_sip_body_xml::parse(const string &s) {\n\tassert(xml_doc == NULL);\n\n\txml_doc = xmlReadMemory(s.c_str(), s.size(), \"noname.xml\", NULL, \n\t\tXML_PARSE_NOBLANKS | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);\n\tif (!xml_doc) {\n\t\tlog_file->write_report(\"Failed to parse xml document.\",\n\t\t\t\"t_sip_body_xml::parse\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t} else {\n\t\tMEMMAN_NEW(xml_doc);\n\t}\n\t\n\treturn (xml_doc != NULL);\n}\n\n////////////////////////////////////\n// class t_sip_body_opaque\n////////////////////////////////////\n\nt_sip_body_opaque::t_sip_body_opaque() : t_sip_body()\n{}\n\nt_sip_body_opaque::t_sip_body_opaque(string s) : \n\tt_sip_body(),\n\topaque(s)\n{}\n\nstring t_sip_body_opaque::encode(void) const {\n\treturn opaque;\n}\n\nt_sip_body *t_sip_body_opaque::copy(void) const {\n\tt_sip_body_opaque *sb =  new t_sip_body_opaque(*this);\n\tMEMMAN_NEW(sb);\n\treturn sb;\n}\n\nt_body_type t_sip_body_opaque::get_type(void) const {\n\treturn BODY_OPAQUE;\n}\n\nt_media t_sip_body_opaque::get_media(void) const {\n\treturn t_media(\"application\", \"octet-stream\");\n}\n\nsize_t t_sip_body_opaque::get_encoded_size(void) const {\n\treturn opaque.size();\n}\n\n////////////////////////////////////\n// class t_sip_body_sipfrag\n////////////////////////////////////\n\nt_sip_body_sipfrag::t_sip_body_sipfrag(t_sip_message *m) : t_sip_body() {\n\tsipfrag = m->copy();\n}\n\nt_sip_body_sipfrag::~t_sip_body_sipfrag() {\n\tMEMMAN_DELETE(sipfrag);\n\tdelete sipfrag;\n}\n\nstring t_sip_body_sipfrag::encode(void) const {\n\treturn sipfrag->encode(false);\n}\n\nt_sip_body *t_sip_body_sipfrag::copy(void) const {\n\tt_sip_body_sipfrag *sb = new t_sip_body_sipfrag(sipfrag);\n\tMEMMAN_NEW(sb);\n\treturn sb;\n}\n\nt_body_type t_sip_body_sipfrag::get_type(void) const {\n\treturn BODY_SIPFRAG;\n}\n\nt_media t_sip_body_sipfrag::get_media(void) const {\n\treturn t_media(\"message\", \"sipfrag\");\n}\n\n////////////////////////////////////\n// class t_sip_body_dtmf_relay\n////////////////////////////////////\n\nt_sip_body_dtmf_relay::t_sip_body_dtmf_relay() : t_sip_body() {\n\tsignal = '0';\n\tduration = 250;\n}\n\nt_sip_body_dtmf_relay::t_sip_body_dtmf_relay(char _signal, uint16 _duration) :\n\tsignal(_signal), duration(_duration)\n{}\n\nstring t_sip_body_dtmf_relay::encode(void) const {\n\tstring s = \"Signal=\";\n\ts += signal;\n\ts += CRLF;\n\t\n\ts += \"Duration=\";\n\ts += int2str(duration);\n\ts += CRLF;\n\t\n\treturn s;\n}\n\nt_sip_body *t_sip_body_dtmf_relay::copy(void) const {\n\tt_sip_body_dtmf_relay *sb = new t_sip_body_dtmf_relay(*this);\n\tMEMMAN_NEW(sb);\n\treturn sb;\t\n}\n\nt_body_type t_sip_body_dtmf_relay::get_type(void) const {\n\treturn BODY_DTMF_RELAY;\n}\n\nt_media t_sip_body_dtmf_relay::get_media(void) const {\n\treturn t_media(\"application\", \"dtmf-relay\");\n}\n\nbool t_sip_body_dtmf_relay::parse(const string &s) {\n\tsignal = 0;\n\tduration = 250;\n\t\n\tbool valid = false;\n\tvector<string> lines = split_linebreak(s);\n\t\n\tfor (vector<string>::iterator i = lines.begin(); i != lines.end(); i++) {\n\t\tstring line = trim(*i);\n\t\tif (line.empty()) continue;\n\t\t\n\t\tvector<string> l = split_on_first(line, '=');\n\t\tif (l.size() != 2) continue;\n\t\t\n\t\tstring parameter = tolower(trim(l[0]));\n\t\tstring value = tolower(trim(l[1]));\n\t\t\n\t\tif (value.empty()) continue;\n\t\t\n\t\tif (parameter == \"signal\") {\n\t\t\tif (!is_valid_dtmf_sym(value[0])) return false;\n\t\t\tsignal = value[0];\n\t\t\tvalid = true;\n\t\t} else if (parameter == \"duration\") {\n\t\t\tduration = atoi(value.c_str());\n\t\t\tif (duration == 0) return false;\n\t\t}\n\t}\n\t\n\treturn valid;\n}\n\n////////////////////////////////////\n// class t_sip_body_plain_text\n////////////////////////////////////\n\nt_sip_body_plain_text::t_sip_body_plain_text() :\n\tt_sip_body()\n{}\n\nt_sip_body_plain_text::t_sip_body_plain_text(const string &_text) :\n\tt_sip_body(),\n\ttext(_text)\n{}\n\nstring t_sip_body_plain_text::encode(void) const {\n\treturn text;\n}\n\nt_sip_body *t_sip_body_plain_text::copy(void) const {\n\tt_sip_body *sb = new t_sip_body_plain_text(*this);\n\tMEMMAN_NEW(sb);\n\treturn sb;\n}\n\nt_body_type t_sip_body_plain_text::get_type(void) const {\n\treturn BODY_PLAIN_TEXT;\n}\n\nt_media t_sip_body_plain_text::get_media(void) const {\n\treturn t_media(\"text\", \"plain\");\n}\n\nsize_t t_sip_body_plain_text::get_encoded_size(void) const {\n\treturn text.size();\n}\n\n////////////////////////////////////\n// class t_sip_body_html_text\n////////////////////////////////////\n\nt_sip_body_html_text::t_sip_body_html_text(const string &_text) :\n\tt_sip_body(),\n\ttext(_text)\n{}\n\nstring t_sip_body_html_text::encode(void) const {\n\treturn text;\n}\n\nt_sip_body *t_sip_body_html_text::copy(void) const {\n\tt_sip_body *sb = new t_sip_body_html_text(*this);\n\tMEMMAN_NEW(sb);\n\treturn sb;\n}\n\nt_body_type t_sip_body_html_text::get_type(void) const {\n\treturn BODY_HTML_TEXT;\n}\n\nt_media t_sip_body_html_text::get_media(void) const {\n\treturn t_media(\"text\", \"html\");\n}\n\nsize_t t_sip_body_html_text::get_encoded_size(void) const {\n\treturn text.size();\n}\n"
  },
  {
    "path": "src/parser/sip_body.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// SIP bodies\n#ifndef _H_SIP_BODY\n#define _H_SIP_BODY\n\n#include <commoncpp/config.h>\n#include <string>\n#include <libxml/tree.h>\n\n#include \"media_type.h\"\n\n//@{\n/** @name Utilies for XML body parsing */\n/**\n * Check the tag name of an XML node.\n * @param node [in] (xmlNode *) The XML node to check.\n * @param tag [in] (const char *) The tag name.\n * @param namespace [in] (const char *) The namespace of the tag.\n * @return true if the node has the tag name within the name space.\n */\n#define IS_XML_TAG(node, tag, namespace)\\\n\t\t\t\t((node)->type == XML_ELEMENT_NODE &&\\\n\t\t\t\t(node)->ns &&\\\n\t\t\t\txmlStrEqual((node)->ns->href, BAD_CAST (namespace)) &&\\\n\t\t\t\txmlStrEqual((node)->name, BAD_CAST (tag)))\n\t\t\t\t\n/**\n * Check the attribute name of an XML attribute.\n */\n#define IS_XML_ATTR(attr, attr_name, namespace)\\\n\t\t\t\t ((attr)->type == XML_ATTRIBUTE_NODE &&\\\n\t\t\t\t (attr)->ns &&\\\n\t\t\t\t xmlStrEqual((attr)->ns->href, BAD_CAST (namespace)) &&\\\n\t\t\t\t xmlStrEqual((attr)->name, BAD_CAST (attr_name)))\n//@}\n\nclass t_sip_message;\n\nusing namespace std;\n\n/** Body type. */\nenum t_body_type {\n\tBODY_OPAQUE,\t\t/**< Opaque body. */\n\tBODY_SDP,\t\t/**< SDP */\n\tBODY_SIPFRAG,\t\t/**< message/sipfrag RFC 3420 */\n\tBODY_DTMF_RELAY,\t/**< DTMF relay as defined by Cisco */\n\tBODY_SIMPLE_MSG_SUM,\t/**< Simple message summary RFC 3842 */\n\tBODY_PLAIN_TEXT,\t/**< Plain text for messaging */\n\tBODY_HTML_TEXT,\t\t/**< HTML text for messaging */\n\tBODY_PIDF_XML,\t\t/**< pidf+xml RFC 3863 */\n\tBODY_IM_ISCOMPOSING_XML\t/**< im-iscomposing+xml RFC 3994 */\n};\n\n/** Abstract base class for SIP bodies. */\nclass t_sip_body {\npublic:\n\t/**\n\t * Indicates if the body content is invalid.\n\t * This will be set by the body parser.\n\t */\n\tbool \tinvalid;\n\n\t/** Constructor. */\n\tt_sip_body();\n\t\n\tvirtual ~t_sip_body() {}\n\n\t/**\n\t * Encode the body.\n\t * @return Text encoded body.\n\t */\n\tvirtual string encode(void) const = 0;\n\n\t/**\n\t * Create a copy of the body.\n\t * @return Copy of the body.\n\t */\n\tvirtual t_sip_body *copy(void) const = 0;\n\n\t/** \n\t * Get type of body.\n\t * @return body type.\n\t */\n\tvirtual t_body_type get_type(void) const = 0;\n\t\n\t/**\n\t * Get content type for this type of body.\n\t * @return Content type.\n\t */\n\tvirtual t_media get_media(void) const = 0;\n\t\n\t/**\n\t * Check if all local IP address are correctly filled in. This\n\t * check is an integrity check to help debugging the auto IP\n\t * discover feature.\n\t */\n\tvirtual bool local_ip_check(void) const;\n\t\n\t/**\n\t * Return the size of the encoded body. This method encodes the body\n\t * to calculate the size. When a more efficient algorithm is available\n\t * a sub class may override this method.\n\t * @return The size of the encoded body in bytes.\n\t */\n\tvirtual size_t get_encoded_size(void) const;\n};\n\n/** Abstract base class for XML formatted bodies. */\nclass t_sip_body_xml : public t_sip_body {\nprotected:\n\txmlDoc\t\t*xml_doc;\t/**< XML document */\n\t\n\t/**\n\t * Create an empty XML document.\n\t * Override this method to create the specific XML document.\n\t * @param xml_version [in] The XML version of the document.\n\t * @param charset [in] The character set of the document.\n\t */\n\tvirtual void create_xml_doc(const string &xml_version = \"1.0\", const string &charset = \"UTF-8\");\n\t\n\t/** Remove the XML document */\n\tvirtual void clear_xml_doc(void);\n\t\n\t/** \n\t * Copy the XML document from this body to another body.\n\t * @param to_body [in] The body to copy the XML body to.\n\t */\n\tvirtual void copy_xml_doc(t_sip_body_xml *to_body) const;\n\t\npublic:\n\t/** Constructor */\n\tt_sip_body_xml();\n\t\n\t/** Destructor */\n\tvirtual ~t_sip_body_xml();\n\t\n\tvirtual string encode(void) const;\n\t\n\t/**\n\t * Parse a text representation of the body.\n\t * The result is stored in @ref xml_doc\n\t * @param s [in] Text to parse.\n\t * @return True if parsing and state extracting succeeded, false otherwise.\n\t * @pre xml_doc == NULL\n\t * @post If parsing succeeds then xml_doc != NULL\n\t */\n\tvirtual bool parse(const string &s);\n};\n\n\n/**\n * This body can contain any type of body. The contents are\n * unparsed and thus opaque.\n */\nclass t_sip_body_opaque : public t_sip_body {\npublic:\n\tstring\topaque; /**< The body contents. */\n\n\t/** Construct body with empty content. */\n\tt_sip_body_opaque();\n\n\t/**\n\t * Construct a body with opaque content.\n\t * @param s [in] The content.\n\t */\n\tt_sip_body_opaque(string s);\n\t\n\tstring encode(void) const;\n\tt_sip_body *copy(void) const;\n\tt_body_type get_type(void) const;\n\tt_media get_media(void) const;\n\tvirtual size_t get_encoded_size(void) const;\n};\n\n// RFC 3420\n// sipfrag body\nclass t_sip_body_sipfrag : public t_sip_body {\npublic:\n\tt_sip_message\t*sipfrag;\n\n\tt_sip_body_sipfrag(t_sip_message *m);\n\t~t_sip_body_sipfrag();\n\tstring encode(void) const;\n\tt_sip_body *copy(void) const;\n\tt_body_type get_type(void) const;\n\tt_media get_media(void) const;\n};\n\n// application/dtmf-relay body\nclass t_sip_body_dtmf_relay : public t_sip_body {\npublic:\n\tchar\tsignal;\n\tuint16\tduration; // ms\n\t\n\tt_sip_body_dtmf_relay();\n\tt_sip_body_dtmf_relay(char _signal, uint16 _duration);\n\tstring encode(void) const;\n\tt_sip_body *copy(void) const;\n\tt_body_type get_type(void) const;\n\tt_media get_media(void) const;\n\tbool parse(const string &s);\n};\n\n/** Plain text body. */\nclass t_sip_body_plain_text : public t_sip_body {\npublic:\n\tstring\ttext;\t/**< The text */\n\t\n\t/** Construct a body with empty text. */\n\tt_sip_body_plain_text();\n\t\n\t/**\n\t * Constructor.\n\t * @param _text [in] The body text.\n\t */\n\tt_sip_body_plain_text(const string &_text);\n\t\n\tstring encode(void) const;\n\tt_sip_body *copy(void) const;\n\tt_body_type get_type(void) const;\n\tt_media get_media(void) const;\n\tvirtual size_t get_encoded_size(void) const;\n};\n\n/** Html text body. */\nclass t_sip_body_html_text : public t_sip_body {\npublic:\n\tstring\ttext;\t/**< The text */\n\t\n\t/**\n\t * Constructor.\n\t * @param _text [in] The body text.\n\t */\n\tt_sip_body_html_text(const string &_text);\n\t\n\tstring encode(void) const;\n\tt_sip_body *copy(void) const;\n\tt_body_type get_type(void) const;\n\tt_media get_media(void) const;\n\tvirtual size_t get_encoded_size(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/parser/sip_message.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <fstream>\n#include <iostream>\n#include <sstream>\n\n#include \"sip_message.h\"\n#include \"util.h\"\n#include \"parse_ctrl.h\"\n#include \"sdp/sdp.h\"\n#include \"audits/memman.h\"\n\n////////////////////////////////////\n// class t_sip_message\n////////////////////////////////////\n\nt_sip_message::t_sip_message() :\n\tlocal_ip_(0)\n{\n\tsrc_ip_port.clear();\n\tversion = SIP_VERSION;\n\tbody = NULL;\n}\n\nt_sip_message::t_sip_message(const t_sip_message& m) :\n\t\tlocal_ip_(m.local_ip_),\n\t\tsrc_ip_port(m.src_ip_port),\n\t\tversion(m.version),\n\t\thdr_accept(m.hdr_accept),\n\t\thdr_accept_encoding(m.hdr_accept_encoding),\n\t\thdr_accept_language(m.hdr_accept_language),\n\t\thdr_alert_info(m.hdr_alert_info),\n\t\thdr_allow(m.hdr_allow),\n\t\thdr_allow_events(m.hdr_allow_events),\n\t\thdr_auth_info(m.hdr_auth_info),\n\t\thdr_authorization(m.hdr_authorization),\n\t\thdr_call_id(m.hdr_call_id),\n\t\thdr_call_info(m.hdr_call_info),\n\t\thdr_contact(m.hdr_contact),\n\t\thdr_content_disp(m.hdr_content_disp),\n\t\thdr_content_encoding(m.hdr_content_encoding),\n\t\thdr_content_language(m.hdr_content_language),\n\t\thdr_content_length(m.hdr_content_length),\n\t\thdr_content_type(m.hdr_content_type),\n\t\thdr_cseq(m.hdr_cseq),\n\t\thdr_date(m.hdr_date),\n\t\thdr_error_info(m.hdr_error_info),\n\t\thdr_event(m.hdr_event),\n\t\thdr_expires(m.hdr_expires),\n\t\thdr_from(m.hdr_from),\n\t\thdr_in_reply_to(m.hdr_in_reply_to),\n\t\thdr_max_forwards(m.hdr_max_forwards),\n\t\thdr_min_expires(m.hdr_min_expires),\n\t\thdr_min_se(m.hdr_min_se),\n\t\thdr_mime_version(m.hdr_mime_version),\n\t\thdr_organization(m.hdr_organization),\n\t\thdr_p_asserted_identity(m.hdr_p_asserted_identity),\n\t\thdr_p_preferred_identity(m.hdr_p_preferred_identity),\n\t\thdr_priority(m.hdr_priority),\n\t\thdr_privacy(m.hdr_privacy),\n\t\thdr_proxy_authenticate(m.hdr_proxy_authenticate),\n\t\thdr_proxy_authorization(m.hdr_proxy_authorization),\n\t\thdr_proxy_require(m.hdr_proxy_require),\n\t\thdr_rack(m.hdr_rack),\n\t\thdr_reason(m.hdr_reason),\n\t\thdr_record_route(m.hdr_record_route),\n\t\thdr_refer_sub(m.hdr_refer_sub),\n\t\thdr_refer_to(m.hdr_refer_to),\n\t\thdr_referred_by(m.hdr_referred_by),\n\t\thdr_replaces(m.hdr_replaces),\n\t\thdr_reply_to(m.hdr_reply_to),\n\t\thdr_require(m.hdr_require),\n\t\thdr_request_disposition(m.hdr_request_disposition),\n\t\thdr_retry_after(m.hdr_retry_after),\n\t\thdr_route(m.hdr_route),\n\t\thdr_rseq(m.hdr_rseq),\n\t\thdr_server(m.hdr_server),\n\t\thdr_service_route(m.hdr_service_route),\n\t\thdr_session_expires(m.hdr_session_expires),\n\t\thdr_sip_etag(m.hdr_sip_etag),\n\t\thdr_sip_if_match(m.hdr_sip_if_match),\n\t\thdr_subject(m.hdr_subject),\n\t\thdr_subscription_state(m.hdr_subscription_state),\n\t\thdr_supported(m.hdr_supported),\n\t\thdr_timestamp(m.hdr_timestamp),\n\t\thdr_to(m.hdr_to),\n\t\thdr_unsupported(m.hdr_unsupported),\n\t\thdr_user_agent(m.hdr_user_agent),\n\t\thdr_via(m.hdr_via),\n\t\thdr_warning(m.hdr_warning),\n\t\thdr_www_authenticate(m.hdr_www_authenticate),\n\t\tunknown_headers(m.unknown_headers)\n{\n\tif (m.body) {\n\t\tbody = m.body->copy();\n\t} else {\n\t\tbody = NULL;\n\t}\n}\n\nt_sip_message::~t_sip_message() {\n\tif (body) {\n\t\tMEMMAN_DELETE(body);\n\t\tdelete body;\n\t}\n}\n\nt_msg_type t_sip_message::get_type(void) const {\n\treturn MSG_SIPFRAG;\n}\n\nvoid t_sip_message::add_unknown_header(const string &name,\n\t\t\t\t       const string &value) {\n\tt_parameter h(name, value);\n\tunknown_headers.push_back(h);\n}\n\nbool t_sip_message::is_valid(bool &fatal, string &reason) const {\n\n\t// RFC 3261 8.1.1\n\t// Mandatory headers\n\tif (!hdr_to.is_populated()) {\n\t\tfatal = true;\n\t\treason = \"To-header missing\";\n\t\treturn false;\n\t}\n\n\tif (!hdr_from.is_populated()) {\n\t\tfatal = true;\n\t\treason = \"From header missing\";\n\t\treturn false;\n\t}\n\n\tif (!hdr_cseq.is_populated()) {\n\t\tfatal = true;\n\t\treason = \"CSeq header missing\";\n\t\treturn false;\n\t}\n\n\tif (!hdr_call_id.is_populated()) {\n\t\tfatal = true;\n\t\treason = \"Call-ID header missing\";\n\t\treturn false;\n\t}\n\n\tif (!hdr_via.is_populated()) {\n\t\tfatal = true;\n\t\treason = \"Via header missing\";\n\t\treturn false;\n\t}\n\n\t// RFC 3261 20.15\n\t// Content-Type MUST be present if body is not empty\n\tif (body && !hdr_content_type.is_populated()) {\n\t\tfatal = false;\n\t\treason = \"Content-Type header missing\";\n\t\treturn false;\n\t}\n\t\n\t// RFC 3261 18.4\n\t// The Content-Length header field MUST be used with stream oriented transports.\n\tif (cmp_nocase(hdr_via.via_list.front().transport, \"tcp\") == 0 &&\n\t    !hdr_content_length.is_populated())\n\t{\n\t\tfatal = false;\n\t\treason = \"Content-Length header missing\";\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstring t_sip_message::encode(bool add_content_length) {\n\tstring s;\n\tstring encoded_body;\n\n\t// RFC 3261 7.3.1\n\t// Headers needed by a proxy should be on top\n\ts += hdr_via.encode();\n\ts += hdr_route.encode();\n\ts += hdr_record_route.encode();\n\ts += hdr_service_route.encode();\n\ts += hdr_proxy_require.encode();\n\ts += hdr_max_forwards.encode();\n\ts += hdr_proxy_authenticate.encode();\n\ts += hdr_proxy_authorization.encode();\n\n\t// Order as in many examples\n\ts += hdr_to.encode();\n\ts += hdr_from.encode();\n\ts += hdr_call_id.encode();\n\ts += hdr_cseq.encode();\n\ts += hdr_contact.encode();\n\ts += hdr_content_type.encode();\n\t\n\t// Privacy related headers\n\ts += hdr_privacy.encode();\n\ts += hdr_p_asserted_identity.encode();\n\ts += hdr_p_preferred_identity.encode();\n\n\t// Authentication headers\n\ts += hdr_auth_info.encode();\n\ts += hdr_authorization.encode();\n\ts += hdr_www_authenticate.encode();\n\n\t// Remaining headers in alphabetical order\n\ts += hdr_accept.encode();\n\ts += hdr_accept_encoding.encode();\n\ts += hdr_accept_language.encode();\n\ts += hdr_alert_info.encode();\n\ts += hdr_allow.encode();\n\ts += hdr_allow_events.encode();\n\ts += hdr_call_info.encode();\n\ts += hdr_content_disp.encode();\n\ts += hdr_content_encoding.encode();\n\ts += hdr_content_language.encode();\n\ts += hdr_date.encode();\n\ts += hdr_error_info.encode();\n\ts += hdr_event.encode();\n\ts += hdr_expires.encode();\n\ts += hdr_in_reply_to.encode();\n\ts += hdr_min_expires.encode();\n\ts += hdr_min_se.encode();\n\ts += hdr_mime_version.encode();\n\ts += hdr_organization.encode();\n\ts += hdr_priority.encode();\n\ts += hdr_rack.encode();\n\ts += hdr_reason.encode();\n\ts += hdr_refer_sub.encode();\n\ts += hdr_refer_to.encode();\n\ts += hdr_referred_by.encode();\n\ts += hdr_replaces.encode();\n\ts += hdr_reply_to.encode();\n\ts += hdr_require.encode();\n\ts += hdr_request_disposition.encode();\n\ts += hdr_retry_after.encode();\n\ts += hdr_rseq.encode();\n\ts += hdr_server.encode();\n\ts += hdr_session_expires.encode();\n\ts += hdr_sip_etag.encode();\n\ts += hdr_sip_if_match.encode();\n\ts += hdr_subject.encode();\n\ts += hdr_subscription_state.encode();\n\ts += hdr_supported.encode();\n\ts += hdr_timestamp.encode();\n\ts += hdr_unsupported.encode();\n\ts += hdr_user_agent.encode();\n\ts += hdr_warning.encode();\n\n\t// Unknown headers\n\tfor (list<t_parameter>::const_iterator i = unknown_headers.begin();\n\t     i != unknown_headers.end(); i++)\n\t{\n\t\ts += i->name;\n\t\ts += \": \";\n\t\ts += i->value;\n\t\ts += CRLF;\n\t}\n\n\t// Encode body if present. Set the content length.\n\tif (body) {\n\t\tencoded_body = body->encode();\n\t\thdr_content_length.set_length(encoded_body.size());\n\t} else {\n\t\t// RFC 3261 20.14\n\t\t// If no body is present then Content-Length MUST be 0\n\t\thdr_content_length.set_length(0);\n\t}\n\n\t// Content-Length appears last in examples\n\tif (add_content_length) {\n\t\ts += hdr_content_length.encode();\n\t}\n\n\t// Blank line between headers and body\n\ts += CRLF;\n\n\t// Add body\n\tif (body) s += encoded_body;\n\n\treturn s;\n}\n\nlist<string> t_sip_message::encode_env(void) {\n\tlist<string> l;\n\n\t// RFC 3261 7.3.1\n\t// Headers needed by a proxy should be on top\n\tl.push_back(hdr_via.encode_env());\n\tl.push_back(hdr_route.encode_env());\n\tl.push_back(hdr_record_route.encode_env());\n\tl.push_back(hdr_service_route.encode_env());\n\tl.push_back(hdr_proxy_require.encode_env());\n\tl.push_back(hdr_max_forwards.encode_env());\n\tl.push_back(hdr_proxy_authenticate.encode_env());\n\tl.push_back(hdr_proxy_authorization.encode_env());\n\n\t// Order as in many examples\n\tl.push_back(hdr_to.encode_env());\n\tl.push_back(hdr_from.encode_env());\n\tl.push_back(hdr_call_id.encode_env());\n\tl.push_back(hdr_cseq.encode_env());\n\tl.push_back(hdr_contact.encode_env());\n\tl.push_back(hdr_content_type.encode_env());\n\t\n\t// Authentication headers\n\tl.push_back(hdr_auth_info.encode_env());\n\tl.push_back(hdr_authorization.encode_env());\n\tl.push_back(hdr_www_authenticate.encode_env());\n\n\t// Authentication headers\n\tl.push_back(hdr_auth_info.encode_env());\n\tl.push_back(hdr_authorization.encode_env());\n\tl.push_back(hdr_www_authenticate.encode_env());\n\n\t// Remaining headers in alphabetical order\n\tl.push_back(hdr_accept.encode_env());\n\tl.push_back(hdr_accept_encoding.encode_env());\n\tl.push_back(hdr_accept_language.encode_env());\n\tl.push_back(hdr_alert_info.encode_env());\n\tl.push_back(hdr_allow.encode_env());\n\tl.push_back(hdr_allow_events.encode_env());\n\tl.push_back(hdr_call_info.encode_env());\n\tl.push_back(hdr_content_disp.encode_env());\n\tl.push_back(hdr_content_encoding.encode_env());\n\tl.push_back(hdr_content_language.encode_env());\n\tl.push_back(hdr_date.encode_env());\n\tl.push_back(hdr_error_info.encode_env());\n\tl.push_back(hdr_event.encode_env());\n\tl.push_back(hdr_expires.encode_env());\n\tl.push_back(hdr_in_reply_to.encode_env());\n\tl.push_back(hdr_min_expires.encode_env());\n\tl.push_back(hdr_min_se.encode_env());\n\tl.push_back(hdr_mime_version.encode_env());\n\tl.push_back(hdr_organization.encode_env());\n\tl.push_back(hdr_priority.encode_env());\n\tl.push_back(hdr_rack.encode_env());\n\tl.push_back(hdr_reason.encode_env());\n\tl.push_back(hdr_refer_sub.encode_env());\n\tl.push_back(hdr_refer_to.encode_env());\n\tl.push_back(hdr_referred_by.encode_env());\n\tl.push_back(hdr_replaces.encode_env());\n\tl.push_back(hdr_reply_to.encode_env());\n\tl.push_back(hdr_require.encode_env());\n\tl.push_back(hdr_request_disposition.encode_env());\n\tl.push_back(hdr_retry_after.encode_env());\n\tl.push_back(hdr_rseq.encode_env());\n\tl.push_back(hdr_server.encode_env());\n\tl.push_back(hdr_session_expires.encode_env());\n\tl.push_back(hdr_sip_etag.encode_env());\n\tl.push_back(hdr_sip_if_match.encode_env());\n\tl.push_back(hdr_subject.encode_env());\n\tl.push_back(hdr_subscription_state.encode_env());\n\tl.push_back(hdr_supported.encode_env());\n\tl.push_back(hdr_timestamp.encode_env());\n\tl.push_back(hdr_unsupported.encode_env());\n\tl.push_back(hdr_user_agent.encode_env());\n\tl.push_back(hdr_warning.encode_env());\n\n\t// Unknown headers\n\tfor (list<t_parameter>::const_iterator i = unknown_headers.begin();\n\t     i != unknown_headers.end(); i++)\n\t{\n\t\tstring s = \"SIP_\";\n\t\ts += toupper(replace_char(i->name, '-', '_'));\n\t\ts += '=';\n\t\ts += i->value;\n\t\tl.push_back(s);\n\t}\n\t\n\tl.push_back(hdr_content_length.encode_env());\n\n\treturn l;\n}\n\nt_sip_message *t_sip_message::copy(void) const {\n\tt_sip_message *m = new t_sip_message(*this);\n\tMEMMAN_NEW(m);\n\treturn m;\n}\n\nvoid t_sip_message::set_body_plain_text(const string &text, const string &charset) {\n\t// Content-Type header\n\tt_media mime_type(\"text\", \"plain\");\n\tmime_type.charset = charset;\n\thdr_content_type.set_media(mime_type);\n\t\n\tif (body) {\n\t\tMEMMAN_DELETE(body);\n\t\tdelete body;\n\t}\n\t\n\tbody = new t_sip_body_plain_text(text);\n\tMEMMAN_NEW(body);\n}\n\nbool t_sip_message::set_body_from_file(const string &filename, const t_media &media) {\n\t// Open file and set read pointer at end so we know the size.\n\tifstream f(filename.c_str(), ios::binary);\n\tif (!f) return false;\n\t\n\tostringstream body_stream(ios::binary);\n\t\n\t// Copy file into body\n\tbody_stream << f.rdbuf();\n\t\n\tif (!f.good() || !body_stream.good()) {\n\t\treturn false;\n\t}\n\t\n\t// Create body of correct type\n\tt_sip_body *new_body = NULL;\n\tif (media.type == \"text\" && media.subtype == \"plain\") {\n\t\tt_sip_body_plain_text *text_body = new t_sip_body_plain_text(body_stream.str());\n\t\tMEMMAN_NEW(text_body);\n\n\t\tnew_body = text_body;\n\t} else {\n\t\tt_sip_body_opaque *opaque_body = new t_sip_body_opaque(body_stream.str());\n\t\tMEMMAN_NEW(opaque_body);\n\t\t\n\t\tnew_body = opaque_body;\n\t}\n\t\n\tif (body) {\n\t\tMEMMAN_DELETE(body);\n\t\tdelete body;\n\t}\n\tbody = new_body;\n\t\n\t// Content-Type header\n\thdr_content_type.set_media(media);\n\t\n\treturn true;\n}\n\nsize_t t_sip_message::get_encoded_size(void) {\n\tstring s = encode();\n\treturn s.size();\n}\n\nbool t_sip_message::local_ip_check(void) const {\n\tif (get_type() == MSG_REQUEST && hdr_via.is_populated()) {\n\t\tconst t_via &v = hdr_via.via_list.front();\n\t\tif (v.host == \"0.0.0.0\") return false;\n\t}\n\t\n\tif (hdr_contact.is_populated()) {\n\t\tif (!hdr_contact.any_flag && !hdr_contact.contact_list.empty()) {\n\t\t\tconst t_contact_param &c = hdr_contact.contact_list.front();\n\t\t\tif (c.uri.get_host() == \"0.0.0.0\") return false;\n\t\t}\n\t}\n\t\n\tif (body) {\n\t\treturn body->local_ip_check();\n\t}\n\t\n\treturn true;\n}\n\nvoid t_sip_message::calc_local_ip(void) {\n\t// Do nothing\n}\n\nIPaddr t_sip_message::get_local_ip(void) {\n\tif (local_ip_ == 0) calc_local_ip();\n\treturn local_ip_;\n}\n"
  },
  {
    "path": "src/parser/sip_message.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// SIP message\n\n#ifndef _H_SIP_MESSAGE\n#define _H_SIP_MESSAGE\n\n#include <list>\n#include <string>\n#include \"definitions.h\"\n#include \"hdr_accept.h\"\n#include \"hdr_accept_encoding.h\"\n#include \"hdr_accept_language.h\"\n#include \"hdr_alert_info.h\"\n#include \"hdr_allow.h\"\n#include \"hdr_allow_events.h\"\n#include \"hdr_auth_info.h\"\n#include \"hdr_authorization.h\"\n#include \"hdr_call_id.h\"\n#include \"hdr_call_info.h\"\n#include \"hdr_contact.h\"\n#include \"hdr_content_disp.h\"\n#include \"hdr_content_encoding.h\"\n#include \"hdr_content_language.h\"\n#include \"hdr_content_length.h\"\n#include \"hdr_content_type.h\"\n#include \"hdr_cseq.h\"\n#include \"hdr_date.h\"\n#include \"hdr_error_info.h\"\n#include \"hdr_event.h\"\n#include \"hdr_expires.h\"\n#include \"hdr_from.h\"\n#include \"hdr_in_reply_to.h\"\n#include \"hdr_max_forwards.h\"\n#include \"hdr_min_expires.h\"\n#include \"hdr_min_se.h\"\n#include \"hdr_mime_version.h\"\n#include \"hdr_organization.h\"\n#include \"hdr_p_asserted_identity.h\"\n#include \"hdr_p_preferred_identity.h\"\n#include \"hdr_priority.h\"\n#include \"hdr_privacy.h\"\n#include \"hdr_proxy_authenticate.h\"\n#include \"hdr_proxy_authorization.h\"\n#include \"hdr_proxy_require.h\"\n#include \"hdr_rack.h\"\n#include \"hdr_reason.h\"\n#include \"hdr_record_route.h\"\n#include \"hdr_refer_sub.h\"\n#include \"hdr_refer_to.h\"\n#include \"hdr_referred_by.h\"\n#include \"hdr_replaces.h\"\n#include \"hdr_reply_to.h\"\n#include \"hdr_require.h\"\n#include \"hdr_request_disposition.h\"\n#include \"hdr_retry_after.h\"\n#include \"hdr_route.h\"\n#include \"hdr_rseq.h\"\n#include \"hdr_server.h\"\n#include \"hdr_service_route.h\"\n#include \"hdr_session_expires.h\"\n#include \"hdr_sip_etag.h\"\n#include \"hdr_sip_if_match.h\"\n#include \"hdr_subject.h\"\n#include \"hdr_subscription_state.h\"\n#include \"hdr_supported.h\"\n#include \"hdr_timestamp.h\"\n#include \"hdr_to.h\"\n#include \"hdr_unsupported.h\"\n#include \"hdr_user_agent.h\"\n#include \"hdr_via.h\"\n#include \"hdr_warning.h\"\n#include \"hdr_www_authenticate.h\"\n#include \"parameter.h\"\n#include \"sip_body.h\"\n#include \"sockets/ipaddr.h\"\n\n// Macro's to access the body of a message, eg msg.sdp_body\n#define SDP_BODY\t((t_sdp *)body)\n#define OPAQUE_BODY\t((t_sip_body_opaque)*body)\n\nusing namespace std;\n\nenum t_msg_type {\n\tMSG_REQUEST,\n\tMSG_RESPONSE,\n\tMSG_SIPFRAG,\t// Only a sequence of headers (RFC 3420)\n};\n\n\nclass t_sip_message {\nprotected:\n\t/**\n\t * Local IP address that will be uses for this SIP message.\n\t * The local IP address can only be determined when the destination\n\t * of a SIP message is known (because of multi homing).\n\t */\n\tIPaddr\t\tlocal_ip_;\n\t\npublic:\n\t// The source IP address and port are only set for messages\n\t// received from the network. So the transaction user knows\n\t// where a message comes from.\n\tt_ip_port\t\tsrc_ip_port;\n\n\t// SIP version\n\tstring\t\t\tversion;\n\n\t// All possible headers\n\tt_hdr_accept\t\thdr_accept;\n\tt_hdr_accept_encoding\thdr_accept_encoding;\n\tt_hdr_accept_language\thdr_accept_language;\n\tt_hdr_alert_info\thdr_alert_info;\n\tt_hdr_allow\t\thdr_allow;\n\tt_hdr_allow_events\thdr_allow_events;\n\tt_hdr_auth_info\t\thdr_auth_info;\n\tt_hdr_authorization\thdr_authorization;\n\tt_hdr_call_id\t\thdr_call_id;\n\tt_hdr_call_info\t\thdr_call_info;\n\tt_hdr_contact\t\thdr_contact;\n\tt_hdr_content_disp\thdr_content_disp;\n\tt_hdr_content_encoding\thdr_content_encoding;\n\tt_hdr_content_language\thdr_content_language;\n\tt_hdr_content_length\thdr_content_length;\n\tt_hdr_content_type\thdr_content_type;\n\tt_hdr_cseq\t\thdr_cseq;\n\tt_hdr_date\t\thdr_date;\n\tt_hdr_error_info\thdr_error_info;\n\tt_hdr_event\t\thdr_event;\n\tt_hdr_expires\t\thdr_expires;\n\tt_hdr_from\t\thdr_from;\n\tt_hdr_in_reply_to\thdr_in_reply_to;\n\tt_hdr_max_forwards\thdr_max_forwards;\n\tt_hdr_min_expires\thdr_min_expires;\n\tt_hdr_min_se\t\thdr_min_se;\n\tt_hdr_mime_version\thdr_mime_version;\n\tt_hdr_organization\thdr_organization;\n\tt_hdr_p_asserted_identity hdr_p_asserted_identity;\n\tt_hdr_p_preferred_identity hdr_p_preferred_identity;\n\tt_hdr_priority\t\thdr_priority;\n\tt_hdr_privacy\t\thdr_privacy;\n\tt_hdr_proxy_authenticate  hdr_proxy_authenticate;\n\tt_hdr_proxy_authorization hdr_proxy_authorization;\n\tt_hdr_proxy_require\thdr_proxy_require;\n\tt_hdr_rack\t\thdr_rack;\n\tt_hdr_reason\t\thdr_reason;\n\tt_hdr_record_route\thdr_record_route;\n\tt_hdr_refer_sub\t\thdr_refer_sub;\n\tt_hdr_refer_to\t\thdr_refer_to;\n\tt_hdr_referred_by\thdr_referred_by;\n\tt_hdr_replaces\t\thdr_replaces;\n\tt_hdr_reply_to\t\thdr_reply_to;\n\tt_hdr_require\t\thdr_require;\n\tt_hdr_request_disposition hdr_request_disposition;\n\tt_hdr_retry_after\thdr_retry_after;\n\tt_hdr_route\t\thdr_route;\n\tt_hdr_rseq\t\thdr_rseq;\n\tt_hdr_server\t\thdr_server;\n\tt_hdr_service_route\thdr_service_route;\n\tt_hdr_session_expires\thdr_session_expires;\n\tt_hdr_sip_etag\t\thdr_sip_etag;\n\tt_hdr_sip_if_match\thdr_sip_if_match;\n\tt_hdr_subject\t\thdr_subject;\n\tt_hdr_subscription_state hdr_subscription_state;\n\tt_hdr_supported\t\thdr_supported;\n\tt_hdr_timestamp\t\thdr_timestamp;\n\tt_hdr_to\t\thdr_to;\n\tt_hdr_unsupported\thdr_unsupported;\n\tt_hdr_user_agent\thdr_user_agent;\n\tt_hdr_via\t\thdr_via;\n\tt_hdr_warning\t\thdr_warning;\n\tt_hdr_www_authenticate\thdr_www_authenticate;\n\n\t// Unknown headers are represented by parameters.\n\t// Parameter.name = header name\n\t// Parameter.value = header value\n\tlist<t_parameter>\tunknown_headers;\n\n\t// A SIP message can carry a body\n\tt_sip_body\t\t*body;\n\n\tt_sip_message();\n\tt_sip_message(const t_sip_message& m);\n\tvirtual ~t_sip_message();\n\n\tvirtual t_msg_type get_type(void) const;\n\tvoid add_unknown_header(const string &name, const string &value);\n\n\t// Check if the message is valid. At this class the\n\t// general rules applying to both requests and responses\n\t// is checked.\n\t// fatal is true if one of the headers mandatory for all\n\t// messages is missing (to, from, cseq, call-id, via).\n\t// reason contains a reason string if the message is invalid.\n\tvirtual bool is_valid(bool &fatal, string &reason) const;\n\n\t// Return encoded headers\n\t// The version should be encode by the subclasses.\n\t// Parameter add_content_length indicates if a Content-Length\n\t// header must be added. Usually it must, only for sipfrag bodies\n\t// it may be omitted.\n\tvirtual string encode(bool add_content_length = true);\n\t\n\t// Return list of environment variable settings for all headers\n\t// (see header.h for the format)\n\t// Besides the header variables the following variables will be\n\t// returned as well:\n\t//\n\t// SIP_REQUEST_METHOD, for a request\n\t// SIP_REQUEST_URI, for a request\n\t// SIP_STATUS_CODE, for a response\n\t// SIP_STATUS_REASON, for a response\n\tvirtual list<string> encode_env(void);\n\n\t// Create a copy of the message\n\tvirtual t_sip_message *copy(void) const;\n\t\n\t/**\n\t * Set a plain text body in the message.\n\t * @param text [in] The text.\n\t * @param charset [in] The character set used for encoding.\n\t * @post The Content-Type header is set to \"text/plain\".\n\t * @post If a body was already present then it is deleted.\n\t */\n\tvoid set_body_plain_text(const string &text, const string &charset);\n\t\n\t/**\n\t * Set a body with the contents of a file.\n\t * @param filename [in] The name of the file.\n\t * @param media [in] The mime type of the contents.\n\t * @return True of body is set, false if file could not be read.\n\t */\n\tbool set_body_from_file(const string &filename, const t_media &media);\n\t\n\t/**\n\t * Get the size of an encoded SIP message.\n\t * @return Size in bytes.\n\t */\n\tsize_t get_encoded_size(void);\n\t\n\t/**\n\t * Check if all local IP address are correctly filled in. This\n\t * check is an integrity check to help debugging the auto IP\n\t * discover feature.\n\t */\n\tbool local_ip_check(void) const;\n\t\n\t/** Determine the local IP address for this SIP message. */\n\tvirtual void calc_local_ip(void);\n\t\n\t/**\n\t * Get the local IP address for this SIP message.\n\t * The local IP address can be used as source address for sending\n\t * the message.\n\t * @return The local IP address.\n\t * @return 0, if the local IP address is not determined yet.\n\t */\n\tIPaddr get_local_ip(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/patterns/CMakeLists.txt",
    "content": "project(libtwinkle-patterns)\n\nset(LIBTWINKLE_PATTERNS-SRCS\n\tobserver.cpp\n)\n\nadd_library(libtwinkle-patterns OBJECT ${LIBTWINKLE_PATTERNS-SRCS})\n"
  },
  {
    "path": "src/patterns/observer.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"observer.h\"\n\nusing namespace patterns;\n\nt_subject::~t_subject() {\n\tmtx_observers.lock();\n\tfor (list<t_observer *>::const_iterator it = observers.begin();\n\t     it != observers.end(); ++it)\n\t{\n\t\t(*it)->subject_destroyed();\n\t}\n\tmtx_observers.unlock();\n}\n\nvoid t_subject::attach(t_observer *o) {\n\tmtx_observers.lock();\n\tobservers.push_back(o);\n\tmtx_observers.unlock();\n}\n\nvoid t_subject::detach(t_observer *o) {\n\tmtx_observers.lock();\n\tobservers.remove(o);\n\tmtx_observers.unlock();\n}\n\nvoid t_subject::notify(void) const {\n\tmtx_observers.lock();\n\tfor (list<t_observer *>::const_iterator it = observers.begin();\n\t     it != observers.end(); ++it)\n\t{\n\t\t(*it)->update();\n\t}\n\tmtx_observers.unlock();\n}\n"
  },
  {
    "path": "src/patterns/observer.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Abstract observer pattern.\n */\n\n#ifndef _OBSERVER_H\n#define _OBSERVER_H\n\n#include <list>\n\n#include \"threads/mutex.h\"\n\nusing namespace std;\n\nnamespace patterns {\n\n/** Observer. */\nclass t_observer {\npublic:\n\tvirtual ~t_observer() {};\n\t\n\t/**\n\t * This method is called by an observed subject to indicate its state\n\t * has changed.\n\t */\n\tvirtual void update(void) = 0;\n\t\n\t/** \n\t * This method is called when the subject is destroyed.\n\t */\n\tvirtual void subject_destroyed(void) = 0;\n};\n\n/** An observed subject. */\nclass t_subject {\nprivate:\n\t/** Mutex to protect access to the observers. */\n\tmutable t_recursive_mutex mtx_observers;\n\n\tlist<t_observer *> observers;\t/** Observers of this subject. */\n\t\npublic:\n\tvirtual ~t_subject();\n\t\n\t/**\n\t * Attach an observer.\n\t * @param o [in] The observer.\n\t */\n\tvoid attach(t_observer *o);\n\t\n\t/**\n\t * Detach an observer.\n\t * @param o [in] The observer.\n\t */\n\tvoid detach(t_observer *o);\n\t\n\t/**\n\t * Notify all observers.\n\t */\n\tvoid notify(void) const;\n};\n\n}; // namespace\n\n#endif\n"
  },
  {
    "path": "src/phone.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include <signal.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include \"call_history.h\"\n#include \"call_script.h\"\n#include \"exceptions.h\"\n#include \"phone.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"sdp/sdp.h\"\n#include \"translator.h\"\n#include \"util.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n#include \"parser/parse_ctrl.h\"\n#include \"sockets/socket.h\"\n#include \"stun/stun_transaction.h\"\n\nextern t_phone \t\t*phone;\nextern t_event_queue\t*evq_timekeeper;\nextern string\t\tuser_host;\n\n// t_transfer_data\n\nt_transfer_data::t_transfer_data(t_request *r, unsigned short _lineno, bool _hide_user, \n\t\tt_phone_user *pu) :\n\trefer_request(dynamic_cast<t_request *>(r->copy())),\n\tlineno(_lineno),\n\thide_user(_hide_user),\n\tphone_user(pu)\n{}\n\t\nt_transfer_data::~t_transfer_data() {\n\tMEMMAN_DELETE(refer_request);\n\tdelete refer_request;\n}\n\nt_request *t_transfer_data::get_refer_request(void) const {\n\treturn refer_request;\n}\n\nbool t_transfer_data::get_hide_user(void) const {\n\treturn hide_user;\n}\n\nunsigned short t_transfer_data::get_lineno(void) const {\n\treturn lineno;\n}\n\nt_phone_user *t_transfer_data::get_phone_user(void) const {\n\treturn phone_user;\n}\n\n\n// t_phone\t\n\n///////////\n// Private\n///////////\n\nvoid t_phone::move_line_to_background(unsigned short lineno) {\n\t// R/W lock is held by callers\n\n\t// The line will be released in the background. It should\n\t// immediately release its RTP ports as these maybe needed\n\t// for new calls.\n\tlines.at(lineno)->kill_rtp();\n\t\n\tcleanup_3way_state(lineno);\n\t\n\t// Move the line to the back of the vector.\n\tlines.push_back(lines.at(lineno));\n\tlines.back()->line_number = lines.size() - 1;\n\t\n\t// Create a new line for making calls.\n\tlines.at(lineno) = new t_line(this, lineno);\n\tMEMMAN_NEW(lines.at(lineno));\n\t\n\t// The new line must have the same RTP port as the\n\t// releasing line, otherwise it may conflict with\n\t// the other lines. Due to call transfers, the port\n\t// number may be unrelated to the line position.\n\tlines.at(lineno)->rtp_port = lines.back()->get_rtp_port();\n\t\n\tlog_file->write_header(\"t_phone::move_line_to_background\",\n\t\tLOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Moved line \");\n\tlog_file->write_raw(lineno + 1);\n\tlog_file->write_raw(\" to background position \");\n\tlog_file->write_raw(lines.back()->get_line_number() + 1);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\t// Notify the user interface of the line state change\n\tui->cb_line_state_changed();\n}\n\nvoid t_phone::cleanup_dead_lines(void) {\n\tt_rwmutex_writer x(lines_mtx);\n\n\t// Only remove idle lines at the end of the dead pool to avoid\n\t// moving lines in the vector.\n\twhile (lines.size() > NUM_CALL_LINES && lines.back()->get_state() == LS_IDLE)\n\t{\n\t\t\n\t\tlog_file->write_header(\"t_phone::cleanup_dead_lines\",\n\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Removed dead line \");\n\t\tlog_file->write_raw(lines.back()->get_line_number() + 1);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tMEMMAN_DELETE(lines.back());\n\t\tdelete lines.back();\n\t\tlines.pop_back();\n\t}\n}\n\nvoid t_phone::move_releasing_lines_to_background(void) {\n\tt_rwmutex_writer x(lines_mtx);\n\n\t// NOTE: the line on the REFERRER position is not moved to the\n\t//       background as a subscription may still be active.\n\tfor (int i = 0; i < NUM_USER_LINES; i++) {\n\t\tif (lines.at(i)->get_substate() == LSSUB_RELEASING &&\n\t\t    !lines.at(i)->get_keep_seized()) \n\t\t{\n\t\t\tmove_line_to_background(i);\n\t\t}\n\t}\n}\n\nvoid t_phone::cleanup_3way_state(unsigned short lineno) {\n\tassert(lineno < lines.size());\n\n\tt_mutex_guard x(mutex_3way);\n\n\t// Clean up 3-way data if the line was involved in a 3-way\n\tif (is_3way)\n\t{\n\t\tbool line_in_3way = false;\n\t\tt_audio_session *as_peer;\n\t\tt_line *line_peer;\n\n\t\tif (lineno == line1_3way->get_line_number()) {\n\t\t\tline_in_3way = true;\n\t\t\tline_peer = line2_3way;\n\t\t} else if (lineno == line2_3way->get_line_number()) {\n\t\t\tline_in_3way = true;\n\t\t\tline_peer = line1_3way;\n\t\t}\n\n\t\tif (line_in_3way) {\n\t\t\t// Stop the 3-way mixing on the peer line\n\t\t\tas_peer = line_peer->get_audio_session();\n\t\t\tif (as_peer) as_peer->stop_3way();\n\n\t\t\t// Make the peer line the active line\n\t\t\tset_active_line(line_peer->get_line_number());\n\t\t\t\n\t\t\t// If the 3-way was with mixed codec sample rates, then\n\t\t\t// the remaining audio session might have a mismatch\n\t\t\t// between the sound card sample rate and the codec\n\t\t\t// sample rate. In that case clear the sample rate by\n\t\t\t// toggling the audio session off and on.\n\t\t\tif (!as_peer->matching_sample_rates()) {\n\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\"Hold/retrieve call to align codec and sound card.\",\n\t\t\t\t\t\"t_phone::line_cleared\", LOG_NORMAL, LOG_DEBUG);\n\t\t\t\tline_peer->hold(true);\n\t\t\t\tline_peer->retrieve();\n\t\t\t}\n\n\t\t\tis_3way = false;\n\t\t\tline1_3way = NULL;\n\t\t\tline2_3way = NULL;\n\t\t\tui->cb_line_state_changed();\n\t\t}\n\t}\n\n}\n\nvoid t_phone::cleanup_3way(void) {\n\tif (!is_3way) return;\n\t\n\tif (line1_3way->get_substate() == LSSUB_IDLE) {\n\t\tcleanup_3way_state(line1_3way->get_line_number());\n\t} else if (line2_3way->get_substate() == LSSUB_IDLE) {\n\t\tcleanup_3way_state(line2_3way->get_line_number());\n\t}\n}\n\nvoid t_phone::invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, bool no_fork, bool anonymous)\n{\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Ignore if active line is not idle\n\tif (lines[active_line]->get_state() != LS_IDLE) {\n\t\treturn;\n\t}\n\n\tlines[active_line]->invite(pu, to_uri, to_display, subject, no_fork, anonymous);\n}\n\nvoid t_phone::answer(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Ignore if active line is idle\n\tif (lines[active_line]->get_state() == LS_IDLE) return;\n\n\tlines[active_line]->answer();\n}\n\nvoid t_phone::reject(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Ignore if active line is idle\n\tif (lines[active_line]->get_state() == LS_IDLE) return;\n\n\tlines[active_line]->reject();\n}\n\nvoid t_phone::reject(unsigned short line) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tif (line > NUM_USER_LINES) return;\n\tif (lines[line]->get_state() == LS_IDLE) return;\n\t\n\tlines[line]->reject();\n}\n\nvoid t_phone::redirect(const list<t_display_url> &destinations, int code, string reason)\n{\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Ignore if active line is idle\n\tif (lines[active_line]->get_state() == LS_IDLE) return;\n\n\tlines[active_line]->redirect(destinations, code, reason);\n}\n\nvoid t_phone::end_call(void) {\n\tt_rwmutex_writer x(lines_mtx);\n\n\t// If 3-way is active then end call on both lines\n\tif (is_3way && (\n\t    active_line == line1_3way->get_line_number() ||\n\t    active_line == line2_3way->get_line_number()))\n\t{\n\t\tif (sys_config->get_hangup_both_3way()) {\n\t\t\tline1_3way->end_call();\n\t\t\tline2_3way->end_call();\n\t\t\t\n\t\t\t// NOTE: moving a line to the dying pool causes the\n\t\t\t// 3way line pointers to be cleared.\n\t\t\tunsigned short lineno1 = line1_3way->get_line_number();\n\t\t\tunsigned short lineno2 = line2_3way->get_line_number();\n\t\t\tmove_line_to_background(lineno1);\n\t\t\tmove_line_to_background(lineno2);\n\t\t} else {\n\t\t\t// Hangup the active line, and make the next\n\t\t\t// line active.\n\t\t\tint l = active_line;\n\t\t\tactivate_line((l+1) % NUM_USER_LINES);\n\t\t\tlines.at(l)->end_call();\n\t\t\tmove_line_to_background(l);\n\t\t}\n\t\t\n\t\treturn;\n\t}\n\n\t// Ignore if active line is idle\n\tif (lines.at(active_line)->get_state() == LS_IDLE) return;\n\n\tlines.at(active_line)->end_call();\n\n\tmove_line_to_background(active_line);\n}\n\nvoid t_phone::registration(t_phone_user *pu, t_register_type register_type, \n\t\tunsigned long expires)\n{\n\tpu->registration(register_type, false, expires);\n}\n\nvoid t_phone::options(t_phone_user *pu, const t_url &to_uri, const string &to_display) {\n\tpu->options(to_uri, to_display);\n}\n\nvoid t_phone::options(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tlines[active_line]->options();\n}\n\nbool t_phone::hold(bool rtponly) {\n\t// A line in a 3-way call cannot be held\n\tif (is_3way && (\n\t    active_line == line1_3way->get_line_number() ||\n\t    active_line == line2_3way->get_line_number()))\n\t{\n\t\treturn false;\n\t}\n\n\tt_rwmutex_reader x(lines_mtx);\n\treturn lines[active_line]->hold(rtponly);\n}\n\nvoid t_phone::retrieve(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->retrieve();\n}\n\nvoid t_phone::refer(const t_url &uri, const string &display) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->refer(uri, display);\n}\n\nvoid t_phone::refer(unsigned short lineno_from, unsigned short lineno_to) \n{\n\tt_rwmutex_future_writer x(lines_mtx);\n\n\t// The nicest transfer is an attended transfer. An attended transfer\n\t// is only possible of the transfer target supports the 'replaces'\n\t// extension (RFC 3891).\n\t// If 'replaces' is not supported, then a transfer with consultation\n\t// is done. First hang up the consultation call, then transfer the\n\t// line.\n\t// HACK: if the call is in progress, then assume that Replaces is\n\t//       supported. We don't know if it is as the call is not established\n\t//       yet. An in-progress call can only be replaced if the user\n\t//       deliberately allowed this (allow_transfer_consultation_inprog).\n\tif (lines.at(lineno_to)->remote_extension_supported(EXT_REPLACES)) {\n\t\tlog_file->write_report(\"Remote end supports 'replaces'.\\n\"\\\n\t\t\t\"Attended transfer.\",\n\t\t\t\"t_phone::refer\");\n\t\trefer_attended(lineno_from, lineno_to);\n\t} else if (get_line_substate(lineno_to) == LSSUB_OUTGOING_PROGRESS) {\n\t\tlog_file->write_report(\"Call transfer while consultation in progress.\\n\"\\\n\t\t\t\"Attended transfer.\",\n\t\t\t\"t_phone::refer\");\n\t\trefer_attended(lineno_from, lineno_to);\t\n\t} else {\n\t\tlog_file->write_report(\"Remote end does not support 'replaces'.\\n\"\\\n\t\t\t\"Transfer with consultation.\",\n\t\t\t\"t_phone::refer\");\n\t\trefer_consultation(lineno_from, lineno_to);\n\t}\n}\n\n// Attended call transfer\n// See draft-ietf-sipping-cc-transfer-07 7.3\nvoid t_phone::refer_attended(unsigned short lineno_from, unsigned short lineno_to) \n{\n\tt_rwmutex_reader x(lines_mtx);\n\n\tt_line *line = lines.at(lineno_to);\n\t\n\tswitch (line->get_substate())\n\t{\n\tcase LSSUB_ESTABLISHED:\n\t\t// Transfer allowed\n\t\tbreak;\n\tcase LSSUB_OUTGOING_PROGRESS:\n\t\t{\n\t\t\tunsigned short dummy;\n\t\t\tt_user *user_config = get_line_user(lineno_to);\n\t\t\tif (!user_config->get_allow_transfer_consultation_inprog() ||\n\t\t\t    !is_line_transfer_consult(lineno_to, dummy))\n\t\t\t{\n\t\t\t\t// Transfer not allowed\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Transfer allowed\n\t\tbreak;\n\tdefault:\n\t\t// Transfer not allowed\n\t\treturn;\n\t};\n\t\n\tt_user *user_config = get_line_user(lineno_from);\n\t\n\t// draft-ietf-sipping-cc-transfer-07 section 7.3\n\t// The call must be referred to the contact URI of the far-end.\n\t// As the contact URI may not be globally routable, the AoR\n\t// may be used alternatively.\n\tt_url uri;\n\tstring display;\n\tif (user_config->get_attended_refer_to_aor()) \n\t{\n\t\tif (line->get_substate() == LSSUB_OUTGOING_PROGRESS) {\n\t\t\turi = line->get_remote_uri_pending();\n\t\t\tdisplay = line->get_remote_target_display_pending();\n\t\t} else {\n\t\t\turi = line->get_remote_uri();\n\t\t\tdisplay = line->get_remote_target_display();\n\t\t}\n\t} else {\n\t\tif (line->get_substate() == LSSUB_OUTGOING_PROGRESS) {\n\t\t\turi = line->get_remote_target_uri_pending();\n\t\t\tdisplay = line->get_remote_target_display_pending();\n\t\t} else {\n\t\t\turi = line->get_remote_target_uri();\n\t\t\tdisplay = line->get_remote_target_display();\n\t\t}\n\t}\n\n\t// Create Replaces header for replacing the call on lineno_to\n\tt_hdr_replaces hdr_replaces;\n\t\n\tif (line->get_substate() == LSSUB_OUTGOING_PROGRESS) {\n\t\thdr_replaces.set_call_id(line->get_call_id_pending());\n\t\thdr_replaces.set_from_tag(line->get_local_tag_pending());\n\t\thdr_replaces.set_to_tag(line->get_remote_tag_pending());\n\t} else {\n\t\thdr_replaces.set_call_id(line->get_call_id());\n\t\thdr_replaces.set_from_tag(line->get_local_tag());\n\t\thdr_replaces.set_to_tag(line->get_remote_tag());\n\t}\n\t\n\turi.add_header(hdr_replaces);\n\t\n\t// draft-ietf-sipping-cc-transfer-07 section 7.3\n\t// If the call is referred to the AoR, then add a Require header\n\t// that requires the 'Replaces' extension, to make the correct phone\n\t// ring in case of forking.\n\tif (user_config->get_attended_refer_to_aor()) {\n\t\tt_hdr_require hdr_require;\n\t\thdr_require.add_feature(EXT_REPLACES);\n\t\turi.add_header(hdr_require);\n\t}\n\t\n\t// Transfer call\n\tlines.at(lineno_from)->refer(uri, display);\n}\n\n// Call transfer with consultation\n// See draft-ietf-sipping-cc-transfer-07 7\nvoid t_phone::refer_consultation(unsigned short lineno_from, unsigned short lineno_to) \n{\n\tt_rwmutex_writer x(lines_mtx);\n\tt_line *line = lines.at(lineno_to);\n\t\n\tif (line->get_substate() != LSSUB_ESTABLISHED) {\n\t\treturn;\n\t}\n\t\n\t// Refer call to the URI of the far-end\n\tt_url uri = line->get_remote_uri();\n\tstring display = line->get_remote_display();\n\t\n\t// End consultation call\n\tline->end_call();\n\n\tmove_line_to_background(lineno_to);\n\t\n\t// Transfer call\n\tlines.at(lineno_from)->refer(uri, display);\n}\n\nvoid t_phone::setup_consultation_call(const t_url &uri, const string &display) {\n\tunsigned short consult_line;\n\tif (!get_idle_line(consult_line)) {\n\t\tlog_file->write_report(\"Cannot get idle line for consultation call.\",\n\t\t\t\"t_phone::setup_consultation_call\");\n\t\treturn;\n\t}\n\t\n\tunsigned short xfer_line = active_line;\n\tt_user *user_config = get_line_user(xfer_line);\n\t\n\tt_phone_user *pu = find_phone_user(user_config->get_profile_name());\n\tif (!pu) {\n\t\tlog_file->write_header(\"t_phone::setup_consultation_call\", \n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user_config->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n\t\n\tactivate_line(consult_line);\n\t\n\tstring subject = TRANSLATE(\"Call transfer - %1\");\n\tsubject = replace_first(subject, \"%1\",\n\t\t\tui->format_sip_address(user_config, \n\t\t\t\tget_remote_display(xfer_line), \n\t\t\t\tget_remote_uri(xfer_line)));\n\t\t\t\n\tbool no_fork = false;\n\tif (user_config->get_allow_transfer_consultation_inprog()) {\n\t\t// If the configuration allows a call to be transferred\n\t\t// while the consultation call is still in progress, then\n\t\t// we send a no-fork request disposition in the INVTE\n\t\t// to setup the consultation call. This way we know that\n\t\t// the call has not been forked and it should be possible\n\t\t// to replace the early dialog.\n\t\t// \n\t\t// The scenario is:\n\t\t// A calls B\n\t\t// B sets up a consultation call to C (no-fork)\n\t\t// B sends a REFER with replaces to A\n\t\t// A sends an INVITE with replaces to C.\n\t\t//\n\t\t// NOTE: this is a non-standard implementation. RFC 3891\n\t\t//       does not allow to replace an early dialog not\n\t\t//       setup by the UA. In this case the REFER from A to C\n\t\t//       intends to replace the dialog from B to C, but C\n\t\t//       did not setup the B-C dialog itself.\n\t\tno_fork = true;\n\t}\n\t\n\tinvite(pu, uri, display, subject, no_fork, false);\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tlines.at(consult_line)->set_is_transfer_consult(true, xfer_line);\n\tlines.at(xfer_line)->set_to_be_transferred(true, consult_line);\n\t\n\tui->cb_consultation_call_setup(user_config, consult_line);\n}\n\nvoid t_phone::activate_line(unsigned short l) {\n\tunsigned short a = get_active_line();\n\tif (a == l) return;\n\n\t// Just switch the active line if there is a conference.\n\tif (is_3way) {\n\t\tset_active_line(l);\n\t\tui->cb_line_state_changed();\n\t\treturn;\n\t}\n\n\n\t// Put the current active line on hold if it has a call.\n\t// Only established calls can be put on-hold. Transient calls\n\t// should be torn down or just kept in the same transient state\n\t// when switching to the other line.\n\tif (get_line(a)->get_state() == LS_BUSY && !hold()) {\n\t\t// The line is busy but could not be put on-hold. Determine\n\t\t// what to do based on the line sub state.\n\t\tswitch(get_line(a)->get_substate()) {\n\t\tcase LSSUB_OUTGOING_PROGRESS:\n\t\t\t// User has outgoing call in progress on the active\n\t\t\t// line, but decided to switch line, so tear down\n\t\t\t// the call.\n\t\t\tend_call();\n\t\t\tui->cb_stop_call_notification(a);\n\t\t\tbreak;\n\t\tcase LSSUB_INCOMING_PROGRESS:\n\t\t\t// The incoming call on the current active will stay,\n\t\t\t// just stop the ring tone.\n\t\t\tui->cb_stop_call_notification(a);\n\t\t\tbreak;\n\t\tcase LSSUB_ANSWERING:\n\t\t\t// Answering is in progress, so call cannot be put\n\t\t\t// on-hold. Tear down the call.\n\t\t\tend_call();\n\t\t\tbreak;\n\t\tcase LSSUB_RELEASING:\n\t\t\t// The releasing call on the current line will get\n\t\t\t// released. No need to take any action here.\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// This should not happen.\n\t\t\tlog_file->write_report(\"ERROR: Call cannot be put on hold.\",\n\t\t\t\t\"t_phone::activate_line\");\n\t\t}\n\t}\n\n\tset_active_line(l);\n\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Retrieve the call on the new active line unless that line\n\t// is transferring a call and the user profile indicates that\n\t// the referrer holds the call during call transfer.\n\tt_user *user_config = lines[l]->get_user();\n\tif (get_line_refer_state(l) == REFST_NULL || \n\t    (user_config && !user_config->get_referrer_hold()))\n\t{\n\t\tretrieve();\n\t}\n\n\t// Play ring tone, if the new active line has an incoming call\n\t// in progress.\n\tif (get_line(l)->get_substate() == LSSUB_INCOMING_PROGRESS) {\n\t\tui->cb_play_ringtone(l);\n\t}\n\n\tui->cb_line_state_changed();\n}\n\nvoid t_phone::send_dtmf(char digit, bool inband, bool info) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->send_dtmf(digit, inband, info);\n}\n\nvoid t_phone::start_timer(t_phone_timer timer, t_phone_user *pu) {\n\tt_tmr_phone\t*t;\n\tt_user\t\t*user_config = pu->get_user_profile();\n\n\tswitch(timer) {\n\tcase PTMR_NAT_KEEPALIVE:\n\t\tt = new t_tmr_phone(user_config->get_timer_nat_keepalive() * 1000, timer, this);\n\t\tMEMMAN_NEW(t);\n\t\tpu->id_nat_keepalive = t->get_object_id();\n\t\tbreak;\n\tcase PTMR_TCP_PING:\n\t\tt = new t_tmr_phone(user_config->get_timer_tcp_ping() * 1000, timer, this);\n\t\tMEMMAN_NEW(t);\n\t\tpu->id_tcp_ping = t->get_object_id();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_phone::stop_timer(t_phone_timer timer, t_phone_user *pu) {\n\tunsigned short\t*id;\n\n\tswitch(timer) {\n\tcase PTMR_REGISTRATION:\n\t\tid = &pu->id_registration;\n\t\tbreak;\n\tcase PTMR_NAT_KEEPALIVE:\n\t\tid = &pu->id_nat_keepalive;\n\t\tbreak;\n\tcase PTMR_TCP_PING:\n\t\tid = &pu->id_tcp_ping;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tif (*id != 0) evq_timekeeper->push_stop_timer(*id);\n\t*id = 0;\n}\n\nvoid t_phone::start_set_timer(t_phone_timer timer, long time, t_phone_user *pu) {\n\tt_tmr_phone\t*t;\n\n\n\tswitch(timer) {\n\tcase PTMR_REGISTRATION:\n\t\tlong new_time;\n\n\t\t// Re-register before registration expires\n\t\tif (pu->get_last_reg_failed() || time <= RE_REGISTER_DELTA * 1000) {\n\t\t\tnew_time = time;\n\t\t} else {\n\t\t\tnew_time = time - (RE_REGISTER_DELTA * 1000);\n\t\t}\n\t\tt = new t_tmr_phone(new_time, timer, this);\n\t\tMEMMAN_NEW(t);\n\t\tpu->id_registration = t->get_object_id();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_phone::handle_response_out_of_dialog(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_phone_user *pu = match_phone_user(r, tuid);\n\tif (!pu) {\n\t\tlog_file->write_report(\"Response does not match any pending request.\",\n\t\t\t\"t_phone::handle_response_out_of_dialog\");\n\t\treturn;\n\t}\n\t\n\tlog_file->write_header(\"t_phone::handle_response_out_of_dialog\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Out of dialog matches phone user: \");\n\tlog_file->write_raw(pu->get_user_profile()->get_profile_name());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tpu->handle_response_out_of_dialog(r, tuid, tid);\n}\n\nvoid t_phone::handle_response_out_of_dialog(StunMessage *r, t_tuid tuid) {\n\tt_phone_user *pu = match_phone_user(r, tuid);\n\tif (!pu) {\n\t\tlog_file->write_report(\"STUN response does not match any pending request.\",\n\t\t\t\"t_phone::handle_response_out_of_dialog\");\n\t\treturn;\n\t}\n\t\n\tpu->handle_response_out_of_dialog(r, tuid);\n}\n\nt_phone_user *t_phone::find_phone_user(const string &profile_name) const {\n\n\tfor (list<t_phone_user *>::const_iterator i = phone_users.begin();\n\t     i != phone_users.end(); ++i)\n\t{\n\t\tif (!(*i)->is_active()) continue;\n\t\t\n\t\tt_user *user_config = (*i)->get_user_profile();\n\t\tif (user_config->get_profile_name() == profile_name) {\n\t\t\treturn *i;\n\t\t}\n\t}\n\t\n\treturn NULL;\n}\n\nt_phone_user *t_phone::find_phone_user(const t_url &user_uri) const {\n\n\tfor (list<t_phone_user *>::const_iterator i = phone_users.begin();\n\t\t     i != phone_users.end(); ++i)\n\t{\n\t\tif (!(*i)->is_active()) continue;\n\t\t\n\t\tt_user *user_config = (*i)->get_user_profile();\n\t\tif (t_url(user_config->create_user_uri(false)) == user_uri) {\n\t\t\treturn *i;\n\t\t}\n\t}\n\t\n\treturn NULL;\n}\n\nt_phone_user *t_phone::match_phone_user(t_response *r, t_tuid tuid, bool active_only) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); ++i)\n\t{\n\t\tif (active_only && !(*i)->is_active()) continue;\n\t\tif ((*i)->match(r, tuid)) return *i;\n\t}\n\t\n\treturn NULL;\n}\n\nt_phone_user *t_phone::match_phone_user(t_request *r, bool active_only) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); ++i)\n\t{\n\t\tif (active_only && !(*i)->is_active()) continue;\n\t\tif ((*i)->match(r)) return *i;\n\t}\n\t\n\treturn NULL;\n}\n\nt_phone_user *t_phone::match_phone_user(StunMessage *r, t_tuid tuid, bool active_only) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); ++i)\n\t{\n\t\tif (active_only && !(*i)->is_active()) continue;\n\t\tif ((*i)->match(r, tuid)) return *i;\n\t}\n\t\n\treturn NULL;\n}\n\nint t_phone::hunt_line(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Send incoming call to active line if it is idle.\n\tif (lines.at(active_line)->get_substate() == LSSUB_IDLE) {\n\t\treturn active_line;\n\t}\n\t\n\tif (sys_config->get_call_waiting() || all_lines_idle()) {\n\t\t// Send the INVITE to the first idle unseized line\n\t\tfor (unsigned short i = 0; i < NUM_USER_LINES; i++) {\n\t\t\tif (lines[i]->get_substate() == LSSUB_IDLE) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn -1;\n}\n\n//////////////\n// Protected\n//////////////\n\nvoid t_phone::recvd_provisional(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_provisional(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog response\n\t// Provisional responses should only be given for INVITE.\n\t// A response for an INVITE is always in a dialog.\n\t// Ignore provisional responses for other requests.\n}\n\nvoid t_phone::recvd_success(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_success(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog responses\n\thandle_response_out_of_dialog(r, tuid, tid);\n}\n\nvoid t_phone::recvd_redirect(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_redirect(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog responses\n\thandle_response_out_of_dialog(r, tuid, tid);\n}\n\nvoid t_phone::recvd_client_error(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_client_error(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog responses\n\thandle_response_out_of_dialog(r, tuid, tid);\n}\n\nvoid t_phone::recvd_server_error(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_server_error(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog responses\n\thandle_response_out_of_dialog(r, tuid, tid);\n}\n\nvoid t_phone::recvd_global_error(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_global_error(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog responses\n\thandle_response_out_of_dialog(r, tuid, tid);\n}\n\nvoid t_phone::post_process_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tcleanup_dead_lines();\n\tmove_releasing_lines_to_background();\n\tcleanup_3way();\n}\n\nvoid t_phone::recvd_invite(t_request *r, t_tid tid) {\n\tt_rwmutex_future_writer x(lines_mtx);\n\n\t// Check if this INVITE is a retransmission.\n\t// Once the TU sent a 2XX repsonse on an INVITE it has to deal\n\t// with retransmissions.\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->is_invite_retrans(r)) {\n\t\t\tlines[i]->process_invite_retrans();\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// RFC 3261 12.2.2\n\t// An INVITE with a To-header without a tag is an initial\n\t// INVITE\n\tif (r->hdr_to.tag == \"\") {\n\t\trecvd_initial_invite(r, tid);\n\t} else {\n\t\trecvd_re_invite(r, tid);\n\t}\t\n}\n\nvoid t_phone::recvd_initial_invite(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tlist <string> unsupported;\n\tt_call_record call_record;\n\t\n\t// Find out for which user this INVITE is.\n\tt_phone_user *pu = match_phone_user(r, true);\n\tif (!pu) {\n\t\tresp = r->create_response(R_404_NOT_FOUND);\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Do not create a call history record as this is a misrouted\n\t\t// call.\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\t\n\t// Reject call if phone is not active\n\tif (!is_active) {\n\t\tresp = r->create_response(R_480_TEMP_NOT_AVAILABLE);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\t\n\tt_user *user_config = pu->get_user_profile();\n\n\t// Check if the far end requires any unsupported extensions\n\tif (!user_config->check_required_ext(r, unsupported))\n\t{\n\t\t// Not all required extensions are supported\n\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Do not create a call history record here. The far-end\n\t\t// should retry the call without the extension, so this\n\t\t// is not a missed call from the user point of view.\n\t\t\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\t\n\t// RFC 3891 3\n\t// If a replaces header is present, check if it matches a dialog\n\tint replace_line = -1;\n\tif (r->hdr_replaces.is_populated() && user_config->get_ext_replaces()) {\n\t\tbool early_matched = false;\n\t\tbool no_fork_req_disposition =\n\t\t\tr->hdr_request_disposition.is_populated() &&\n\t\t\tr->hdr_request_disposition.fork_directive == t_hdr_request_disposition::NO_FORK;\n\t\t\n\t\tt_rwmutex_reader x(lines_mtx);\n\n\t\tfor (size_t i = 0; i < lines.size(); i++) {\n\t\t\tif (lines.at(i)->match_replaces(r->hdr_replaces.call_id,\n\t\t\t\tr->hdr_replaces.to_tag,\n\t\t\t\tr->hdr_replaces.from_tag,\n\t\t\t\tno_fork_req_disposition,\n\t\t\t\tearly_matched))\n\t\t\t{\n\t\t\t\treplace_line = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (replace_line >= NUM_CALL_LINES) {\n\t\t\t// Replaces header matches a releasing line.\n\t\t\tresp = r->create_response(R_603_DECLINE);\n\t\t\tsend_response(resp, 0, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\treturn;\n\t\t} else if (replace_line >= 0) {\n\t\t\tif (replace_line == active_line) {\n\t\t\t\tif (r->hdr_replaces.early_only && !early_matched) {\n\t\t\t\t\tresp = r->create_response(R_486_BUSY_HERE);\n\t\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\t\tdelete resp;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// The existing call will be torn down only after\n\t\t\t\t// it has been checked that this incoming INVITE\n\t\t\t\t// is not rejected by the user, e.g. DND.\n\t\t\t} else {\n\t\t\t\t// Implementation decision:\n\t\t\t\t// Don't allow a held call to be replaced.\n\t\t\t\tresp = r->create_response(R_486_BUSY_HERE);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\t\n\t\t\t\t// Create a call history record\n\t\t\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\t\t\tuser_config->get_profile_name());\n\t\t\t\tcall_record.fail_call(resp);\n\t\t\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\t// Replaces does not match any line.\n\t\t\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\t\t\tsend_response(resp, 0, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\t// Hunt for an idle line to handle the call.\n\tint hunted_line = -1;\n\tif (replace_line >= 0) {\n\t\thunted_line = replace_line;\n\t} else {\n\t\thunted_line = hunt_line();\n\t}\n\t\n\tt_display_url display_url;\n\tlist<t_display_url> cf_dest; // call forwarding destinations\n\t\n\t// Call user defineable incoming call script to determine how\n\t// to handle this call\n\tt_script_result script_result;\n\t\n\tif (!user_config->get_script_incoming_call().empty()) {\n\t\t// Send 100 Trying as the script might take a while\n\t\tresp = r->create_response(R_100_TRYING);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tt_call_script script(user_config, t_call_script::TRIGGER_IN_CALL, hunted_line + 1);\n\t\tscript.exec_action(script_result, r);\n\t\t\n\t\tif (!script_result.display_msgs.empty()) {\n\t\t\tstring text(join_strings(script_result.display_msgs, \"\\n\"));\n\t\t\tui->cb_display_msg(text, MSG_NO_PRIO);\n\t\t}\n\t\t\n\t\t// Override display name with caller name returned by script\n\t\tif (!script_result.caller_name.empty()) {\n\t\t\tr->hdr_from.display_override = script_result.caller_name;\n\t\t\tlog_file->write_header(\"t_phone::recvd_invite\", \n\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Override display name with caller name:\\n\");\n\t\t\tlog_file->write_raw(script_result.caller_name);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\n\t\n\tt_call_script script_in_call_failed(user_config, t_call_script::TRIGGER_IN_CALL_FAILED, 0);\n\t\n\t// Lookup address in address book.\n\tif (script_result.caller_name.empty() &&\n\t\tsys_config->get_ab_lookup_name() && \n\t\t(sys_config->get_ab_override_display() || r->hdr_from.display.empty())) \n\t{\n\t\t// Send 100 Trying as name lookup might take a while\n\t\tresp = r->create_response(R_100_TRYING);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tstring name = ui->get_name_from_abook(user_config, r->hdr_from.uri);\n\t\tif (!name.empty()) {\n\t\t\tr->hdr_from.display_override = name;\n\t\t\tlog_file->write_header(\"t_phone::recvd_invite\", \n\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\n\t\t\t\t\"Override display name with address book name:\\n\");\n\t\t\tlog_file->write_raw(name);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\t\n\t\n\t// Perform the action in the script_result.\n\t// NOTE: the default action is \"continue\"\n\tswitch (script_result.action) {\n\tcase t_script_result::ACTION_CONTINUE:\n\t\t// Continue with call\n\t\tbreak;\n\tcase t_script_result::ACTION_AUTOANSWER:\n\t\tlog_file->write_report(\"Incoming call script action: autoanswer\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tbreak;\n\tcase t_script_result::ACTION_REJECT:\n\t\tlog_file->write_report(\"Incoming call script action: reject\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tresp = r->create_response(R_603_DECLINE, script_result.reason);\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t\tbreak;\n\tcase t_script_result::ACTION_DND:\n\t\tlog_file->write_report(\"Incoming call script action: dnd\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tresp = r->create_response(R_480_TEMP_NOT_AVAILABLE, \n\t\t\t\tscript_result.reason);\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t\tbreak;\n\tcase  t_script_result::ACTION_REDIRECT:\n\t\tlog_file->write_report(\"Incoming call script action: redirect\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tui->expand_destination(user_config, \n\t\t\tscript_result.contact, display_url);\n\t\tif (display_url.is_valid()) {\n\t\t\tcf_dest.clear();\n\t\t\tcf_dest.push_back(display_url);\n\t\t\tresp = r->create_response(R_302_MOVED_TEMPORARILY);\n\t\t\tresp->hdr_contact.set_contacts(cf_dest);\n\t\t} else {\n\t\t\tlog_file->write_report(\"Invalid redirect contact\",\n\t\t\t\t\"t_phone::recvd_invite\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR); \n\t\t}\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t\tbreak;\n\tdefault:\n\t\tlog_file->write_report(\"Error in incoming call script\",\n\t\t\t\"t_phone::recvd_invite\", LOG_NORMAL, LOG_WARNING);\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR); \n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t\tbreak;\n\t}\t\t\n\n\t// Call forwarding always\n\t// NOTE: if a call script returned the autoanswer action, then\n\t//       call forwarding should be bypassed\n\tif (pu->service->get_cf_active(CF_ALWAYS, cf_dest) &&\n\t\tscript_result.action == t_script_result::ACTION_CONTINUE) \n\t{\n\t\tlog_file->write_report(\"Call redirection unconditional\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tresp = r->create_response(R_302_MOVED_TEMPORARILY);\n\t\tresp->hdr_contact.set_contacts(cf_dest);\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\n\t// Do not disturb\n\t// RFC 3261 21.4.18\n\t// NOTE: if a call script returned the autoanswer action, then\n\t//       do not disturb should be bypassed\n\tif (pu->service->is_dnd_active() &&\n\t\tscript_result.action == t_script_result::ACTION_CONTINUE) \n\t{\n\t\tlog_file->write_report(\"Do not disturb\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tresp = r->create_response(R_480_TEMP_NOT_AVAILABLE);\n\t\tsend_response(resp, 0, tid);\n\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\t\n\t// RFC 3891\n\tbool auto_answer_replace_call = false;\n\tif (replace_line >= 0) {\n\t\t// This call replaces an existing call. Tear down this existing\n\t\t// call. This will clear the active line.\n\t\tlog_file->write_report(\"End call due to Replaces header.\",\n\t\t\t\t\"t_phone::recvd_initial_invite\");\n\t\t\t\t\n\t\tt_rwmutex_writer x(lines_mtx);\n\t\tif (lines.at(replace_line)->get_substate() == LSSUB_INCOMING_PROGRESS) {\n\t\t\tui->cb_stop_call_notification(replace_line);\n\t\t\tlines.at(replace_line)->reject();\n\t\t} else {\n\t\t\tlines.at(replace_line)->end_call();\n\t\t\tauto_answer_replace_call = true;\n\t\t}\n\t\t\n\t\tmove_line_to_background(replace_line);\n\t}\n\n\t// Auto answer\n\tif (hunted_line == active_line) {\n\t\t// Auto-answer is only applicable to the active line.\n\t\t\n\t\tt_rwmutex_reader x(lines_mtx);\n\t\tif (replace_line >= 0 && auto_answer_replace_call) {\n\t\t\t// RFC 3891\n\t\t\t// This call replaces an existing established call, answer immediate.\n\t\t\tlines.at(active_line)->set_auto_answer(true);\n\t\t} else if (pu->service->is_auto_answer_active() ||\n\t\t\tscript_result.action == t_script_result::ACTION_AUTOANSWER) \n\t\t{\n\t\t\t// Auto answer\n\t\t\tlog_file->write_report(\"Auto answer\",\n\t\t\t\t\"t_phone::recvd_invite\");\n\t\t\tlines.at(active_line)->set_auto_answer(true);\n\t\t}\n\t}\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\t// Send INVITE to hunted line\n\tif (hunted_line >= 0) {\n\t\tlines.at(hunted_line)->recvd_invite(pu, r, tid,\n\t\t\tscript_result.ringtone);\n\t\treturn;\n\t}\n\t\n\t// The phone is busy\n\t// Call forwarding busy\n\tif (pu->service->get_cf_active(CF_BUSY, cf_dest)) {\n\t\tlog_file->write_report(\"Call redirection busy\",\n\t\t\t\"t_phone::recvd_invite\");\n\t\tresp = r->create_response(R_302_MOVED_TEMPORARILY);\n\t\tresp->hdr_contact.set_contacts(cf_dest);\n\t\tsend_response(resp, 0, tid);\n\t\t\n\t\t// Create a call history record\n\t\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\t\tuser_config->get_profile_name());\n\t\tcall_record.fail_call(resp);\n\t\tcall_history->add_call_record(call_record);\n\t\t\n\t\t// Trigger call script\n\t\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\n\t// Send busy response\n\tresp = r->create_response(R_486_BUSY_HERE);\n\tsend_response(resp, 0, tid);\n\t\n\t// Create a call history record\n\tcall_record.start_call(r, t_call_record::DIR_IN, \n\t\tuser_config->get_profile_name());\n\tcall_record.fail_call(resp);\n\tcall_history->add_call_record(call_record);\n\t\n\t// Trigger call script\n\tscript_in_call_failed.exec_notify(resp);\n\t\t\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_re_invite(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tlist <string> unsupported;\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// RFC 3261 12.2.2\n\t// A To-header with a tag is a mid-dialog request.\n\t// Find a line that matches the request\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tt_phone_user *pu = lines[i]->get_phone_user();\n\t\t\tassert(pu);\n\t\t\tt_user *user_config = pu->get_user_profile();\n\t\t\t\n\t\t\t// Check if the far end requires any unsupported extensions\n\t\t\tif (!user_config->check_required_ext(r, unsupported))\n\t\t\t{\n\t\t\t\t// Not all required extensions are supported\n\t\t\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\t\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\n\t\t\tlines[i]->recvd_invite(pu, r, tid, \"\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// No dialog matches with the request.\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_ack(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tlines[i]->recvd_ack(r, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_cancel(t_request *r, t_tid cancel_tid,\n\t\tt_tid target_tid)\n{\n\tt_response *resp;\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match_cancel(r, target_tid)) {\n\t\t\tlines[i]->recvd_cancel(r, cancel_tid, target_tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, cancel_tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_bye(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tlist <string> unsupported;\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tt_user *user_config = lines[i]->get_user();\n\t\t\tassert(user_config);\n\n\t\t\tif (!user_config->check_required_ext(r, unsupported))\n\t\t\t{\n\t\t\t\t// Not all required extensions are supported\n\t\t\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\t\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\t\t\t\n\t\t\n\t\t\tlines[i]->recvd_bye(r, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_options(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tif (r->hdr_to.tag ==\"\") {\n\t\t// Out-of-dialog OPTIONS\n\t\tt_phone_user *pu = find_phone_user_out_dialog_request(r, tid);\n\t\tif (pu) {\n\t\t\tresp = pu->create_options_response(r);\n\t\t\tsend_response(resp, 0, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t}\t\t\n\t} else {\n\t\t// In-dialog OPTIONS\n\t\tt_line *l = find_line_in_dialog_request(r, tid);\n\t\tif (l) {\n\t\t\tl->recvd_options(r, tid);\n\t\t}\n\t}\n}\n\nt_phone_user *t_phone::find_phone_user_out_dialog_request(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tlist <string> unsupported;\n\t\n\t// Find out for which user this request is.\n\tt_phone_user *pu = match_phone_user(r, true);\n\tif (!pu) {\n\t\tresp = r->create_response(R_404_NOT_FOUND);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn NULL;\n\t}\n\n\t// Check if the far end requires any unsupported extensions\n\tif (!pu->get_user_profile()->check_required_ext(r, unsupported))\n\t{\n\t\t// Not all required extensions are supported\n\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn NULL;\n\t}\n\t\n\treturn pu;\n}\n\nt_line *t_phone::find_line_in_dialog_request(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tlist <string> unsupported;\n\tt_rwmutex_reader x(lines_mtx);\n\t\n\t// RFC 3261 12.2.2\n\t// A To-header with a tag is a mid-dialog request.\n\t// No dialog matches with the request.\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tt_user *user_config = lines[i]->get_user();\n\t\t\tassert(user_config);\t\t\n\t\t\n\t\t\t// Check if the far end requires any unsupported extensions\n\t\t\tif (!user_config->check_required_ext(r, unsupported))\n\t\t\t{\n\t\t\t\t// Not all required extensions are supported\n\t\t\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\t\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn NULL;\n\t\t\t}\t\t\n\n\t\t\treturn lines[i];\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\treturn NULL;\n}\n\nvoid t_phone::recvd_register(t_request *r, t_tid tid) {\n\t// The softphone is not a registrar.\n\tt_response *resp = r->create_response(R_403_FORBIDDEN);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\t// TEST ONLY: code for testing a 423 Interval Too Brief\n\t/*\n\tif (r->hdr_contact.contact_list.front().get_expires() < 30) {\n\t\tt_response *resp = r->create_response(\n\t\t\t\t\tR_423_INTERVAL_TOO_BRIEF);\n\t\tresp->hdr_min_expires.set_time(30);\n\t\tsend_response(resp, 0, tid);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\n\t// Code for testing a 200 OK response (register)\n\tt_response *resp = r->create_response(R_200_OK);\n\tresp->hdr_contact.set_contacts(r->hdr_contact.contact_list);\n\tresp->hdr_contact.contact_list.front().set_expires(30);\n\tresp->hdr_date.set_now();\n\tsend_response(resp, 0, tid);\n\tdelete resp;\n\n\t// Code for testing 200 OK response (de-register)\n\tt_response *resp = r->create_response(R_200_OK);\n\tsend_response(resp, 0, tid);\n\tdelete resp;\n\n\t// Code for testing 200 OK response (query)\n\tt_response *resp = r->create_response(R_200_OK);\n\tt_contact_param contact;\n\tcontact.uri.set_url(\"sip:aap@xs4all.nl\");\n\tresp->hdr_contact.add_contact(contact);\n\tcontact.uri.set_url(\"sip:noot@xs4all.nl\");\n\tresp->hdr_contact.add_contact(contact);\n\tsend_response(resp, 0, tid);\n\tdelete resp;\n\n\t// Code for testing a 401 response (register)\n\tif (r->hdr_authorization.is_populated() &&\n\t    r->hdr_authorization.credentials_list.front().digest_response.\n\t    \tnonce == \"0123456789abcdef\")\n\t{\n\t\tt_response *resp = r->create_response(R_200_OK);\n\t\tresp->hdr_contact.set_contacts(r->hdr_contact.contact_list);\n\t\tresp->hdr_contact.contact_list.front().set_expires(30);\n\t\tresp->hdr_date.set_now();\n\t\tsend_response(resp, 0, tid);\n\t\tdelete resp;\n\t} else {\n\t\tt_response *resp = r->create_response(R_401_UNAUTHORIZED);\n\t\tt_challenge c;\n\t\tc.auth_scheme = AUTH_DIGEST;\n\t\tc.digest_challenge.realm = \"mtel.nl\";\n\t\tif (r->hdr_authorization.is_populated()) {\n\t\t\tc.digest_challenge.nonce = \"0123456789abcdef\";\n\t\t\tc.digest_challenge.stale = true;\n\t\t} else {\n\t\t\tc.digest_challenge.nonce = \"aaaaaa0123456789\";\n\t\t}\n\t\tc.digest_challenge.opaque = \"secret\";\n\t\tc.digest_challenge.algorithm = ALG_MD5;\n\t\t// c.digest_challenge.qop_options.push_back(QOP_AUTH);\n\t\t// c.digest_challenge.qop_options.push_back(QOP_AUTH_INT);\n\t\tresp->hdr_www_authenticate.set_challenge(c);\n\t\tsend_response(resp, 0, tid);\n\t}\n\t*/\n}\n\nvoid t_phone::recvd_prack(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tlines[i]->recvd_prack(r, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_subscribe(t_request *r, t_tid tid) {\n\tt_response *resp;\n\n\tif (r->hdr_event.event_type != SIP_EVENT_REFER) {\n\t\t// Non-supported event type\n\t\tresp = r->create_response(R_489_BAD_EVENT);\n\t\tresp->hdr_allow_events.add_event_type(SIP_EVENT_REFER);\n\t\tsend_response(resp, 0 ,tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\n\tt_rwmutex_reader x(lines_mtx);\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tlines[i]->recvd_subscribe(r, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (r->hdr_to.tag == \"\") {\n\t\t// A REFER outside a dialog is not allowed by Twinkle\n\t\tif (r->hdr_event.event_type == SIP_EVENT_REFER) {\n\t\t\t// RFC 3515 2.4.4\n\t\t\tresp = r->create_response(R_403_FORBIDDEN);\n\t\t\tsend_response(resp, 0 ,tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_notify(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tt_phone_user *pu;\n\t\n\t// Check support for the notified event\n\tif (!SIP_EVENT_SUPPORTED(r->hdr_event.event_type)) {\n\t\t// Non-supported event type\n\t\tresp = r->create_response(R_489_BAD_EVENT);\n\t\tADD_SUPPORTED_SIP_EVENTS(resp->hdr_allow_events);\n\t\tsend_response(resp, 0 ,tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\t\n\t// MWI or presence notification\n\tif (r->hdr_event.event_type == SIP_EVENT_MSG_SUMMARY ||\n\t    r->hdr_event.event_type == SIP_EVENT_PRESENCE)\n\t{\n\t\tpu = match_phone_user(r, true);\n\t\tif (pu) {\n\t\t\tpu->recvd_notify(r, tid);\n\t\t} else {\n\t\t\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\t\t\tsend_response(resp, 0 ,tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t}\n\t\t\n\t\treturn;\n\t}\n\n\t// REFER notification\n\tt_rwmutex_future_writer x(lines_mtx);\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tlines[i]->recvd_notify(r, tid);\n\t\t\tif (lines[i]->get_refer_state() == REFST_NULL) {\n\t\t\t\t// Refer subscription has finished.\n\t\t\t\tlog_file->write_report(\"Refer subscription terminated.\",\n\t\t\t\t\t\"t_phone::recvd_notify\");\n\n\t\t\t\tif (lines[i]->get_substate() == LSSUB_RELEASING ||\n\t\t\t\t    lines[i]->get_substate() == LSSUB_IDLE)\n\t\t\t\t{\n\t\t\t\t\t// The line is (being) cleared already. So this\n\t\t\t\t\t// NOTIFY signals the end of the refer subscription\n\t\t\t\t\t// attached to this line.\n\t\t\t\t\tcleanup_dead_lines();\n\t\t\t\t\treturn;\n\t\t\t\t} else if (lines[i]->is_refer_succeeded()) {\n\t\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\t\"Refer succeeded. End call with referee,\",\n\t\t\t\t\t\t\"t_phone::recvd_notify\");\n\t\t\t\t\tlines[i]->end_call();\n\t\t\t\t} else {\n\t\t\t\t\tlog_file->write_report(\"Refer failed.\",\n\t\t\t\t\t\t\"t_phone::recvd_notify\");\n\n\t\t\t\t\tt_user *user_config = lines[i]->get_user();\n\t\t\t\t\tassert(user_config);\n\t\t\t\t\tif (user_config->get_referrer_hold() &&\n\t\t\t\t\t    lines[i]->get_is_on_hold())\n\t\t\t\t\t{\n\t\t\t\t\t\t// Retrieve the call if the line is active.\n\t\t\t\t\t\tif (i == active_line) {\n\t\t\t\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\t\t\t\"Retrieve call with referee.\",\n\t\t\t\t\t\t\t\t\"t_phone::recvd_notify\");\n\t\t\t\t\t\t\tlines[i]->retrieve();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (r->hdr_to.tag == \"\") {\n\t\t// NOTIFY outside a dialog is not allowed.\n\t\tresp = r->create_response(R_403_FORBIDDEN);\n\t\tsend_response(resp, 0 ,tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_refer(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tt_phone_user *pu = lines[i]->get_phone_user();\n\t\t\tassert(pu);\n\t\t\tt_user *user_config = pu->get_user_profile();\n\t\t\t\n\t\t\tlist <string> unsupported;\n\t\t\tif (!user_config->check_required_ext(r, unsupported))\n\t\t\t{\n\t\t\t\t// Not all required extensions are supported\n\t\t\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\t\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\n\t\t\t// Reject if a 3-way call is established.\n\t\t\tif (is_3way) {\n\t\t\t\tlog_file->write_report(\"3-way call active. Reject REFER.\",\n\t\t\t\t\t\"t_phone::recvd_refer\");\n\t\t\t\tresp = r->create_response(R_603_DECLINE);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// Reject if the line is on-hold.\n\t\t\tif (is_3way || lines[i]->get_is_on_hold()) {\n\t\t\t\tlog_file->write_report(\"Line is on-hold. Reject REFER.\",\n\t\t\t\t\t\"t_phone::recvd_refer\");\n\t\t\t\tresp = r->create_response(R_603_DECLINE);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check if a refer is already in progress\n\t\t\tif (i == LINENO_REFERRER ||\n\t\t\t    lines[LINENO_REFERRER]->get_state() != LS_IDLE ||\n\t\t\t    incoming_refer_data != NULL)\n\t\t\t{\n\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\"A REFER is still in progress. Reject REFER.\",\n\t\t\t\t\t\"t_phone::recvd_refer\");\n\t\t\t\tresp = r->create_response(R_603_DECLINE);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!lines[i]->recvd_refer(r, tid)) {\n\t\t\t\t// Refer has been rejected.\n\t\t\t\tlog_file->write_report(\"Incoming REFER rejected.\",\n\t\t\t\t\t\"t_phone::recvd_refer\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// Make sure the line stays seized if the far-end ends the\n\t\t\t// call, so a line will be available if the user gives permission\n\t\t\t// for the call transfer.\n\t\t\tlines[i]->set_keep_seized(true);\n\t\t\tincoming_refer_data = new t_transfer_data(r, i, \n\t\t\t\t\tlines[i]->get_hide_user(), pu);\n\t\t\tMEMMAN_NEW(incoming_refer_data);\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\tif (r->hdr_to.tag == \"\") {\n\t\t// Twinkle does not allow a REFER outside a dialog.\n\t\tresp = r->create_response(R_403_FORBIDDEN);\n\t\tsend_response(resp, 0 ,tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\t\n\t\nvoid t_phone::recvd_refer_permission(bool permission) {\n\tif (!incoming_refer_data) {\n\t\t// This should not happen\n\t\tlog_file->write_report(\"Incoming REFER data is gone.\",\n\t\t\t\"t_phone::recvd_refer_permission\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\treturn;\n\t}\n\n\tunsigned short i = incoming_refer_data->get_lineno();\n\tt_request *r = incoming_refer_data->get_refer_request();\n\tbool hide_user = incoming_refer_data->get_hide_user();\n\tt_phone_user *pu = incoming_refer_data->get_phone_user();\n\tt_user *user_config = pu->get_user_profile();\n\t\t\t\n\tt_rwmutex_future_writer x(lines_mtx);\n\tlines[i]->recvd_refer_permission(permission, r);\n\t\n\tif (!permission) {\n\t\tlog_file->write_report(\"Incoming REFER rejected.\",\n\t\t\t\"t_phone::recvd_refer_permission\");\n\t\tlines[i]->set_keep_seized(false);\n\t\tmove_releasing_lines_to_background();\n\t\t\n\t\tMEMMAN_DELETE(incoming_refer_data);\n\t\tdelete incoming_refer_data;\n\t\tincoming_refer_data = NULL;\n\t\treturn;\n\t} else {\n\t\tlog_file->write_report(\"Incoming REFER allowed.\",\n\t\t\t\"t_phone::recvd_refer_permission\");\n\t}\n\t\n\tif (lines[i]->get_substate() == LSSUB_ESTABLISHED) {\n\t\t// Put line on-hold and place it in the referrer line\n\t\tlog_file->write_report(\n\t\t\t\"Hold call before calling the refer-target.\",\n\t\t\t\"t_phone::recvd_refer_permission\");\n\n\t\tif (user_config->get_referee_hold()) {\n\t\t\tlines[i]->hold();\n\t\t} else {\n\t\t\t// The user profile indicates that the line should\n\t\t\t// not be put on-hold, i.e. do not send re-INVITE.\n\t\t\t// So only stop RTP.\n\t\t\tlines[i]->hold(true);\n\t\t}\n\t}\n\n\t// Move the original line to the REFERRER line (the line may be idle\n\t// already).\n\tt_line *l = lines[i];\n\tlines[i] = lines[LINENO_REFERRER];\n\tlines[i]->line_number = i;\n\tlines[LINENO_REFERRER] = l;\n\tlines[LINENO_REFERRER]->line_number = LINENO_REFERRER;\n\tlines[LINENO_REFERRER]->set_keep_seized(false);\n\n\tui->cb_line_state_changed();\n\n\t// Setup call to the Refer-To destination\n\tlog_file->write_report(\"Call refer-target.\",\n\t\t\"t_phone::recvd_refer_permission\");\n\t\n\tt_hdr_replaces hdr_replaces;\n\tt_hdr_require hdr_require;\n\t\n\t// Analyze headers in Refer-To URI.\n\t// For an attended call transfer the Refer-To URI\n\t// will contain a Replaces header and possibly a Require\n\t// header. Other headers are ignored for now.\n\t// See draft-ietf-sipping-cc-transfer-07 7.3\n\tif (!r->hdr_refer_to.uri.get_headers().empty()) {\n\t\ttry {\n\t\t\tlist<string> parse_errors;\n\t\t\tt_sip_message *m = t_parser::parse_headers(\n\t\t\t\t\tr->hdr_refer_to.uri.get_headers(),\n\t\t\t\t\tparse_errors);\n\t\t\thdr_replaces = m->hdr_replaces;\n\t\t\thdr_require = m->hdr_require;\n\t\t\tMEMMAN_DELETE(m);\n\t\t\tdelete m;\n\t\t} catch (int) {\n\t\t\tlog_file->write_header(\"t_phone::recvd_refer_permission\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Cannot parse headers in Refer-To URI\\n\");\n\t\t\tlog_file->write_raw(r->hdr_refer_to.uri.encode());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\n\t\n\tui->cb_call_referred(user_config, i, r);\n\t\n\tlines[i]->invite(pu, \n\t\tr->hdr_refer_to.uri.copy_without_headers(),\n\t\tr->hdr_refer_to.display, \"\", r->hdr_referred_by, \n\t\thdr_replaces, hdr_require, t_hdr_request_disposition(),\n\t\thide_user);\n\tlines[i]->open_dialog->is_referred_call = true;\n\t\n\tMEMMAN_DELETE(incoming_refer_data);\n\tdelete incoming_refer_data;\n\tincoming_refer_data = NULL;\n\t\n\treturn;\n}\n\nvoid t_phone::recvd_info(t_request *r, t_tid tid) {\n\tt_response *resp;\n\tlist <string> unsupported;\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r)) {\n\t\t\tt_user *user_config = lines[i]->get_user();\n\t\t\tassert(user_config);\n\n\t\t\tif (!user_config->check_required_ext(r, unsupported))\n\t\t\t{\n\t\t\t\t// Not all required extensions are supported\n\t\t\t\tresp = r->create_response(R_420_BAD_EXTENSION);\n\t\t\t\tresp->hdr_unsupported.set_features(unsupported);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\treturn;\n\t\t\t}\t\t\t\n\t\t\n\t\t\tlines[i]->recvd_info(r, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tresp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tsend_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone::recvd_message(t_request *r, t_tid tid) {\n\tif (r->hdr_to.tag ==\"\") {\n\t\t// Out-of-dialog MESSAGE\n\t\tt_phone_user *pu = find_phone_user_out_dialog_request(r, tid);\n\t\tif (pu) {\n\t\t\tpu->recvd_message(r, tid);\n\t\t}\t\t\n\t} else {\n\t\t// In-dialog MESSAGE\n\t\tt_line *l = find_line_in_dialog_request(r, tid);\n\t\tif (l) {\n\t\t\tl->recvd_message(r, tid);\n\t\t}\n\t}\n}\n\nvoid t_phone::post_process_request(t_request *r, t_tid cancel_tid, t_tid target_tid) {\n\tcleanup_dead_lines();\n\tmove_releasing_lines_to_background();\n\tcleanup_3way();\n}\n\n\nvoid t_phone::failure(t_failure failure, t_tid tid) {\n\t// TODO\n}\n\nvoid t_phone::recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->match(r, tuid)) {\n\t\t\tlines[i]->recvd_stun_resp(r, tuid, tid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// out-of-dialog STUN responses\n\thandle_response_out_of_dialog(r, tuid);\n}\n\nvoid t_phone::handle_event_timeout(t_event_timeout *e) {\n\tt_timer\t\t\t*t = e->get_timer();\n\tt_tmr_phone\t\t*tmr_phone;\n\tt_tmr_line\t\t*tmr_line;\n\tt_tmr_subscribe\t\t*tmr_subscribe;\n\tt_tmr_publish\t\t*tmr_publish;\n\tt_object_id\t\tline_id;\n\t\n\tswitch (t->get_type()) {\n\tcase TMR_PHONE:\n\t\ttmr_phone = dynamic_cast<t_tmr_phone *>(t);\n\t\ttimeout(tmr_phone->get_phone_timer(), tmr_phone->get_object_id());\n\t\tbreak;\n\tcase TMR_LINE:\n\t\ttmr_line = dynamic_cast<t_tmr_line *>(t);\n\t\tline_timeout(tmr_line->get_line_id(), tmr_line->get_line_timer(), \n\t\t\ttmr_line->get_dialog_id());\n\t\tbreak;\n\tcase TMR_SUBSCRIBE:\n\t\ttmr_subscribe = dynamic_cast<t_tmr_subscribe *>(t);\n\t\tline_id = tmr_subscribe->get_line_id();\n\t\tif (line_id == 0) {\n\t\t\tsubscription_timeout(tmr_subscribe->get_subscribe_timer(), \n\t\t\t\ttmr_subscribe->get_object_id());\n\t\t} else {\t\n\t\t\tline_timeout_sub(line_id, tmr_subscribe->get_subscribe_timer(),\n\t\t\t\ttmr_subscribe->get_dialog_id(),\n\t\t\t\ttmr_subscribe->get_sub_event_type(), \n\t\t\t\ttmr_subscribe->get_sub_event_id());\n\t\t}\n\t\tbreak;\n\tcase TMR_PUBLISH:\n\t\ttmr_publish = dynamic_cast<t_tmr_publish *>(t);\n\t\tpublication_timeout(tmr_publish->get_publish_timer(), \n\t\t\ttmr_publish->get_object_id());\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_phone::line_timeout(t_object_id id, t_line_timer timer, t_object_id did) {\n\t// If there is no line with id anymore, then the timer expires\n\t// silently.\n\tt_line *line = get_line_by_id(id);\n\tif (line) {\n\t\tline->timeout(timer, did);\n\t}\n}\n\nvoid t_phone::line_timeout_sub(t_object_id id, t_subscribe_timer timer, t_object_id did,\n\t\tconst string &event_type, const string &event_id)\n{\n\t// If there is no line with id anymore, then the timer expires\n\t// silently.\n\tt_line *line = get_line_by_id(id);\n\tif (line) {\n\t\tline->timeout_sub(timer, did, event_type, event_id);\n\t}\n}\n\nvoid t_phone::subscription_timeout(t_subscribe_timer timer, t_object_id id_timer)\n{\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t\t     i != phone_users.end(); i++)\n\t{\n\t\tif ((*i)->match_subscribe_timer(timer, id_timer)) {\n\t\t\t(*i)->timeout_sub(timer, id_timer);\n\t\t}\n\t}\n}\n\nvoid t_phone::publication_timeout(t_publish_timer timer, t_object_id id_timer) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t\t     i != phone_users.end(); i++)\n\t{\n\t\tif ((*i)->match_publish_timer(timer, id_timer)) {\n\t\t\t(*i)->timeout_publish(timer, id_timer);\n\t\t}\n\t}\n}\n\nvoid t_phone::timeout(t_phone_timer timer, unsigned short id_timer) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\tswitch (timer) {\n\tcase PTMR_REGISTRATION:\n\t\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t\t     i != phone_users.end(); ++i)\n\t\t{\n\t\t\tif ((*i)->id_registration == id_timer) {\n\t\t\t\t(*i)->timeout(timer);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTMR_NAT_KEEPALIVE:\n\t\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t\t     i != phone_users.end(); ++i)\n\t\t{\n\t\t\tif ((*i)->id_nat_keepalive == id_timer) {\n\t\t\t\t(*i)->timeout(timer);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTMR_TCP_PING:\n\t\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t\t     i != phone_users.end(); ++i)\n\t\t{\n\t\t\tif ((*i)->id_tcp_ping == id_timer) {\n\t\t\t\t(*i)->timeout(timer);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_phone::handle_broken_connection(t_event_broken_connection *e) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\n\t// Find the phone user that was associated with the connection.\n\t// This phone user has to handle the event.\n\tt_phone_user *pu = find_phone_user(e->get_user_uri());\n\tif (pu) {\n\t\tpu->handle_broken_connection();\n\t} else {\n\t\tlog_file->write_header(\"t_phone::handle_broken_connection\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Cannot find active phone user \");\n\t\tlog_file->write_raw(e->get_user_uri().encode());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t}\n}\n\n\n///////////\n// Public\n///////////\n\nt_phone::t_phone() : t_transaction_layer(), lines(NUM_CALL_LINES) {\n\tis_active = true;\n\tactive_line = 0;\n\n\t// Create phone lines\n\tfor (unsigned short i = 0; i < NUM_CALL_LINES; i++) {\n\t\tlines[i] = new t_line(this, i);\n\t\tMEMMAN_NEW(lines[i]);\n\t}\n\n\t// Initialize 3-way conference data\n\tis_3way = false;\n\tline1_3way = NULL;\n\tline2_3way = NULL;\n\t\n\tincoming_refer_data = NULL;\n\t\n\tstruct timeval t;\n\tgettimeofday(&t, NULL);\n\tstartup_time = t.tv_sec;\n\t\n\t// NOTE: The RTP ports for the lines are initialized after the\n\t// system settings have been read.\n}\n\nt_phone::~t_phone() {\n\t// Delete phone lines\n\tlog_file->write_header(\"t_phone::~t_phone\");\n\tlog_file->write_raw(\"Number of lines to cleanup: \");\n\tlog_file->write_raw(lines.size());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tif (incoming_refer_data) {\n\t\tMEMMAN_DELETE(incoming_refer_data);\n\t\tdelete incoming_refer_data;\n\t}\n\t\n\tfor (unsigned short i = 0; i < lines.size(); i++) {\n\t\tMEMMAN_DELETE(lines[i]);\n\t\tdelete lines[i];\n\t}\n\t\n\tt_rwmutex_writer x(phone_users_mtx);\n\t// Delete all phone users\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); i++)\n\t{\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n}\n\nvoid t_phone::pub_invite(t_user *user, \n\t\tconst t_url &to_uri, const string &to_display,\n\t\tconst string &subject, bool anonymous)\n{\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tinvite(pu, to_uri, to_display, subject, false, anonymous);\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_invite\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_answer(void) {\n\tlock();\n\tanswer();\n\tunlock();\n}\n\nvoid t_phone::pub_reject(void) {\n\tlock();\n\treject();\n\tunlock();\n}\n\nvoid t_phone::pub_reject(unsigned short line) {\n\tlock();\n\treject(line);\n\tunlock();\n}\n\nvoid t_phone::pub_redirect(const list<t_display_url> &destinations, int code, string reason)\n{\n\tlock();\n\tredirect(destinations, code, reason);\n\tunlock();\n}\n\nvoid t_phone::pub_end_call(void) {\n\tlock();\n\tend_call();\n\tunlock();\n}\n\nvoid t_phone::pub_registration(t_user *user,\n\t\tt_register_type register_type,\n\t\tunsigned long expires)\n{\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tregistration(pu, register_type, expires);\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_registration\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_options(t_user *user,\n\t\tconst t_url &to_uri, const string &to_display) \n{\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\toptions(pu, to_uri, to_display);\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_options\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_options(void) {\n\tlock();\n\toptions();\n\tunlock();\n}\n\nbool t_phone::pub_hold(void) {\n\tlock();\n\tbool retval = hold();\n\tunlock();\n\treturn retval;\n}\n\nvoid t_phone::pub_retrieve(void) {\n\tlock();\n\tretrieve();\n\tunlock();\n}\n\nvoid t_phone::pub_refer(const t_url &uri, const string &display) {\n\tlock();\n\trefer(uri, display);\n\tunlock();\n}\n\nvoid t_phone::pub_setup_consultation_call(const t_url &uri, const string &display) {\n\tlock();\n\tsetup_consultation_call(uri, display);\n\tunlock();\n}\n\nvoid t_phone::pub_refer(unsigned short lineno_from, unsigned short lineno_to) {\n\tlock();\n\trefer(lineno_from, lineno_to);\n\tunlock();\n}\n\nvoid t_phone::mute(bool enable) {\n\tlock();\n\n\t// In a 3-way call, both lines must be muted\n\tif (is_3way && (\n\t    active_line == line1_3way->get_line_number() ||\n\t    active_line == line2_3way->get_line_number()))\n\t{\n\t\tline1_3way->mute(enable);\n\t\tline2_3way->mute(enable);\n\t}\n\telse\n\t{\n\t\tt_rwmutex_reader x(lines_mtx);\n\t\tlines[active_line]->mute(enable);\n\t}\n\n\tunlock();\n}\n\nvoid t_phone::pub_activate_line(unsigned short l) {\n\tlock();\n\tactivate_line(l);\n\tunlock();\n}\n\nvoid t_phone::pub_send_dtmf(char digit, bool inband, bool info) {\n\tlock();\n\tsend_dtmf(digit, inband, info);\n\tunlock();\n}\n\nbool t_phone::pub_seize(void) {\n\tbool retval;\n\tt_rwmutex_reader x(lines_mtx);\n\t\n\tretval = lines[active_line]->seize();\n\t\n\treturn retval;\n}\n\nbool t_phone::pub_seize(unsigned short line) {\n\tassert(line < NUM_USER_LINES);\n\tbool retval;\n\tt_rwmutex_reader x(lines_mtx);\n\t\n\tretval = lines[line]->seize();\n\t\n\treturn retval;\n}\n\nvoid t_phone::pub_unseize(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->unseize();\n}\n\nvoid t_phone::pub_unseize(unsigned short line) {\n\tassert(line < NUM_USER_LINES);\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[line]->unseize();\n}\n\nvoid t_phone::pub_confirm_zrtp_sas(unsigned short line) {\n\tassert(line < NUM_USER_LINES);\n\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[line]->confirm_zrtp_sas();\n}\n\nvoid t_phone::pub_confirm_zrtp_sas(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->confirm_zrtp_sas();\n}\n\nvoid t_phone::pub_reset_zrtp_sas_confirmation(unsigned short line) {\n\tassert(line < NUM_USER_LINES);\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[line]->reset_zrtp_sas_confirmation();\n}\n\nvoid t_phone::pub_reset_zrtp_sas_confirmation(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->reset_zrtp_sas_confirmation();\n}\n\nvoid t_phone::pub_enable_zrtp(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->enable_zrtp();\n}\n\nvoid t_phone::pub_zrtp_request_go_clear(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[active_line]->zrtp_request_go_clear();\n}\n\nvoid t_phone::pub_zrtp_go_clear_ok(unsigned short line) {\n\tassert(line < NUM_USER_LINES);\n\tt_rwmutex_reader x(lines_mtx);\n\tlines[line]->zrtp_go_clear_ok();\n}\n\nvoid t_phone::pub_subscribe_mwi(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tpu->subscribe_mwi();\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_subscribe_mwi\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_unsubscribe_mwi(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tpu->unsubscribe_mwi();\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_unsubscribe_mwi\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_subscribe_presence(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tpu->subscribe_presence();\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_subscribe_presence\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_unsubscribe_presence(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tpu->unsubscribe_presence();\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_unsubscribe_presence\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_publish_presence(t_user *user, t_presence_state::t_basic_state basic_state) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tpu->publish_presence(basic_state);\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_publish_presence\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nvoid t_phone::pub_unpublish_presence(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tpu->unpublish_presence();\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_publish_presence\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_footer();\n\t}\n}\n\nbool t_phone::pub_send_message(t_user *user, const t_url &to_uri, const string &to_display,\n\t\tconst t_msg &msg)\n{\n\tbool retval = true;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tretval = pu->send_message(to_uri, to_display, msg);\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_send_message\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tretval = false;\n\t}\n\t\n\treturn retval;\n}\n\nbool t_phone::pub_send_im_iscomposing(t_user *user, const t_url &to_uri, const string &to_display,\n\t\t\tconst string &state, time_t refresh)\n{\n\tbool retval = true;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\t\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tretval = pu->send_im_iscomposing(to_uri, to_display, state, refresh);\n\t} else {\n\t\tlog_file->write_header(\"t_phone::pub_send_im_iscomposing\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"User profile not active: \");\n\t\tlog_file->write_raw(user->get_profile_name());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tretval = false;\n\t}\n\t\n\treturn retval;\n}\n\nt_phone_state t_phone::get_state(void) const {\n\tt_rwmutex_reader x(lines_mtx);\n\tfor (unsigned short i = 0; i < NUM_USER_LINES; i++) {\n\t\tif (lines[i]->get_state() == LS_IDLE) {\n\n\t\t\treturn PS_IDLE;\n\t\t}\n\t}\n\n\t// All lines are busy, so the phone is busy.\n\treturn PS_BUSY;\n}\n\nbool t_phone::all_lines_idle(void) const {\n\tt_rwmutex_reader x(lines_mtx);\n\tfor (unsigned short i = 0; i < NUM_USER_LINES; i++) {\n\t\tif (lines[i]->get_substate() != LSSUB_IDLE) {\n\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// All lines are idle\n\treturn true;\n}\n\nbool t_phone::get_idle_line(unsigned short &lineno) const {\n\tt_rwmutex_reader x(lines_mtx);\n\t\n\tbool found_idle_line = false;\n\tfor (unsigned short i = 0; i < NUM_USER_LINES; i++) {\n\t\tif (lines[i]->get_substate() == LSSUB_IDLE) {\n\t\t\tlineno = i;\n\t\t\tfound_idle_line = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\treturn found_idle_line;\n}\n\nvoid t_phone::set_active_line(unsigned short l) {\n\tt_rwmutex_reader x(lines_mtx);\n\tassert (l < NUM_USER_LINES);\n\tactive_line = l;\n}\n\nunsigned short t_phone::get_active_line(void) const {\n\treturn active_line;\n}\n\nt_line *t_phone::get_line_by_id(t_object_id id) const {\n\tt_rwmutex_reader x(lines_mtx);\n\tfor (size_t i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->get_object_id() == id) {\n\t\t\treturn lines[i];\n\t\t}\n\t}\n\t\n\treturn NULL;\n}\n\nt_line *t_phone::get_line(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\treturn lines[lineno];\n}\n\nbool t_phone::authorize(t_user *user, t_request *r, t_response *resp) \n{\n\tbool result = false;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->authorize(r, resp);\n\t\n\treturn result;\n}\n\nvoid t_phone::remove_cached_credentials(t_user *user, const string &realm) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) pu->remove_cached_credentials(realm);\n}\n\nbool t_phone::get_is_registered(t_user *user) {\n\tbool result = false;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->get_is_registered();\n\t\n\treturn result;\n}\n\nbool t_phone::get_last_reg_failed(t_user *user) {\n\tbool result = false;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->get_last_reg_failed();\n\t\n\treturn result;\n}\n\nt_line_state t_phone::get_line_state(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tt_line_state s = get_line(lineno)->get_state();\n\treturn s;\n}\n\nt_line_substate t_phone::get_line_substate(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tt_line_substate s = get_line(lineno)->get_substate();\n\treturn s;\n}\n\nbool t_phone::is_line_on_hold(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->get_is_on_hold();\n\treturn b;\n}\n\nbool t_phone::is_line_muted(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->get_is_muted();\n\treturn b;\n}\n\nbool t_phone::is_line_transfer_consult(unsigned short lineno,\n\t\tunsigned short &transfer_from_line) const \n{\n\tassert(lineno < lines.size());\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->get_is_transfer_consult(transfer_from_line);\n\treturn b;\t\n}\n\nbool t_phone::line_to_be_transferred(unsigned short lineno, \n\t\tunsigned short &transfer_to_line) const\n{\n\tassert(lineno < lines.size());\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->get_to_be_transferred(transfer_to_line);\n\treturn b;\n}\n\nbool t_phone::is_line_encrypted(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->get_is_encrypted();\n\treturn b;\n}\n\nbool t_phone::is_line_auto_answered(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->get_auto_answer();\n\treturn b;\n}\n\nt_refer_state t_phone::get_line_refer_state(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tt_refer_state s = get_line(lineno)->get_refer_state();\n\treturn s;\n}\n\nt_user *t_phone::get_line_user(unsigned short lineno) {\n\tassert(lineno < lines.size());\n\tt_rwmutex_reader x(lines_mtx);\n\tt_user *user = get_line(lineno)->get_user();\n\treturn user;\n}\n\nbool t_phone::has_line_media(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tbool b = get_line(lineno)->has_media();\n\treturn b;\n}\n\nbool t_phone::is_mwi_subscribed(t_user *user) const {\n\tbool result = false;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->is_mwi_subscribed();\n\t\n\treturn result;\n}\n\nbool t_phone::is_mwi_terminated(t_user *user) const {\n\tbool result = false;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->is_mwi_terminated();\n\t\n\treturn result;\n}\n\nt_mwi t_phone::get_mwi(t_user *user) const {\n\tt_mwi result;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->mwi;\n\t\n\treturn result;\n}\n\nbool t_phone::is_presence_terminated(t_user *user) const {\n\tbool result = false;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) result = pu->is_presence_terminated();\n\t\n\treturn result;\n}\n\nt_url t_phone::get_remote_uri(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tt_url uri = get_line(lineno)->get_remote_uri();\n\treturn uri;\n}\n\nstring t_phone::get_remote_display(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\t\n\tt_rwmutex_reader x(lines_mtx);\n\tstring display = get_line(lineno)->get_remote_display();\n\treturn display;\n}\n\nbool t_phone::part_of_3way(unsigned short lineno) {\n\tt_mutex_guard x(mutex_3way);\n\n\tif (!is_3way) {\n\t\treturn false;\n\t}\n\n\tif (line1_3way->get_line_number() == lineno) {\n\t\treturn true;\n\t}\n\n\tif (line2_3way->get_line_number() == lineno) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nt_line *t_phone::get_3way_peer_line(unsigned short lineno) {\n\tt_mutex_guard x(mutex_3way);\n\n\tif (!is_3way) {\n\t\treturn NULL;\n\t}\n\n\tif (line1_3way->get_line_number() == lineno) {\n\t\treturn line2_3way;\n\t}\n\n\treturn line1_3way;\n}\n\nbool t_phone::join_3way(unsigned short lineno1, unsigned short lineno2) {\n\tassert(lineno1 < NUM_USER_LINES);\n\tassert(lineno2 < NUM_USER_LINES);\n\n\tt_mutex_guard x3(mutex_3way);\n\tt_rwmutex_reader x(lines_mtx);\n\n\t// Check if there isn't a 3-way already\n\tif (is_3way) {\n\t\treturn false;\n\t}\n\n\t// Both lines must have a call.\n\tif (lines[lineno1]->get_substate() != LSSUB_ESTABLISHED ||\n\t    lines[lineno2]->get_substate() != LSSUB_ESTABLISHED)\n\t{\n\t\treturn false;\n\t}\n\n\t// One of the lines must be on-hold\n\tt_line *held_line, *talking_line;\n\tif (lines[lineno1]->get_is_on_hold()) {\n\t\theld_line = lines[lineno1];\n\t\ttalking_line = lines[lineno2];\n\t} else if (lines[lineno2]->get_is_on_hold()) {\n\t\theld_line = lines[lineno2];\n\t\ttalking_line = lines[lineno1];\n\t} else {\n\t\treturn false;\n\t}\n\n\t// Set 3-way data\n\tis_3way = true;\n\tline1_3way = talking_line;\n\tline2_3way = held_line;\n\n\t// The user may have put both lines on-hold. In this case the\n\t// talking line is on-hold too!\n\tif (talking_line->get_is_on_hold()) {\n\t\t// Retrieve the held call\n\t\t// As the 3-way indication (is_3way) is set, the audio sessions\n\t\t// will automatically connect to each other.\n\t\ttalking_line->retrieve();\n\t} else {\t\n\t\t// Start the 3-way on the talking line\n\t\tt_audio_session *as_talking = talking_line->get_audio_session();\n\t\tif (as_talking) as_talking->start_3way();\n\t}\n\n\t// Retrieve the held call\n\theld_line->retrieve();\n\n\treturn true;\n}\n\nvoid t_phone::notify_refer_progress(t_response *r, unsigned short referee_lineno) {\n\tt_rwmutex_reader x(lines_mtx);\n\n\tif (lines[LINENO_REFERRER]->get_state() != LS_IDLE) {\n\t\tlines[LINENO_REFERRER]->notify_refer_progress(r);\n\n\t\tif (!lines[LINENO_REFERRER]->active_dialog ||\n\t\t    lines[LINENO_REFERRER]->active_dialog->get_state() != DS_CONFIRMED)\n\t\t{\n\t\t\t// The call to the referrer has already been\n\t\t\t// terminated.\n\t\t\treturn;\n\t\t}\n\n\t\tif (r->is_final()) {\n\t\t\tif (r->is_success()) {\n\t\t\t\t// Reference was successful, end the call with\n\t\t\t\t// with the referrer.\n\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\"t_phone::notify_refer_progress\");\n\t\t\t\tlog_file->write_raw(\n\t\t\t\t\t\"Call to refer-target succeeded.\\n\");\n\t\t\t\tlog_file->write_raw(\n\t\t\t\t\t\"End call with referrer.\\n\");\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\t\tlines[LINENO_REFERRER]->end_call();\n\t\t\t} else {\n\t\t\t\t// Reference failed, retrieve the call with the\n\t\t\t\t// referrer.\n\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\"t_phone::notify_refer_progress\");\n\t\t\t\tlog_file->write_raw(\n\t\t\t\t\t\"Call to refer-target failed.\\n\");\n\t\t\t\tlog_file->write_raw(\n\t\t\t\t\t\"Restore call with referrer.\\n\");\n\t\t\t\tlog_file->write_footer();\n\n\t\t\t\t// Retrieve the parked line\n\t\t\t\tt_line *l = lines[referee_lineno];\n\t\t\t\tlines[referee_lineno] = lines[LINENO_REFERRER];\n\t\t\t\tlines[referee_lineno]->line_number = referee_lineno;\n\t\t\t\tlines[LINENO_REFERRER] = l;\n\t\t\t\tlines[LINENO_REFERRER]->line_number = LINENO_REFERRER;\n\t\t\t\t\n\t\t\t\t// Retrieve the call if the line is active\n\t\t\t\tif (referee_lineno == active_line) {\n\t\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\t\"Retrieve call with referrer.\",\n\t\t\t\t\t\t\"t_phone::notify_refer_progress\");\n\t\t\t\t\tlines[referee_lineno]->retrieve();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tt_user *user_config = lines[referee_lineno]->get_user();\n\t\t\t\tassert(user_config);\n\t\t\t\t\n\t\t\t\tui->cb_retrieve_referrer(user_config, referee_lineno);\n\t\t\t}\n\t\t}\n\t}\n}\n\nt_call_info t_phone::get_call_info(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tt_call_info call_info = get_line(lineno)->get_call_info();\n\treturn call_info;\n}\n\nt_call_record t_phone::get_call_hist(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tt_call_record call_hist = get_line(lineno)->call_hist_record;\n\treturn call_hist;\n}\n\nstring t_phone::get_ringtone(unsigned short lineno) const {\n\tassert(lineno < lines.size());\n\n\tt_rwmutex_reader x(lines_mtx);\n\tstring ringtone = get_line(lineno)->get_ringtone();\n\treturn ringtone;\n}\n\ntime_t t_phone::get_startup_time(void) const {\n\treturn startup_time;\n}\n\nvoid t_phone::init_rtp_ports(void) {\n\tt_rwmutex_reader x(lines_mtx);\n\tfor (size_t i = 0; i < lines.size(); i++) {\n\t\tlines[i]->init_rtp_port();\n\t}\n}\n\nbool t_phone::add_phone_user(const t_user &user_config, t_user **dup_user) {\n\t\n\tt_phone_user *existing_phone_user = NULL;\n\tt_rwmutex_writer x(phone_users_mtx);\n\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); i++)\n\t{\n\t\tt_user *user = (*i)->get_user_profile();\n\t\t\n\t\t// If the profile is already added, then just activate it.\n\t\tif (user->get_profile_name() == user_config.get_profile_name())\n\t\t{\t\n\t\t\texisting_phone_user = (*i);\n\t\t\t// Continue checking to see if activating this user\n\t\t\t// does not conflict with another already active user.\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\t// Check if there is already another profile for the same\n\t\t// user.\n\t\tif (user->get_name() == user_config.get_name() &&\n\t\t    user->get_domain() == user_config.get_domain() &&\n\t\t    (*i)->is_active())\n\t\t{\n\t\t\t*dup_user = user;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Check if there is already another profile having\n\t\t// the same contact name.\n\t\tif (user->get_contact_name() == user_config.get_contact_name() &&\n\t\t    phone->get_ip_sip_locked(user, AUTO_IP4_ADDRESS) == phone->get_ip_sip_locked(&user_config, AUTO_IP4_ADDRESS) &&\n\t\t    (*i)->is_active())\n\t\t{\n\t\t\t*dup_user = user;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Activate existing profile\n\tif (existing_phone_user) {\n\t\tif (!existing_phone_user->is_active()) {\n\t\t\texisting_phone_user->activate(user_config);\n\t\t}\n\n\t\treturn true;\n\t}\n\t\n\t// Add the user\n\tt_phone_user *pu = new t_phone_user(user_config);\n\tMEMMAN_NEW(pu);\n\tphone_users.push_back(pu);\n\t\n\treturn true;\n}\n\nvoid t_phone::remove_phone_user(const t_user &user_config) {\n\tt_rwmutex_writer x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user_config.get_profile_name());\n\tif (pu) pu->deactivate();\n}\n\nlist<t_user *> t_phone::ref_users(void) {\n\tlist<t_user *> l;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); i++)\n\t{\n\t\tif (!(*i)->is_active()) continue;\n\t\tl.push_back((*i)->get_user_profile());\n\t}\n\t\n\treturn l;\n}\n\nt_user *t_phone::ref_user_display_uri(const string &display_uri) {\n\tt_user *u = NULL;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); i++)\n\t{\n\t\tif (!(*i)->is_active()) continue;\n\t\tif ((*i)->get_user_profile()->get_display_uri() == display_uri) {\n\t\t\tu = (*i)->get_user_profile();\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\treturn u;\n}\n\nt_user *t_phone::ref_user_profile(const string &profile_name) {\n\tt_user *u = NULL;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(profile_name);\n\tif (pu) u = pu->get_user_profile();\n\t\n\treturn u;\n}\n\nt_service *t_phone::ref_service(t_user *user) {\n\tassert(user);\n\tt_service *srv = NULL;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) srv = pu->service;\n\t\n\treturn srv;\n}\n\nt_buddy_list *t_phone::ref_buddy_list(t_user *user) {\n\tassert(user);\n\tt_buddy_list *l = NULL;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) l = pu->get_buddy_list();\n\t\n\treturn l;\n}\n\nt_presence_epa *t_phone::ref_presence_epa(t_user *user) {\n\tassert(user);\n\tt_presence_epa *epa = NULL;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) epa = pu->get_presence_epa();\n\t\n\treturn epa;\n}\n\nstring t_phone::get_ip_sip(const t_user *user, const string &auto_ip) const {\n\tt_rwmutex_reader x(phone_users_mtx);\n\treturn get_ip_sip_locked(user, auto_ip);\n}\n\nstring t_phone::get_ip_sip_locked(const t_user *user, const string &auto_ip) const {\n\tstring result;\n\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tresult = pu->get_ip_sip(auto_ip);\n\t} else {\n\t\tresult = LOCAL_IP;\n\t}\n\t\n\tif (result == AUTO_IP4_ADDRESS) result = auto_ip;\n\t\n\treturn result;\n}\n\nunsigned short t_phone::get_public_port_sip(const t_user *user) const {\n\tunsigned short result;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tresult = pu->get_public_port_sip();\n\t} else {\n\t\tresult = sys_config->get_sip_port();\n\t}\n\t\n\treturn result;\n}\n\nbool t_phone::use_stun(t_user *user) {\n\tbool result;\n\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tresult = pu->use_stun;\n\t} else {\n\t\tresult = false;\n\t}\n\t\n\treturn result;\n}\n\nbool t_phone::use_nat_keepalive(t_user *user) {\n\tbool result;\n\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tresult = pu->use_nat_keepalive;\n\t} else {\n\t\tresult = false;\n\t}\n\t\n\treturn result;\t\n}\n\nvoid t_phone::disable_stun(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) pu->use_stun = false;\n}\n\nvoid t_phone::sync_nat_keepalive(t_user *user) {\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) pu->sync_nat_keepalive();\n}\n\nbool t_phone::stun_discover_nat(list<string> &msg_list) {\n\tbool retval = true;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tfor (list<t_phone_user *>::iterator i = phone_users.begin();\n\t     i != phone_users.end(); ++i)\n\t{\n\t\tif (!(*i)->is_active()) continue;\n\t\tt_user *user_config = (*i)->get_user_profile();\n\t\t\n\t\tif (user_config->get_sip_transport() == SIP_TRANS_UDP ||\n\t\t    user_config->get_sip_transport() == SIP_TRANS_AUTO)\n\t\t{\n\t\t\tif (user_config->get_use_stun())\n\t\t\t{\n\t\t\t\tstring msg;\n\t\t\t\tif (!::stun_discover_nat(*i, msg)) {\n\t\t\t\t\tstring s(\"User profile: \");\n\t\t\t\t\ts + user_config->get_profile_name();\n\t\t\t\t\ts += \"\\n\\n\";\n\t\t\t\t\ts += msg;\n\t\t\t\t\tmsg_list.push_back(s);\n\t\t\t\t\tretval = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t(*i)->use_nat_keepalive = user_config->get_enable_nat_keepalive();\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn retval;\n}\n\nbool t_phone::stun_discover_nat(t_user *user, string &msg) {\n\tbool retval = true;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tif (user->get_sip_transport() == SIP_TRANS_UDP ||\n\t    user->get_sip_transport() == SIP_TRANS_AUTO)\n\t{\n\t\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\t\tif (user->get_use_stun()) {\n\t\t\tif (pu) retval = ::stun_discover_nat(pu, msg);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (pu) pu->use_nat_keepalive = user->get_enable_nat_keepalive();\n\t\t}\n\t}\n\t\n\treturn retval;\n}\n\nt_response *t_phone::create_options_response(t_user *user, t_request *r,\n\t\t\t\t\tbool in_dialog)\n{\n\tt_response *resp;\n\t\n\tt_rwmutex_reader x(phone_users_mtx);\n\tt_phone_user *pu = find_phone_user(user->get_profile_name());\n\tif (pu) {\n\t\tresp = pu->create_options_response(r, in_dialog);\n\t} else {\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t}\n\t\n\treturn resp;\n}\n\nvoid t_phone::init(void) {\n\t\n\tlist<t_user *> user_list = ref_users();\n\t\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++)\n\t{\n\t\t// Automatic registration at startup if requested\n\t\tif ((*i)->get_register_at_startup()) {\n\t\t\tpub_registration(*i, REG_REGISTER, DUR_REGISTRATION(*i));\n\t\t} else {\n\t\t\t// No registration will be done, so initialize extensions now.\n\t\t\tinit_extensions(*i);\n\t\t}\n\t\t\n\t\t// NOTE: Extension initialization is done after registration. \n\t\t//       This way STUN will have set the correct\n\t\t//       IP adres (STUN is done as part of registration.)\n\t}\n}\n\nvoid t_phone::init_extensions(t_user *user_config) {\n\t// Subscribe to MWI\n\tif (user_config->get_mwi_solicited()) {\n\t\tpub_subscribe_mwi(user_config);\n\t}\n\t\n\t// Publish presence\n\tif (user_config->get_pres_publish_startup()) {\n\t\tpub_publish_presence(user_config, t_presence_state::ST_BASIC_OPEN);\n\t}\n\t\n\t// Subscribe to presence\n\tpub_subscribe_presence(user_config);\n}\n\nbool t_phone::set_sighandler(void) const {\n\tstruct sigaction sa;\n\tmemset(&sa, 0, sizeof(sa));\n\n\tsa.sa_handler = phone_sighandler;\n\tsa.sa_flags = SA_RESTART;\n\tif (sigaction (SIGCHLD, &sa, NULL) < 0) return false;\n\tif (sigaction (SIGTERM, &sa, NULL) < 0) return false;\n\tif (sigaction (SIGINT, &sa, NULL) < 0) return false;\n\t\n\treturn true;\n}\n\n\nvoid t_phone::terminate(void) {\n\tstring msg;\n\t\n\tlines_mtx.lockRead();\n\t// Clear all lines\n\tlog_file->write_report(\"Clear all lines.\",\n\t\t\"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\tfor (size_t i = 0; i < NUM_CALL_LINES; i++) {\n\t\tswitch (lines[i]->get_substate()) {\n\t\tcase LSSUB_IDLE:\n\t\tcase LSSUB_RELEASING:\n\t\t\tbreak;\n\t\tcase LSSUB_SEIZED:\n\t\t\tlines[i]->unseize();\n\t\t\tbreak;\n\t\tcase LSSUB_INCOMING_PROGRESS:\n\t\t\tui->cb_stop_call_notification(i);\n\t\t\tlines[i]->reject();\n\t\t\tbreak;\n\t\tcase LSSUB_OUTGOING_PROGRESS:\n\t\t\tui->cb_stop_call_notification(i);\n\t\t\t// Fall thru\n\t\tcase LSSUB_ANSWERING:\n\t\tcase LSSUB_ESTABLISHED:\n\t\t\tlines[i]->end_call();\n\t\t\tbreak;\n\t\t}\n\t}\n\tlines_mtx.unlock();\n\t\n\t// Deactivate phone\n\tis_active = false;\n\t\n\t// De-register all registered users.\n\tlist<t_user *> user_list = ref_users();\n\tui->cb_display_msg(\"Deregistering phone...\");\n\tfor (list<t_user *>::iterator i = user_list.begin();\n\t     i != user_list.end(); i++)\n\t{\n\t\t// Unsubscribe MWI\n\t\tif (is_mwi_subscribed(*i)) {\n\t\t\tmsg = (*i)->get_profile_name();\n\t\t\tmsg += \": Unsubscribe MWI.\";\n\t\t\tlog_file->write_report(msg,\n\t\t\t\t\"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tpub_unsubscribe_mwi(*i);\n\t\t}\n\t\t\n\t\t// Unpublish presence\n\t\tpub_unpublish_presence(*i);\n\t\t\n\t\t// Unsubscribe presence\n\t\tpub_unsubscribe_presence(*i);\n\t\t\n\t\t// De-register\n\t\tif (get_is_registered(*i)) {\n\t\t\tmsg = (*i)->get_profile_name();\n\t\t\tmsg += \": Deregister.\";\n\t\t\tlog_file->write_report(msg,\n\t\t\t\t\"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\t\t\tpub_registration(*i, REG_DEREGISTER);\n\t\t}\n\t}\n\t\n\t// Wait till phone is deregistered.\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++)\n\t{\n\t\twhile (get_is_registered(*i)) {\n\t\t\tsleep(1);\n\t\t}\n\t\tmsg = (*i)->get_profile_name();\n\t\tmsg += \": Registration terminated.\";\n\t\tlog_file->write_report(msg, \"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\t}\n\t\n\t// Wait for MWI unsubscription\n\tint mwi_wait = 0;\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++)\n\t{\n\t\twhile (!is_mwi_terminated(*i) && mwi_wait <= DUR_UNSUBSCRIBE_GUARD/1000) {\n\t\t\tsleep(1);\n\t\t\tmwi_wait++;\n\t\t}\n\t\tmsg = (*i)->get_profile_name();\n\t\tmsg += \": MWI subscription terminated.\";\n\t\tlog_file->write_report(msg, \"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\t}\n\t\n\t// Wait for presence unsubscription\n\tint presence_wait = 0;\n\tfor (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++)\n\t{\n\t\twhile (!is_presence_terminated(*i) && presence_wait <= DUR_UNSUBSCRIBE_GUARD/1000) {\n\t\t\tsleep(1);\n\t\t\tpresence_wait++;\n\t\t}\n\t\tmsg = (*i)->get_profile_name();\n\t\tmsg += \": presence subscriptions terminated.\";\n\t\tlog_file->write_report(msg, \"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\t}\n\t\t\n\t// Wait till all lines are idle\n\tlog_file->write_report(\"Waiting for all lines to become idle.\",\n\t\t\"t_phone::terminate\", LOG_NORMAL, LOG_DEBUG);\n\tint dur = 0;\n\twhile (dur < QUIT_IDLE_WAIT) {\n\t\tif (all_lines_idle()) break;\n\t\tsleep(1);\n\t\tdur++;\n\t}\n\t\t\n\t// Force lines to idle state if they could not be cleared\n\t// gracefully\n\tlines_mtx.lockRead();\n\tfor (size_t i = 0; i < lines.size(); i++) {\n\t\tif (lines[i]->get_substate() != LSSUB_IDLE) {\n\t\t\tmsg = \"Force line %1 to idle state.\";\n\t\t\tmsg = replace_first(msg, \"%1\", int2str(i));\n\t\t\tlog_file->write_report(msg, \"t_phone::terminate\", \n\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlines[i]->force_idle();\n\t\t}\n\t}\n\tlines_mtx.unlock();\n\t\n\tlog_file->write_report(\"Finished phone termination.\",\n\t\t\"t_phone::terminate\",  LOG_NORMAL, LOG_DEBUG);\n}\n\nvoid *phone_uas_main(void *arg) {\n\tphone->run();\n\treturn NULL;\n}\n\nvoid *phone_sigwait(void *arg) {\n\tsigset_t\tsigset;\n\tint\t\tsig;\n\tint\t\tchild_status;\n\tpid_t\t\tpid;\n\n\tsigemptyset(&sigset);\n\tsigaddset(&sigset, SIGINT);\n\tsigaddset(&sigset, SIGTERM);\n\tsigaddset(&sigset, SIGCHLD);\n\n\twhile (true) {\n\t\t// When SIGCONT is received after SIGSTOP, sigwait returns\n\t\t// with EINTR ??\n\t\tif (sigwait(&sigset, &sig) == EINTR) continue;\n\t\t\n\t\tswitch (sig) {\n\t\tcase SIGINT:\n\t\t\tlog_file->write_report(\"SIGINT received.\", \"::phone_sigwait\");\n\t\t\tui->cmd_quit();\n\t\t\treturn NULL;\n\t\tcase SIGTERM:\n\t\t\tlog_file->write_report(\"SIGTERM received.\", \"::phone_sigwait\");\n\t\t\tui->cmd_quit();\n\t\t\treturn NULL;\n\t\tcase SIGCHLD:\n\t\t\t// Cleanup terminated child process\n\t\t\tpid = wait(&child_status);\n\t\t\tlog_file->write_header(\"::phone_sigwait\");\n\t\t\tlog_file->write_raw(\"SIGCHLD received.\\n\");\n\t\t\tlog_file->write_raw(\"Pid \");\n\t\t\tlog_file->write_raw((int)pid);\n\t\t\tlog_file->write_raw(\" terminated.\\n\");\n\t\t\tlog_file->write_footer();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tlog_file->write_header(\"::phone_sigwait\", LOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unexpected signal (\");\n\t\t\tlog_file->write_raw(sig);\n\t\t\tlog_file->write_raw(\") received.\\n\");\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\n}\n\nvoid phone_sighandler(int sig) {\n\tint\t\tchild_status;\n\tpid_t\t\tpid;\n\t\n\t// Minimal processing should be done in a signal handler.\n\t// No I/O should be performed.\n\tswitch (sig) {\n\tcase SIGINT:\n\t\t// Post a quit command instead of executing it. As executing\n\t\t// involves a lock that may lead to a deadlock.\n\t\tui->cmd_quit_async();\n\t\tbreak;\n\tcase SIGTERM:\n\t\tui->cmd_quit_async();\n\t\tbreak;\n\tcase SIGCHLD:\n\t\t// Cleanup terminated child process\n\t\tpid = wait(&child_status);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "src/phone.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _PHONE_H\n#define _PHONE_H\n\n#include <list>\n#include <string>\n#include <vector>\n#include <sys/time.h>\n#include \"auth.h\"\n#include \"call_history.h\"\n#include \"dialog.h\"\n#include \"id_object.h\"\n#include \"phone_user.h\"\n#include \"protocol.h\"\n#include \"service.h\"\n#include \"transaction_layer.h\"\n#include \"im/msg_session.h\"\n#include \"mwi/mwi.h\"\n#include \"sockets/url.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"presence/presence_state.h\"\n\n// Number of phone lines\n// One line is used by Twinkle internally to park the call towards a\n// referrer while the refer is in progress.\n// Besides the lines for making calls, ephemeral lines will be created\n// for parking calls that are being released. By parking a releasing\n// call, the line visible to the user is free for making new calls.\n#define NUM_CALL_LINES\t3\t// Total numbers of phone lines for making calls\n#define NUM_USER_LINES\t2\t// #lines usable for the user\n\n#define LINENO_REFERRER\t2\t// Internal lineno for referrer\n\n// Number of seconds to wait till all lines are idle when terminating\n// Twinkle\n#define QUIT_IDLE_WAIT\t2\n\nusing namespace std;\nusing namespace im;\n\n// Forward declarations\nclass t_dialog;\nclass t_client_request;\nclass t_line;\nclass t_call_info;\n\nenum t_phone_state {\n\tPS_IDLE,\n\tPS_BUSY\n};\n\nenum t_line_state {\n\tLS_IDLE,\n\tLS_BUSY\n};\n\nenum t_line_substate {\n\t// Idle sub states\n\tLSSUB_IDLE,\t\t\t// line is idle\n\tLSSUB_SEIZED,\t\t\t// user has seized the line to call\n\n\t// Busy sub states\n\tLSSUB_INCOMING_PROGRESS,\t// incoming call in progress\n\tLSSUB_OUTGOING_PROGRESS,\t// outgoing call in progress\n\tLSSUB_ANSWERING,\t\t// sent 200 OK, waiting for ACK\n\tLSSUB_ESTABLISHED,\t\t// call established\n\tLSSUB_RELEASING\t\t\t// call is being released (BYE sent)\n};\n\nclass t_transfer_data {\nprivate:\n\t// The received REFER request\n\tt_request\t*refer_request;\n\t\n\t// Line number on which REFER was received\n\tunsigned short\tlineno;\n\t\n\t// Indicates if triggered INVITE must be anonymous\n\tbool\t\thide_user;\n\t\n\tt_phone_user\t*phone_user;\n\t\npublic:\n\tt_transfer_data(t_request *r, unsigned short _lineno, bool _hide_user, \n\t\tt_phone_user *pu);\n\t~t_transfer_data();\n\t\n\tt_request *get_refer_request(void) const;\n\tunsigned short get_lineno(void) const;\n\tbool get_hide_user(void) const;\n\tt_phone_user *get_phone_user(void) const;\n};\n\nclass t_phone : public t_transaction_layer {\nprivate:\n\t// Indicates if the phone is active, accepting calls.\n\tbool\t\t\tis_active;\n\n\t// Phone users\n\tlist<t_phone_user *>\tphone_users;\n\tmutable t_rwmutex phone_users_mtx;\n\n\t// Phone lines\n\t// The first NUM_CALL_LINES are for making phone calls.\n\t// The tail of the vector is for releasing lines in the background.\n\tvector<t_line *>\tlines;\n\tmutable t_rwmutex lines_mtx;\n\n\t// Operations like invite, end_call work on the active line\n\tunsigned short\t\tactive_line;\n\n\t// 3-way conference data\n\tbool\t\t\tis_3way;\t// indicates an acitive 3-way\n\tt_line\t\t\t*line1_3way;\t// first line in 3-way conf\n\tt_line\t\t\t*line2_3way;\t// second line in 3-way conf\n\tmutable t_recursive_mutex mutex_3way;\n\t\n\t// Call transfer data. When a REFER comes in, the user has\n\t// to give permission before the triggered INVITE can be sent.\n\t// While the user interface presents the question to the user,\n\t// the data related to the incoming REFER is stored here.\n\tt_transfer_data\t\t*incoming_refer_data;\n\t\n\t// Time of startup\n\ttime_t\t\t\tstartup_time;\n\t\n\t// Line release operations\n\t// Move a line to the background so it will be released in the\n\t// background.\n\tvoid move_line_to_background(unsigned short lineno);\n\t\n\t// Move all call lines that are in releasing state to the\n\t// background.\n\tvoid move_releasing_lines_to_background(void);\n\t\n\t// Destroy lines in the background that are idle.\n\tvoid cleanup_dead_lines(void);\n\t\n\t// If a line was part of a 3way, then remove it from the\n\t// 3way conference data.\n\tvoid cleanup_3way_state(unsigned short lineno);\n\t\n\t// If one of the lines of a 3way calls has become idle, then\n\t// cleanup the 3way conference data.\n\tvoid cleanup_3way(void);\n\n\t/** @name Actions */\n\t//@{\n\t/**\n\t * Send an INVITE\n\t * @param pu The phone user making this call.\n\t * @param to_uri The URI to be used a request-URI and To header URI\n\t * @param to_display Display name for To header.\n\t * @param subject If not empty, this string will go into the Subject header.\n\t * @param no_fork If true, put a no-fork request disposition in the outgoing INVITE\n\t * @param anonymous Inidicates if the INVITE should be sent anonymous.\n\t */\n\tvoid invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,\n\t\tconst string &subject, bool no_fork, bool anonymous);\n\t\t\n\tvoid answer(void);\n\tvoid redirect(const list<t_display_url> &destinations, int code, string reason = \"\");\n\tvoid reject(void);\n\tvoid reject(unsigned short line);\n\tvoid end_call(void);\n\tvoid registration(t_phone_user *pu, t_register_type register_type,\n\t\t\t\t\tunsigned long expires = 0);\n\t//@}\n\n\t// OPTIONS outside dialog\n\tvoid options(t_phone_user *pu, const t_url &to_uri, const string &to_display = \"\");\n\n\t// OPTIONS inside dialog\n\tvoid options(void);\n\n\tbool hold(bool rtponly = false); // returns false is call cannot be put on hold\n\tvoid retrieve(void);\n\n\t// Transfer a call (send REFER to far-end)\n\tvoid refer(const t_url &uri, const string &display);\n\t\n\t// Call transfer with consultation (attended)\n\t// Transfer the far-end on line lineno_from to the far-end of lineno_to.\n\tvoid refer(unsigned short lineno_from, unsigned short lineno_to);\n\tvoid refer_attended(unsigned short lineno_from, unsigned short lineno_to);\n\tvoid refer_consultation(unsigned short lineno_from, unsigned short lineno_to);\n\t\n\t// Setup a consultation call for transferring the call on the active\n\t// line. The active line is put on-hold and the consultation call is\n\t// made on an idle line.\n\tvoid setup_consultation_call(const t_url &uri, const string &display);\n\n\t// Make line l active. If the current line is busy, then that call\n\t// will be put on-hold. If line l has a call on-hold, then that\n\t// call will be retrieved.\n\tvoid activate_line(unsigned short l);\n\n\t// Send a DTMF digit\n\tvoid send_dtmf(char digit, bool inband, bool info);\n\n\tvoid set_active_line(unsigned short l);\n\n\t// Handle responses for out-of-dialog requests\n\tvoid handle_response_out_of_dialog(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid handle_response_out_of_dialog(StunMessage *r, t_tuid tuid);\n\t\n\t// Match an incoming message to a phone user\n\tt_phone_user *match_phone_user(t_response *r, t_tuid tuid, bool active_only = false);\n\tt_phone_user *match_phone_user(t_request *r, bool active_only = false);\n\tt_phone_user *match_phone_user(StunMessage *r, t_tuid tuid, bool active_only = false);\n\t\n\t/**\n\t * Hunt for an idle line to hande an incoming call.\n\t * @return The number of the line to handle the call (starting at 0).\n\t * @return -1 if there is no line to handle the call.\n\t */\n\tint hunt_line(void);\n\nprotected:\n\t/**\n\t * Find a phone user that can handle an out-of-dialog request.\n\t * If there is no phone user that can handle the request, then this\n\t * method will send an appropriate failure response on the request.\n\t * @param r [in] The request.\n\t * @param tid [in] Transaction id of the request transaction.\n\t * @return The phone user, if there is a phone user that can handle the request.\n\t * @return NULL, otherwise.\n\t */\n\tt_phone_user *find_phone_user_out_dialog_request(t_request *r, t_tid tid);\n\t\n\t/**\n\t * Find a line that can handle an in-dialog request.\n\t * If there is no line that can handle the request, then this\n\t * method will send an appropriate failure response on the request.\n\t * @param r [in] The request.\n\t * @param tid [in] Transaction id of the request transaction.\n\t * @return The line, if there is a line that can handle the request.\n\t * @return NULL, otherwise.\n\t */\n\tt_line *find_line_in_dialog_request(t_request *r, t_tid tid);\n\n\t// Variation of get_ip_sip() for when phone_users_mtx is already locked\n\tstring get_ip_sip_locked(const t_user *user, const string &auto_ip) const;\n\t// Events\n\tvoid recvd_provisional(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_success(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_redirect(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_client_error(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_server_error(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_global_error(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid post_process_response(t_response *r, t_tuid tuid, t_tid tid);\n\n\tvoid recvd_invite(t_request *r, t_tid tid);\n\tvoid recvd_initial_invite(t_request *r, t_tid tid);\n\tvoid recvd_re_invite(t_request *r, t_tid tid);\n\tvoid recvd_ack(t_request *r, t_tid tid);\n\tvoid recvd_cancel(t_request *r, t_tid cancel_tid, t_tid target_tid);\n\tvoid recvd_bye(t_request *r, t_tid tid);\n\tvoid recvd_options(t_request *r, t_tid tid);\n\tvoid recvd_register(t_request *r, t_tid tid);\n\tvoid recvd_prack(t_request *r, t_tid tid);\n\tvoid recvd_subscribe(t_request *r, t_tid tid);\n\tvoid recvd_notify(t_request *r, t_tid tid);\n\tvoid recvd_refer(t_request *r, t_tid tid);\n\tvoid recvd_info(t_request *r, t_tid tid);\n\tvoid recvd_message(t_request *r, t_tid tid);\n\tvoid post_process_request(t_request *r, t_tid cancel_tid, t_tid target_tid);\n\n\tvoid failure(t_failure failure, t_tid tid);\n\t\n\tvoid recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid);\n\t\n\tvoid recvd_refer_permission(bool permission);\n\t\n\t/** @name Timeout handlers */\n\t//@{\n\tvirtual void handle_event_timeout(t_event_timeout *e);\n\t\n\t/**\n\t * Process expiry of line timer.\n\t * @param id [in] Line id of the line associate with the timer.\n\t * @param timer [in] Type of line timer.\n\t * @param did [in] Dialog id if timer is for a dialog, 0 otherwise.\n\t */\n\tvoid line_timeout(t_object_id id, t_line_timer timer, t_object_id did);\n\t\n\t/**\n\t * Process expiry of a line subscription timer (REFER subscription).\n\t * @param id [in] Line id of the line associate with the timer.\n\t * @param timer [in] Type of subcription timer.\n\t * @param did [in] Dialog id associated with the timer.\n\t * @param event_type [in] Event type of the subscription.\n\t * @param event_id [in] Event id of the subscription.\n\t */\n\tvoid line_timeout_sub(t_object_id id, t_subscribe_timer timer, t_object_id did,\n\t\tconst string &event_type, const string &event_id);\n\t\t\n\t/**\n\t * Process expiry of a subcription timer.\n\t * @param timer [in] Type of subcription timer.\n\t * @param id_timer [in] Timer id of expired timer.\n\t */\n\tvoid subscription_timeout(t_subscribe_timer timer, t_object_id id_timer);\n\t\n\t/**\n\t * Process expiry of a publication timer.\n\t * @param timer [in] Type of publication timer.\n\t * @param id_timer [in] Timer id of expired timer.\n\t */\n\tvoid publication_timeout(t_publish_timer timer, t_object_id id_timer);\n\t\n\t/**\n\t * Process expiry of phone timer.\n\t * @param timer [in] Type of phone timer.\n\t * @param id_timer [in] Timer id of expired timer.\n\t */\n\tvoid timeout(t_phone_timer timer, unsigned short id_timer);\n\t//@}\n\t\n\tvirtual void handle_broken_connection(t_event_broken_connection *e);\n\npublic:\n\tt_phone();\n\tvirtual ~t_phone();\n\t\n\t// Get line based on object id\n\t// Returns NULL if there is no such line.\n\tt_line *get_line_by_id(t_object_id id) const;\n\t\n\t// Get line based on line number\n\tt_line *get_line(unsigned short lineno) const;\n\n\t// Get busy/idle state of the phone\n\t// PS_IDLE - at least one line is idle\n\t// PS_BUSY - all lines are busy\n\tt_phone_state get_state(void) const;\n\t\n\t// Returns true if all lines are in the LSSUB_IDLE state\n\tbool all_lines_idle(void) const;\n\t\n\t// Get an idle user line.\n\t// If no line is idle, then false is returned.\n\tbool get_idle_line(unsigned short &lineno) const;\n\t\n\t// Actions to be called by the user interface.\n\t// These methods first lock the phone, then call the corresponding\n\t// private method and then unlock the phone.\n\t// The private methods should only be called by the phone, line,\n\t// and dialog objects to avoid deadlocks.\n\tvoid pub_invite(t_user *user,\n\t\tconst t_url &to_uri, const string &to_display,\n\t\tconst string &subject, bool anonymous);\n\tvoid pub_answer(void);\n\tvoid pub_reject(void);\n\tvoid pub_reject(unsigned short line);\n\tvoid pub_redirect(const list<t_display_url> &destinations, int code, string reason = \"\");\n\tvoid pub_end_call(void);\n\tvoid pub_registration(t_user *user, t_register_type register_type,\n\t\t\t\t\t\tunsigned long expires = 0);\n\tvoid pub_options(t_user *user, \n\t\t\tconst t_url &to_uri, const string &to_display = \"\");\n\tvoid pub_options(void);\n\tbool pub_hold(void);\n\tvoid pub_retrieve(void);\n\tvoid pub_refer(const t_url &uri, const string &display);\n\tvoid pub_refer(unsigned short lineno_from, unsigned short lineno_to);\n\tvoid pub_setup_consultation_call(const t_url &uri, const string &display);\n\tvoid mute(bool enable);\n\t\n\tvoid pub_activate_line(unsigned short l);\n\tvoid pub_send_dtmf(char digit, bool inband, bool info);\n\t\n\t// ZRTP actions\n\tvoid pub_confirm_zrtp_sas(unsigned short line);\n\tvoid pub_confirm_zrtp_sas(void);\n\tvoid pub_reset_zrtp_sas_confirmation(unsigned short line);\n\tvoid pub_reset_zrtp_sas_confirmation(void);\n\tvoid pub_enable_zrtp(void);\n\tvoid pub_zrtp_request_go_clear(void);\n\tvoid pub_zrtp_go_clear_ok(unsigned short line);\n\n\t// Join 2 lines in a 3-way conference. Returns false if 3-way cannot\n\t// be setup\n\tbool join_3way(unsigned short lineno1, unsigned short lineno2);\n\n\t// Seize the line.\n\t// Returns false if seizure failed.\n\tbool pub_seize(void); // active line\n\tbool pub_seize(unsigned short line);\n\t\n\t// Unseize the line\n\tvoid pub_unseize(void); // active line\n\tvoid pub_unseize(unsigned short line);\n\t\n\t/** @name MWI */\n\t//@{\n\t/**\n\t * Subscribe to MWI.\n\t * @param user [in] The user profile of the subscribing user.\n\t */\n\tvoid pub_subscribe_mwi(t_user *user);\n\t\n\t/**\n\t * Unsubscribe to MWI.\n\t * @param user [in] The user profile of the unsubscribing user.\n\t */\n\tvoid pub_unsubscribe_mwi(t_user *user);\n\t//@}\n\t\n\t/** @name Presence */\n\t//@{\n\t/**\n\t * Subscribe to presence of buddies in buddy list.\n\t  * @param user [in] The user profile of the subscribing user.\n\t */\n\tvoid pub_subscribe_presence(t_user *user);\n\t\n\t/**\n\t * Unsubscribe to presence of buddies in buddy list.\n\t * @param user [in] The user profile of the unsubscribing user.\n\t */\n\tvoid pub_unsubscribe_presence(t_user *user);\n\t\n\t/**\n\t * Publish presence state.\n\t * @param user [in] The user profile of the user publishing.\n\t * @param basic_state [in] The basic presence state to publish.\n\t */\n\tvoid pub_publish_presence(t_user *user, t_presence_state::t_basic_state basic_state);\n\t\n\t/**\n\t * Unpublish presence state.\n\t * @param user [in] The user profile of the user unpublishing.\n\t */\n\tvoid pub_unpublish_presence(t_user *user);\n\t//@}\n\t\n\t/** @name Instant messaging */\n\t//@{\n\t/**\n\t * Send a message.\n\t * @param user [in] User profile of user sending the message.\n\t * @param to_uri [in] Destination URI of recipient.\n\t * @param to_display [in] Display name of recipient.\n\t * @param msg [in] Message to send.\n\t * @return True if sending succeeded, false otherwise.\n\t */\n\tbool pub_send_message(t_user *user, const t_url &to_uri, const string &to_display,\n\t\t\tconst t_msg &msg);\n\t\t\t\n\t/**\n\t * Send a message composing state indication.\n\t * @param user [in] User profile of user sending the message.\n\t * @param to_uri [in] Destination URI of recipient.\n\t * @param to_display [in] Display name of recipient.\n\t * @param state [in] Message composing state.\n\t * @param refresh [in] The refresh interval in seconds (when state is active).\n\t * @return True if sending succeeded, false otherwise.\n\t * @note For the idle state, the value of refresh has no meaning.\n\t */\n\tbool pub_send_im_iscomposing(t_user *user, const t_url &to_uri, const string &to_display,\n\t\t\tconst string &state, time_t refresh);\n\t//@}\n\n\tunsigned short get_active_line(void) const;\n\n\t// Authorize the request based on the challenge in the response\n\t// Returns false if authorization fails.\n\tbool authorize(t_user *user, t_request *r, t_response *resp);\n\t\n\t// Remove cached credentials for a particular user/realm\n\tvoid remove_cached_credentials(t_user *user, const string &realm);\n\n\tbool get_is_registered(t_user *user);\n\tbool get_last_reg_failed(t_user *user);\n\tt_line_state get_line_state(unsigned short lineno) const;\n\tt_line_substate get_line_substate(unsigned short lineno) const;\n\tbool is_line_on_hold(unsigned short lineno) const;\n\tbool is_line_muted(unsigned short lineno) const;\n\tbool is_line_transfer_consult(unsigned short lineno, \n\t\tunsigned short &transfer_from_line) const;\n\tbool line_to_be_transferred(unsigned short lineno, \n\t\tunsigned short &transfer_to_line) const;\n\tbool is_line_encrypted(unsigned short lineno) const;\n\tbool is_line_auto_answered(unsigned short lineno) const;\n\tt_refer_state get_line_refer_state(unsigned short lineno) const;\n\tt_user *get_line_user(unsigned short lineno);\n\tbool has_line_media(unsigned short lineno) const;\n\tbool is_mwi_subscribed(t_user *user) const;\n\tbool is_mwi_terminated(t_user *user) const;\n\tt_mwi get_mwi(t_user *user) const;\n\t\n\t/**\n\t * Check if all presence subscriptions for a particular user are terminated.\n\t * @param user [in] User profile of the user.\n\t * @return True if all presence susbcriptions are terminated, otherwise false.\n\t */\n\tbool is_presence_terminated(t_user *user) const;\n\t\n\t// Get remote uri/display of the active call on a line.\n\t// If there is no call, then an empty uri/display is returned.\n\tt_url get_remote_uri(unsigned short lineno) const;\n\tstring get_remote_display(unsigned short lineno) const;\n\n\t// Return if a line is part of a 3-way conference\n\tbool part_of_3way(unsigned short lineno);\n\n\t// Get the peer line in a 3-way conference\n\tt_line *get_3way_peer_line(unsigned short lineno);\n\n\t// Notify progress of a reference. r is the response to the INVITE\n\t// caused by a REFER. referee_lineno is the line number of the line\n\t// that is setting up there reference call.\n\tvoid notify_refer_progress(t_response *r, unsigned short referee_lineno);\n\n\t// Get call info record for a line.\n\tt_call_info get_call_info(unsigned short lineno) const;\n\t\n\t// Get the call history record for a line\n\tt_call_record get_call_hist(unsigned short lineno) const;\n\t\n\t// Get ring tone for a line\n\tstring get_ringtone(unsigned short lineno) const;\n\t\n\t// Get the startup time of the phone\n\ttime_t get_startup_time(void) const;\n\n\t// Initialize the RTP port values for all lines.\n\tvoid init_rtp_ports(void);\n\t\n\t/**\n\t * Add a phone user.\n\t * @param user_config [in] User profile of the user to add.\n\t * @param dup_user [out] Profile of duplicate user.\n\t * @return false, if there is already a phone user with the same name\n\t * and domain. In this case dup_user is a pointer to the user config\n\t * of that user.\n\t * @return true, if the phone user was added successfully.\n\t * @note if there is already a user with exactly the same user config\n\t * then true is returned, but the user is not added again. The user\n\t * will be activated if it was inactive though.\n\t */\n\tbool add_phone_user(const t_user &user_config, t_user **dup_user);\n\t\n\t/**\n\t * Deactivate the phone user.\n\t * @param user_config [in] User profile of the user to deactivate.\n\t */\n\tvoid remove_phone_user(const t_user &user_config);\n\n\t/**\n\t * Get a list of user profiles of all phone users.\n\t * @return List of user profiles.\n\t */\n\tlist<t_user *> ref_users(void);\n\t\n\t/**\n\t * Get the user profile of a user for which user->get_display_uri() ==\n\t * display_uri.\n\t * @param display_uri [in] Display URI.\n\t * @return User profile.\n\t */\n\tt_user *ref_user_display_uri(const string &display_uri);\n\t\n\t/**\n\t * Get the user profile matching the profile name.\n\t * @param profile_name [in] User profile name.\n\t * @return User profile.\n\t */\n\tt_user *ref_user_profile(const string &profile_name);\n\t\n\t/**\n\t * Get service information for a phone user.\n\t * @param user [in] User profile of the phone user.\n\t * @return Service object.\n\t */\n\tt_service *ref_service(t_user *user);\n\t\n\t/**\n\t * Get the buddy list of a phone user.\n\t * @param user [in] User profile of the phone user.\n\t * @return Buddy list.\n\t */\n\tt_buddy_list *ref_buddy_list(t_user *user);\n\t\n\t/**\n\t * Get the presence event publication agent of a phone user.\n\t * @param user [in] User profile of the phone user.\n\t * @return The presence EPA.\n\t */\n\tt_presence_epa *ref_presence_epa(t_user *user);\n\t\n\t/**\n\t * Find active phone user\n\t * @param profile_name [in] User profile name.\n\t * @return The phone user for the user profile, NULL if there is no active phone user.\n\t */\n\tt_phone_user *find_phone_user(const string &profile_name) const;\n\t\n\t/** \n\t * Find active phone user\n\t * @param user_uri [in] The user URI (AoR) of the user to find.\n\t * @return The phone user for the URI, NULL if there is no active phone user.\n\t */\n\tt_phone_user *find_phone_user(const t_url &user_uri) const;\n\t\n\t/**\n\t * Get local IP address for SIP.\n\t * @param user [in] The user profile of the user for whom to get the IP address.\n\t * @param auto_ip [in] IP address to use if no IP address has been determined through\n\t *                     some NAT procedure.\n\t * @return The IP address.\n\t */\n\tstring get_ip_sip(const t_user *user, const string &auto_ip) const;\n\t\n\t/**\n\t * Get local port for SIP.\n\t * @param user [in] User profile for user for whom to get the port.\n\t * @return SIP port.\n\t */ \n\tunsigned short get_public_port_sip(const t_user *user) const;\n\t\n\t/** Indicates if STUN is used. */\n\tbool use_stun(t_user *user);\n\t\n\t// Indicates if a NAT keepalive mechanism is used\n\tbool use_nat_keepalive(t_user *user);\n\t\n\t/** Disable STUN for a user. */\n\tvoid disable_stun(t_user *user);\n\t\n\t/** Synchronize sending of NAT keep alives with user configuration settings. */\n\tvoid sync_nat_keepalive(t_user *user);\n\t\n\t// Perform NAT discovery for all users having STUN enabled.\n\t// If NAT discovery indicates that STUN cannot be used for 1 or more\n\t// users, then false will be returned and msg_list contains a list\n\t// of messages to be shown to the user.\n\tbool stun_discover_nat(list<string> &msg_list);\n\t\n\t// Perform NAT discovery for a single user.\n\tbool stun_discover_nat(t_user *user, string &msg);\n\t\n\t// Create a response to an OPTIONS request\n\t// Argument 'in-dialog' indicates if the OPTIONS response is\n\t// sent within a dialog.\n\tt_response *create_options_response(t_user *user, t_request *r,\n\t\t\t\t\tbool in_dialog = false);\n\t\t\t\t\t\n\t// Timer operations\n\tvoid start_timer(t_phone_timer timer, t_phone_user *pu);\n\tvoid stop_timer(t_phone_timer timer, t_phone_user *pu);\n\n\t// Start a timer with the time set in the time-argument.\n\tvoid start_set_timer(t_phone_timer timer, long time, t_phone_user *pu);\n\t\n\t/**\n\t * Initialize the phone functions.\n\t * Register all active users with auto register.\n\t * Initialize extensions for users without auto register.\n\t */\n\tvoid init(void);\n\t\n\t/**\n\t * Initialize SIP extensions like MWI and presence.\n\t * @param user_config [in] User for which the extensions must be initialized.\n\t */\n\tvoid init_extensions(t_user *user_config);\n\t\n\t/**\n\t * Set the signal handler to handler for LinuxThreads.\n\t * @return True if successful, false otherwise.\n\t */\n\tbool set_sighandler(void) const;\n\t\n\t/**\n\t * Terminate the phone functions.\n\t * Release all calls, don't accept any new calls.\n\t * Deregister all active users.\n\t */\n\tvoid terminate(void);\n};\n\n// Main function for the UAS part of the phone\nvoid *phone_uas_main(void *arg);\n\n// Entry function of thread catching signals to terminate\n// the application in a graceful manner if NPLT is used.\nvoid *phone_sigwait(void *arg);\n\n// Signal handler to process signals if LinuxThreads is used.\nvoid phone_sighandler(int sig);\n\n#endif\n"
  },
  {
    "path": "src/phone_user.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"phone_user.h\"\n#include \"log.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"im/im_iscomposing_body.h\"\n#include \"presence/presence_epa.h\"\n\nextern t_phone \t\t*phone;\nextern t_event_queue\t*evq_timekeeper;\nextern t_event_queue\t*evq_sender;\nextern string\t\tuser_host;\nextern string\t\tlocal_hostname;\n\nvoid t_phone_user::cleanup_mwi_dialog(void) {\n\tif (mwi_dialog && mwi_dialog->get_subscription_state() == SS_TERMINATED) {\n\t\tstring reason_termination = mwi_dialog->get_reason_termination();\n\t\tbool may_resubscribe = mwi_dialog->get_may_resubscribe();\n\t\tunsigned long dur_resubscribe = mwi_dialog->get_resubscribe_after();\n\t\t\n\t\tMEMMAN_DELETE(mwi_dialog);\n\t\tdelete mwi_dialog;\n\t\tmwi_dialog = NULL;\n\t\tstun_binding_inuse_mwi = false;\n\t\tcleanup_stun_data();\n\t\tcleanup_nat_keepalive();\n\t\t\n\t\tif (mwi_auto_resubscribe) {\n\t\t\tif (may_resubscribe) {\n\t\t\t\tif (dur_resubscribe > 0) {\n\t\t\t\t\tstart_resubscribe_mwi_timer(dur_resubscribe * 1000);\n\t\t\t\t} else {\n\t\t\t\t\tsubscribe_mwi();\n\t\t\t\t}\n\t\t\t} else if (reason_termination.empty()) {\n\t\t\t\tstart_resubscribe_mwi_timer(DUR_MWI_FAILURE * 1000);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid t_phone_user::cleanup_stun_data(void) {\n\tif (!use_stun) return;\n\t\n\tif (!stun_binding_inuse_registration &&\n\t    !stun_binding_inuse_mwi && stun_binding_inuse_presence == 0)\n\t{\n\t\tstun_public_ip_sip = 0;\n\t\tstun_public_port_sip = 0;\n\t}\n}\n\nvoid t_phone_user::cleanup_nat_keepalive(void) {\n\tif (register_ip_port.ipaddr == 0 && register_ip_port.port == 0 &&\n\t    !mwi_dialog)\n\t{\n\t\tif (id_nat_keepalive) phone->stop_timer(PTMR_NAT_KEEPALIVE, this);\n\t}\n}\n\nvoid t_phone_user::sync_nat_keepalive(void) {\n\tif (user_config->get_enable_nat_keepalive() && !id_nat_keepalive) {\n\t\tsend_nat_keepalive();\n\t\tphone->start_timer(PTMR_NAT_KEEPALIVE, this);\n\t}\n}\n\nvoid t_phone_user::cleanup_tcp_ping(void) {\n\tif (register_ip_port.ipaddr == 0 && register_ip_port.port == 0) {\n\t\tif (id_tcp_ping) phone->stop_timer(PTMR_TCP_PING, this);\n\t}\n}\n\nvoid t_phone_user::cleanup_registration_data(void) {\n\tregister_ip_port.ipaddr = 0;\n\tregister_ip_port.port = 0;\n\tstun_binding_inuse_registration = false;\n\tcleanup_stun_data();\n\tcleanup_nat_keepalive();\n\tcleanup_tcp_ping();\n}\n\nt_phone_user::t_phone_user(const t_user &profile)\n{\n\tuser_config = profile.copy();\n\t\n\tservice = new t_service(user_config);\n\tMEMMAN_NEW(service);\n\t\n\tbuddy_list = new t_buddy_list(this);\n\tMEMMAN_NEW(buddy_list);\n\t\n\tpresence_epa = new t_presence_epa(this);\n\tMEMMAN_NEW(presence_epa);\n\t\n\tstring err_msg;\n\tif (buddy_list->load(err_msg)) {\n\t\tlog_file->write_header(\"t_phone_user::t_phone_user\");\n\t\tlog_file->write_raw(user_config->get_profile_name());\n\t\tlog_file->write_raw(\": buddy list loaded.\\n\");\n\t\tlog_file->write_footer();\n\t} else {\n\t\tlog_file->write_header(\"t_phone_user::t_phone_user\", LOG_NORMAL, LOG_CRITICAL);\n\t\tlog_file->write_raw(user_config->get_profile_name());\n\t\tlog_file->write_raw(\": falied to load buddy list.\\n\");\n\t\tlog_file->write_raw(err_msg);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t}\n\t\n\tactive = true;\n\n\tr_options = NULL;\n\tr_register = NULL;\n\tr_deregister = NULL;\n\tr_query_register = NULL;\n\tr_message = NULL;\n\tr_stun = NULL;\n\t\n\t// Initialize registration data\n\t// Call-ID cannot be set here as user_host is not determined yet.\n\tregister_seqnr = NEW_SEQNR;\n\tis_registered = false;\n\tregister_ip_port.ipaddr = 0L;\n\tregister_ip_port.port = 0;\n\tlast_reg_failed = false;\n\t\n\t// Initialize STUN data\n\tstun_public_ip_sip = 0L;\n\tstun_public_port_sip = 0;\n\tstun_binding_inuse_registration = false;\n\tstun_binding_inuse_mwi = false;\n\tstun_binding_inuse_presence = 0;\n\tregister_after_stun = false;\n\tmwi_subscribe_after_stun = false;\n\tpresence_subscribe_after_stun = false;\n\tuse_stun = false;\n\tuse_nat_keepalive = false;\n\t\n\t// Timers\n\tid_registration = 0;\n\tid_nat_keepalive = 0;\n\tid_tcp_ping = 0;\n\tid_resubscribe_mwi = 0;\n\t\n\t// MWI\n\tmwi_dialog = NULL;\n\tmwi_auto_resubscribe = false;\n}\n\nt_phone_user::~t_phone_user() {\n\t// Stop timers\n\tif (id_registration) phone->stop_timer(PTMR_REGISTRATION, this);\n\tif (id_nat_keepalive) phone->stop_timer(PTMR_NAT_KEEPALIVE, this);\n\tif (id_tcp_ping) phone->stop_timer(PTMR_TCP_PING, this);\n\n\t// Delete pointers\n\tif (r_options) {\n\t\tMEMMAN_DELETE(r_options);\n\t\tdelete r_options;\n\t}\n\tif (r_register) {\n\t\tMEMMAN_DELETE(r_register);\n\t\tdelete r_register;\n\t}\n\tif (r_deregister) {\n\t\tMEMMAN_DELETE(r_deregister);\n\t\tdelete r_deregister;\n\t}\n\tif (r_query_register) {\n\t\tMEMMAN_DELETE(r_query_register);\n\t\tdelete r_query_register;\n\t}\n\tif (r_message) {\n\t\tMEMMAN_DELETE(r_message);\n\t\tdelete r_message;\n\t}\n\tif (r_stun) {\n\t\tMEMMAN_DELETE(r_stun);\n\t\tdelete r_stun;\n\t}\n\t\n\tfor (list<t_request *>::iterator it = pending_messages.begin();\n\t     it != pending_messages.end(); ++it)\n\t{\n\t\tMEMMAN_DELETE(*it);\n\t\tdelete *it;\n\t}\n\t\n\tif (mwi_dialog) {\n\t\tMEMMAN_DELETE(mwi_dialog);\n\t\tdelete mwi_dialog;\n\t}\n\t\n\tMEMMAN_DELETE(service);\n\tdelete service;\n\tMEMMAN_DELETE(presence_epa);\n\tdelete presence_epa;\n\tMEMMAN_DELETE(buddy_list);\n\tdelete buddy_list;\n\tbuddy_list = NULL;\n\tMEMMAN_DELETE(user_config);\n\tdelete user_config;\n}\n\nt_user *t_phone_user::get_user_profile(void) {\n\treturn user_config;\n}\n\nt_buddy_list *t_phone_user::get_buddy_list(void) {\n\treturn buddy_list;\n}\n\nt_presence_epa *t_phone_user::get_presence_epa(void) {\n\treturn presence_epa;\n}\n\nvoid t_phone_user::registration(t_register_type register_type, bool re_register,\n\t\tunsigned long expires)\n{\n\t// If STUN is enabled, then do a STUN query before registering to\n\t// determine the public IP address.\n\tif (register_type == REG_REGISTER && use_stun) {\n\t\tif (stun_public_ip_sip == 0) {\n\t\t\tsend_stun_request();\n\t\t\tregister_after_stun = true;\n\t\t\tregistration_time = expires;\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tstun_binding_inuse_registration = true;\n\t}\n\n\t// Stop registration timer for non-query request\n\tif (register_type != REG_QUERY) {\n\t\tphone->stop_timer(PTMR_REGISTRATION, this);\n\t}\n\n\t// Create call-id if no call-id is created yet\n\tif (register_call_id == \"\") {\n\t\tregister_call_id = NEW_CALL_ID(user_config);\n\t}\n\n\t// RFC 3261 10.2\n\t// Construct REGISTER request\n\n\tt_request *req = create_request(REGISTER, \n\t\t\tt_url(string(USER_SCHEME) + \":\" + user_config->get_domain()));\n\n\t// To\n\treq->hdr_to.set_uri(user_config->create_user_uri(false));\n\treq->hdr_to.set_display(user_config->get_display(false));\n\n\t//Call-ID\n\treq->hdr_call_id.set_call_id(register_call_id);\n\n\t// CSeq\n\treq->hdr_cseq.set_method(REGISTER);\n\treq->hdr_cseq.set_seqnr(++register_seqnr);\n\n\t// Contact\n        t_contact_param contact;\n\n        switch (register_type) {\n        case REG_REGISTER:\n        \t// URI\n                contact.uri.set_url(user_config->create_user_contact(false,\n                \t\th_ip2str(req->get_local_ip())));\n                \n                // Expires\n                if (expires > 0) {\n\t\t\tif (user_config->get_registration_time_in_contact()) {\n\t\t\t\tcontact.set_expires(expires);\n\t\t\t} else {\n\t\t\t\treq->hdr_expires.set_time(expires);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// q-value\n\t\tif (user_config->get_reg_add_qvalue()) {\n\t\t\tcontact.set_qvalue(user_config->get_reg_qvalue());\n\t\t}\n\n                req->hdr_contact.add_contact(contact);\n                break;\n        case REG_DEREGISTER:\n                contact.uri.set_url(user_config->create_user_contact(false,\n                \t\th_ip2str(req->get_local_ip())));\n \t\tif (user_config->get_registration_time_in_contact()) {\n\t\t\tcontact.set_expires(0);\n\t\t} else {\n\t\t\treq->hdr_expires.set_time(0);\n\t\t}\n                req->hdr_contact.add_contact(contact);\n                break;\n        case REG_DEREGISTER_ALL:\n                req->hdr_contact.set_any();\n                req->hdr_expires.set_time(0);\n                break;\n        default:\n                break;\n        }\n\n\t// Allow\n\tSET_HDR_ALLOW(req->hdr_allow, user_config);\n\n\t// Store request in the proper place\n\tt_tuid tuid;\n\n        switch(register_type) {\n        case REG_REGISTER:\n\t\t// Delete a possible pending registration request\n\t\tif (r_register) {\n\t\t\tMEMMAN_DELETE(r_register);\n\t\t\tdelete r_register;\n\t\t}\n                r_register = new t_client_request(user_config, req, 0);\n\t\tMEMMAN_NEW(r_register);\n                tuid = r_register->get_tuid();\n\n                // Store expiration time for re-registration.\n                registration_time = expires;\n                break;\n        case REG_QUERY:\n\t\t// Delete a possible pending query registration request\n\t\tif (r_query_register) {\n\t\t\tMEMMAN_DELETE(r_query_register);\n\t\t\tdelete r_query_register;\n\t\t}\n                r_query_register = new t_client_request(user_config, req, 0);\n\t\tMEMMAN_NEW(r_query_register);\n                tuid = r_query_register->get_tuid();\n                break;\n        case REG_DEREGISTER:\n        case REG_DEREGISTER_ALL:\n\t\t// Delete a possible pending de-registration request\n\t\tif (r_deregister) {\n\t\t\tMEMMAN_DELETE(r_deregister);\n\t\t\tdelete r_deregister;\n\t\t}\n                r_deregister = new t_client_request(user_config, req, 0);\n\t\tMEMMAN_NEW(r_deregister);\n                tuid = r_deregister->get_tuid();\n                break;\n        default:\n                assert(false);\n        }\n\n        // Send REGISTER\n        authorizor.set_re_register(re_register);\n\tui->cb_register_inprog(user_config, register_type);\n        phone->send_request(user_config, req, tuid);\n\tMEMMAN_DELETE(req);\n        delete req;\n}\n\nvoid t_phone_user::options(const t_url &to_uri, const string &to_display) {\n\t// RFC 3261 11.1\n\t// Construct OPTIONS request\n\n\tt_request *req = create_request(OPTIONS, to_uri);\n\n\t// To\n\treq->hdr_to.set_uri(to_uri);\n\treq->hdr_to.set_display(to_display);\n\n\t// Call-ID\n\treq->hdr_call_id.set_call_id(NEW_CALL_ID(user_config));\n\n\t// CSeq\n\treq->hdr_cseq.set_method(OPTIONS);\n\treq->hdr_cseq.set_seqnr(NEW_SEQNR);\n\n\t// Accept\n\treq->hdr_accept.add_media(t_media(\"application\",\"sdp\"));\n\n\t// Store and send request\n\t// Delete a possible pending options request\n\tif (r_options) {\n\t\tMEMMAN_DELETE(r_options);\n\t\tdelete r_options;\n\t}\n\tr_options = new t_client_request(user_config, req, 0);\n\tMEMMAN_NEW(r_options);\n\tphone->send_request(user_config, req, r_options->get_tuid());\n\tMEMMAN_DELETE(req);\n\tdelete req;\n}\n\nvoid t_phone_user::handle_response_out_of_dialog(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_client_request **current_cr;\n\tt_request *req;\n\tbool is_register = false;\n\tt_buddy *buddy;\n\t\n\tif (r_register && r_register->get_tuid() == tuid) {\n\t\tcurrent_cr = &r_register;\n\t\tis_register = true;\n\t} else if (r_deregister && r_deregister->get_tuid() == tuid) {\n\t\tcurrent_cr = &r_deregister;\n\t\tis_register = true;\n\t} else if (r_query_register && r_query_register->get_tuid() == tuid) {\n\t\tcurrent_cr = &r_query_register;\n\t\tis_register = true;\n\t} else if (r_options && r_options->get_tuid() == tuid) {\n\t\tcurrent_cr = &r_options;\n\t} else if (r_message && r_message->get_tuid() == tuid) {\n\t\tcurrent_cr = &r_message;\n\t} else if (mwi_dialog && mwi_dialog->match_response(r, tuid)) {\n\t\tmwi_dialog->recvd_response(r, tuid, tid);\n\t\tcleanup_mwi_dialog();\n\t\treturn;\n\t} else if (presence_epa && presence_epa->match_response(r, tuid)) {\n\t\tpresence_epa->recv_response(r, tuid, tid);\n\t\treturn;\n\t} else if (buddy_list->match_response(r, tuid, &buddy)) {\n\t\tbuddy->recvd_response(r, tuid, tid);\n\t\tif (buddy->must_delete_now()) buddy_list->del_buddy(*buddy);\n\t\treturn;\n\t} else {\n\t\t// Response does not match any pending request.\n\t\tlog_file->write_report(\"Response does not match any pending request.\",\n\t\t\t\"t_phone_user::handle_response_out_of_dialog\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\treturn;\n\t}\n\n\treq = (*current_cr)->get_request();\n\n\t// Authentication\n\tif (r->must_authenticate()) {\n\t\tif (authorize(req, r)) {\n\t\t\tresend_request(req, is_register, *current_cr);\n\t\t\treturn;\n\t\t}\n\n\t\t// Authentication failed\n\t\t// Handle the 401/407 as a normal failure response\n\t}\n\t\n\t// RFC 3263 4.3\n\t// Failover\n\tif (r->code == R_503_SERVICE_UNAVAILABLE) {\n\t\tif (req->next_destination()) {\n\t\t\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\t\"t_phone_user::handle_response_out_of_dialog\");\n\t\t\tresend_request(req, is_register, *current_cr);\n\t\t\treturn;\n\t\t}\t\t\t\n\t}\n\n\t// Redirect failed request if there is another destination\n\tif (r->get_class() > R_2XX && user_config->get_allow_redirection()) {\n\t\t// If the response is a 3XX response then add redirection\n\t\t// contacts\n\t\tif (r->get_class() == R_3XX  &&\n\t\t    r->hdr_contact.is_populated())\n\t\t{\n\t\t\t(*current_cr)->redirector.add_contacts(\n\t\t\t\t\tr->hdr_contact.contact_list);\n\t\t}\n\n\t\t// Get next destination\n\t\tt_contact_param contact;\n\t\tif ((*current_cr)->redirector.get_next_contact(contact)) {\n\t\t\t// Ask user for permission to redirect if indicated\n\t\t\t// by user config\n\t\t\tbool permission = true;\n\t\t\tif (user_config->get_ask_user_to_redirect()) {\n\t\t\t\tpermission = ui->cb_ask_user_to_redirect_request(\n\t\t\t\t\t\t\tuser_config,\n\t\t\t\t\t\t\tcontact.uri, contact.display,\n\t\t\t\t\t\t\tr->hdr_cseq.method);\n\t\t\t}\n\n\t\t\tif (permission) {\n\t\t\t\treq->uri = contact.uri;\n\t\t\t\treq->calc_destinations(*user_config);\n\t\t\t\tui->cb_redirecting_request(user_config, contact);\n\t\t\t\tresend_request(req, is_register, *current_cr);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t// REGISTER (register)\n\tif (r_register && r_register->get_tuid() == tuid) {\n\t\tbool re_register;\n\t\thandle_response_register(r, re_register);\n\t\tMEMMAN_DELETE(r_register);\n\t\tdelete r_register;\n\t\tr_register = NULL;\n\t\tif (re_register) registration(REG_REGISTER, authorizor.get_re_register(), \n\t\t\t\tregistration_time);\n\t\treturn;\n\t}\n\n\t// REGISTER (de-register)\n\tif (r_deregister && r_deregister->get_tuid() == tuid) {\n\t\thandle_response_deregister(r);\n\t\tMEMMAN_DELETE(r_deregister);\n\t\tdelete r_deregister;\n\t\tr_deregister = NULL;\n\t\treturn;\n\t}\n\n\t// REGISTER (query)\n\tif (r_query_register && r_query_register->get_tuid() == tuid) {\n\t\thandle_response_query_register(r);\n\t\tMEMMAN_DELETE(r_query_register);\n\t\tdelete r_query_register;\n\t\tr_query_register = NULL;\n\t\treturn;\n\t}\n\n\n\t// OPTIONS\n\tif (r_options && r_options->get_tuid() == tuid) {\n\t\thandle_response_options(r);\n\t\tMEMMAN_DELETE(r_options);\n\t\tdelete r_options;\n\t\tr_options = NULL;\n\t\treturn;\n\t}\n\t\n\t// MESSAGE\n\tif (r_message && r_message->get_tuid() == tuid) {\n\t\thandle_response_message(r);\n\t\tMEMMAN_DELETE(r_message);\n\t\tdelete r_message;\n\t\tr_message = NULL;\n\t\t\n\t\t// Send next pending MESSAGE\n\t\tif (!pending_messages.empty()) {\n\t\t\tt_request *req = pending_messages.front();\n\t\t\tpending_messages.pop_front();\n\t\t\tr_message = new t_client_request(user_config, req, 0);\n\t\t\tMEMMAN_NEW(r_message);\n\t\t\tphone->send_request(user_config, req, r_message->get_tuid());\n\t\t\tMEMMAN_DELETE(req);\n\t\t\tdelete req;\t\t\t\n\t\t}\n\t\t\n\t\treturn;\n\t}\n\n\t// Response does not match any pending request. Do nothing.\n}\n\nvoid t_phone_user::resend_request(t_request *req, bool is_register, t_client_request *cr) {\n\t// A new sequence number must be assigned\n\tif (is_register) {\n\t\treq->hdr_cseq.set_seqnr(++register_seqnr);\n\t} else {\n\t\treq->hdr_cseq.seqnr++;\n\t}\n\n\t// Create a new via-header. Otherwise the\n\t// request will be seen as a retransmission\n\tIPaddr local_ip = req->get_local_ip();\n\treq->hdr_via.via_list.clear();\n\tt_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));\n\treq->hdr_via.add_via(via);\n\n\tcr->renew(0);\n\tphone->send_request(user_config, req, cr->get_tuid());\n}\n\nvoid t_phone_user::handle_response_out_of_dialog(StunMessage *r, t_tuid tuid) {\n\tif (!r_stun || r_stun->get_tuid() != tuid) {\n\t\t// Response does not match pending STUN request\n\t\treturn;\n\t}\n\t\n\tif (r->msgHdr.msgType == BindResponseMsg && r->hasMappedAddress) {\n\t\t// The STUN response contains the public IP.\n\t\tstun_public_ip_sip = r->mappedAddress.ipv4.addr;\n\t\tstun_public_port_sip = r->mappedAddress.ipv4.port;\n                MEMMAN_DELETE(r_stun);\n                delete r_stun;\n                r_stun = NULL;\n                \n                if (register_after_stun) {\n                \tregister_after_stun = false;\n                \tregistration(REG_REGISTER, false, registration_time);\n                }\n                \n                if (mwi_subscribe_after_stun) {\n                \tmwi_subscribe_after_stun = false;\n                \tsubscribe_mwi();\n                }\n                \n                if (presence_subscribe_after_stun) {\n                \tpresence_subscribe_after_stun = false;\n                \tbuddy_list->stun_completed();\n                }\n                \n                return;\n\t}\n\t\n\tif (r->msgHdr.msgType == BindErrorResponseMsg && r->hasErrorCode) {\n\t\t// STUN request failed.\n                ui->cb_stun_failed(user_config, r->errorCode.errorClass * 100 +\n                \tr->errorCode.number, r->errorCode.reason);\n\t} else {\t\n\t\t// No satisfying STUN response was received.\n \t       ui->cb_stun_failed(user_config);\n\t}\n\t\n        MEMMAN_DELETE(r_stun);\n        delete r_stun;\n        r_stun = NULL;\n\t\n        if (register_after_stun) {\n\t\t// Retry registration later.\n\t\tbool first_failure = !last_reg_failed;\n\t\tlast_reg_failed = true;\n\t\tis_registered = false;\n\t\tui->cb_register_stun_failed(user_config, first_failure);\n\t\tphone->start_set_timer(PTMR_REGISTRATION, DUR_REG_FAILURE * 1000, this);\n\t\tregister_after_stun = false;\n        }\n        \n        if (mwi_subscribe_after_stun) {\n        \t// Retry MWI subscription later\n        \tstart_resubscribe_mwi_timer(DUR_MWI_FAILURE * 1000);\n        \tmwi_subscribe_after_stun = false;\n        }\n        \n        if (presence_subscribe_after_stun) {\n        \tbuddy_list->stun_failed();\n        \tpresence_subscribe_after_stun = false;\n        }\n}\n\nvoid t_phone_user::handle_response_register(t_response *r, bool &re_register) {\n\tt_contact_param *c;\n\tunsigned long expires;\n\tunsigned long e;\n\tbool first_failure, first_success;\n\n\tre_register = false;\n\t\n\t// Store the destination IP address/port of the REGISTER message.\n\t// To this destination the NAT keep alive packets will be sent.\n\tt_request *req = r_register->get_request();\n\treq->get_destination(register_ip_port, *user_config);\n\n        switch(r->get_class()) {\n        case R_2XX:\n                last_reg_failed = false;\n\n                // Stop registration timer if one was running\n                phone->stop_timer(PTMR_REGISTRATION, this);\n\n                c = r->hdr_contact.find_contact(req->hdr_contact.contact_list.front().uri);\n                if (!c) {               \t\n\t               \tif (!user_config->get_allow_missing_contact_reg()) {\n\t\t\t\tis_registered = false;\n\n\t              \t\tlog_file->write_report(\n        \t       \t\t\t\"Contact header is missing.\",\n               \t\t\t\t\"t_phone_user::handle_response_register\",\n               \t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\t\n\t\t\t\tui->cb_invalid_reg_resp(user_config,\n\t\t\t\t\tr, \"Contact header missing.\");\n\t\t\t\tcleanup_registration_data();\n\t\t\t\treturn;\n                        } else {\n                        \tlog_file->write_report(\n                        \t\t\"Cannot find matching contact header.\",\n                        \t\t\"t_phone_user::handle_response_register\",\n                        \t\tLOG_NORMAL, LOG_DEBUG);\n                        }\n                }\n\n                if (c && c->is_expires_present() && c->get_expires() != 0) {\n                        expires = c->get_expires();\n                }\n                else if (r->hdr_expires.is_populated() &&\n                         r->hdr_expires.time != 0)\n                {\n                        expires = r->hdr_expires.time;\n                }\n                else {\t\n               \t\tif (!user_config->get_allow_missing_contact_reg()) {\n\t\t\t\tis_registered = false;\n\t\t\t\t\n               \t\t\tlog_file->write_report(\n               \t\t\t\t\"Expires parameter/header mising.\",\n               \t\t\t\t\"t_phone_user::handle_response_register\",\n               \t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\t\n\t\t\t\tui->cb_invalid_reg_resp(user_config,\n\t\t\t\t\tr, \"Expires parameter/header mising.\");\n\t\t\t\tcleanup_registration_data();\n\t\t\t\treturn;\n                        }\n                        \n                        expires = user_config->get_registration_time();\n                        \n                        // Assume a default expiration of 3600 sec if no expiry\n                        // time was returned.\n                        if (expires == 0) expires = 3600;\n                }\n\n                // Start new registration timer\n                // The maximum value of the timer can be 2^32-1 s\n                // The maximum timer that we can handle however is 2^31-1 ms\n                e = (expires > 2147483 ? 2147483 : expires);\n                phone->start_set_timer(PTMR_REGISTRATION, e * 1000, this);\n\t\t// Save the Service-Route if present the response contains any\n\n\t\t// RFC 3608 6\n\t\t// Collect the service route to route later initial requests.\n\t\tif (r->hdr_service_route.is_populated()) {\n\t\t\tservice_route = r->hdr_service_route.route_list;\n\t\t\tlog_file->write_header(\"t_phone_user::handle_response_register\");\n\t\t\tlog_file->write_raw(\"Store service route:\\n\");\n\t\t\tfor (list<t_route>::const_iterator it = service_route.begin();\n\t\t\t     it != service_route.end(); ++it)\n\t\t\t{\n\t\t\t\tlog_file->write_raw(it->encode());\n\t\t\t\tlog_file->write_endl();\n\t\t\t}\n\t\t\tlog_file->write_footer();\n\t\t} else {\n\t\t\tif (!service_route.empty())\n\t\t\t{\n\t\t\t\tlog_file->write_report(\"Clear service route.\",\n\t\t\t\t\t\"t_phone_user::handle_response_register\");\n\t\t\t\tservice_route.clear();\n\t\t\t}\n\t\t}\n\n\t\tfirst_success = !is_registered;\n                is_registered = true;\n\t\tui->cb_register_success(user_config, r, expires, first_success);\n\t\t\n\t\t// Start sending NAT keepalive packets when STUN is used\n\t\t// (or in case of symmetric firewall)\n\t\tif (use_nat_keepalive && id_nat_keepalive == 0) {\n\t\t\t// Just start the NAT keepalive timer. The REGISTER\n\t\t\t// message itself created the NAT binding. So there is\n\t\t\t// no need to send a NAT keep alive packet now.\n\t\t\tphone->start_timer(PTMR_NAT_KEEPALIVE, this);\n\t\t}\n\t\t\n\t\t// Start sending TCP ping packets on persistent TCP connections.\n\t\tif (user_config->get_persistent_tcp() && id_tcp_ping == 0) {\n\t\t\tphone->start_timer(PTMR_TCP_PING, this);\n\t\t}\n\t\t\n\t\t// Registration succeeded. If solicited MWI is provisioned\n\t\t// and no MWI subscription is established yet, then subscribe\n\t\t// to MWI.\n\t\tif (user_config->get_mwi_solicited() && !mwi_auto_resubscribe) {\n\t\t\tsubscribe_mwi();\n\t\t}\n\t\t\n\t\t// Publish presence state if not yet published.\n\t\tif (user_config->get_pres_publish_startup() && \n\t\t    presence_epa->get_epa_state() == t_epa::EPA_UNPUBLISHED)\n\t\t{\n\t\t\tpublish_presence(t_presence_state::ST_BASIC_OPEN);\n\t\t}\n\t\t\n\t\t// Subscribe to buddy list presence if not done so.\n\t\tif (!buddy_list->get_is_subscribed()) {\n\t\t\tsubscribe_presence();\n\t\t}\n\n                break;\n        case R_4XX:\n                is_registered = false;\n\n                // RFC 3261 10.3\n                if (r->code == R_423_INTERVAL_TOO_BRIEF) {\n                        if (!r->hdr_min_expires.is_populated()) {\n                                // Violation of RFC 3261 10.3 item 7\n\t\t\t\tlog_file->write_report(\"Min-Expires header missing from 423 response.\",\n\t\t\t\t\t\"t_phone_user::handle_response_register\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n                                ui->cb_invalid_reg_resp(user_config, r,\n                                        \"Min-Expires header missing.\");\n\t\t\t\tcleanup_registration_data();\n                                return;\n                        }\n\n                        if (r->hdr_min_expires.time <= registration_time) {\n                                // Wrong Min-Expires time\n                                string s = \"Min-Expires (\";\n                                s += ulong2str(r->hdr_min_expires.time);\n                                s += \") is smaller than the requested \";\n                                s += \"time (\";\n                                s += ulong2str(registration_time);\n                                s += \")\";\n                                log_file->write_report(s, \"t_phone_user::handle_response_register\",\n                                \tLOG_NORMAL, LOG_WARNING);\n                                ui->cb_invalid_reg_resp(user_config, r, s);\n\t\t\t\tcleanup_registration_data();\n                                return;\n                        }\n\n                        // Automatic re-register with Min-Expires time\n                        registration_time = r->hdr_min_expires.time;\n                        re_register = true;\n                        // No need to cleanup STUN data as a new REGISTER will be\n                        // sent immediately.\n                        return;\n                }\n\n\t\t// If authorization failed, then do not start the continuous\n\t\t// re-attempts. When authorization fails the user is asked\n\t\t// for credentials (in GUI). So the user cancelled these\n\t\t// questions and should not be bothered with the same question\n\t\t// again every 30 seconds. The user does not have the\n\t\t// credentials.\n\t\tif (r->code == R_401_UNAUTHORIZED ||\n\t\t    r->code == R_407_PROXY_AUTH_REQUIRED)\n\t\t{\n\t\t\tlast_reg_failed = true;\n\t\t\tui->cb_register_failed(user_config, r, true);\t\t\t\n\t\n\t\t\tcleanup_registration_data();\n\t\t\treturn;\n\t\t}\n\n                // fall thru\n        default:\n\t\tfirst_failure = !last_reg_failed;\n                last_reg_failed = true;\n                is_registered = false;\n                authorizor.remove_from_cache(\"\"); // Clear credentials cache\n\t\tui->cb_register_failed(user_config, r, first_failure);\n                phone->start_set_timer(PTMR_REGISTRATION, DUR_REG_FAILURE * 1000, this);\n\n\t\tcleanup_registration_data();\n        }\n}\n\nvoid t_phone_user::handle_response_deregister(t_response *r) {\n\tis_registered = false;\n\tlast_reg_failed = false;\n\n\tif (r->is_success()) {\n\t\tui->cb_deregister_success(user_config, r);\n\t} else {\n\t\tui->cb_deregister_failed(user_config, r);\n\t}\n\t\n\tcleanup_registration_data();\n}\n\nvoid t_phone_user::handle_response_query_register(t_response *r) {\n\tif (r->is_success()) {\n\t\tui->cb_fetch_reg_result(user_config, r);\n\t} else {\n\t\tui->cb_fetch_reg_failed(user_config, r);\n\t}\n}\n\nvoid t_phone_user::handle_response_options(t_response *r) {\n\tui->cb_options_response(r);\n}\n\nvoid t_phone_user::handle_response_message(t_response *r) {\n\tt_request *req = r_message->get_request();\n\t\n\tif (req->body && req->body->get_type() == BODY_IM_ISCOMPOSING_XML) {\n\t\t// Response on message composing indication.\n\t\tif (r->code == R_415_UNSUPPORTED_MEDIA_TYPE) {\n\t\t\t// RFC 3994 4\n\t\t\t// In SIP-based IM, The composer MUST cease transmitting \n\t\t\t// status messages if the receiver returned a 415 status \n\t\t\t// code (Unsupported Media Type) in response to MESSAGE \n\t\t\t// request containing the status indication.\n\t\t\tui->cb_im_iscomposing_not_supported(user_config, r);\n\t\t}\n\t} else {\n\t\t// Response on instant message\n\t\tui->cb_message_response(user_config, r, req);\n\t}\n}\n\nvoid t_phone_user::subscribe_mwi(void) {\n\tmwi_auto_resubscribe = true;\n\t\n\tif (mwi_dialog) {\n\t\t// This situation may occur, when an unsubscription is still\n\t\t// in progress. The subscibe will be retried after the unsubscription\n\t\t// is finished. Note that mwi_auto_resubscribe has been set to true\n\t\t// to trigger an automatic subscription.\n\t\tlog_file->write_header(\"t_phone_user::subscribe_mwi\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"MWI dialog already exists.\\n\");\n\t\tlog_file->write_raw(\"Subscription state: \");\n\t\tlog_file->write_raw(t_subscription_state2str(mwi_dialog->get_subscription_state()));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\n\t\treturn;\n\t}\n\t\n\t// If STUN is enabled, then do a STUN query before registering to\n\t// determine the public IP address.\n\tif (use_stun) {\n\t\tif (stun_public_ip_sip == 0)\n\t\t{\n\t\t\tsend_stun_request();\n\t\t\tmwi_subscribe_after_stun = true;\n\t\t\treturn;\n\t\t}\n\t\tstun_binding_inuse_mwi = true;\n\t}\n\t\n\tmwi_dialog = new t_mwi_dialog(this);\n\tMEMMAN_NEW(mwi_dialog);\n\t\n\t// RFC 3842 4.1\n\t// The example flow shows:\n\t// Request-URI = mail_user@mailbox_server\n\t// To = user@domain\n\tmwi_dialog->subscribe(DUR_MWI(user_config), user_config->get_mwi_uri(),\n\t\tuser_config->create_user_uri(false), user_config->get_display(false));\n\t\t\n\t// Start sending NAT keepalive packets when STUN is used\n\t// (or in case of symmetric firewall)\n\tif (use_nat_keepalive && id_nat_keepalive == 0) {\n\t\t// Just start the NAT keepalive timer. The SUBSCRIBE\n\t\t// message will create the NAT binding. So there is\n\t\t// no need to send a NAT keep alive packet now.\n\t\tphone->start_timer(PTMR_NAT_KEEPALIVE, this);\n\t}\n\t\t\n\tcleanup_mwi_dialog();\n}\n\nvoid t_phone_user::unsubscribe_mwi(void) {\n\tmwi_auto_resubscribe = false;\n\tstop_resubscribe_mwi_timer();\n\tmwi.set_status(t_mwi::MWI_UNKNOWN);\n\t\n\tif (mwi_dialog) {\n\t\tmwi_dialog->unsubscribe();\n\t\tcleanup_mwi_dialog();\n\t}\n\t\n\tui->cb_update_mwi();\n}\n\nbool t_phone_user::is_mwi_subscribed(void) const {\n\tif (mwi_dialog) {\n\t\treturn mwi_dialog->get_subscription_state() == SS_ESTABLISHED;\n\t}\n\t\n\treturn false;\n}\n\nbool t_phone_user::is_mwi_terminated(void) const {\n\treturn mwi_dialog == NULL;\n}\n\nvoid t_phone_user::handle_mwi_unsolicited(t_request *r, t_tid tid) {\n\tif (user_config->get_mwi_solicited()) {\n\t\t// Unsolicited MWI is not supported\n\t\tt_response *resp = r->create_response(R_403_FORBIDDEN);\n\t\tphone->send_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn;\n\t}\n\t\n\tif (r->body && r->body->get_type() == BODY_SIMPLE_MSG_SUM) {\n\t\tt_simple_msg_sum_body *body = dynamic_cast<t_simple_msg_sum_body *>(r->body);\n\t\tmwi.set_msg_waiting(body->get_msg_waiting());\n\t\t\n\t\tt_msg_summary summary;\n\t\tif (body->get_msg_summary(MSG_CONTEXT_VOICE, summary)) {\n\t\t\tmwi.set_voice_msg_summary(summary);\n\t\t}\n\t\t\n\t\tmwi.set_status(t_mwi::MWI_KNOWN);\n\t}\n\t\n\tt_response *resp = r->create_response(R_200_OK);\n\tphone->send_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\t\n\tui->cb_update_mwi();\n}\n\nvoid t_phone_user::subscribe_presence(void) {\n\tassert(buddy_list);\n\tbuddy_list->subscribe_presence();\n}\n\nvoid t_phone_user::unsubscribe_presence(void) {\n\tassert(buddy_list);\n\tbuddy_list->unsubscribe_presence();\n}\n\nvoid t_phone_user::publish_presence(t_presence_state::t_basic_state basic_state) {\n\tassert(presence_epa);\n\tpresence_epa->publish_presence(basic_state);\n}\n\nvoid t_phone_user::unpublish_presence(void) {\n\tassert(presence_epa);\n\tpresence_epa->unpublish();\n}\n\nbool t_phone_user::is_presence_terminated(void) const {\n\tassert(buddy_list);\n\treturn buddy_list->is_presence_terminated();\n}\n\nbool t_phone_user::send_message(const t_url &to_uri, const string &to_display, \n\t\tconst t_msg &msg)\n{\n\tt_request *req = create_request(MESSAGE, to_uri);\n\t\n\t// To\n\treq->hdr_to.set_uri(to_uri);\n\treq->hdr_to.set_display(to_display);\n\n\t// Call-ID\n\treq->hdr_call_id.set_call_id(NEW_CALL_ID(user_config));\n\n\t// CSeq\n\treq->hdr_cseq.set_method(MESSAGE);\n\treq->hdr_cseq.set_seqnr(NEW_SEQNR);\n\t\n\t// Subject\n\tif (!msg.subject.empty()) {\n\t\treq->hdr_subject.set_subject(msg.subject);\n\t}\n\t\n\t// Body and Content-Type\n\tif (!msg.has_attachment) {\n\t\t// A message without an attachment is a text message.\n\t\treq->set_body_plain_text(msg.message, MSG_TEXT_CHARSET);\n\t} else {\n\t\t// Send message with file attachment\n\t\tif (!req->set_body_from_file(msg.attachment_filename, msg.attachment_media)) {\n\t\t\tlog_file->write_header(\"t_phone_user::send_message\", LOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Could not read file \");\n\t\t\tlog_file->write_raw(msg.attachment_filename);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tMEMMAN_DELETE(req);\n\t\t\tdelete req;\n\t\t\t\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Content-Disposition\n\t\treq->hdr_content_disp.set_type(DISPOSITION_ATTACHMENT);\n\t\treq->hdr_content_disp.set_filename(msg.attachment_save_as_name);\n\t}\n\t\n\t// Store and send request\n\t// Delete a possible pending options request\n\tif (r_message) {\n\t\t// RFC 3428 8\n\t\t// Send only 1 message at a time.\n\t\t// Store the message. It will be sent if the previous\n\t\t// message transaction is finished.\n\t\tpending_messages.push_back(req);\n\t} else {\n\t\tr_message = new t_client_request(user_config, req, 0);\n\t\tMEMMAN_NEW(r_message);\n\t\tphone->send_request(user_config, req, r_message->get_tuid());\n\t\tMEMMAN_DELETE(req);\n\t\tdelete req;\n\t}\n\t\n\treturn true;\n}\n\nbool t_phone_user::send_im_iscomposing(const t_url &to_uri, const string &to_display, \n\t\t\tconst string &state, time_t refresh)\n{\n\tt_request *req = create_request(MESSAGE, to_uri);\n\t\n\t// To\n\treq->hdr_to.set_uri(to_uri);\n\treq->hdr_to.set_display(to_display);\n\n\t// Call-ID\n\treq->hdr_call_id.set_call_id(NEW_CALL_ID(user_config));\n\n\t// CSeq\n\treq->hdr_cseq.set_method(MESSAGE);\n\treq->hdr_cseq.set_seqnr(NEW_SEQNR);\n\t\n\t// Body and Content-Type\n\tt_im_iscomposing_xml_body *body = new t_im_iscomposing_xml_body;\n\tMEMMAN_NEW(body);\n\t\n\tbody->set_state(state);\n\tbody->set_refresh(refresh);\n\t\n\treq->body = body;\n\treq->hdr_content_type.set_media(body->get_media());\n\t\n\t// Store and send request\n\t// Delete a possible pending options request\n\tif (r_message) {\n\t\t// RFC 3428 8\n\t\t// Send only 1 message at a time.\n\t\t// Store the message. It will be sent if the previous\n\t\t// message transaction is finished.\n\t\tpending_messages.push_back(req);\n\t} else {\n\t\tr_message = new t_client_request(user_config, req, 0);\n\t\tMEMMAN_NEW(r_message);\n\t\tphone->send_request(user_config, req, r_message->get_tuid());\n\t\tMEMMAN_DELETE(req);\n\t\tdelete req;\n\t}\n\t\n\treturn true;\n}\n\nvoid t_phone_user::recvd_message(t_request *r, t_tid tid) {\n\tt_response *resp;\n\t\n\tif (!r->body || !MESSAGE_CONTENT_TYPE_SUPPORTED(*r)) {\n\t\tresp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);\n\t\t// RFC 3261 21.4.13\n\t\tSET_MESSAGE_HDR_ACCEPT(resp->hdr_accept);\n\t\tphone->send_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\tif (r->body && r->body->get_type() == BODY_IM_ISCOMPOSING_XML) {\n\t\t// Message composing indication\n\t\tt_im_iscomposing_xml_body *sb = dynamic_cast<t_im_iscomposing_xml_body *>(r->body);\n\t\tim::t_composing_state state = im::string2composing_state(sb->get_state());\n\t\ttime_t refresh = sb->get_refresh();\n\t\t\n\t\tui->cb_im_iscomposing_request(user_config, r, state, refresh);\n\t\tresp = r->create_response(R_200_OK);\n\t} else {\n\t\tbool accepted = ui->cb_message_request(user_config, r);\n\t\tif (accepted) {\n\t\t\tresp = r->create_response(R_200_OK);\n\t\t} else {\n\t\t\tif (user_config->get_im_max_sessions() == 0) {\n\t\t\t\tresp = r->create_response(R_603_DECLINE);\n\t\t\t} else {\n\t\t\t\tresp = r->create_response(R_486_BUSY_HERE);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tphone->send_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone_user::recvd_notify(t_request *r, t_tid tid) {\n\tbool partial_match = false;\n\t\n\tif (r->hdr_to.tag.empty()) {\n\t\t// Unsolicited NOTIFY\n\t\thandle_mwi_unsolicited(r, tid);\n\t\treturn;\n\t}\n\t\n\tif (mwi_dialog && mwi_dialog->match_request(r, partial_match)) {\n\t\t// Solicited NOTIFY\n\t\tmwi_dialog->recvd_request(r, 0, tid);\n\t\tcleanup_mwi_dialog();\n\t\treturn;\n\t}\n\t\n\t// A NOTIFY may be received before a 2XX on SUBSCRIBE.\n\t// In this case the NOTIFY will establish the dialog.\n\tif (partial_match && mwi_dialog->get_remote_tag().empty()) {\n\t\tmwi_dialog->recvd_request(r, 0, tid);\n\t\tcleanup_mwi_dialog();\n\t\treturn;\n\t}\n\t\n\tt_buddy *buddy;\n\tif (buddy_list->match_request(r, &buddy)) {\n\t\tbuddy->recvd_request(r, 0, tid);\n\t\tif (buddy->must_delete_now()) buddy_list->del_buddy(*buddy);\n\t\treturn;\n\t}\n\t\n\t// RFC 3265 4.4.9\n\t// A SUBSCRIBE request may have forked. So multiple NOTIFY's\n\t// can be received. Twinkle simply rejects additional NOTIFY's with\n\t// a 481. This should terminate the forked dialog, such that only\n\t// one dialog will remain.\n\tt_response *resp = r->create_response(R_481_TRANSACTION_NOT_EXIST);\n\tphone->send_response(resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n}\n\nvoid t_phone_user::send_stun_request(void) {\n\tif (r_stun) {\n\t\tlog_file->write_report(\"STUN request already in progress.\",\n\t\t\t\"t_phone_user::send_stun_request\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t}\n\n\tStunMessage req;\n\tStunAtrString username;\n\tusername.sizeValue = 0;\n\tstunBuildReqSimple(&req, username, false, false);\n\tr_stun = new t_client_request(user_config, &req, 0);\n\tMEMMAN_NEW(r_stun);\n\tphone->send_request(user_config, &req, r_stun->get_tuid());\n\treturn;\n}\n\n// NOTE: The term \"NAT keep alive\" does not cover all uses. The keep alives will\n//       also be sent when there is a symmetric firewall without NAT.\nvoid t_phone_user::send_nat_keepalive(void) {\n\t// Send keep-alive to registrar/proxy\n\tif (register_ip_port.ipaddr != 0 && register_ip_port.port != 0 &&\n\t    register_ip_port.transport == \"udp\") \n\t{\n\t\tevq_sender->push_nat_keepalive(register_ip_port.ipaddr, register_ip_port.port);\n\t}\n\t\n\t// Send keep-alive to MWI mailbox if different from registrar/proxy\n\tif (mwi_dialog) {\n\t\tt_ip_port mwi_ip_port = mwi_dialog->get_remote_ip_port();\n\t\t    \n\t\tif (!mwi_ip_port.is_null() && mwi_ip_port != register_ip_port &&\n\t\t    mwi_ip_port.transport == \"udp\")\n\t\t{\n\t\t\tevq_sender->push_nat_keepalive(mwi_ip_port.ipaddr, mwi_ip_port.port);\n\t\t}\n\t}\n}\n\nvoid t_phone_user::send_tcp_ping(void) {\n\tif (register_ip_port.ipaddr != 0 && register_ip_port.port != 0 &&\n\t    register_ip_port.transport == \"tcp\") \n\t{\n\t\tevq_sender->push_tcp_ping(user_config->create_user_uri(false),\n\t\t\t\tregister_ip_port.ipaddr, register_ip_port.port);\n\t}\n}\n\nvoid t_phone_user::timeout(t_phone_timer timer) {\n\tswitch (timer) {\n\tcase PTMR_REGISTRATION:\n\t\tid_registration = 0;\n\t\t\n\t\t// Registration expired. Re-register.\n\t\tif (is_registered || last_reg_failed) {\n\t\t\t// Re-register if no register is pending\n\t\t\tif (!r_register) {\n\t\t\t\tregistration(REG_REGISTER, true, registration_time);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTMR_NAT_KEEPALIVE:\n\t\tid_nat_keepalive = 0;\n\t\t\n\t\t// Send a new NAT keepalive packet\n\t\tif (use_nat_keepalive) {\n\t\t\tsend_nat_keepalive();\n\t\t\tphone->start_timer(PTMR_NAT_KEEPALIVE, this);\n\t\t}\n\t\tbreak;\n\tcase PTMR_TCP_PING:\n\t\tid_tcp_ping = 0;\n\t\t\n\t\t// Send a TCP ping;\n\t\tif (user_config->get_persistent_tcp()) {\n\t\t\tsend_tcp_ping();\n\t\t\tphone->start_timer(PTMR_TCP_PING, this);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_phone_user::timeout_sub(t_subscribe_timer timer, t_object_id id_timer) \n{\n\tt_buddy *buddy;\n\t\n\tswitch (timer) {\n\tcase STMR_SUBSCRIPTION:\n\t\tif (mwi_dialog && mwi_dialog->match_timer(timer, id_timer)) {\n\t\t\tmwi_dialog->timeout(timer);\n\t\t} else if (buddy_list->match_timer(timer, id_timer, &buddy)) {\n\t\t\tbuddy->timeout(timer, id_timer);\n\t\t\tif (buddy->must_delete_now()) buddy_list->del_buddy(*buddy);\n\t\t} else if (id_timer == id_resubscribe_mwi) {\n\t\t\t// Try to subscribe to MWI\n\t\t\tid_resubscribe_mwi = 0;\n\t\t\tsubscribe_mwi();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_phone_user::timeout_publish(t_publish_timer timer, t_object_id id_timer) {\n\tswitch (timer) {\n\tcase PUBLISH_TMR_PUBLICATION:\n\t\tif (presence_epa->match_timer(timer, id_timer)) {\n\t\t\tpresence_epa->timeout(timer);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_phone_user::handle_broken_connection(void) {\n\tlog_file->write_header(\"t_phone_user::handle_broken_connection\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Handle broken connection for \");\n\tlog_file->write_raw(user_config->get_profile_name());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\n\t// A persistent connection has been broken. The connection must be re-established\n\t// by registering again. This is only needed when the user was registered already.\n\t// If no registration was present, then the persistent connection should not have\n\t// been established.\n\tif (is_registered) {\n\t\t// Re-register if no register is pending\n\t\tif (!r_register) {\n\t\t\tlog_file->write_header(\"t_phone_user::handle_broken_connection\", \n\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Re-establish broken connection for \");\n\t\t\tlog_file->write_raw(user_config->get_profile_name());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tregistration(REG_REGISTER, true, registration_time);\n\t\t}\n\t}\n}\n\nbool t_phone_user::match_subscribe_timer(t_subscribe_timer timer, t_object_id id_timer) const \n{\n\tt_buddy *buddy;\n\t\n\tif (mwi_dialog && mwi_dialog->match_timer(timer, id_timer)) {\n\t\treturn true;\n\t}\n\t\n\tt_phone_user *self = const_cast<t_phone_user *>(this);\n\tif (self->buddy_list->match_timer(timer, id_timer, &buddy)) {\n\t\treturn true;\n\t}\n\t\n\treturn id_timer == id_resubscribe_mwi;\n}\n\nbool t_phone_user::match_publish_timer(t_publish_timer timer, t_object_id id_timer) const \n{\n\tassert(presence_epa);\n\treturn presence_epa->match_timer(timer, id_timer);\n}\n\nvoid t_phone_user::start_resubscribe_mwi_timer(unsigned long duration) {\n\tt_tmr_subscribe\t*t;\n\tt = new t_tmr_subscribe(duration, STMR_SUBSCRIPTION, 0, 0, SIP_EVENT_MSG_SUMMARY, \"\");\n\tMEMMAN_NEW(t);\n\tid_resubscribe_mwi = t->get_object_id();\n\t\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_phone_user::stop_resubscribe_mwi_timer(void) {\n\tif (id_resubscribe_mwi != 0) {\n\t\tevq_timekeeper->push_stop_timer(id_resubscribe_mwi);\n\t\tid_resubscribe_mwi = 0;\n\t}\n}\n\nt_request *t_phone_user::create_request(t_method m, const t_url &request_uri) const {\n\tt_request *req = new t_request(m);\n\tMEMMAN_NEW(req);\n\n\t// From\n\treq->hdr_from.set_uri(user_config->create_user_uri(false));\n\treq->hdr_from.set_display(user_config->get_display(false));\n\treq->hdr_from.set_tag(NEW_TAG);\n\n\t// Max-Forwards header (mandatory)\n\treq->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);\n\n\t// User-Agent\n\tSET_HDR_USER_AGENT(req->hdr_user_agent);\n\t\n\t// Set request URI and calculate destinations. By calculating\n\t// destinations now, the request can be resend to a next destination\n\t// if failover is needed.\n\tif (m == REGISTER) {\n\t\t// For a REGISTER do not use the service route for routing.\n\t\treq->uri = request_uri;\n\t} else {\n\t\t// RFC 3608\n\t\t// For all other requests, use the service route set for routing.\n\t\treq->set_route(request_uri, service_route);\n\t}\n\n\treq->calc_destinations(*user_config);\n\t\n        // The Via header can only be created after the destinations\n        // are calculated, because the destination deterimines which\n        // local IP address should be used.\n\t\n\t// Via\n\tIPaddr local_ip = req->get_local_ip();\n\tt_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));\n\treq->hdr_via.add_via(via);\n\n\treturn req;\n}\n\nt_response *t_phone_user::create_options_response(t_request *r,\n\t\tbool in_dialog) const\n{\n\tt_response *resp;\n\n\t// RFC 3261 11.2\n\tswitch(phone->get_state()) {\n\tcase PS_IDLE:\n\t\tif (!in_dialog && service->is_dnd_active()) {\n\t\t\tresp = r->create_response(R_480_TEMP_NOT_AVAILABLE);\n\t\t} else {\n\t\t\tresp = r->create_response(R_200_OK);\n\t\t}\n\t\tbreak;\n\tcase PS_BUSY:\n\t\tif (in_dialog) {\n\t\t\tresp = r->create_response(R_200_OK);\n\t\t} else {\n\t\t\tresp = r->create_response(R_486_BUSY_HERE);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tSET_HDR_ALLOW(resp->hdr_allow, user_config);\n\tSET_HDR_ACCEPT(resp->hdr_accept);\n\tSET_HDR_ACCEPT_ENCODING(resp->hdr_accept_encoding);\n\tSET_HDR_ACCEPT_LANGUAGE(resp->hdr_accept_language);\n\tSET_HDR_SUPPORTED(resp->hdr_supported, user_config);\n\n\tif (user_config->get_ext_100rel() != EXT_DISABLED) {\n\t\tresp->hdr_supported.add_feature(EXT_100REL);\n\t}\n\n\t// TODO: include SDP body if requested (optional)\n\n\treturn resp;\n}\n\nbool t_phone_user::get_is_registered(void) const {\n\treturn is_registered;\n}\n\nbool t_phone_user::get_last_reg_failed(void) const {\n\treturn last_reg_failed;\n}\n\nstring t_phone_user::get_ip_sip(const string &auto_ip) const {\n\tif (stun_public_ip_sip) return h_ip2str(stun_public_ip_sip);\n\tif (user_config->get_use_nat_public_ip()) return h_ip2str(gethostbyname(user_config->get_nat_public_ip()));\n\tif (LOCAL_IP == AUTO_IP4_ADDRESS) return auto_ip;\n\treturn LOCAL_IP;\n}\n\nunsigned short t_phone_user::get_public_port_sip(void) const {\n\tif (stun_public_port_sip) return stun_public_port_sip;\n\treturn sys_config->get_sip_port();\n}\n\nlist<t_route> t_phone_user::get_service_route(void) const {\n\treturn service_route;\n}\n\nbool t_phone_user::match(t_response *r, t_tuid tuid) const {\n\tt_buddy *dummy;\n\n\tif (r_register && r_register->get_tuid() == tuid) {\n\t\treturn true;\n\t} else if (r_deregister && r_deregister->get_tuid() == tuid) {\n\t\treturn true;\n\t} else if (r_query_register && r_query_register->get_tuid() == tuid) {\n\t\treturn true;\n\t} else if (r_options && r_options->get_tuid() == tuid) {\n\t\treturn true;\n\t} else if (r_message && r_message->get_tuid() == tuid) {\n\t\treturn true;\n\t} else if (mwi_dialog && mwi_dialog->match_response(r, tuid)) {\n\t\treturn true;\n\t} else if (presence_epa && presence_epa->match_response(r, tuid)) {\n\t\treturn true;\n\t} else if (buddy_list && buddy_list->match_response(r, tuid, &dummy)) {\n\t\treturn true;\n\t} else {\n\t\t// Response does not match any pending request.\n\t\treturn false;\n\t}\n}\n\nbool t_phone_user::match(t_request *r) const {\n\tif (!r->hdr_to.tag.empty()) {\n\t\t// Match in-dialog requests\n\t\tif (mwi_dialog) {\n\t\t\tbool partial_match = false;\n\t\t\tif (mwi_dialog->match_request(r, partial_match)) return true;\n\t\t\tif (partial_match) return true;\n\t\t} else if (buddy_list) {\n\t\t\tt_buddy *dummy;\n\t\t\tif (buddy_list->match_request(r, &dummy)) return true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Match on contact URI\n\t// NOTE: the host-part is not matched with the IP address to avoid\n\t//       NAT traversal problems. Some providers, using hosted NAT\n\t//       traversal, send an INVITE to username@<public_ip>. Twinkle\n\t//       only knows the <private_ip> in this case though. This is a\n\t//       fault on the provider side.\n\tif (r->uri.get_user() == user_config->get_contact_name()) {\n\t\treturn true;\n\t}\n\t\n\t// Match on user URI\n\tif (r->uri.get_user() == user_config->get_name() &&\n\t    r->uri.get_host() == user_config->get_domain())\n\t{\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nbool t_phone_user::match(StunMessage *r, t_tuid tuid) const {\n\tif (r_stun && r_stun->get_tuid() == tuid) return true;\n\treturn false;\n}\n\nbool t_phone_user::authorize(t_request *r, t_response *resp) {\n\tif (authorizor.authorize(user_config, r, resp)) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid t_phone_user::resend_request(t_request *req, t_client_request *cr) {\n\treturn resend_request(req, false, cr);\n}\n\nvoid t_phone_user::remove_cached_credentials(const string &realm) {\n\tauthorizor.remove_from_cache(realm);\n}\n\nbool t_phone_user::is_active(void) const {\n\treturn active;\n}\n\nvoid t_phone_user::activate(const t_user &user) {\n\t// Replace old user config with the new passed user config, because\n\t// the user config might have been edited while this phone user was\n\t// inactive.\n\tdelete user_config;\n\tMEMMAN_DELETE(user_config);\n\tuser_config = user.copy();\n\t\n\t// Initialize registration data\n\tregister_seqnr = NEW_SEQNR;\n\tis_registered = false;\n\tregister_ip_port.ipaddr = 0L;\n\tregister_ip_port.port = 0;\n\tlast_reg_failed = false;\n\t\n\t// Initialize STUN data\n\tstun_public_ip_sip = 0L;\n\tstun_public_port_sip = 0;\n\tuse_stun = false;\n\tuse_nat_keepalive = false;\n\t\n\tactive = true;\n}\n\nvoid t_phone_user::deactivate(void) {\n\t// Stop timers\n\tif (id_registration) phone->stop_timer(PTMR_REGISTRATION, this);\n\tif (id_nat_keepalive) phone->stop_timer(PTMR_NAT_KEEPALIVE, this);\n\tif (id_tcp_ping) phone->stop_timer(PTMR_TCP_PING, this);\n\tif (id_resubscribe_mwi) stop_resubscribe_mwi_timer();\n\n\t// Clear MWI\n\tif (mwi_dialog) {\n\t\tMEMMAN_DELETE(mwi_dialog);\n\t\tdelete mwi_dialog;\n\t\tmwi_dialog = NULL;\n\t}\n\tmwi.set_status(t_mwi::MWI_UNKNOWN);\n\tmwi_auto_resubscribe = false;\n\t\n\t// Clear presence state\n\t// presence_epa->clear();\n\tbuddy_list->clear_presence();\n\t\n\t// Clear STUN\n\tstun_binding_inuse_registration = false;\n\tstun_binding_inuse_mwi = false;\n\tstun_binding_inuse_presence = 0;\n\tcleanup_registration_data();\n\tcleanup_stun_data();\n\t\n\tactive = false;\n}\n"
  },
  {
    "path": "src/phone_user.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// State of active phone user\n\n#ifndef _PHONE_USER_H\n#define _PHONE_USER_H\n\n#include <string>\n#include <list>\n#include \"auth.h\"\n#include \"protocol.h\"\n#include \"service.h\"\n#include \"transaction_layer.h\"\n#include \"user.h\"\n#include \"im/msg_session.h\"\n#include \"mwi/mwi.h\"\n#include \"mwi/mwi_dialog.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"stun/stun.h\"\n#include \"presence/buddy.h\"\n#include \"sockets/ipaddr.h\"\n\nusing namespace std;\nusing namespace im;\n\n// Forward declarations\nclass t_client_request;\nclass t_presence_epa;\n\nclass t_phone_user {\nprivate:\n\tt_user\t\t\t*user_config;\n\n\t// State\n\t// Indicates that this user is active. A non-active user\n\t// is not removed from the user list as some transactions may\n\t// still need the user info after the user got de-activated.\n\tbool\t\t\tactive;\n\n\t// Requests outside a dialog\n\tt_client_request\t*r_options;\n\tt_client_request\t*r_register;\n\tt_client_request\t*r_deregister;\n\tt_client_request\t*r_query_register;\n\tt_client_request\t*r_message;\n\t\n\t// STUN request\n\tt_client_request\t*r_stun;\n\t\n\t/**\n\t * Pending MESSAGE requests.\n\t * Twinkle will only send one message at a time per user.\n\t * This satisfies the requirements in RFC 3428 8.\n\t * NOTE: as an optimization a message queue per destination\n\t * could be kept. This way a pending message for one destination\n\t * will not block a message for another destination.\n\t */\n\tlist<t_request *> pending_messages;\n\n\t/** @name Registration data */\n\t//@{\n\tstring\t\t\tregister_call_id;  /**< Call-ID for REGISTER requests. */\n\tunsigned long\t\tregister_seqnr;    /**< Last seqnr issued. */\n\tbool\t\t\tis_registered;     /**< Indicates if user is registered. */\n\tunsigned long\t\tregistration_time; /**< Expiration in seconds */\n\tbool\t\t\tlast_reg_failed;   /** Indicates if last registration failed. */\n\t\n\t/** Destination of last REGISTER */\n\tt_ip_port\t\tregister_ip_port;\n\t\n\t/** Service Route, collected from REGISTER responses */\n\tlist<t_route>\t\tservice_route;\n\t//@}\n\t\n\t// A STUN request can be triggered by the following events:\n\t//\n\t// * Registration\n\t// * MWI subscription.\n\t//\n\t// These events should take place after the STUN transaction has\n\t// finished. The following indicators indicate which events should\n\t// take place.\n\tbool\t\tregister_after_stun;\n\tbool\t\tmwi_subscribe_after_stun;\n\tbool\t\tstun_binding_inuse_registration;\n\tbool\t\tstun_binding_inuse_mwi;\n\t\n\t// Authorizor\n\tt_auth\t\t\tauthorizor;\n\t\n\t// MWI dialog\n\tt_mwi_dialog\t\t*mwi_dialog;\n\t\n\t/**\n\t * Indicates if MWI must be automatically resubscribed to, if the\n\t * subscription terminates with a reason telling that resubscription\n\t * is possible.\n\t */\n\tbool\t\t\tmwi_auto_resubscribe;\n\t\n\t/** Buddy list for presence susbcriptions. */\n\tt_buddy_list\t\t*buddy_list;\n\t\n\t/** Event publication agent for presence */\n\tt_presence_epa\t\t*presence_epa;\n\t\n\t/**\n\t * Resend the request: a new sequence number will be assigned and a new via\n\t * header created (new transaction).\n\t * @param req [in] The request to resend.\n\t * @param is_register [in] indicates if this request is a register\n\t * @param cr [in] is the current client request wrapper for this request.\n\t * @note In case of a REGISTER, the internal register seqnr will be increased.\n\t */\n\tvoid resend_request(t_request *req, bool is_register, t_client_request *cr);\n\n\t/** @name Handle responses. */\n\t//@{\n\t/**\n\t * Process a repsonse on a registration request. \n\t * @param r [in] The response.\n\t * @param re_register [out] Indicates if an automatic re-registration needs\n\t * to be done.\n\t */\n\tvoid handle_response_register(t_response *r, bool &re_register);\n\t\n\t/**\n\t * Process a response on a de-registration request.\n\t * @param r [in] The response.\n\t */\n\tvoid handle_response_deregister(t_response *r);\n\t\n\t/**\n\t * Process a response on a registration query request.\n\t * @param r [in] The response.\n\t */\n\tvoid handle_response_query_register(t_response *r);\n\n\t/**\n\t * Process a response on an OPTIONS request.\n\t * @param r [in] The response.\n\t */\n\tvoid handle_response_options(t_response *r);\n\t\n\t/**\n\t * Process a response on a MESSAGE request.\n\t * @param r [in] The response.\n\t */\n\tvoid handle_response_message(t_response *r);\n\t//@}\n\t\n\t/** Send a NAT keep alive packet. */\n\tvoid send_nat_keepalive(void);\n\t\n\t/** Send a TCP ping packet. */\n\tvoid send_tcp_ping(void);\n\t\n\t/** Handle MWI dialog termination. */\n\tvoid cleanup_mwi_dialog(void);\n\t\n\t/** Cleanup registration data for STUN and NAT keep alive. */\n\tvoid cleanup_registration_data(void);\n\t\npublic:\n\t/** @name Timers */\n\t//@{\n\tunsigned short\t\tid_registration;\t/**< Registration timeout */\n\tunsigned short\t\tid_nat_keepalive;\t/**< NAT keepalive interval */\n\tunsigned short\t\tid_tcp_ping;\t\t/**< TCP ping timer */\n\tunsigned short\t\tid_resubscribe_mwi; \t/**< MWI re-subscribe after failure */\n\t//@}\n\t\n\t/** Supplementary services */\n\tt_service\t*service;\n\t\n\t/** Message Waiting Indication data. */\n\tt_mwi\t\tmwi;\n\t\n\t/** @name STUN data */\n\t//@{\n\tbool\t\tuse_stun; \t\t/**< Indicates if STUN must be used. */\n\tbool\t\tuse_nat_keepalive; \t/**< Send NAT keepalive ? */\n\tIPaddr\tstun_public_ip_sip; \t/**< Public IP for SIP */\n\tunsigned short\tstun_public_port_sip; \t/**< Public port for SIP */\n\t\n\t/** Number of presence subscriptions using the STUN binding. */\n\tunsigned short\tstun_binding_inuse_presence;\t\n\t\n\t/**< Subscribe to presence after STUN transaction completed. */\n\tbool\t\tpresence_subscribe_after_stun;\t\n\t//@}\n\t\n\t/** \n\t * The constructor will create a copy of profile.\n\t * @param profile [in] User profile of this phone user.\n\t */\n\tt_phone_user(const t_user &profile);\n\t\n\t/** Destructor. */\n\t~t_phone_user();\n\t\n\t/** Send STUN request. */\n\tvoid send_stun_request(void);\n\t\n\t/** Cleanup STUN data if not in use anymore. */\n\tvoid cleanup_stun_data(void);\n\t\n\t/** Stop sending NAT keep alives when not necessary anymore. */\n\tvoid cleanup_nat_keepalive(void);\n\t\n\t/** \n\t * Synchronize the sending of NAT keep alives with the user config.\n\t * Start sending if keep alives are enabled but currently not being\n\t * sent.\n\t */\n\tvoid sync_nat_keepalive(void);\n\t\n\t/** Stop sending TCP ping packets when not necessary anumore. */\n\tvoid cleanup_tcp_ping(void);\n\t\n\t/** @name Getters */\n\t//@{\n\tt_user *get_user_profile(void);\n\tt_buddy_list *get_buddy_list(void);\n\tt_presence_epa *get_presence_epa(void);\n\t//@}\n\t\n\t// Handle responses for out-of-dialog requests\n\tvoid handle_response_out_of_dialog(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid handle_response_out_of_dialog(StunMessage *r, t_tuid tuid);\n\t\n\t/**\n\t * Send a registration, de-registration or query registration request.\n\t * @param register_type [in] Type of registration request.\n\t * @param re_register [in] Indicates if this registration request is a re-registration.\n\t * @param expires [in] Epxiry time to put in registration request.\n\t * @note If needed a STUN request is sent before doing a registration.\n\t */\n\tvoid registration(t_register_type register_type, bool re_register,\n\t\t\t\t\tunsigned long expires = 0);\n\t\t\t\t\t\n\t// OPTIONS outside dialog\n\tvoid options(const t_url &to_uri, const string &to_display = \"\");\n\t\n\t/** @name MWI */\n\t//@{\n\t/** Subscribe to MWI. */\n\tvoid subscribe_mwi(void);\n\t\n\t/** Unsusbcribe to MWI. */\n\tvoid unsubscribe_mwi(void);\n\t\n\t/**\n\t * Check if an MWI subscription is established.\n\t * @return true, if an MWI subscription is established.\n\t * @return false, otherwise\n\t */\n\tbool is_mwi_subscribed(void) const;\n\t\n\t/**\n\t * Check if an MWI dialog does exist.\n\t * @return true, if there is no MWI subscription dialog.\n\t * @return false, otherwise\n\t */\n\tbool is_mwi_terminated(void) const;\n\t\n\t/**\n\t * Process an unsolicited NOTIFY for MWI.\n\t * @param r [in] The NOTIFY request.\n\t * @param tid [in] Transaction identifier of the NOTIFY transaction.\n\t */\n\tvoid handle_mwi_unsolicited(t_request *r, t_tid tid);\n\t//@}\n\t\n\t/** @name Presence */\n\t//@{\n\t/** Subscribe to presence of buddies in buddy list. */\n\tvoid subscribe_presence(void);\n\t\n\t/** Unsusbcribe to presence of buddies in buddy list. */\n\tvoid unsubscribe_presence(void);\n\t\n\t/**\n\t * Check if all presence subscriptions are terminated.\n\t * @return true, if all presence subscriptions are terminated.\n\t * @return false, otherwise\n\t */\n\tbool is_presence_terminated(void) const;\n\t\n\t/**\n\t * Publish presence.\n\t * @param basic_state [in] The basic presence state to publish.\n\t */\n\tvoid publish_presence(t_presence_state::t_basic_state basic_state);\n\t\n\t/** Unpublish presence. */\n\tvoid unpublish_presence(void);\n\t//@}\n\t\n\t/**\n\t * Send a text message.\n\t * @param to_uri [in] Destination URI of recipient.\n\t * @param to_display [in] Display name of recipient.\n\t * @param msg [in] The message to send.\n\t * @return True if sending succeeded, otherwise false.\n\t */\n\tbool send_message(const t_url &to_uri, const string &to_display, const t_msg &message);\n\t\n\t/**\n\t * @param to_uri [in] Destination URI of recipient.\n\t * @param to_display [in] Display name of recipient.\n\t * @param state [in] Message composing state.\n\t * @param refresh [in] The refresh interval in seconds (when state is active).\n\t * @return True if sending succeeded, false otherwise.\n\t * @note For the idle state, the value of refresh has no meaning.\n\t */\n\tbool send_im_iscomposing(const t_url &to_uri, const string &to_display, \n\t\t\tconst string &state, time_t refresh);\n\t\n\t/**\n\t * Process incoming MESSAGE request.\n\t * @param r [in] The MESSAGE request.\n\t * @param tid [in] Transaction id of the request transaction.\n\t */\n\tvoid recvd_message(t_request *r, t_tid tid);\n\t\n\t/**\n\t * Process incoming NOTIFY reqeust.\n\t * @param r [in] The NOTIFY request.\n\t * @param tid [in] Transaction id of the request transaction.\n\t */\n\tvoid recvd_notify(t_request *r, t_tid tid);\n\t\n\t/** @name Process timeouts */\n\t//@{\n\t/** \n\t * Process phone timer expiry.\n\t * @param timer [in] Type of expired phone timer.\n\t */\n\tvoid timeout(t_phone_timer timer);\n\t\n\t/**\n\t * Proces subscribe timer expiry.\n\t * @param timer [in] Type of expired subscribe timer.\n\t * @param id_timer [in] Id of expired timer.\n\t */\n\tvoid timeout_sub(t_subscribe_timer timer, t_object_id id_timer);\n\t\n\t/**\n\t * Proces publish timer expiry.\n\t * @param timer [in] Type of expired subscribe timer.\n\t * @param id_timer [in] Id of expired timer.\n\t */\n\tvoid timeout_publish(t_publish_timer timer, t_object_id id_timer);\n\t//@}\n\t\n\t/** Handle a broken persistent connection. */\n\tvoid handle_broken_connection(void);\n\t\n\t/** Match subscribe timeout with a subcription\n\t * @param timer [in] Type of expired subscribe timer.\n\t * @param id_timer [in] Id of expired timer.\n\t * @return True if timer matches a subscription owned by the phone user.\n\t * @return False, otherwise.\n\t */\n\tbool match_subscribe_timer(t_subscribe_timer timer, t_object_id id_timer) const;\n\t\n\t/** Match publish timeout with a subcription\n\t * @param timer [in] Type of expired publish timer.\n\t * @param id_timer [in] Id of expired timer.\n\t * @return True if timer matches a publication owned by the phone user.\n\t * @return False, otherwise.\n\t */\n\tbool match_publish_timer(t_publish_timer timer, t_object_id id_timer) const;\n\t\n\t/**\n\t * Start the re-subscribe timer after an MWI subscription failure.\n\t * @param duration [in] Duration before trying a re-subscribe (s)\n\t */\n\tvoid start_resubscribe_mwi_timer(unsigned long duration);\n\t\n\t/** Stop MWI re=subscribe timer. */\n\tvoid stop_resubscribe_mwi_timer(void);\n\t\n\t/**\n\t * Create request. \n\t * Headers that are the same for each request\n\t * are already populated: Via, From, Max-Forwards, User-Agent.\n\t * All possible destinations for a failover are calculated.\n\t * @param m [in] Request method.\n\t * @param request_uri [in] Request-URI.\n\t * @return The created request.\n\t */\n\tt_request *create_request(t_method m, const t_url &request_uri) const;\n\t\n\t// Create a response to an OPTIONS request\n\t// Argument 'in-dialog' indicates if the OPTIONS response is\n\t// sent within a dialog.\n\tt_response *create_options_response(t_request *r,\n\t\t\t\t\tbool in_dialog = false) const;\n\t\n\t// Get registration status\n\tbool get_is_registered(void) const;\n\tbool get_last_reg_failed(void) const;\n\t\n\t/**\n\t * Get local IP address for SIP.\n\t * @param auto_ip [in] IP address to use if no IP address has been determined through\n\t *                     some NAT procedure.\n\t * @return The IP address.\n\t */\n\tstring get_ip_sip(const string &auto_ip) const;\n\t\n\t/**\n\t * Get local port for SIP.\n\t * @return SIP port.\n\t */ \n\tunsigned short get_public_port_sip(void) const;\n\t\n\t/** \n\t * Get the service route.\n\t * @return The service route.\n\t */\n\tlist<t_route> get_service_route(void) const;\n\n\t// Try to match message with phone user\n\tbool match(t_response *r, t_tuid tuid) const;\n\tbool match(t_request *r) const;\n\tbool match(StunMessage *r, t_tuid tuid) const;\n\t\n\t/**\n\t * Authorize the request based on the challenge in the response\n\t * @param r [inout] The request to be authorized.\n\t * @param resp [in] The response containing the challenge (401/407).\n\t * @param True if authorization succeeds, false otherwise.\n\t * @post On successful return the request r contains the correct authorization\n\t * header (based on 401/407 response).\n\t */\n\tbool authorize(t_request *r, t_response *resp);\n\t\n\t/**\n\t * Resend the request: a new sequence number will be assigned and a new via\n\t * header created (new transaction).\n\t * @param req [in] The request to resend.\n\t * @param cr [in] is the current client request wrapper for this request.\n\t * @note In case of a REGISTER, the internal register seqnr will be increased.\n\t */\n\tvoid resend_request(t_request *req, t_client_request *cr);\n\t\n\t/**\n\t * Remove cached credentials for a particular realm.\n\t * @param realm [in] The realm.\n\t */\n\tvoid remove_cached_credentials(const string &realm);\n\t\n\t/**\n\t * Check if this phone user is active.\n\t * @return True if phone user is active, false otherwise.\n\t */\n\tbool is_active(void) const;\n\t\n\t/**\n\t * Activate phone user.\n\t * @param user [in] The user profile of the user.\n\t * @note The passed user profile will replace the current user profile\n\t * owned by phone user. During the deactivated state the profile may\n\t * have been update.\n\t */\n\tvoid activate(const t_user &user);\n\t\n\t/** Deactivate phone user. */\n\tvoid deactivate(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/presence/CMakeLists.txt",
    "content": "project(libtwinkle-presence)\n\nset(LIBTWINKLE_PRESENCE-SRCS\n\tbuddy.cpp\n\tpidf_body.cpp\n\tpresence_dialog.cpp\n\tpresence_epa.cpp\n\tpresence_state.cpp\n\tpresence_subscription.cpp\n)\n\nadd_library(libtwinkle-presence OBJECT ${LIBTWINKLE_PRESENCE-SRCS})\n"
  },
  {
    "path": "src/presence/buddy.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"buddy.h\"\n\n#include <cassert>\n\n#include \"log.h\"\n#include \"phone.h\"\n#include \"phone_user.h\"\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n\nextern t_phone\t\t*phone;\nextern t_event_queue\t*evq_timekeeper;\n\n/** Buddy */\n\nvoid t_buddy::cleanup_presence_dialog(void) {\n\tassert(phone_user);\n\n\tif (presence_dialog && presence_dialog->get_subscription_state() == SS_TERMINATED) {\n\t\tstring reason_termination = presence_dialog->get_reason_termination();\n\t\tbool may_resubscribe = presence_dialog->get_may_resubscribe();\n\t\tunsigned long dur_resubscribe = presence_dialog->get_resubscribe_after();\n\t\t\n\t\tMEMMAN_DELETE(presence_dialog);\n\t\tdelete presence_dialog;\n\t\tpresence_dialog = NULL;\n\t\tphone_user->stun_binding_inuse_presence--;\n\t\tphone_user->cleanup_stun_data();\n\t\tphone_user->cleanup_nat_keepalive();\n\t\t\n\t\tif (presence_auto_resubscribe) {\n\t\t\tif (may_resubscribe) {\n\t\t\t\tif (dur_resubscribe > 0) {\n\t\t\t\t\tstart_resubscribe_presence_timer(dur_resubscribe * 1000);\n\t\t\t\t} else {\n\t\t\t\t\tsubscribe_presence();\n\t\t\t\t}\n\t\t\t} else if (reason_termination.empty()) {\n\t\t\t\tstart_resubscribe_presence_timer(DUR_PRESENCE_FAILURE * 1000);\n\t\t\t}\n\t\t}\n\t}\n}\n\nt_buddy::t_buddy() :\n\tphone_user(NULL),\n\tmay_subscribe_presence(false),\n\tpresence_state(this),\n\tpresence_dialog(NULL),\n\tsubscribe_after_stun(false),\n\tpresence_auto_resubscribe(false),\n\tdelete_after_presence_terminated(false),\n\tid_resubscribe_presence(0)\n{\n}\n\nt_buddy::t_buddy(t_phone_user *_phone_user) :\n\tphone_user(_phone_user),\n\tmay_subscribe_presence(false),\n\tpresence_state(this),\n\tpresence_dialog(NULL),\n\tsubscribe_after_stun(false),\n\tpresence_auto_resubscribe(false),\n\tdelete_after_presence_terminated(false),\n\tid_resubscribe_presence(0)\n{\n}\n\nt_buddy::t_buddy(t_phone_user *_phone_user, const string _name, const string &_sip_address) :\n\tphone_user(_phone_user),\n\tname(_name),\n\tsip_address(_sip_address),\n\tmay_subscribe_presence(false),\n\tpresence_state(this),\n\tpresence_dialog(NULL),\n\tsubscribe_after_stun(false),\n\tpresence_auto_resubscribe(false),\n\tdelete_after_presence_terminated(false),\n\tid_resubscribe_presence(0)\n{\n}\n\nt_buddy::t_buddy(const t_buddy &other) :\n\tphone_user(other.phone_user),\n\tname(other.name),\n\tsip_address(other.sip_address),\n\tmay_subscribe_presence(other.may_subscribe_presence),\n\tpresence_state(this),\n\tpresence_dialog(NULL),\n\tsubscribe_after_stun(false),\n\tpresence_auto_resubscribe(false),\n\tdelete_after_presence_terminated(false),\n\tid_resubscribe_presence(0)\n{}\n\nt_buddy::~t_buddy() {\n\tif (presence_dialog) {\n\t\tMEMMAN_DELETE(presence_dialog);\n\t\tdelete presence_dialog;\n\t}\n}\n\nstring t_buddy::get_name(void) const {\n\treturn name;\n}\n\nstring t_buddy::get_sip_address(void) const {\n\treturn sip_address;\n}\n\nbool t_buddy::get_may_subscribe_presence(void) const {\n\treturn may_subscribe_presence;\n}\n\nconst t_presence_state *t_buddy::get_presence_state(void) const {\n\treturn &presence_state;\n}\n\nt_user *t_buddy::get_user_profile(void) {\n\tassert(phone_user);\n\treturn phone_user->get_user_profile();\n}\n\nt_buddy_list *t_buddy::get_buddy_list(void) {\n\tassert(phone_user);\n\treturn phone_user->get_buddy_list();\n}\n\nvoid t_buddy::set_phone_user(t_phone_user *_phone_user) {\n\tphone_user = _phone_user;\n}\n\nvoid t_buddy::set_name(const string &_name) {\n\tname = _name;\n\tnotify();\n}\n\nvoid t_buddy::set_sip_address(const string &_sip_address) {\n\tsip_address = _sip_address;\n\tnotify();\n}\n\nvoid t_buddy::set_may_subscribe_presence(bool _may_subscribe_presence) {\n\tmay_subscribe_presence = _may_subscribe_presence;\n\tnotify();\n}\n\nbool t_buddy::match_response(t_response *r, t_tuid tuid) const {\n\treturn (presence_dialog && presence_dialog->match_response(r, tuid));\n}\n\nbool t_buddy::match_request(t_request *r) const {\n\tif (!presence_dialog) return false;\n\t\n\tbool partial_match = false;\n\tbool match = presence_dialog->match_request(r, partial_match);\n\t\n\tif (match) return true;\n\t\n\tif (partial_match && presence_dialog->get_remote_tag().empty()) {\n\t\t// A NOTIFY may be received before a 2XX on SUBSCRIBE.\n\t\t// In this case the NOTIFY will establish the dialog.\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nbool t_buddy::match_timer(t_subscribe_timer timer, t_object_id id_timer) const {\n\tif (presence_dialog && presence_dialog->match_timer(timer, id_timer)) {\n\t\treturn true;\n\t}\n\t\n\treturn id_timer == id_resubscribe_presence;\n}\n\nvoid t_buddy::timeout(t_subscribe_timer timer, t_object_id id_timer) {\n\tswitch (timer) {\n\tcase STMR_SUBSCRIPTION:\n\t\tif (presence_dialog && presence_dialog->match_timer(timer, id_timer)) {\n\t\t\t(void)presence_dialog->timeout(timer);\n\t\t\tcleanup_presence_dialog();\n\t\t} else if (id_timer == id_resubscribe_presence) {\n\t\t\t// Try to subscribe to presence\n\t\t\tid_resubscribe_presence = 0;\n\t\t\tsubscribe_presence();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nvoid t_buddy::recvd_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tif (presence_dialog) {\n\t\tpresence_dialog->recvd_response(r, tuid, tid);\n\t\tcleanup_presence_dialog();\n\t}\n}\n\nvoid t_buddy::recvd_request(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (presence_dialog) {\n\t\tpresence_dialog->recvd_request(r, tuid, tid);\n\t\tcleanup_presence_dialog();\n\t}\n}\n\nvoid t_buddy::start_resubscribe_presence_timer(unsigned long duration) {\n\tt_tmr_subscribe\t*t;\n\tt = new t_tmr_subscribe(duration, STMR_SUBSCRIPTION, 0, 0, SIP_EVENT_PRESENCE, \"\");\n\tMEMMAN_NEW(t);\n\tid_resubscribe_presence = t->get_object_id();\n\t\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_buddy::stop_resubscribe_presence_timer(void) {\n\tif (id_resubscribe_presence != 0) {\n\t\tevq_timekeeper->push_stop_timer(id_resubscribe_presence);\n\t\tid_resubscribe_presence = 0;\n\t}\n}\n\nvoid t_buddy::stun_completed(void) {\n\tif (subscribe_after_stun) {\n\t\tsubscribe_after_stun = false;\n\t\tsubscribe_presence();\n\t}\n}\n\nvoid t_buddy::stun_failed(void) {\n\tif (subscribe_after_stun) {\n\t\tsubscribe_after_stun = false;\n\t\tstart_resubscribe_presence_timer(DUR_PRESENCE_FAILURE * 1000);\n\t}\n}\n\nvoid t_buddy::subscribe_presence(void) {\n\tassert(phone_user);\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tif (!may_subscribe_presence) return;\n\t\n\tpresence_auto_resubscribe = true;\n\n\tif (presence_dialog) {\n\t\t// Already subscribed.\n\t\tlog_file->write_header(\"t_buddy::subscribe_presence\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Already subscribed to presence: \");\n\t\tlog_file->write_raw(name);\n\t\tlog_file->write_raw(\", \");\n\t\tlog_file->write_raw(sip_address);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\t\n\t// If STUN is enabled, then do a STUN query before registering to\n\t// determine the public IP address.\n\tif (phone_user->use_stun) {\n\t\tif (phone_user->stun_public_ip_sip == 0)\n\t\t{\n\t\t\tphone_user->send_stun_request();\n\t\t\tphone_user->presence_subscribe_after_stun = true;\n\t\t\tsubscribe_after_stun = true;\n\t\t\treturn;\n\t\t}\n\t\tphone_user->stun_binding_inuse_presence++;\n\t}\n\t\n\tpresence_dialog = new t_presence_dialog(phone_user, &presence_state);\n\tMEMMAN_NEW(presence_dialog);\n\t\n\tstring dest = ui->expand_destination(user_config, sip_address);\n\tt_url dest_url(dest);\n\tif (!dest_url.is_valid()) {\n\t\tlog_file->write_header(\"t_buddy::subscribe_presence\", LOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Invalid SIP address: \");\n\t\tlog_file->write_raw(sip_address);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\treturn;\n\t}\n\t\n\tpresence_dialog->subscribe(DUR_PRESENCE(user_config), dest_url, dest_url, \"\");\n\t\n\t// Start sending NAT keepalive packets when STUN is used\n\t// (or in case of symmetric firewall)\n\tif (phone_user->use_nat_keepalive && phone_user->id_nat_keepalive == 0) {\n\t\t// Just start the NAT keepalive timer. The SUBSCRIBE\n\t\t// message will create the NAT binding. So there is\n\t\t// no need to send a NAT keep alive packet now.\n\t\tphone->start_timer(PTMR_NAT_KEEPALIVE, phone_user);\n\t}\n\t\n\tcleanup_presence_dialog();\n}\n\nvoid t_buddy::unsubscribe_presence(bool remove) {\n\tpresence_auto_resubscribe = false;\n\tstop_resubscribe_presence_timer();\n\tpresence_state.set_basic_state(t_presence_state::ST_BASIC_UNKNOWN);\n\tdelete_after_presence_terminated = remove;\n\n\tif (presence_dialog) {\n\t\tpresence_dialog->unsubscribe();\n\t\tcleanup_presence_dialog();\n\t}\n}\n\nbool t_buddy::create_file_record(vector<string> &v) const {\n\tif (delete_after_presence_terminated) return false;\n\n\tv.clear();\n\tv.push_back(name);\n\tv.push_back(sip_address);\n\tv.push_back((may_subscribe_presence ? \"y\" : \"n\"));\n\t\n\treturn true;\n}\n\nbool t_buddy::populate_from_file_record(const vector<string> &v) {\n\tif (v.size() !=3 ) return false;\n\t\n\tname = v[0];\n\tsip_address = v[1];\n\tmay_subscribe_presence = (v[2] == \"y\");\n\t\n\treturn true;\n}\n\nbool t_buddy::operator==(const t_buddy &other) const {\n\treturn (name == other.name && sip_address == other.sip_address);\n}\n\nvoid t_buddy::clear_presence(void) {\n\tif (id_resubscribe_presence) stop_resubscribe_presence_timer();\n\t\n\tif (presence_dialog) {\n\t\tMEMMAN_DELETE(presence_dialog);\n\t\tdelete presence_dialog;\n\t\tpresence_dialog = NULL;\n\t}\n\t\n\tpresence_state.set_basic_state(t_presence_state::ST_BASIC_UNKNOWN);\n}\n\nbool t_buddy::is_presence_terminated(void) const {\n\treturn presence_dialog == NULL;\n}\n\nbool t_buddy::must_delete_now(void) const {\n\treturn delete_after_presence_terminated && is_presence_terminated();\n}\n\n/** Buddy list */\n\nvoid t_buddy_list::add_record(const t_buddy &record) {\n\tt_buddy r(record);\n\tr.set_phone_user(phone_user);\n\tutils::t_record_file<t_buddy>::add_record(r);\n}\n\nt_buddy_list::t_buddy_list(t_phone_user *_phone_user) :\n\tphone_user(_phone_user),\n\tis_subscribed(false)\n{\n\tt_user *user_config = phone_user->get_user_profile();\n\n\tset_header(\"name|sip_address|subscribe\");\n\tset_separator('|');\n\t\n\tstring filename = user_config->get_profile_name() + BUDDY_FILE_EXT;\n\tstring f = user_config->expand_filename(filename);\n\tset_filename(f);\n}\n\nt_user *t_buddy_list::get_user_profile(void) {\n\treturn phone_user->get_user_profile();\n}\n\nt_buddy *t_buddy_list::add_buddy(const t_buddy &buddy) {\n\tt_buddy *b = NULL;\n\t\n\tmtx_records.lock();\n\tadd_record(buddy);\n\t\n\t// KLUDGE: this code assumes that the buddy is added at the end.\n\tb = &records.back();\n\tmtx_records.unlock();\n\t\n\tlog_file->write_header(\"t_buddy_list::add_buddy\");\n\tlog_file->write_raw(\"Added buddy: \");\n\tlog_file->write_raw(b->get_name());\n\tlog_file->write_raw(\", \");\n\tlog_file->write_raw(b->get_sip_address());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\treturn b;\n}\n\nvoid t_buddy_list::del_buddy(const t_buddy &buddy) {\n\tmtx_records.lock();\n\t\n\tlist<t_buddy>::iterator it = find(records.begin(), records.end(), buddy);\n\t\t\t\n\tif (it == records.end()) {\n\t\tmtx_records.unlock();\n\t\treturn;\n\t}\n\t\n\tlog_file->write_header(\"t_buddy_list::del_buddy\");\n\tlog_file->write_raw(\"Delete buddy: \");\n\tlog_file->write_raw(buddy.get_name());\n\tlog_file->write_raw(\", \");\n\tlog_file->write_raw(buddy.get_sip_address());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\trecords.erase(it);\n\t\n\tmtx_records.unlock();\n}\n\nbool t_buddy_list::match_response(t_response *r, t_tuid tuid, t_buddy **buddy) {\n\t*buddy = NULL;\n\t\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tif (it->match_response(r, tuid)) {\n\t\t\t*buddy = &(*it);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tmtx_records.unlock();\n\treturn *buddy != NULL;\n}\n\nbool t_buddy_list::match_request(t_request *r, t_buddy **buddy) {\n\t*buddy = NULL;\n\t\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tif (it->match_request(r)) {\n\t\t\t*buddy = &(*it);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tmtx_records.unlock();\n\treturn *buddy != NULL;\n}\n\nbool t_buddy_list::match_timer(t_subscribe_timer timer, t_object_id id_timer, t_buddy **buddy) {\n\t*buddy = NULL;\n\t\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tif (it->match_timer(timer, id_timer)) {\n\t\t\t*buddy = &(*it);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tmtx_records.unlock();\n\treturn *buddy != NULL;\n}\n\nvoid t_buddy_list::stun_completed(void) {\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tit->stun_completed();\n\t}\n\t\n\tmtx_records.unlock();\n}\n\nvoid t_buddy_list::stun_failed(void) {\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tit->stun_failed();\n\t}\n\t\n\tmtx_records.unlock();\n}\n\nvoid t_buddy_list::subscribe_presence(void) {\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tit->subscribe_presence();\n\t}\n\t\n\tis_subscribed = true;\n\t\n\tmtx_records.unlock();\n}\n\nvoid t_buddy_list::unsubscribe_presence(void) {\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tit->unsubscribe_presence();\n\t}\n\t\n\tis_subscribed = false;\n\t\n\tmtx_records.unlock();\n}\n\nbool t_buddy_list::get_is_subscribed() const {\n\tbool result;\n\t\n\tmtx_records.lock();\n\tresult = is_subscribed;\n\tmtx_records.unlock();\n\t\n\treturn result;\n}\n\nvoid t_buddy_list::clear_presence(void) {\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::iterator it = records.begin(); it != records.end(); ++it) {\n\t\tit->clear_presence();\n\t}\n\t\n\tis_subscribed = false;\n\t\n\tmtx_records.unlock();\n}\n\nbool t_buddy_list::is_presence_terminated(void) const {\n\tbool result = true;\n\tmtx_records.lock();\n\t\n\tfor (list<t_buddy>::const_iterator it = records.begin(); it != records.end(); ++it) {\n\t\tif (!it->is_presence_terminated()) {\n\t\t\tresult = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tmtx_records.unlock();\n\t\n\treturn result;\n}\n"
  },
  {
    "path": "src/presence/buddy.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Buddy list\n */\n\n#ifndef _BUDDY_H\n#define _BUDDY_H\n\n#include <string>\n#include <set>\n\n#include \"presence_state.h\"\n#include \"presence_dialog.h\"\n\n#include \"sockets/url.h\"\n#include \"utils/record_file.h\"\n#include \"patterns/observer.h\"\n\n// Forward declaration\nclass t_phone_user;\nclass t_buddy_list;\n\n#define BUDDY_FILE_EXT\t\t\".bud\"\n\nusing namespace std;\n\n/** Buddy */\nclass t_buddy : public utils::t_record, public patterns::t_subject {\nprivate:\n\t/** Phone user owning this buddy. */\n\tt_phone_user\t*phone_user;\n\n\t/** Name of buddy (for display only) */\n\tstring\tname;\n\t\n\t/** SIP address of the buddy. */\n\tstring\tsip_address;\n\t\n\t/** Indicates if the user may subscribe to the presence state of the buddy. */\n\tbool\tmay_subscribe_presence;\n\t\n\t/** Presence state. */\n\tt_presence_state\tpresence_state;\n\t\n\t/** Presence subscription dialog. */\n\tt_presence_dialog\t*presence_dialog;\n\t\n\t/** Subscribe to presence after STUN transaction completed. */\n\tbool\tsubscribe_after_stun;\n\t\n\t/**\n\t * Indicates if presence must be automatically resubscribed to, if the\n\t * subscription terminates with a reason telling that resubscription\n\t * is possible.\n\t */\n\tbool\tpresence_auto_resubscribe;\n\t\n\t/** \n\t * Indicates if the buddy must be deleted after the presence subscription\n\t * has been terminated.\n\t */\n\tbool\tdelete_after_presence_terminated;\n\t\n\t/** Handle presence dialog termination. */\n\tvoid cleanup_presence_dialog(void);\n\t\npublic:\n\t/** Interval before trying to resubscribe to presence after a failure. */\n\tt_object_id\tid_resubscribe_presence;\n\t\n\t/** Constructor. */\n\tt_buddy();\n\n\t/** Constructor. */\n\tt_buddy(t_phone_user *_phone_user);\n\n\t/** Constructor. */\n\tt_buddy(t_phone_user *_phone_user, const string _name, const string &_sip_address);\n\t\n\t/** Copy constructor. */\n\tt_buddy(const t_buddy &other);\n\t\n\t/** Destructor. */\n\tvirtual ~t_buddy();\n\t\n\t/** @name Getters */\n\t//@{\n\tstring get_name(void) const;\n\tstring get_sip_address(void) const;\n\tbool get_may_subscribe_presence(void) const;\n\tconst t_presence_state *get_presence_state(void) const;\n\t//@}\n\t\n\t/**\n\t * Get user profile for the user owning this buddy.\n\t * @return User profile.\n\t */\n\tt_user *get_user_profile(void);\n\t\n\t/**\n\t * Get the buddy list containing this buddy.\n\t * @return Buddy list\n\t */\n\tt_buddy_list *get_buddy_list(void);\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_phone_user(t_phone_user *_phone_user);\n\tvoid set_name(const string &_name);\n\tvoid set_sip_address(const string &_sip_address);\n\tvoid set_may_subscribe_presence(bool _may_subscribe_presence);\n\t//@}\n\t\n\t/**\n\t * Match response with a buddy. It matches if the buddy\n\t * has a presence dialog that matches with the response.\n\t * @param r [in] The response.\n\t * @param tuid [in] Transaction user id.\n\t * @return True if the response matches, otherwise false.\n\t */\n\tbool match_response(t_response *r, t_tuid tuid) const;\n\t\n\t/**\n\t * Match request with buddy list. It matches if a buddy in the list\n\t * has a presence dialog that matches with the request.\n\t * @param r [in] The request.\n\t * @return True if the request matches, otherwise false.\n\t */\n\tbool match_request(t_request *r) const;\n\t\n\t/**\n\t * Match a timer id with a running timer.\n\t * @param timer [in] The running timer.\n\t * @param id_timer [in] The timer id.\n\t * @return true, if timer id matches with timer.\n\t * @return false, otherwise.\n\t */\n\tbool match_timer(t_subscribe_timer timer, t_object_id id_timer) const;\n\t\n\t/**\n\t * Process timeout.\n\t * @param timer [in] The timer that expired.\n\t * @param id_timer [in] The timer id.\n\t */\n\tvoid timeout(t_subscribe_timer timer, t_object_id id_timer);\n\n\t/**\n\t * Handle received response.\n\t * @param r [in] The response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid recvd_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Handle received request.\n\t * @param r [in] The request.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid recvd_request(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Start the re-subscribe timer after a presence subscription failure.\n\t * @param duration [in] Duration before trying a re-subscribe (s)\n\t */\n\tvoid start_resubscribe_presence_timer(unsigned long duration);\n\t\n\t/** Stop presence re-subscribe timer. */\n\tvoid stop_resubscribe_presence_timer(void);\n\t\n\t/**\n\t * By calling this method, successful STUN completion is signalled to\n\t * the buddy. It will subscribe to presence if it was waiting for STUN.\n\t */\n\tvoid stun_completed(void);\n\t\n\t/**\n\t * By calling this method, a STUN failure is signalled to\n\t * the buddy. It will reschedule a presence subscription if it is\n\t * waiting for STUN to complete.\n\t */\n\tvoid stun_failed(void);\n\t\n\t/** Subscribe to presence of the buddy if we may do so. */\n\tvoid subscribe_presence(void);\n\t\n\t/** Unsubscribe to presence.\n\t * @param remove [in] Indicates if the buddy must be deleted after unsubscription.\n\t */\n\tvoid unsubscribe_presence(bool remove = false);\n\t\n\tvirtual bool create_file_record(vector<string> &v) const;\n\tvirtual bool populate_from_file_record(const vector<string> &v);\n\t\n\t/** Compare 2 buddies for equality (same SIP address) */\n\tbool operator==(const t_buddy &other) const;\n\t\n\t/** Clear presence state. */\n\tvoid clear_presence(void);\n\t\n\t/**\n\t * Check if presence subscription is terminated.\n\t * @return true, if presence subscriptions is terminated.\n\t * @return false, otherwise\n\t */\n\tbool is_presence_terminated(void) const;\n\t\n\t/**\n\t * Check if buddy must be deleted.\n\t * @return true, if the buddy must be deleted immediately.\n\t * @return false, otherwise.\n\t */\n\tbool must_delete_now(void) const;\n};\n\n/** List of buddies for a particular account. */\nclass t_buddy_list : public utils::t_record_file<t_buddy> {\nprivate:\n\t/** Phone user owning this buddy list. */\n\tt_phone_user\t*phone_user;\n\t\n\t/** \n\t * Indicates if subscribe is done. This indicator will be set to false\n\t * when you call unsubscribe.\n\t */\n\tbool\t\tis_subscribed;\n\nprotected:\n\tvirtual void add_record(const t_buddy &record);\n\t\npublic:\n\t/** Constructor. */\n\tt_buddy_list(t_phone_user *_phone_user);\n\t\n\t/**\n\t * Get the user profile for this buddy list.\n\t * @return User profile.\n\t */\n\tt_user *get_user_profile(void);\n\n\t/**\n\t * Add a buddy.\n\t * @param buddy [in] Buddy to add.\n\t * @return Pointer to added buddy.\n\t * @note This method adds a copy of the buddy. It returns a pointer to this copy.\n\t */\n\tt_buddy *add_buddy(const t_buddy &buddy);\n\t\n\t/**\n\t * Delete a buddy.\n\t * @param buddy [in] Buddy to delete.\n\t */\n\tvoid del_buddy(const t_buddy &buddy);\n\t\n\t/**\n\t * Match response with buddy list. It matches if a buddy in the list\n\t * has a presence dialog that matches with the response.\n\t * @param r [in] The response.\n\t * @param tuid [in] Transaction user id.\n\t * @param buddy [out] On a match, this parameter contains the matching buddy.\n\t * @return True if the response matches, otherwise false.\n\t */\n\tbool match_response(t_response *r, t_tuid tuid, t_buddy **buddy);\n\t\n\t/**\n\t * Match request with buddy list. It matches if a buddy in the list\n\t * has a presence dialog that matches with the request.\n\t * @param r [in] The request.\n\t * @param buddy [out] On a match, this parameter contains the matching buddy.\n\t * @return True if the request matches, otherwise false.\n\t */\n\tbool match_request(t_request *r, t_buddy **buddy);\n\t\n\t/**\n\t * Match a timer id with a running timer. A timer id matches with the\n\t * buddy list if it matches with one of the buddies in the list.\n\t * @param timer [in] The running timer.\n\t * @param id_timer [in] The timer id.\n\t * @param buddy [out] On a match, this parameter contains the matching buddy.\n\t * @return true, if timer id matches with timer.\n\t * @return false, otherwise.\n\t */\n\tbool match_timer(t_subscribe_timer timer, t_object_id id_timer, t_buddy **buddy);\n\t\n\t/**\n\t * By calling this method, successful STUN completion is signalled to the buddy\n\t * list. The buddy list will now start presence subscriptions that were waiting\n\t * for STUN to complete.\n\t */\n\tvoid stun_completed(void);\n\t\n\t/**\n\t * By calling this method, a STUN failure is signalled to the buddy list.\n\t * The buddy list will reschedule presence subscriptions that were waiting\n\t * for STUN to complete.\n\t */\n\tvoid stun_failed(void);\n\t\n\t/** Subscribe to presence of all buddies in the list. */\n\tvoid subscribe_presence(void);\n\t\n\t/** Unsubscribe to presence of all buddies in the list. */\n\tvoid unsubscribe_presence(void);\n\t\n\t/**\n\t * Check if user is subcribed to buddy list presence.\n\t * @return True if subscribed, otherwise false.\n\t */\n\tbool get_is_subscribed() const;\n\t\n\t/** Clear presence state of all buddies. */\n\tvoid clear_presence(void);\n\t\n\t/**\n\t * Check if all presence subscriptions are terminated.\n\t * @return true, if all presence subscriptions are terminated.\n\t * @return false, otherwise\n\t */\n\tbool is_presence_terminated(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/presence/pidf_body.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"pidf_body.h\"\n\n#include <cassert>\n#include <libxml/parser.h>\n\n#include \"log.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\n#define PIDF_XML_VERSION\t\"1.0\"\n#define PIDF_NAMESPACE\t\t\"urn:ietf:params:xml:ns:pidf\"\n\n#define IS_PIDF_TAG(node, tag)\tIS_XML_TAG(node, tag, PIDF_NAMESPACE)\n\t\t\t\t\n#define IS_PIDF_ATTR(attr, attr_name) IS_XML_ATTR(attr, attr_name, PIDF_NAMESPACE)\n\nbool t_pidf_xml_body::extract_status(void) {\n\tassert(xml_doc);\n\n\txmlNode *root_element = NULL;\n\t\n\t// Get root\n\troot_element = xmlDocGetRootElement(xml_doc);\n\tif (!root_element) {\n\t\tlog_file->write_report(\"PIDF document has no root element.\",\n\t\t\t\"t_pidf_xml_body::extract_status\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\treturn false;\n\t}\n\t\n\t// Check if root is <presence>\n\tif (!IS_PIDF_TAG(root_element, \"presence\")) {\n\t\tlog_file->write_report(\"PIDF document has invalid root element.\",\n\t\t\t\"t_pidf_xml_body::extract_status\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\treturn false;\n\t}\n\t\n\tpres_entity.clear();\n\ttuple_id.clear();\n\tbasic_status.clear();\n\t\n\t// Get presence entity\n\txmlChar *prop_entity = xmlGetProp(root_element, BAD_CAST \"entity\");\n\tif (prop_entity) {\n\t\tpres_entity = (char *)prop_entity;\n\t} else {\n\t\tlog_file->write_report(\"Presence entity is missing.\",\n\t\t\t\"t_pidf_xml_body::extract_status\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t}\n\t\n\txmlNode *child = root_element->children;\n\t\n\t// Process children of root.\n\tfor (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) {\n\t\t// Process tuple\n\t\tif (IS_PIDF_TAG(cur_node, \"tuple\")) {\n\t\t\tprocess_pidf_tuple(cur_node);\n\t\t\t\n\t\t\t// Process the first tuple and then stop.\n\t\t\t// Currently there is no support for multiple tuples\n\t\t\t// or additional elements.\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\treturn true;\n}\n\nvoid t_pidf_xml_body::process_pidf_tuple(xmlNode *tuple) {\n\tassert(tuple);\n\t\n\t// Get tuple id.\n\txmlChar *id = xmlGetProp(tuple, BAD_CAST \"id\");\n\tif (id) {\n\t\ttuple_id = (char *)id;\n\t} else {\n\t\tlog_file->write_report(\"Tuple id is missing.\",\n\t\t\t\"t_pidf_xml_body::process_pidf_tuple\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t}\n\t\n\t// Find status element\n\txmlNode *child = tuple->children;\n\tfor (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) {\n\t\t// Process status\n\t\tif (IS_PIDF_TAG(cur_node, \"status\")) {\n\t\t\tprocess_pidf_status(cur_node);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid t_pidf_xml_body::process_pidf_status(xmlNode *status) {\n\tassert(status);\n\t\n\txmlNode *child = status->children;\n\tfor (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) {\n\t\t// Process status\n\t\tif (IS_PIDF_TAG(cur_node, \"basic\")) {\n\t\t\tprocess_pidf_basic(cur_node);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid t_pidf_xml_body::process_pidf_basic(xmlNode *basic) {\n\tassert(basic);\n\t\n\txmlNode *child = basic->children;\n\tif (child && child->type == XML_TEXT_NODE) {\n\t\tbasic_status = tolower((char*)child->content);\n\t} else {\n\t\tlog_file->write_report(\"<basic> element has no content.\",\n\t\t\t\"t_pidf_xml_body::process_pidf_basic\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t}\n}\n\nvoid t_pidf_xml_body::create_xml_doc(const string &xml_version, const string &charset) {\n\tt_sip_body_xml::create_xml_doc(xml_version, charset);\n\t\n\t// presence\n\txmlNode *node_presence = xmlNewNode(NULL, BAD_CAST \"presence\");\n\txmlNs *ns_pidf = xmlNewNs(node_presence, BAD_CAST PIDF_NAMESPACE, NULL);\n\txmlNewProp(node_presence, BAD_CAST \"entity\", BAD_CAST pres_entity.c_str());\n\txmlDocSetRootElement(xml_doc, node_presence);\n\t\n\t// tuple\n\txmlNode *node_tuple = xmlNewChild(node_presence, ns_pidf, BAD_CAST \"tuple\", NULL);\n\txmlNewProp(node_tuple, BAD_CAST \"id\", BAD_CAST tuple_id.c_str());\n\t\n\t// status\n\txmlNode *node_status = xmlNewChild(node_tuple, ns_pidf, BAD_CAST \"status\", NULL);\n\t\n\t// basic\n\txmlNewChild(node_status, ns_pidf, \n\t\tBAD_CAST \"basic\", BAD_CAST basic_status.c_str());\n}\n\nt_pidf_xml_body::t_pidf_xml_body() : t_sip_body_xml ()\n{}\n\nt_sip_body *t_pidf_xml_body::copy(void) const {\n\tt_pidf_xml_body *body = new t_pidf_xml_body(*this);\n\tMEMMAN_NEW(body);\n\t\n\t// Clear the xml_doc pointer in the new body, as a copy of the\n\t// XML document must be copied to the body.\n\tbody->xml_doc = NULL;\n\t\n\tcopy_xml_doc(body);\n\t\n\treturn body;\n}\n\nt_body_type t_pidf_xml_body::get_type(void) const {\n\treturn BODY_PIDF_XML;\n}\n\nt_media t_pidf_xml_body::get_media(void) const {\n\treturn t_media(\"application\", \"pidf+xml\");\n}\n\nstring t_pidf_xml_body::get_pres_entity(void) const {\n\treturn pres_entity;\n}\n\nstring t_pidf_xml_body::get_tuple_id(void) const {\n\treturn tuple_id;\n}\n\nstring t_pidf_xml_body::get_basic_status(void) const {\n\treturn basic_status;\n}\n\nvoid t_pidf_xml_body::set_pres_entity(const string &_pres_entity) {\n\tclear_xml_doc();\n\tpres_entity = _pres_entity;\n}\n\nvoid t_pidf_xml_body::set_tuple_id(const string &_tuple_id) {\n\tclear_xml_doc();\n\ttuple_id = _tuple_id;\n}\n\nvoid t_pidf_xml_body::set_basic_status(const string &_basic_status) {\n\tclear_xml_doc();\n\tbasic_status = _basic_status;\n}\n\nbool t_pidf_xml_body::parse(const string &s) {\n\tif (t_sip_body_xml::parse(s)) {\n\t\tif (!extract_status()) {\n\t\t\tMEMMAN_DELETE(xml_doc);\n\t\t\txmlFreeDoc(xml_doc);\n\t\t\txml_doc = NULL;\n\t\t}\n\t}\n\t\n\treturn (xml_doc != NULL);\n}\n"
  },
  {
    "path": "src/presence/pidf_body.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * RFC 3863 pidf+xml body\n */\n\n#ifndef _PIDF_BODY_H\n#define _PIDF_BODY_H\n\n#include <string>\n#include <libxml/tree.h>\n#include \"parser/sip_body.h\"\n\n#define PIDF_STATUS_BASIC_OPEN\t\t\"open\"\n#define PIDF_STATUS_BASIC_CLOSED\t\"closed\"\n\n/** RFC 3863 pidf+xml body */\nclass t_pidf_xml_body : public t_sip_body_xml {\nprivate:\n\tstring\t\tpres_entity;\t\t/**< Presence entity */\n\tstring\t\ttuple_id;\t/**< Id of tuple containing the basic status. */\n\tstring\t\tbasic_status;\t/**< Value of basic-tag */\n\t\n\t/**\n\t * Extract the status information from a PIDF document.\n\t * This will populate the state attributes.\n\t * @return True if PIDF document is valid, otherwise false.\n\t * @pre The @ref pidf_doc should contain a valid PIDF document.\n\t */\n\tbool extract_status(void);\n\t\n\t/**\n\t * Process tuple element.\n\t * @param tuple [in] tuple element.\n\t */\n\tvoid process_pidf_tuple(xmlNode *tuple);\n\t\n\t/**\n\t * Process status element.\n\t * @param status [in] status element.\n\t */\n\tvoid process_pidf_status(xmlNode *status);\n\t\n\t/**\n\t * Process basic element.\n\t * @param basic [in] basic element.\n\t */\n\tvoid process_pidf_basic(xmlNode *basic);\n\t\nprotected:\n\t/**\n\t * Create a pidf document from the values stored in the attributes.\n\t */\n\tvirtual void create_xml_doc(const string &xml_version = \"1.0\", const string &charset = \"UTF-8\");\n\t\npublic:\n\t/** Constructor */\n\tt_pidf_xml_body();\n\t\n\tvirtual t_sip_body *copy(void) const;\n\tvirtual t_body_type get_type(void) const;\n\tvirtual t_media get_media(void) const;\n\t\n\t/** @name Getters */\n\t//@{\n\tstring get_pres_entity(void) const;\n\tstring get_tuple_id(void) const;\n\tstring get_basic_status(void) const;\n\t//@}\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_pres_entity(const string &_pres_entity);\n\tvoid set_tuple_id(const string &_tuple_id);\n\tvoid set_basic_status(const string &_basic_status);;\n\t//@}\n\t\n\t/**\n\t * Parse a text representation of the body.\n\t * If parsing succeeds, then the state is extracted.\n\t * @param s [in] Text to parse.\n\t * @return True if parsing and state extracting succeeded, false otherwise.\n\t */\n\tvirtual bool parse(const string &s);\n};\n\n#endif\n"
  },
  {
    "path": "src/presence/presence_dialog.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"presence_dialog.h\"\n\n#include \"presence_subscription.h\"\n#include \"phone_user.h\"\n#include \"audits/memman.h\"\n\nt_presence_dialog::t_presence_dialog(t_phone_user *_phone_user, t_presence_state *presence_state) :\n\tt_subscription_dialog(_phone_user)\n{\n\tsubscription = new t_presence_subscription(this, presence_state);\n\tMEMMAN_NEW(subscription);\n}\n\nt_presence_dialog *t_presence_dialog::copy(void) {\n\t// Copy is not needed.\n\tassert(false);\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/presence/presence_dialog.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Dialog for presence subscription (RFC 3856)\n */\n\n#ifndef _PRESENCE_DIALOG_H\n#define _PRESENCE_DIALOG_H\n\n#include \"subscription_dialog.h\"\n#include \"presence_state.h\"\n\n// Forward declaration\nclass t_phone_user;\n\n/** Dialog for presence subscription (RFC 3856) */\nclass t_presence_dialog : public t_subscription_dialog {\npublic:\n\t/**\n\t * Constructor.\n\t * @param _phone_user [in] Phone user owning the dialog.\n\t * @param presence_state [in] Presence state that is updated by notification\n\t * on this dialog\n\t */\n\tt_presence_dialog(t_phone_user *_phone_user, t_presence_state *presence_state);\n\t\n\tvirtual t_presence_dialog *copy(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/presence/presence_epa.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"presence_epa.h\"\n\n#include \"pidf_body.h\"\n#include \"audits/memman.h\"\n#include \"parser/hdr_event.h\"\n\nt_presence_epa::t_presence_epa(t_phone_user *pu) :\n\tt_epa(pu, SIP_EVENT_PRESENCE, t_url(pu->get_user_profile()->create_user_uri(false))),\n\tbasic_state(t_presence_state::ST_BASIC_CLOSED),\n\ttuple_id(NEW_PIDF_TUPLE_ID)\n{}\n\nt_presence_state::t_basic_state t_presence_epa::get_basic_state(void) const {\n\treturn basic_state;\n}\n\nbool t_presence_epa::recv_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_epa::recv_response(r, tuid, tid);\n\t\n\t// Notify observers so they can get the latest publication state.\n\tnotify();\n\t\n\treturn true;\n}\n\nvoid t_presence_epa::publish_presence(t_presence_state::t_basic_state _basic_state) {\n\tif (_basic_state != t_presence_state::ST_BASIC_CLOSED &&\n\t    _basic_state != t_presence_state::ST_BASIC_OPEN)\n\t{\n\t\t// Cannot publish internal states.\n\t\treturn;\n\t}\n\t\n\tt_user *user_config = phone_user->get_user_profile();\n\tbasic_state = _basic_state;\n\t\n\t// Create PIDF document\n\tt_pidf_xml_body *pidf = new t_pidf_xml_body();\n\tMEMMAN_NEW(pidf);\n\tpidf->set_pres_entity(user_config->create_user_uri(false));\n\tpidf->set_tuple_id(tuple_id);\n\tpidf->set_basic_status(t_presence_state::basic_state2pidf_str(_basic_state));\n\t\n\tpublish(user_config->get_pres_publication_time(), pidf);\n\t\n\t// NOTE: the observers will be notified of the state change, when the\n\t//       PUBLISH response is received.\n}\n\nvoid t_presence_epa::clear(void) {\n\tt_epa::clear();\n\tbasic_state = t_presence_state::ST_BASIC_CLOSED;\n}\n"
  },
  {
    "path": "src/presence/presence_epa.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Presence Event Publication Agent (EPA) [RFC 3903]\n */\n\n#ifndef _PRESENCE_EPA_H\n#define _PRESENCE_EPA_H\n\n#include <string>\n\n#include \"epa.h\"\n#include \"presence_state.h\"\n#include \"patterns/observer.h\"\n\nusing namespace std;\n\n/** Presence Event Publication Agent (EPA) [RFC 3903] */\nclass t_presence_epa : public t_epa, public patterns::t_subject {\nprivate:\n\t/** Basic presence state. */\n\tt_presence_state::t_basic_state\tbasic_state;\n\t\n\t/** Tuple id to be put in the PIDF documents. */\n\tstring tuple_id;\n\t\npublic:\n\t/** Constructor */\n\tt_presence_epa(t_phone_user *pu);\n\n\t/** @name Getters */\n\t//@{\n\tt_presence_state::t_basic_state get_basic_state(void) const;\n\t//@}\n\t\n\tvirtual bool recv_response(t_response *r, t_tuid tuid, t_tid tid);\n\n\t/**\n\t * Publish presence state.\n\t * @param _basic_state [in] The basic presence state.\n\t * @pre _basic_state must be one of the following values:\n\t * - @ref ST_BASIC_CLOSED\n\t * - @ref ST_BASIC_OPEN\n\t */\n\tvoid publish_presence(t_presence_state::t_basic_state _basic_state);\n\t\n\tvirtual void clear(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/presence/presence_state.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"presence_state.h\"\n\n#include <cassert>\n#include \"buddy.h\"\n#include \"pidf_body.h\"\n#include \"log.h\"\n\nstring t_presence_state::basic_state2str(t_presence_state::t_basic_state state) {\n\tswitch (state) {\n\tcase ST_BASIC_UNKNOWN:\n\t\treturn \"unknown\";\n\tcase ST_BASIC_CLOSED:\n\t\treturn \"closed\";\n\tcase ST_BASIC_OPEN:\n\t\treturn \"open\";\n\tcase ST_BASIC_FAILED:\n\t\treturn \"failed\";\n\tcase ST_BASIC_REJECTED:\n\t\treturn \"rejeceted\";\n\tdefault:\n\t\treturn \"UNKNOWN\";\n\t}\n}\n\nstring t_presence_state::basic_state2pidf_str(t_presence_state::t_basic_state state) {\n\tif (state == ST_BASIC_OPEN) {\n\t\treturn PIDF_STATUS_BASIC_OPEN;\n\t}\n\t\n\t// Convert all other states to \"closed\".\n\treturn PIDF_STATUS_BASIC_CLOSED;\n}\n\nt_presence_state::t_presence_state() {\n\tassert(false);\n}\n\nt_presence_state::t_presence_state(t_buddy *_buddy) :\n\tbuddy(_buddy),\n\tbasic_state(ST_BASIC_UNKNOWN)\n{\n}\n\nt_presence_state::t_basic_state t_presence_state::get_basic_state(void) const {\n\tt_basic_state result;\n\tmtx_state.lock();\n\tresult = basic_state;\n\tmtx_state.unlock();\n\treturn result;\n}\n\nstring t_presence_state::get_failure_msg(void) const {\n\tstring result;\n\tmtx_state.lock();\n\tresult = failure_msg;\n\tmtx_state.unlock();\n\treturn result;\n}\n\nvoid t_presence_state::set_basic_state(t_presence_state::t_basic_state state) {\n\tmtx_state.lock();\n\tbasic_state = state;\n\t\n\tlog_file->write_header(\"t_presence_state::set_basic_state\", LOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Presence state changed to: \");\n\tlog_file->write_raw(basic_state2str(basic_state));\n\tlog_file->write_endl();\n\tlog_file->write_raw(buddy->get_sip_address());\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tmtx_state.unlock();\n\t\n\t// Notify the stat change to all observers of the buddy.\n\tbuddy->notify();\n}\n\nvoid t_presence_state::set_failure_msg(const string &msg) {\n\tmtx_state.lock();\n\tfailure_msg = msg;\n\tmtx_state.unlock();\n}\n"
  },
  {
    "path": "src/presence/presence_state.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Presence state (RFC 3863)\n */\n\n#ifndef _PRESENCE_STATE_H\n#define _PRESENCE_STATE_H\n\n#include <string>\n#include \"threads/mutex.h\"\n\nusing namespace std;\n\n// Forward declaration\nclass t_buddy;\n\n/** Presence state */\nclass t_presence_state {\npublic:\n\t/** Basic state (RFC 3863 4.1.4) */\n\tenum t_basic_state {\n\t\tST_BASIC_UNKNOWN, /**< Presence state is unknown. */\n\t\tST_BASIC_CLOSED,  /**< Unable to accept communication. */\n\t\tST_BASIC_OPEN,\t  /**< Ready to accept communication. */\n\t\tST_BASIC_FAILED,  /**< Failed to determine basic state. */\n\t\tST_BASIC_REJECTED,/**< Subscription has been rejected. */\n\t};\n\t\n\t/**\n\t * Convert a basic state to a string representation for internal usage.\n\t * @param state [in] A basic state value.\n\t * @return String representation of the basic state.\n\t */\n\tstatic string basic_state2str(t_basic_state state);\n\t\n\t/**\n\t * Convert a basic state to a PIDF string representation.\n\t * @param state [in] A basic state value.\n\t * @return PIDF representation of the basic state.\n\t */\n\tstatic string basic_state2pidf_str(t_basic_state state);\n\t\nprivate:\n\t/** Mutex for concurrent access to the presence state. */\n\tmutable t_mutex\tmtx_state;\n\t\n\t/** Buddy owning this state. */\n\tt_buddy\t\t*buddy;\n\t\n\t/** Basic presence state. */\n\tt_basic_state\tbasic_state;\n\t\n\t/** Detailed failure message */\n\tstring\t\tfailure_msg;\n\t\n\t/** Protect the default constructor from being used. */\n\tt_presence_state();\n\t\npublic:\n\t/** Constructor. */\n\tt_presence_state(t_buddy *_buddy);\n\t\n\t/** @name Getters */\n\t//@{\n\tt_basic_state get_basic_state(void) const;\n\tstring get_failure_msg(void) const;\n\t//@}\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_basic_state(t_basic_state state);\n\tvoid set_failure_msg(const string &msg);\n\t//@}\n};\n\n#endif\n"
  },
  {
    "path": "src/presence/presence_subscription.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"presence_subscription.h\"\n\n#include <cassert>\n\n#include \"pidf_body.h\"\n\n#include \"log.h\"\n#include \"util.h\"\n#include \"parser/hdr_event.h\"\n#include \"audits/memman.h\"\n\nt_request *t_presence_subscription::create_subscribe(unsigned long expires) const {\n\tt_request *r = t_subscription::create_subscribe(expires);\n\tSET_PRESENCE_HDR_ACCEPT(r->hdr_accept);\n\t\n\treturn r;\n}\n\nt_presence_subscription::t_presence_subscription(t_presence_dialog *_dialog, t_presence_state *_state) :\n\tt_subscription(_dialog, SR_SUBSCRIBER, SIP_EVENT_PRESENCE),\n\tpresence_state(_state)\n{\n}\n\nbool t_presence_subscription::recv_notify(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (t_subscription::recv_notify(r, tuid, tid)) return true;\n\n\tbool unsupported_body = false;\n\t\n\t// NOTE: if the subscription is still pending (RFC 3265 3.2.4), then the\n\t//       information in the body has no meaning.\n\t// NOTE: a NOTIFY request may have no body (RFC 3856 6.6.2)\n\tif (r->body && r->body->get_type() == BODY_PIDF_XML &&\n\t    !is_pending()) \n\t{\n\t\tt_pidf_xml_body *body = dynamic_cast<t_pidf_xml_body *>(r->body);\n\t\tassert(body);\n\t\t\n\t\tstring basic = body->get_basic_status();\n\t\tif (basic == PIDF_STATUS_BASIC_OPEN) {\n\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_OPEN);\n\t\t} else if (basic == PIDF_STATUS_BASIC_CLOSED) {\n\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_CLOSED);\n\t\t} else {\n\t\t\tlog_file->write_header(\"t_presence_subscription::recv_notify\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unknown basic status in pidf: \");\n\t\t\tlog_file->write_raw(basic);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_UNKNOWN);\n\t\t}\n\t}\n\t\n\t// Verify if there is an usupported body.\n\tif (r->body && r->body->get_type() != BODY_PIDF_XML) {\n\t\tunsupported_body = true;\n\t}\n\n\tif (state == SS_TERMINATED) {\n\t\tif (may_resubscribe) {\n\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_UNKNOWN);\n\t\t\tlog_file->write_report(\"Presence subscription terminated.\",\n\t\t\t\t\"t_presence_subscription::recv_notify\");\n\t\t} else {\n\t\t\tif (reason_termination == EV_REASON_REJECTED) {\n\t\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_REJECTED);\n\t\t\t\t\n\t\t\t\tlog_file->write_report(\"Presence agent rejected the subscription.\",\n\t\t\t\t\t\"t_presence_subscription::recv_notify\");\n\t\t\t} else {\n\t\t\t\t// The PA ended the subscription and indicated\n\t\t\t\t// that resubscription is not possible. So no presence status\n\t\t\t\t// can be retrieved anymore. This should not happen.\n\t\t\t\t// Show it as a failure to the user.\n\t\t\t\tpresence_state->set_failure_msg(reason_termination);\n\t\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_FAILED);\n\t\t\t\t\n\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\"Presence agent permanently terminated the subscription.\",\n\t\t\t\t\t\"t_presence_subscription::recv_notify\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tt_response *resp;\n\tif (unsupported_body) {\n\t\tresp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);\n\t\tSET_PRESENCE_HDR_ACCEPT(r->hdr_accept);\n\t} else {\n\t\tresp = r->create_response(R_200_OK);\n\t}\n\tsend_response(user_config, resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\t\n\treturn true;\n}\n\nbool t_presence_subscription::recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid) {\n\t// Parent handles the SUBSCRIBE response\n\t(void)t_subscription::recv_subscribe_response(r, tuid, tid);\n\n\t// If the subscription is terminated after the SUBSCRIBE response, it means\n\t// that subscription failed.\n\tif (state == SS_TERMINATED) {\n\t\tif (r->code == R_403_FORBIDDEN || r->code == R_603_DECLINE) {\n\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_REJECTED);\n\t\t\t\n\t\t\tlog_file->write_report(\"Presence subscription rejected.\",\n\t\t\t\t\"t_presence_subscription::recv_subscribe_response\");\n\t\t} else {\n\t\t\tstring failure = int2str(r->code);\n\t\t\tfailure += ' ';\n\t\t\tfailure += r->reason;\n\t\t\tpresence_state->set_failure_msg(failure);\n\t\t\tpresence_state->set_basic_state(t_presence_state::ST_BASIC_FAILED);\n\t\t\t\n\t\t\tlog_file->write_report(\"Presence subscription failed.\",\n\t\t\t\t\"t_presence_subscription::recv_subscribe_response\");\n\t\t}\n\t}\n\t\n\treturn true;\n}\n"
  },
  {
    "path": "src/presence/presence_subscription.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Presence subscription (RFC 3856)\n */\n\n#ifndef _PRESENCE_SUBSCRIPTION_H\n#define _PRESENCE_SUBSCRIPTION_H\n\n#include \"presence_state.h\"\n#include \"presence_dialog.h\"\n#include \"subscription.h\"\n\n/** Subscription to the presence event (RFC 3856) */\nclass t_presence_subscription : public t_subscription {\nprivate:\n\tt_presence_state\t*presence_state;\n\t\nprotected:\n\tvirtual t_request *create_subscribe(unsigned long expires) const;\n\t\npublic:\n\t/**\n\t * Constructor.\n\t * @param _dialog [in] Dialog for the presence subscription.\n\t * @param _state [in] Current presence state.\n\t */\n\tt_presence_subscription(t_presence_dialog *_dialog, t_presence_state *_state);\n\n\tvirtual bool recv_notify(t_request *r, t_tuid tuid, t_tid tid);\n\tvirtual bool recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid);\n};\n\n#endif\n"
  },
  {
    "path": "src/prohibit_thread.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"prohibit_thread.h\"\n\nvoid i_prohibit_thread::add_prohibited_thread(void) {\n\tprohibited_mutex.lock();\n\tprohibited_threads.insert(t_thread::self());\n\tprohibited_mutex.unlock();\n}\n\nvoid i_prohibit_thread::remove_prohibited_thread(void) {\n\tprohibited_mutex.lock();\n\tprohibited_threads.erase(t_thread::self());\n\tprohibited_mutex.unlock();\n}\n\nbool i_prohibit_thread::is_prohibited_thread(void) const {\n\tprohibited_mutex.lock();\n\tbool result = (prohibited_threads.find(t_thread::self()) != prohibited_threads.end());\n\tprohibited_mutex.unlock();\n\t\n\treturn result;\n}\n"
  },
  {
    "path": "src/prohibit_thread.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _PROHIBIT_THREAD_H\n#define _PROHIBIT_THREAD_H\n\n#include <set>\n#include \"threads/mutex.h\"\n#include \"threads/thread.h\"\n\nusing namespace std;\n\n// This class implements an interface to keep track of thread id's that are\n// prohibited from some actions, e.g. taking a certain lock\n\nclass i_prohibit_thread {\nprivate:\n\t// List of thread id's that are prohibited from some action\n\tmutable t_mutex\t\t\tprohibited_mutex;\n\tset<pthread_t>\t\tprohibited_threads;\n\t\npublic:\n\t// Operations on the prohibited set of thread id's\n\t// Add/remove the thread id of the calling thread\n\tvoid add_prohibited_thread(void);\n\tvoid remove_prohibited_thread(void);\n\t\n\t// Returns true if the current thread is prohibited\n\tbool is_prohibited_thread(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/protocol.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _PROTOCOL_H\n#define _PROTOCOL_H\n\n#include \"twinkle_config.h\"\n#include \"parser/hdr_supported.h\"\n\n/** Carriage Return Line Feed */\n#define CRLF\t\t\"\\r\\n\"\n\n/** TCP PING packet to be sent on a TCP connection. */\n#define TCP_PING_PACKET\tCRLF CRLF\n\n/** Product name */\n#define PRODUCT_NAME\t\"Twinkle\"\n\n/** Product version */\n#define PRODUCT_VERSION\tVERSION\n\n/**\n * When a SIP message is created, some addresses will be filled in\n * by the sender thread as only this thread knows the source IP\n * address of an outgoing message.\n * The sender thread will look for occurrences of the AUTO_IP4_ADDRESS\n * and replace it with the source IP address.\n */\n#define AUTO_IP4_ADDRESS\t\"255.255.255.255\"\n\n/** Display name for anonymous calling */\n#define ANONYMOUS_DISPLAY\t\"Anonymous\"\n\n/** SIP-URI for anonymous calling */\n#define ANONYMOUS_URI\t\t\"sip:anonymous@anonymous.invalid\"\n\n/** Types of failures. */\nenum t_failure {\n\tFAIL_TIMEOUT,\t/**< Transaction timed out */\n\tFAIL_TRANSPORT\t/**< Transport failure */\n};\n\n/** Call transfer types */\nenum t_transfer_type {\n\tTRANSFER_BASIC,\t\t/**< Basic transfer (blind) */\n\tTRANSFER_CONSULT,\t/**< Transfer with consultation (possibly attended) */\n\tTRANSFER_OTHER_LINE\t/**< Transfer call to other line */\n};\n\n/** State of a call transfer at the referrer. */\nenum t_refer_state {\n\tREFST_NULL,\t\t/**< No REFER in progress */\n\tREFST_W4RESP,\t\t/**< REFER sent, waiting for response */\n\tREFST_W4NOTIFY,\t\t/**< Response received, waiting for 1st NOTIFY */\n\tREFST_PENDING,\t\t/**< REFER received, but not granted yet */\n\tREFST_ACTIVE,\t\t/**< Referee granted refer */\n};\n\n/** Types of registration requests */\nenum t_register_type {\n\tREG_REGISTER,\n\tREG_QUERY,\n\tREG_DEREGISTER,\n\tREG_DEREGISTER_ALL\n};\n\n/**\n * RFC 3261 Annex A\n * SIP timers\n */\nenum t_sip_timer {\n\tTIMER_T1,\n\tTIMER_T2,\n\tTIMER_T4,\n\tTIMER_A,\n\tTIMER_B,\n\tTIMER_C,\n\tTIMER_D,\n\tTIMER_E,\n\tTIMER_F,\n\tTIMER_G,\n\tTIMER_H,\n\tTIMER_I,\n\tTIMER_J,\n\tTIMER_K\n};\n\n// All durations are in msec\n#define DURATION_T1\t500\n#define DURATION_T2\t4000\n#define DURATION_T4\t5000\n#define DURATION_A\tDURATION_T1\n#define DURATION_B\t(64 * DURATION_T1)\n#define DURATION_C\t180000\n#define DURATION_D\t32000\n#define DURATION_E\tDURATION_T1\n#define DURATION_F\t(64 * DURATION_T1)\n#define DURATION_G\tDURATION_T1\n#define DURATION_H\t(64 * DURATION_T1)\n#define DURATION_I\tDURATION_T4\n#define DURATION_J\t(64 * DURATION_T1)\n#define DURATION_K\tDURATION_T4\n\n/** Time to keep an idle connection open before closing */\n#define DUR_IDLE_CONNECTION\t(64 * DURATION_T1)\n\n/** UA (phone) timers */\nenum t_phone_timer {\n\tPTMR_REGISTRATION,\t/**< Registration (failure) timeout */\n\tPTMR_NAT_KEEPALIVE,\t/**< NAT binding refresh timeout for STUN */\n\tPTMR_TCP_PING,\t\t/**< TCP ping interval */\n};\n\n/** UA (line) timers */\nenum t_line_timer {\n\tLTMR_ACK_TIMEOUT,\t/**< Waiting for ACK */\n\tLTMR_ACK_GUARD,\t\t/**< After this timer ACK is lost for good */\n\tLTMR_INVITE_COMP,\t/**< After this timer INVITE transiction is considered complete. */\n\tLTMR_NO_ANSWER,\t\t/**< This timer expires if the callee does not answer. The call will be torn down. */\n\tLTMR_RE_INVITE_GUARD,\t/**< re-INVITE timeout */\n\tLTMR_100REL_TIMEOUT,\t/**< Waiting for PRACK */\n\tLTMR_100REL_GUARD,\t/**< After this timer PRACK is lost for good */\n\tLTMR_GLARE_RETRY,\t/**< Waiting before retry re-INVITE after glare */\n\tLTMR_CANCEL_GUARD,\t/**< Guard for situation where CANCEL has been responded to, but 487 on INVITE is never received. */\n\tLTMR_SESSION_REFRESH,\t/**< Time to send a Session Refresh Request */\n\tLTMR_SESSION_EXPIRE,\t/**< Session Expiration */\n};\n\n/** Subscription timers. */\nenum t_subscribe_timer {\n\tSTMR_SUBSCRIPTION,\t/**< Subscription timeout */\n};\n\n/** Publication timers. */\nenum t_publish_timer {\n\tPUBLISH_TMR_PUBLICATION,\t/**< Publication timeout */\n};\n\n/** STUN timers. */\nenum t_stun_timer {\n\tSTUN_TMR_REQ_TIMEOUT,\t/**< Waiting for response */\n};\n\n\n/** No answer timer (ms) */\n#define DUR_NO_ANSWER(u)\t((u)->get_timer_noanswer() * 1000)\n\n/** @name Registration timers */\n//@{\n/** Registration duration (seconds) */\n#define DUR_REGISTRATION(u)\t((u)->get_registration_time())\n\n/**< Re-register 5 seconds before expiry **/\n#define RE_REGISTER_DELTA\t5\n\n/** Re-registration interval after reg. failure */\n#define DUR_REG_FAILURE         30\n//@}\n\n/** NAT keepalive timer (s) default value */\n#define DUR_NAT_KEEPALIVE\t30\n\n/** Default TCP ping interval (s) */\n#define DUR_TCP_PING\t\t30\n\n/**\n * re-INVITE guard timer (ms). This timer guards against the situation\n * where a UAC has sent a re-INVITE, received a 1XX but never receives\n * a final response. No timer for this is defined in RFC 3261\n */\n#define DUR_RE_INVITE_GUARD\t10000\n\n/**\n * Guard for situation where CANCEL has been \n * responded to, but 487 on INVITE is never eceived.\n * This situation is not defined by RFC 3261\n */\n#define DUR_CANCEL_GUARD\t(64 * DURATION_T1)\n\n// MWI timers (s)\n#define DUR_MWI(u)\t\t((u)->get_mwi_subscription_time())\n#define DUR_MWI_FAILURE\t\t30\n\n// Presence timers (s)\n#define DUR_PRESENCE(u)\t\t((u)->get_pres_subscription_time())\n#define DUR_PRESENCE_FAILURE\t30\n\n// RFC 3261 14.1\n// Maximum values (10th of sec) for timers for retrying a re-INVITE after\n// a glare (491 response).\n#define MAX_GLARE_RETRY_NOT_OWN\t20\n#define MAX_GLARE_RETRY_OWN\t40\n\n// Calculate the glare retry duration (ms)\n#define DUR_GLARE_RETRY_NOT_OWN\t((rand() % (MAX_GLARE_RETRY_NOT_OWN + 1)) * 100)\n#define DUR_GLARE_RETRY_OWN\t((rand() % (MAX_GLARE_RETRY_OWN - \\\n\t\t\t\tMAX_GLARE_RETRY_NOT_OWN) + 1 + MAX_GLARE_RETRY_NOT_OWN)\\\n\t\t\t\t* 100)\n\n// RFC 3262\n// PRACK timers\n#define DUR_100REL_TIMEOUT\tDURATION_T1\n#define DUR_100REL_GUARD\t(64 * DURATION_T1)\n\n// refer subscription timer (s)\n// RFC 3515 does not define the length of the timer.\n// It should be long enough to notify the result of an INVITE.\n#define DUR_REFER_SUBSCRIPTION\t60 // Used when refer is always permitted\n#define DUR_REFER_SUB_INTERACT\t90 // Used when user has to grant permission\n\n// Minimum duration of a subscription\n#define MIN_DUR_SUBSCRIPTION\t60\n\n// Duration to wait before re-subscribing after termination of\n// a subscription\n#define DUR_RESUBSCRIBE\t\t30\n\n// After an unsubscribe has been sent, a NOTIFY will should come in.\n// In case the NOTIFY does not come, this guard timer (ms) will assure\n// that the subscription will be cleaned up.\n#define DUR_UNSUBSCRIBE_GUARD\t4000\n\n// Minimum interval for Session-Expires\n#define MIN_SESSION_INTERVAL\t90\n\n// RFC 3489\n// STUN retransmission timer intervals (ms)\n// The RFC states that the interval should start at 100ms. But that\n// seems to short. We start at 200 and do 8 instead of 9 transmissions.\n#define DUR_STUN_START_INTVAL\t200\n#define DUR_STUN_MAX_INTVAL\t1600\n\n// Maximum number of transmissions\n#define STUN_MAX_TRANSMISSIONS\t8\n\n// RFC 3261\n#ifndef RFC3261_COOKIE\n#define RFC3261_COOKIE\t\"z9hG4bK\"\n#endif\n\n// Max forwards RFC 3261 8.1.1.6\n#define MAX_FORWARDS\t70\n\n// Length of tags in from and to headers\n#define TAG_LEN\t\t5\n\n// Create a new tag\n#define NEW_TAG\t\trandom_token(TAG_LEN)\n\n// Length of call-id (before domain)\n#define CALL_ID_LEN\t15\n\n// Create a new call-id\n#define NEW_CALL_ID(u)\t(random_token(CALL_ID_LEN) + '@' + LOCAL_HOSTNAME)\n\n// Create a new sequence number fo CSeq header\n#define NEW_SEQNR\trand() % 1000 + 1\n\n// Length of cnonce\n#define CNONCE_LEN\t10\n\n// Create a cnonce\n#define NEW_CNONCE\trandom_hexstr(CNONCE_LEN)\n\n/** Length of tuple id in PIDF documents. */\n#define PIDF_TUPLE_ID_LEN\t6\n\n/** Create a new PIDF tuple id. */\n#define NEW_PIDF_TUPLE_ID\trandom_token(PIDF_TUPLE_ID_LEN)\n\n/** Character set encoding for outgoing text messages */\n#define MSG_TEXT_CHARSET\t\"utf-8\"\n\n/** @name Definitions for akav1-md5 authentication. */\n#define AKA_RANDLEN\t16\n#define AKA_AUTNLEN\t16\n#define AKA_CKLEN\t16\n#define AKA_IKLEN\t16\n#define AKA_AKLEN\t6\n#define AKA_OPLEN\t16\n#define AKA_RESLEN\t8\n#define AKA_SQNLEN\t6\n#define AKA_RESHEXLEN\t16\n#define AKA_AMFLEN\t2\n#define AKA_KLEN\t16\n\n// Set Allow header with methods that can be handled by the phone\n#define SET_HDR_ALLOW(h, u)\t{ (h).add_method(INVITE); \\\n\t\t\t\t  (h).add_method(ACK); \\\n\t\t\t\t  (h).add_method(BYE); \\\n\t\t\t\t  (h).add_method(CANCEL); \\\n\t\t\t\t  (h).add_method(OPTIONS); \\\n\t\t\t\t  if ((u)->get_ext_100rel() != EXT_DISABLED) {\\\n\t\t\t\t  \t(h).add_method(PRACK);\\\n\t\t\t\t  }\\\n\t\t\t\t  (h).add_method(REFER); \\\n\t\t\t\t  (h).add_method(NOTIFY); \\\n\t\t\t\t  (h).add_method(SUBSCRIBE); \\\n\t\t\t\t  (h).add_method(INFO); \\\n\t\t\t\t  (h).add_method(MESSAGE); \\\n\t\t\t\t}\n\n// Set Supported header with supported extensions\n#define SET_HDR_SUPPORTED(h, u)\t{ if ((u)->get_ext_replaces()) {\\\n\t\t\t\t\t(h).add_feature(EXT_REPLACES);\\\n\t\t\t\t  }\\\n\t\t\t\t  (h).add_feature(EXT_NOREFERSUB);\\\n\t\t\t\t  (h).add_feature(EXT_TIMER);\\\n\t\t\t\t}\n\n// Set Accept header with accepted body types\n#define SET_HDR_ACCEPT(h)\t{ (h).add_media(t_media(\"application\",\\\n\t\t\t\t  \"sdp\")); }\n\t\t\t\t  \n/** \n * Check if the content type of an instant message is supported \n * @param h [in] A SIP message.\n */\n#define MESSAGE_CONTENT_TYPE_SUPPORTED(h)\\\n\t\t\t\t((h).hdr_content_type.media.type == \"application\" ||\\\n\t\t\t\t (h).hdr_content_type.media.type == \"audio\" ||\\\n\t\t\t\t (h).hdr_content_type.media.type == \"image\" ||\\\n\t\t\t\t (h).hdr_content_type.media.type == \"text\" ||\\\n\t\t\t\t (h).hdr_content_type.media.type == \"video\")\n\t\t\t\t \n/** \n * Set Accept header with accepted body types for instant messaging. \n * @param h [inout] A SIP message.\n */\n#define SET_MESSAGE_HDR_ACCEPT(h)\t{ (h).add_media(t_media(\"application/*\"));\\\n\t\t\t\t\t  (h).add_media(t_media(\"audio/*\"));\\\n\t\t\t\t\t  (h).add_media(t_media(\"image/*\"));\\\n\t\t\t\t\t  (h).add_media(t_media(\"text/*\"));\\\n\t\t\t\t\t  (h).add_media(t_media(\"video/*\")); }\n\n/** \n * Set Accept header with accepted body types for presence.\n * @param h [inout] A SIP message.\n */\n#define SET_PRESENCE_HDR_ACCEPT(h)\t{ (h).add_media(t_media(\"application\",\\\n\t\t\t\t  \"pidf+xml\")); }\n\t\t\t\t  \n/** \n * Set Accept header with accepted body types for MWI. \n * @param h [inout] A SIP message.\n */\n#define SET_MWI_HDR_ACCEPT(h)\t{ (h).add_media(t_media(\"application\",\\\n\t\t\t\t  \"simple-message-summary\")); }\n\n/** \n * Set Accept-Encoding header with accepted encodings. \n * @param h [inout] A SIP message.\n */\n#define SET_HDR_ACCEPT_ENCODING(h)\\\n\t\t\t\t{ (h).add_coding(t_coding(\"identity\")); }\n\t\t\t\t\n/** \n * Check if content encoding is supported \n * @param h [inout] A SIP message.\n */\n#define CONTENT_ENCODING_SUPPORTED(ce)\\\n\t\t\t\t(cmp_nocase(ce, \"identity\") == 0)\n\n// Set Accept-Language header with accepted languages\n#define SET_HDR_ACCEPT_LANGUAGE(h)\\\n\t\t\t\t{ (h).add_language(t_language(\"en\")); }\n\n// Set User-Agent header\n#define SET_HDR_USER_AGENT(h)\t{ (h).add_server(t_server(PRODUCT_NAME,\\\n\t\t\t\t\tPRODUCT_VERSION)); }\n\n// Set Server header\n#define SET_HDR_SERVER(h)\t{ (h).add_server(t_server(PRODUCT_NAME,\\\n\t\t\t\t\tPRODUCT_VERSION)); }\n\n// Set Organization header\n#define SET_HDR_ORGANIZATION(h, u)\t{ if ((u)->get_organization() != \"\") {\\\n\t\t\t\t\t(h).set_name((u)->get_organization()); }}\n\n// Check if an event is supported by Twinkle\t\t\t\t\n#define SIP_EVENT_SUPPORTED(e)\t((e) == SIP_EVENT_REFER ||\\\n\t\t\t\t (e) == SIP_EVENT_MSG_SUMMARY ||\\\n\t\t\t\t (e) == SIP_EVENT_PRESENCE)\n\n// Add the supported events to the Allow-Events header\n#define ADD_SUPPORTED_SIP_EVENTS(h)\t{ (h).add_event_type(SIP_EVENT_REFER);\\\n\t\t\t\t\t  (h).add_event_type(SIP_EVENT_MSG_SUMMARY);\\\n\t\t\t\t\t  (h).add_event_type(SIP_EVENT_PRESENCE); }\n\n#endif\n"
  },
  {
    "path": "src/redirect.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include \"redirect.h\"\n\nbool t_redirector::contact_already_added(const t_contact_param contact) const {\n\tfor (list<t_contact_param>::const_iterator i = try_contacts.begin();\n\t     i != try_contacts.end(); i++)\n\t{\n\t\tif (i->uri == contact.uri) return true;\n\t}\n\n\tfor (list<t_contact_param>::const_iterator i = done_contacts.begin();\n\t     i != done_contacts.end(); i++)\n\t{\n\t\tif (i->uri == contact.uri) return true;\n\t}\n\n\tif (contact.uri == org_dest) return true;\n\n\treturn false;\n}\n\nt_redirector::t_redirector(const t_url &_org_dest, int _max_redirections) {\n\tnum_contacts = 0;\n\torg_dest = _org_dest;\n\tmax_redirections = _max_redirections;\n}\n\nbool t_redirector::get_next_contact(t_contact_param &contact) {\n\tif (try_contacts.empty()) return false;\n\n\tcontact = try_contacts.front();\n\ttry_contacts.pop_front();\n\tdone_contacts.push_back(contact);\n\n\treturn true;\n}\n\nvoid t_redirector::add_contacts(const list<t_contact_param> &contacts) {\n\tif (num_contacts >= max_redirections) return;\n\n\tlist<t_contact_param> l = contacts;\n\tl.sort();\n\n\tfor (list<t_contact_param>::iterator i = l.begin(); i != l.end(); i++) {\n\t\tif (!contact_already_added(*i)) {\n\t\t\ttry_contacts.push_back(*i);\n\t\t\tnum_contacts++;\n\n\t\t\tif (num_contacts >= max_redirections) break;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/redirect.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_REDIRECT\n#define _H_REDIRECT\n\n#include <list>\n#include \"user.h\"\n#include \"parser/hdr_contact.h\"\n#include \"sockets/url.h\"\n\nusing namespace std;\n\nclass t_redirector {\nprivate:\n\t// Total number of contacts in try and done lists\n\tint\t\t\tnum_contacts;\n\n\t// Contacts to try\n\tlist<t_contact_param>\ttry_contacts;\n\n\t// Contacts already tried, but unsuccesful\n\tlist<t_contact_param>\tdone_contacts;\n\n\t// Original destination\n\tt_url\t\t\torg_dest;\n\t\n\t// Maximum number of redirections that will be tried\n\tint\t\t\tmax_redirections;\n\n\tbool contact_already_added(const t_contact_param contact) const;\n\n\t// Constructor without parameter should not be used.\n\tt_redirector();\n\npublic:\n\tt_redirector(const t_url &_org_dest, int _max_redirections);\n\n\t// Get the next contact to try\n\t// Returns false if there is no next contact\n\tbool get_next_contact(t_contact_param &contact);\n\n\t// Add contacts. The passed contacts will be sorted on decreasing\n\t// q-value before adding.\n\t// Contacts that are already in the try list or are tried already\n\t// will not be added.\n\t// If the maximum number of redirections is reached then all contacts\n\t// exceeding the maximum will be discarded.\n\tvoid add_contacts(const list<t_contact_param> &contacts);\n};\n\n#endif\n"
  },
  {
    "path": "src/sdp/CMakeLists.txt",
    "content": "project(libtwinkle-sdp)\n\nBISON_TARGET(MyParser sdp_parser.yxx ${CMAKE_CURRENT_BINARY_DIR}/sdp_parser.cxx COMPILE_FLAGS \"-p yysdp\")\nFLEX_TARGET(MyScanner sdp_scanner.lxx ${CMAKE_CURRENT_BINARY_DIR}/sdp_scanner.cxx COMPILE_FLAGS \"-Pyysdp\")\nADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser)\n\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR})\n\nset(LIBTWINKLE_SDP-SRCS\n\tsdp.cpp\n\tsdp_parse_ctrl.cpp\n\t${CMAKE_CURRENT_BINARY_DIR}/sdp_parser.cxx\n\t${CMAKE_CURRENT_BINARY_DIR}/sdp_scanner.cxx\n\tsdp_scanner.cxx\n)\n\nadd_library(libtwinkle-sdp OBJECT ${LIBTWINKLE_SDP-SRCS})\n"
  },
  {
    "path": "src/sdp/sdp.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <algorithm>\n#include <assert.h>\n#include <cstdlib>\n#include <iostream>\n#include \"protocol.h\"\n#include \"sdp_parse_ctrl.h\"\n#include \"sdp.h\"\n#include \"util.h\"\n#include \"parser/hdr_warning.h\"\n#include \"parser/parameter.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\n\nstring sdp_ntwk_type2str(t_sdp_ntwk_type n) {\n\tswitch(n) {\n\tcase SDP_NTWK_NULL:\treturn \"NULL\";\n\tcase SDP_NTWK_IN:\treturn \"IN\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nt_sdp_ntwk_type str2sdp_ntwk_type(string s) {\n\tif (s == \"IN\") return SDP_NTWK_IN;\n\n\tthrow (t_sdp_syntax_error(\"unknown network type: \" + s));\n}\n\nstring sdp_addr_type2str(t_sdp_addr_type a) {\n\tswitch(a) {\n\tcase SDP_ADDR_NULL:\treturn \"NULL\";\n\tcase SDP_ADDR_IP4:\treturn \"IP4\";\n\tcase SDP_ADDR_IP6:\treturn \"IP6\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nt_sdp_addr_type str2sdp_addr_type(string s) {\n\tif (s == \"IP4\") return SDP_ADDR_IP4;\n\tif (s == \"IP6\") return SDP_ADDR_IP6;\n\n\tthrow (t_sdp_syntax_error(\"unknown address type: \" + s));\n}\n\nstring sdp_transport2str(t_sdp_transport t) {\n\tswitch(t) {\n\tcase SDP_TRANS_RTP:\treturn \"RTP/AVP\";\n\tcase SDP_TRANS_UDP:\treturn \"udp\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nt_sdp_transport str2sdp_transport(string s) {\n\tif (s == \"RTP/AVP\") return SDP_TRANS_RTP;\n\tif (s == \"udp\") return SDP_TRANS_UDP;\n\n\t// Other transports are not recognized and are mapped to other.\n\treturn SDP_TRANS_OTHER;\n}\n\nt_sdp_media_type str2sdp_media_type(string s) {\n\tif (s == \"audio\") return SDP_AUDIO;\n\tif (s == \"video\") return SDP_VIDEO;\n\treturn SDP_OTHER;\n}\n\nstring sdp_media_type2str(t_sdp_media_type m) {\n\tswitch(m) {\n\tcase SDP_AUDIO:\t\treturn \"audio\";\n\tcase SDP_VIDEO:\t\treturn \"video\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nstring get_rtpmap(unsigned format, t_audio_codec codec) {\n\tstring rtpmap;\n\t\n\trtpmap = int2str(format);\n\trtpmap += ' ';\n\n\tswitch(codec) {\n\tcase CODEC_G711_ULAW:\n\t\trtpmap += SDP_RTPMAP_G711_ULAW;\n\t\tbreak;\n\tcase CODEC_G711_ALAW:\n\t\trtpmap += SDP_RTPMAP_G711_ALAW;\n\t\tbreak;\n\tcase CODEC_GSM:\n\t\trtpmap += SDP_RTPMAP_GSM;\n\t\tbreak;\n\tcase CODEC_SPEEX_NB:\n\t\trtpmap += SDP_RTPMAP_SPEEX_NB;\n\t\tbreak;\n\tcase CODEC_SPEEX_WB:\n\t\trtpmap += SDP_RTPMAP_SPEEX_WB;\n\t\tbreak;\n\tcase CODEC_SPEEX_UWB:\n\t\trtpmap += SDP_RTPMAP_SPEEX_UWB;\n\t\tbreak;\n\tcase CODEC_ILBC:\n\t\trtpmap += SDP_RTPMAP_ILBC;\n\t\tbreak;\n\tcase CODEC_G722:\n\t\trtpmap += SDP_RTPMAP_G722;\n\t\tbreak;\n\tcase CODEC_G726_16:\n\t\trtpmap += SDP_RTPMAP_G726_16;\n\t\tbreak;\n\tcase CODEC_G726_24:\n\t\trtpmap += SDP_RTPMAP_G726_24;\n\t\tbreak;\n\tcase CODEC_G726_32:\n\t\trtpmap += SDP_RTPMAP_G726_32;\n\t\tbreak;\n\tcase CODEC_G726_40:\n\t\trtpmap += SDP_RTPMAP_G726_40;\n\t\tbreak;\n\tcase CODEC_G729A:\n\t\trtpmap += SDP_RTPMAP_G729A;\n\t\tbreak;\n\tcase CODEC_TELEPHONE_EVENT:\n\t\trtpmap += SDP_RTPMAP_TELEPHONE_EV;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn rtpmap;\n}\n\nstring sdp_media_direction2str(t_sdp_media_direction d) {\n\tswitch(d) {\n\tcase SDP_INACTIVE:\treturn \"inactive\";\n\tcase SDP_SENDONLY:\treturn \"sendonly\";\n\tcase SDP_RECVONLY:\treturn \"recvonly\";\n\tcase SDP_SENDRECV:\treturn \"sendrecv\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\n///////////////////////////////////\n// class t_sdp_origin\n///////////////////////////////////\n\nt_sdp_origin::t_sdp_origin() {\n\tnetwork_type = SDP_NTWK_NULL;\n\taddress_type = SDP_ADDR_NULL;\n}\n\nt_sdp_origin::t_sdp_origin(string _username, string _session_id,\n\t\t           string _session_version, string _address) :\n\t\tusername(_username),\n\t\tsession_id(_session_id),\n\t\tsession_version(_session_version),\n\t\taddress(_address)\n{\n\tnetwork_type = SDP_NTWK_IN;\n\taddress_type = SDP_ADDR_IP4;\n}\n\nstring t_sdp_origin::encode(void) const {\n\tstring s;\n\n\ts = \"o=\";\n\ts += username;\n\ts += ' ' + session_id;\n\ts += ' ' + session_version;\n\ts += ' ' + sdp_ntwk_type2str(network_type);\n\ts += ' ' + sdp_addr_type2str(address_type);\n\ts += ' ' + address;\n\ts += CRLF;\n\n\treturn s;\n}\n\n\n///////////////////////////////////\n// class t_sdp_connection\n///////////////////////////////////\n\nt_sdp_connection::t_sdp_connection() {\n\tnetwork_type = SDP_NTWK_NULL;\n}\n\nt_sdp_connection::t_sdp_connection(string _address) :\n\t\taddress(_address)\n{\n\tnetwork_type = SDP_NTWK_IN;\n\taddress_type = SDP_ADDR_IP4;\n}\n\nstring t_sdp_connection::encode(void) const {\n\tstring s;\n\n\ts = \"c=\";\n\ts += sdp_ntwk_type2str(network_type);\n\ts += ' ' + sdp_addr_type2str(address_type);\n\ts += ' ' + address;\n\ts += CRLF;\n\n\treturn s;\n}\n\n\n///////////////////////////////////\n// class t_sdp_attr\n///////////////////////////////////\n\nt_sdp_attr::t_sdp_attr(string _name) {\n\tname = _name;\n}\n\nt_sdp_attr::t_sdp_attr(string _name, string _value) {\n\tname = _name;\n\tvalue = _value;\n}\n\nstring t_sdp_attr::encode(void) const {\n\tstring s;\n\n\ts = \"a=\";\n\ts += name;\n\n\tif (value != \"\") {\n\t\ts += ':' + value;\n\t}\n\t\n\ts += CRLF;\n\n\treturn s;\n}\n\n\n///////////////////////////////////\n// class t_sdp_media\n///////////////////////////////////\n\nt_sdp_media::t_sdp_media() {\n\tport = 0;\n\tformat_dtmf = 0;\n}\n\nt_sdp_media::t_sdp_media(t_sdp_media_type _media_type,\n\t\t\t unsigned short _port, const list<t_audio_codec> &_formats,\n\t\t\t unsigned short _format_dtmf,\n\t\t\t const map<t_audio_codec, unsigned short> &ac2format)\n{\n\tmedia_type = sdp_media_type2str(_media_type);\n\tport = _port;\n\ttransport = sdp_transport2str(SDP_TRANS_RTP);\n\tformat_dtmf = _format_dtmf;\n\n\tfor (list<t_audio_codec>::const_iterator i = _formats.begin();\n\t     i != _formats.end(); i++)\n\t{\n\t\tmap<t_audio_codec, unsigned short>::const_iterator it;\n\t\tit = ac2format.find(*i);\n\t\tassert(it != ac2format.end());\n\t\tadd_format(it->second, *i);\n\t}\n\n\tif (format_dtmf > 0) {\n\t\tadd_format(format_dtmf, CODEC_TELEPHONE_EVENT);\n\t}\n}\n\nstring t_sdp_media::encode(void) const {\n\tstring s;\n\n\ts = \"m=\";\n\ts += media_type;\n\ts += ' ' + int2str(port);\n\ts += ' ' + transport;\n\n\t// Encode media formats. Note that only one of the format lists\n\t// will be populated depending on the media type.\n\t\n\t// Numeric formats.\n\tfor (list<unsigned short>::const_iterator i = formats.begin();\n\t     i != formats.end(); ++i)\n\t{\n\t\ts += ' ' + int2str(*i);\n\t}\n\t\n\t// Alpha numeric formats.\n\tfor (list<string>::const_iterator i = alpha_num_formats.begin();\n\t     i != alpha_num_formats.end(); ++i)\n\t{\n\t\ts += ' ' + *i;\n\t}\n\n\ts += CRLF;\n\n\t// Connection information.\n\tif (connection.network_type != SDP_NTWK_NULL) {\n\t\ts += connection.encode();\n\t}\n\n\t// Attributes.\n\tfor (list<t_sdp_attr>::const_iterator i = attributes.begin();\n\t     i != attributes.end(); ++i)\n\t{\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nvoid t_sdp_media::add_format(unsigned short f, t_audio_codec codec) {\n\tformats.push_back(f);\n\n\t// RFC 3264 5.1\n\t// All media descriptions SHOULD contain an rtpmap\n\tstring rtpmap = get_rtpmap(f, codec);\n\tattributes.push_back(t_sdp_attr(\"rtpmap\", rtpmap));\n\n\t// RFC 2833 3.9\n\t// Add fmtp parameter\n\tif (format_dtmf > 0 && f == format_dtmf) {\n\t\tstring fmtp = int2str(f);\n\t\tfmtp += ' ';\n\t\tfmtp += \"0-15\";\n\n\t\tattributes.push_back(t_sdp_attr(\"fmtp\", fmtp));\n\t}\n\telse if (codec == CODEC_G729A)\n\t{\n\t\tstring fmtp = int2str(f);\n\n\t\tfmtp += ' ';\n\t\tfmtp += \"annexb=no\"; // annexb=no means G729A\n\n\t\tattributes.push_back(t_sdp_attr(\"fmtp\", fmtp));\n\t}\n}\n\nt_sdp_attr *t_sdp_media::get_attribute(const string &name) {\n\tfor (list<t_sdp_attr>::iterator i = attributes.begin();\n\t     i != attributes.end(); i++)\n\t{\n\t\tif (cmp_nocase(i->name, name) == 0) return &(*i);\n\t}\n\n\t// Attribute does not exist\n\treturn NULL;\n}\n\nlist<t_sdp_attr *> t_sdp_media::get_attributes(const string &name) {\n\tlist<t_sdp_attr *> l;\n\n\tfor (list<t_sdp_attr>::iterator i = attributes.begin();\n\t     i != attributes.end(); i++)\n\t{\n\t\tif (cmp_nocase(i->name, name) == 0) l.push_back(&(*i));\n\t}\n\n\treturn l;\n}\n\nt_sdp_media_direction t_sdp_media::get_direction(void) const {\n\tt_sdp_attr *a;\n\n\tt_sdp_media *self = const_cast<t_sdp_media *>(this);\n\n\ta = self->get_attribute(\"inactive\");\n\tif (a) return SDP_INACTIVE;\n\n\ta = self->get_attribute(\"sendonly\");\n\tif (a) return SDP_SENDONLY;\n\n\ta = self->get_attribute(\"recvonly\");\n\tif (a) return SDP_RECVONLY;\n\n\treturn SDP_SENDRECV;\n}\n\nt_sdp_media_type t_sdp_media::get_media_type(void) const {\n\treturn str2sdp_media_type(media_type);\n}\n\nt_sdp_transport t_sdp_media::get_transport(void) const {\n\treturn str2sdp_transport(transport);\n}\n\n///////////////////////////////////\n// class t_sdp\n///////////////////////////////////\n\nt_sdp::t_sdp() : t_sip_body(), version(0) \n{}\n\nt_sdp::t_sdp(const string &user, const string &sess_id, const string &sess_version, \n\t     const string &user_host, const string &media_host, unsigned short media_port,\n\t     const list<t_audio_codec> &formats, unsigned short format_dtmf,\n\t     const map<t_audio_codec, unsigned short> &ac2format) :\n\t     \tt_sip_body(),\n\t     \tversion(0),\n\t\torigin(user, sess_id, sess_version, user_host),\n\t\tconnection(media_host)\n{\n\tmedia.push_back(t_sdp_media(SDP_AUDIO, media_port, formats, format_dtmf,\n\t\t\t\tac2format));\n}\n\nt_sdp::t_sdp(const string &user, const string &sess_id, const string &sess_version, \n\t\tconst string &user_host, const string &media_host) :\n\t\t\tt_sip_body(),\n\t\t\tversion(0),\n\t\t\torigin(user, sess_id, sess_version, user_host),\n\t\t\tconnection(media_host)\n{}\n\nvoid t_sdp::add_media(const t_sdp_media &m) {\n\tmedia.push_back(m);\n}\n\nstring t_sdp::encode(void) const {\n\tstring s;\n\n\ts = \"v=\" + int2str(version) + CRLF;\n\ts += origin.encode();\n\n\tif (session_name == \"\") {\n\t\t// RFC 3264 5\n\t\t// Session name may no be empty. Recommende is '-'\n\t\ts += \"s=-\";\n\t\ts += CRLF;\n\t} else {\n\t\ts += \"s=\" + session_name + CRLF;\n\t}\n\n\tif (connection.network_type != SDP_NTWK_NULL) {\n\t\ts += connection.encode();\n\t}\n\n\t// RFC 3264 5\n\t// Time parameter should be 0 0\n\ts += \"t=0 0\";\n\ts += CRLF;\n\n\tfor (list<t_sdp_attr>::const_iterator i = attributes.begin();\n\t     i != attributes.end(); i++)\n\t{\n\t\ts += i->encode();\n\t}\n\n\tfor (list<t_sdp_media>::const_iterator i = media.begin();\n\t     i != media.end(); i++)\n\t{\n\t\ts += i->encode();\n\t}\n\n\treturn s;\n}\n\nt_sip_body *t_sdp::copy(void) const {\n\tt_sdp *s = new t_sdp(*this);\n\tMEMMAN_NEW(s);\n\treturn s;\n}\n\nt_body_type t_sdp::get_type(void) const {\n\treturn BODY_SDP;\n}\n\nt_media t_sdp::get_media(void) const {\n\treturn t_media(\"application\", \"sdp\");\n}\n\nbool t_sdp::is_supported(int &warn_code, string &warn_text) const {\n\twarn_text = \"\";\n\n\tif (version != 0) {\n\t\twarn_code = W_399_MISCELLANEOUS;\n\t\twarn_text = \"SDP version \";\n\t\twarn_text += int2str(version);\n\t\twarn_text += \" not supported\";\n\t\treturn false;\n\t}\n\n\tconst t_sdp_media *m = get_first_media(SDP_AUDIO);\n\n\t// Connection information must be present at the session level\n\t// and/or the media level\n\tif (connection.network_type == SDP_NTWK_NULL) {\n\t\tif (m == NULL || m->connection.network_type == SDP_NTWK_NULL) {\n\t\t\twarn_code = W_399_MISCELLANEOUS;\n\t\t\twarn_text = \"c-line missing\";\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\t// Only Internet is supported\n\t\tif (connection.network_type != SDP_NTWK_IN) {\n\t\t\twarn_code = W_300_INCOMPATIBLE_NWK_PROT;\n\t\t\treturn false;\n\t\t}\n\n\t\t// Only IPv4 is supported\n\t\tif (connection.address_type != SDP_ADDR_IP4) {\n\t\t\twarn_code = W_301_INCOMPATIBLE_ADDR_FORMAT;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// There must be at least 1 audio stream with a non-zero port value\n\tif (m == NULL && !media.empty()) {\n\t\twarn_code = W_304_MEDIA_TYPE_NOT_AVAILABLE;\n\t\twarn_text = \"Valid media stream for audio is missing\";\n\t\treturn false;\n\t}\n\t\n\t// RFC 3264 5, RFC 3725 flow IV\n\t// There may be 0 media streams\n\tif (media.empty()) {\n\t\treturn true;\n\t}\n\n\t// Check connection information on media level\n\tif (m->connection.network_type != SDP_NTWK_NULL &&\n\t    m->connection.address_type != SDP_ADDR_IP4) {\n\t\twarn_code = W_301_INCOMPATIBLE_ADDR_FORMAT;\n\t\treturn false;\n\t}\n\n\tif (m->get_transport() != SDP_TRANS_RTP) {\n\t\twarn_code = W_302_INCOMPATIBLE_TRANS_PROT;\n\t\treturn false;\n\t}\n\n\tt_sdp_media *m2 = const_cast<t_sdp_media *>(m);\n\tconst t_sdp_attr *a = m2->get_attribute(\"ptime\");\n\tif (a) {\n\t\tunsigned short p = atoi(a->value.c_str());\n\t\tif (p < MIN_PTIME) {\n\t\t\twarn_code = W_306_ATTRIBUTE_NOT_UNDERSTOOD;\n\t\t\twarn_text = \"Attribute 'ptime' too small. must be >= \";\n\t\t\twarn_text += int2str(MIN_PTIME);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (p > MAX_PTIME) {\n\t\t\twarn_code = W_306_ATTRIBUTE_NOT_UNDERSTOOD;\n\t\t\twarn_text = \"Attribute 'ptime' too big. must be <= \";\n\t\t\twarn_text += int2str(MAX_PTIME);\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\treturn true;\n}\n\nstring t_sdp::get_rtp_host(t_sdp_media_type media_type) const {\n\tconst t_sdp_media *m = get_first_media(media_type);\n\tassert(m != NULL);\n\n\t// If the media line has its own connection information, then\n\t// take the host information from there.\n\tif (m->connection.network_type == SDP_NTWK_IN) {\n\t\treturn m->connection.address;\n\t}\n\n\t// The host information must be in the session connection info\n\tassert(connection.network_type == SDP_NTWK_IN);\n\treturn connection.address;\n}\n\nunsigned short t_sdp::get_rtp_port(t_sdp_media_type media_type) const {\n\tconst t_sdp_media *m = get_first_media(media_type);\n\tassert(m != NULL);\n\n\treturn m->port;\n}\n\nlist<unsigned short> t_sdp::get_codecs(t_sdp_media_type media_type) const {\n\tconst t_sdp_media *m = get_first_media(media_type);\n\tassert(m != NULL);\n\n\treturn m->formats;\n}\n\nstring t_sdp::get_codec_description(t_sdp_media_type media_type,\n\t\tunsigned short codec) const\n{\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\n\tconst list<t_sdp_attr *> attrs = m->get_attributes(\"rtpmap\");\n\tif (attrs.empty()) return \"\";\n\n\tfor (list<t_sdp_attr *>::const_iterator i = attrs.begin();\n\t     i != attrs.end(); i++)\n\t{\n\t\tvector<string> l = split_ws((*i)->value);\n\t\tif (atoi(l.front().c_str()) == codec) {\n\t\t\treturn l.back();\n\t\t}\n\t}\n\n\treturn \"\";\n}\n\nt_audio_codec t_sdp::get_rtpmap_codec(const string &rtpmap) const {\n\tif (rtpmap.empty()) return CODEC_NULL;\n\t\n\tvector<string> rtpmap_elems = split(rtpmap, '/');\n\tif (rtpmap_elems.size() < 2) {\n\t\t// RFC 2327\t\n\t\t// The rtpmap should at least contain the encoding name\n\t\t// and sample rate\n\t\treturn CODEC_UNSUPPORTED;\n\t}\n\t\t\n\tstring codec_name = trim(rtpmap_elems[0]);\n\tint sample_rate = atoi(trim(rtpmap_elems[1]).c_str());\n\t\n\tif (cmp_nocase(codec_name, SDP_AC_NAME_G711_ULAW) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G711_ULAW;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G711_ALAW) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G711_ALAW;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_GSM) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_GSM;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_SPEEX) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_SPEEX_NB;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_SPEEX) == 0 && sample_rate == 16000) {\n\t\treturn CODEC_SPEEX_WB;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_SPEEX) == 0 && sample_rate == 32000) {\n\t\treturn CODEC_SPEEX_UWB;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_ILBC) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_ILBC;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G722) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G722;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G726_16) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G726_16;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G726_24) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G726_24;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G726_32) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G726_32;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G726_40) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G726_40;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_G729) == 0 && sample_rate == 8000) {\n\t\treturn CODEC_G729A;\n\t} else if (cmp_nocase(codec_name, SDP_AC_NAME_TELEPHONE_EV) == 0) {\n\t\treturn CODEC_TELEPHONE_EVENT;\n\t}\n\t\n\treturn CODEC_UNSUPPORTED;\n}\n\nt_audio_codec t_sdp::get_codec(t_sdp_media_type media_type,\n\t\tunsigned short codec) const\n{\n\tstring rtpmap = get_codec_description(media_type, codec);\n\t\n\t// If there is no rtpmap description then use the static\n\t// payload definition as defined by RFC 3551\n\tif (rtpmap.empty()) {\n\t\tswitch(codec) {\n\t\tcase SDP_FORMAT_G711_ULAW:\n\t\t\treturn CODEC_G711_ULAW;\n\t\tcase SDP_FORMAT_G711_ALAW:\n\t\t\treturn CODEC_G711_ALAW;\n\t\tcase SDP_FORMAT_GSM:\n\t\t\treturn CODEC_GSM;\n\t\tdefault:\n\t\t\treturn CODEC_UNSUPPORTED;\n\t\t}\n\t}\n\t\n\t// Use the rtpmap description to map the payload number\n\t// to a codec\n\treturn get_rtpmap_codec(rtpmap);\n}\n\nt_sdp_media_direction t_sdp::get_direction(t_sdp_media_type media_type) const {\n\tconst t_sdp_media *m = get_first_media(media_type);\n\tassert(m != NULL);\n\n\treturn m->get_direction();\n}\n\nstring t_sdp::get_fmtp(t_sdp_media_type media_type, unsigned short codec) const {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\n\tconst list<t_sdp_attr *> attrs = m->get_attributes(\"fmtp\");\n\tif (attrs.empty()) return \"\";\n\n\tfor (list<t_sdp_attr *>::const_iterator i = attrs.begin();\n\t     i != attrs.end(); i++)\n\t{\n\t\tvector<string> l = split_ws((*i)->value);\n\t\tif (atoi(l.front().c_str()) == codec) {\n\t\t\treturn l.back();\n\t\t}\n\t}\n\n\treturn \"\";\n}\n\nint t_sdp::get_fmtp_int_param(t_sdp_media_type media_type, unsigned short codec,\n\t\t\tconst string param) const\n{\n\tstring fmtp = get_fmtp(media_type, codec);\n\tif (fmtp.empty()) return -1;\n\t\n\tint value;\n\tlist<t_parameter> l = str2param_list(fmtp);\n\tlist<t_parameter>::const_iterator it = find(l.begin(), l.end(), t_parameter(param, \"\"));\n\tif (it != l.end()) {\n\t\tvalue = atoi(it->value.c_str());\n\t} else {\n\t\tvalue = -1;\n\t}\n\t\n\treturn value;\n}\n\nunsigned short t_sdp::get_ptime(t_sdp_media_type media_type) const {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\n\tconst t_sdp_attr *a = m->get_attribute(\"ptime\");\n\tif (!a) return 0;\n\treturn atoi(a->value.c_str());\n}\n\nbool t_sdp::get_zrtp_support(t_sdp_media_type media_type) const {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\t\n\tconst t_sdp_attr *a = m->get_attribute(\"zrtp\");\n\tif (!a) return false;\n\treturn true;\n}\n\nvoid t_sdp::set_ptime(t_sdp_media_type media_type, unsigned short ptime) {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\n\tt_sdp_attr a(\"ptime\", int2str(ptime));\n\tm->attributes.push_back(a);\n}\n\nvoid t_sdp::set_direction(t_sdp_media_type media_type, t_sdp_media_direction direction) {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\n\tt_sdp_attr a(sdp_media_direction2str(direction));\n\tm->attributes.push_back(a);\n}\n\nvoid t_sdp::set_fmtp(t_sdp_media_type media_type, unsigned short codec, const string &fmtp) {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\t\n\tstring s = int2str(codec);\n\ts += ' ';\n\ts += fmtp;\n\tt_sdp_attr a(\"fmtp\", s);\n\tm->attributes.push_back(a);\n}\n\nvoid t_sdp::set_fmtp_int_param(t_sdp_media_type media_type, unsigned short codec,\n\t\t\tconst string &param, int value)\n{\n\tstring fmtp(param);\n\tfmtp += '=';\n\tfmtp += int2str(value);\n\tset_fmtp(media_type, codec, fmtp);\n}\n\nvoid t_sdp::set_zrtp_support(t_sdp_media_type media_type) {\n\tt_sdp_media *m = const_cast<t_sdp_media *>(get_first_media(media_type));\n\tassert(m != NULL);\n\t\n\tt_sdp_attr a(\"zrtp\");\n\tm->attributes.push_back(a);\n}\n\nconst t_sdp_media *t_sdp::get_first_media(t_sdp_media_type media_type) const {\n\tfor (list<t_sdp_media>::const_iterator i = media.begin();\n\t     i != media.end(); i++)\n\t{\n\t\tif (i->get_media_type() == media_type && i->port != 0) {\n\t\t\treturn &(*i);\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nbool t_sdp::local_ip_check(void) const {\n\tif (origin.address == AUTO_IP4_ADDRESS) return false;\n\t\n\tif (connection.address == AUTO_IP4_ADDRESS) return false;\n\t\n\tfor (list<t_sdp_media>::const_iterator it = media.begin(); it != media.end(); ++it) {\n\t\tif (it->connection.address == AUTO_IP4_ADDRESS) return false;\n\t}\n\t\n\treturn true;\n}\n"
  },
  {
    "path": "src/sdp/sdp.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Session description\n\n#ifndef _H_SDP\n#define _H_SDP\n\n#include <list>\n#include <map>\n#include <string>\n#include \"audio/audio_codecs.h\"\n#include \"parser/sip_body.h\"\n\n/** User name to be put in o= line of SDP */\n#define SDP_O_USER\t\t\"twinkle\"\n\n// Audio codec formats\n#define SDP_FORMAT_G711_ULAW\t0\n#define SDP_FORMAT_GSM\t\t3\n#define SDP_FORMAT_G711_ALAW\t8\n#define SDP_FORMAT_G722\t\t9\n#define SDP_FORMAT_G729\t\t\t18\n\n// rtpmap values\n#define SDP_RTPMAP_G711_ULAW\t\"PCMU/8000\"\n#define SDP_RTPMAP_GSM\t\t\"GSM/8000\"\n#define SDP_RTPMAP_G711_ALAW\t\"PCMA/8000\"\n#define SDP_RTPMAP_SPEEX_NB\t\"speex/8000\"\n#define SDP_RTPMAP_SPEEX_WB\t\"speex/16000\"\n#define SDP_RTPMAP_SPEEX_UWB\t\"speex/32000\"\n#define SDP_RTPMAP_ILBC\t\t\"iLBC/8000\"\n#define SDP_RTPMAP_G722\t\t\"G722/8000\"\n#define SDP_RTPMAP_G726_16\t\"G726-16/8000\"\n#define SDP_RTPMAP_G726_24\t\"G726-24/8000\"\n#define SDP_RTPMAP_G726_32\t\"G726-32/8000\"\n#define SDP_RTPMAP_G726_40\t\"G726-40/8000\"\n#define SDP_RTPMAP_G729A\t\"G729/8000\"\n#define SDP_RTPMAP_TELEPHONE_EV\t\"telephone-event/8000\"\n\n// Audio codec names\n#define SDP_AC_NAME_G711_ULAW\t\t\"PCMU\"\n#define SDP_AC_NAME_G711_ALAW\t\t\"PCMA\"\n#define SDP_AC_NAME_GSM\t\t\t\"GSM\"\n#define SDP_AC_NAME_SPEEX\t\t\"speex\"\n#define SDP_AC_NAME_ILBC\t\t\"iLBC\"\n#define SDP_AC_NAME_G722\t\t\"G722\"\n#define SDP_AC_NAME_G726_16\t\t\"G726-16\"\n#define SDP_AC_NAME_G726_24\t\t\"G726-24\"\n#define SDP_AC_NAME_G726_32\t\t\"G726-32\"\n#define SDP_AC_NAME_G726_40\t\t\"G726-40\"\n#define SDP_AC_NAME_G729\t\t\"G729\"\n#define SDP_AC_NAME_TELEPHONE_EV\t\"telephone-event\"\n\n// Check on fmtp parameter values\n#define VALID_ILBC_MODE(mode) ((mode) == 20 || (mode == 30))\n\nusing namespace std;\n\nenum t_sdp_ntwk_type {\n\tSDP_NTWK_NULL,\n\tSDP_NTWK_IN\n};\n\nstring sdp_ntwk_type2str(t_sdp_ntwk_type n);\nt_sdp_ntwk_type str2sdp_ntwk_type(string s);\n\n\nenum t_sdp_addr_type {\n\tSDP_ADDR_NULL,\n\tSDP_ADDR_IP4,\n\tSDP_ADDR_IP6\n};\n\nstring sdp_addr_type2str(t_sdp_addr_type a);\nt_sdp_addr_type str2sdp_addr_type(string s);\n\n\n/** Transport protocol */\nenum t_sdp_transport {\n\tSDP_TRANS_RTP,\t\t/**< RTP/AVP */\n\tSDP_TRANS_UDP,\t\t/**< UDP */\n\tSDP_TRANS_OTHER\t\t/**< Another protocol not yet supported */\n};\n\nstring sdp_transport2str(t_sdp_transport t);\nt_sdp_transport str2sdp_transport(string s);\n\n\nenum t_sdp_media_direction {\n\tSDP_INACTIVE,\n\tSDP_SENDONLY,\n\tSDP_RECVONLY,\n\tSDP_SENDRECV\n};\n\nstring sdp_media_direction2str(t_sdp_media_direction d);\n\n\nenum t_sdp_media_type {\n\tSDP_AUDIO,\n\tSDP_VIDEO,\n\tSDP_OTHER\n};\n\nt_sdp_media_type str2sdp_media_type(string s);\nstring sdp_media_type2str(t_sdp_media_type m);\n\n\nclass t_sdp_origin {\npublic:\n\tstring\t\t\tusername;\n\tstring\t\t\tsession_id;\n\tstring\t\t\tsession_version;\n\tt_sdp_ntwk_type\t\tnetwork_type;\n\tt_sdp_addr_type\t\taddress_type;\n\tstring\t\t\taddress;\n\n\tt_sdp_origin();\n\tt_sdp_origin(string _username, string _session_id,\n\t\t     string _session_version, string _address);\n\n\tstring encode(void) const;\n};\n\nclass t_sdp_connection {\npublic:\n\tt_sdp_ntwk_type\t\tnetwork_type;\n\tt_sdp_addr_type\t\taddress_type;\n\tstring\t\t\taddress;\n\n\tt_sdp_connection();\n\tt_sdp_connection(string _address);\n\tstring encode(void) const;\n};\n\nclass t_sdp_attr {\npublic:\n\tstring\t\t\tname;\n\tstring\t\t\tvalue;\n\n\tt_sdp_attr(string _name);\n\tt_sdp_attr(string _name, string _value);\n\tstring encode(void) const;\n};\n\n/** \n * Media definition.\n * The data from an m= line and associated a= lines.\n */\nclass t_sdp_media {\nprivate:\n\t/** Dynamic payload type for DTMF */\n\tunsigned short\t\tformat_dtmf;\n\npublic:\n\t/** The media type, e.g. audio, video */\n\tstring\t\t\tmedia_type;\n\t\n\t/** Port to receive media */\n\tunsigned short\t\tport;\n\t\n\t/** Transport protocol, e.g. RTP/AVP */\n\tstring\t\t\ttransport;\n\t\n\t/** \n\t * @name Media formats \n\t * Depending on the media type, formats are in numeric format or\n\t * alpha numeric format. Only one of the following formats will\n\t * be populated.\n\t */\n\t//@{\n\t/** Media formats in numeric form, i.e. audio codecs */\n\tlist<unsigned short>\tformats;\n\t\n\t/** Media formats in alpha numeric form. */\n\tlist<string>\t\talpha_num_formats;\n\t//@}\n\t\n\t/** Optional connection information if not specified on global level. */\n\tt_sdp_connection\tconnection;\n\t\n\t/** Attributes (a= lines) */\n\tlist <t_sdp_attr>\tattributes;\n\n\tt_sdp_media();\n\tt_sdp_media(t_sdp_media_type _media_type,\n\t\t    unsigned short _port, const list<t_audio_codec> &_formats,\n\t            unsigned short _format_dtmf, \n\t            const map<t_audio_codec, unsigned short> &ac2format);\n\n\tstring encode(void) const;\n\tvoid add_format(unsigned short f, t_audio_codec codec);\n\tt_sdp_attr *get_attribute(const string &name);\n\tlist<t_sdp_attr *>get_attributes(const string &name);\n\tt_sdp_media_direction get_direction(void) const;\n\tt_sdp_media_type get_media_type(void) const;\n\tt_sdp_transport get_transport(void) const;\n};\n\nclass t_sdp : public t_sip_body {\npublic:\n\tunsigned short\t\tversion;\n\tt_sdp_origin\t\torigin;\n\tstring\t\t\tsession_name;\n\tt_sdp_connection\tconnection;\n\tlist<t_sdp_attr>\tattributes;\n\tlist<t_sdp_media>\tmedia;\n\n\tt_sdp();\n\n\t// Create SDP with a single audio media stream\n\tt_sdp(const string &user, const string &sess_id, const string &sess_version, \n\t      const string &user_host, const string &media_host, unsigned short media_port,\n\t      const list<t_audio_codec> &formats, unsigned short format_dtmf,\n\t      const map<t_audio_codec, unsigned short> &ac2format);\n\n\t// Create SDP without media streams\n\tt_sdp(const string &user, const string &sess_id, const string &sess_version, \n\t\tconst string &user_host, const string &media_host);\n\n\t// Add media stream\n\tvoid add_media(const t_sdp_media &m);\n\n\tstring encode(void) const;\n\tt_sip_body *copy(void) const;\n\tt_body_type get_type(void) const;\n\tt_media get_media(void) const;\n\n\t// Return true if the current SDP is supported:\n\t// version is 0\n\t// 1 audio stream RTP\n\t// IN IP4 addressing\n\t// connection at session level only\n\t// If false is returned, then a warning code and text is returned.\n\tbool is_supported(int &warn_code, string &warn_text) const;\n\n\t// Get/set codec/rtp info for first media stream having a non-zero\n\t// value for the port of the given media type\n\tstring get_rtp_host(t_sdp_media_type media_type) const;\n\tunsigned short get_rtp_port(t_sdp_media_type media_type) const;\n\tlist<unsigned short> get_codecs(t_sdp_media_type media_type) const;\n\n\t// Get codec description from rtpmap\n\tstring get_codec_description(t_sdp_media_type media_type,\n\t\t\tunsigned short codec) const;\n\tt_audio_codec get_rtpmap_codec(const string &rtpmap) const;\n\tt_audio_codec get_codec(t_sdp_media_type media_type,\n\t\t\tunsigned short codec) const;\n\tt_sdp_media_direction get_direction(t_sdp_media_type media_type) const;\n\t\n\t// Get ftmp attribute\n\tstring get_fmtp(t_sdp_media_type media_type, unsigned short codec) const;\n\t\n\t// Get a specific parameter from fmtp, assuming the fmtp string is a list\n\t// of paramter=value strings separated by semi-colons\n\t// Returns -1 on failure\n\tint get_fmtp_int_param(t_sdp_media_type media_type, unsigned short codec,\n\t\t\tconst string param) const;\n\n\t// Get ptime. Returns 0 if ptime is not present\n\tunsigned short get_ptime(t_sdp_media_type media_type) const;\n\t\n\tbool get_zrtp_support(t_sdp_media_type media_type) const;\n\t\n\tvoid set_ptime(t_sdp_media_type media_type, unsigned short ptime);\n\tvoid set_direction(t_sdp_media_type media_type, t_sdp_media_direction direction);\n\tvoid set_fmtp(t_sdp_media_type media_type, unsigned short codec, const string &fmtp);\n\tvoid set_fmtp_int_param(t_sdp_media_type media_type, unsigned short codec,\n\t\t\tconst string &param, int value);\n\tvoid set_zrtp_support(t_sdp_media_type media_type);\n\n\t// Returns a pointer to the first media stream in the list of media\n\t// streams having a non-zero port value for the give media type.\n\t// Returns NULL if no such media stream can be found.\n\tconst t_sdp_media *get_first_media(t_sdp_media_type media_type) const;\n\t\n\t/**\n\t * Check if all local IP address are correctly filled in. This\n\t * check is an integrity check to help debugging the auto IP\n\t * discover feature.\n\t */\n\tvirtual bool local_ip_check(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/sdp/sdp_parse_ctrl.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"sdp_parse_ctrl.h\"\n#include \"audits/memman.h\"\n\n// Interface to Bison\nextern int yysdpparse(void);\n\n// Interface to Flex\nstruct yy_buffer_state;\nextern struct yy_buffer_state *yysdp_scan_string(const char *);\nextern void yysdp_delete_buffer(struct yy_buffer_state *);\n\nt_mutex t_sdp_parser::mtx_parser;\nt_sdp_parser::t_context t_sdp_parser::context = t_sdp_parser::X_INITIAL;\nt_sdp *t_sdp_parser::sdp = NULL;\n\nt_sdp *t_sdp_parser::parse(const string &s) {\n\tint ret;\n\tstruct yy_buffer_state *b;\n\t\n\tt_mutex_guard guard(mtx_parser);\n\t\n\tsdp = new t_sdp();\n\tMEMMAN_NEW(sdp);\n\t\n\t// The SDP body should end with a CRLF. Some implementations\n\t// do not send this last CRLF. Allow this deviation by adding\n\t// the last CRLF if it is missing.\n\tchar last_char = s.at(s.size()-1);\n\tif (last_char == '\\n' || last_char == '\\r') {\n\t\t// The SDP parser allows \\r, \\r\\n and \\n as CRLF\n\t\tb = yysdp_scan_string(s.c_str());\n\t} else {\n\t\t// Last CRLF is missing.\n\t\tb = yysdp_scan_string((s + \"\\r\\n\").c_str());\n\t}\n\t\n\tret = yysdpparse();\n\tyysdp_delete_buffer(b);\n\n\tif (ret != 0) {\n\t\tMEMMAN_DELETE(sdp);\n\t\tdelete sdp;\n\t\tsdp = NULL;\n\t\tthrow ret;\n\t}\n\n\treturn sdp;\n}\n\nt_sdp_syntax_error::t_sdp_syntax_error(const string &e) {\n\terror = e;\n}\n"
  },
  {
    "path": "src/sdp/sdp_parse_ctrl.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Parser control\n\n#ifndef _SDP_PARSE_CTRL_H\n#define _SDP_PARSE_CTRL_H\n\n#include \"sdp.h\"\n#include \"threads/mutex.h\"\n\n#define SDP\t\tt_sdp_parser::sdp\n\n#define CTX_INITIAL\t(t_sdp_parser::context = t_sdp_parser::X_INITIAL)\n#define CTX_SAFE\t(t_sdp_parser::context = t_sdp_parser::X_SAFE)\n#define CTX_NUM\t\t(t_sdp_parser::context = t_sdp_parser::X_NUM)\n#define CTX_LINE\t(t_sdp_parser::context = t_sdp_parser::X_LINE)\n\n// The t_sdp_parser controls the direction of the scanner/parser\n// process and it stores the results from the parser.\nclass t_sdp_parser {\nprivate:\n\t/** Mutex to synchronize parse operations */\n\tstatic t_mutex\tmtx_parser;\npublic:\nenum t_context {\n\tX_INITIAL,\t// Initial context\n\tX_SAFE,\t\t// Safe context\n\tX_NUM,\t\t// Number context\n\tX_LINE,\t\t// Whole line context\n};\n\n\tstatic t_context\tcontext;   // Scan context\n\tstatic t_sdp\t\t*sdp;      // SDP that has been parsed\n\n\t// Parse string s. Throw int exception when parsing fails.\n\tstatic t_sdp *parse(const string &s);\n\n};\n\n// Error that can be thrown as exception\nclass t_sdp_syntax_error {\npublic:\n\tstring error;\n\tt_sdp_syntax_error(const string &e);\n};\n\n#endif\n"
  },
  {
    "path": "src/sdp/sdp_parser.h",
    "content": "/* A Bison parser, made by GNU Bison 2.3.  */\n\n/* Skeleton interface for Bison's Yacc-like parsers in C\n\n   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006\n   Free Software Foundation, Inc.\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2, or (at your option)\n   any later version.\n\n   This program is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */\n\n/* As a special exception, you may create a larger work that contains\n   part or all of the Bison parser skeleton and distribute that work\n   under terms of your choice, so long as that work isn't itself a\n   parser generator using the skeleton or a modified version thereof\n   as a parser skeleton.  Alternatively, if you modify or redistribute\n   the parser skeleton itself, you may (at your option) remove this\n   special exception, which will cause the skeleton and the resulting\n   Bison output files to be licensed under the GNU General Public\n   License without this special exception.\n\n   This special exception was added by the Free Software Foundation in\n   version 2.2 of Bison.  */\n\n/* Tokens.  */\n#ifndef YYTOKENTYPE\n# define YYTOKENTYPE\n   /* Put the tokens into the symbol table, so that GDB and other debuggers\n      know about them.  */\n   enum yytokentype {\n     T_NUM = 258,\n     T_TOKEN = 259,\n     T_SAFE = 260,\n     T_LINE = 261,\n     T_CRLF = 262,\n     T_LINE_VERSION = 263,\n     T_LINE_ORIGIN = 264,\n     T_LINE_SESSION_NAME = 265,\n     T_LINE_CONNECTION = 266,\n     T_LINE_ATTRIBUTE = 267,\n     T_LINE_MEDIA = 268,\n     T_LINE_UNKNOWN = 269,\n     T_NULL = 270\n   };\n#endif\n/* Tokens.  */\n#define T_NUM 258\n#define T_TOKEN 259\n#define T_SAFE 260\n#define T_LINE 261\n#define T_CRLF 262\n#define T_LINE_VERSION 263\n#define T_LINE_ORIGIN 264\n#define T_LINE_SESSION_NAME 265\n#define T_LINE_CONNECTION 266\n#define T_LINE_ATTRIBUTE 267\n#define T_LINE_MEDIA 268\n#define T_LINE_UNKNOWN 269\n#define T_NULL 270\n\n\n\n\n#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED\ntypedef union YYSTYPE\n#line 50 \"sdp_parser.yxx\"\n{\n\tint\t\t\tyysdpt_int;\n\tstring\t\t\t*yysdpt_str;\n\tt_sdp_ntwk_type\t\tyysdpt_ntwk_type;\n\tt_sdp_addr_type\t\tyysdpt_addr_type;\n\tt_sdp_connection\t*yysdpt_connection;\n\tlist<t_sdp_attr>\t*yysdpt_attributes;\n\tt_sdp_attr\t\t*yysdpt_attribute;\n\tt_sdp_media\t\t*yysdpt_media;\n\tlist<string>\t\t*yysdpt_token_list;\n}\n/* Line 1529 of yacc.c.  */\n#line 91 \"sdp_parser.h\"\n\tYYSTYPE;\n# define yystype YYSTYPE /* obsolescent; will be withdrawn */\n# define YYSTYPE_IS_DECLARED 1\n# define YYSTYPE_IS_TRIVIAL 1\n#endif\n\nextern YYSTYPE yysdplval;\n\n"
  },
  {
    "path": "src/sdp/sdp_parser.yxx",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n%{\n#include <algorithm>\n#include <cstdio>\n#include <string>\n#include \"sdp_parse_ctrl.h\"\n#include \"sdp.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\n\nextern int yysdplex(void);\nvoid yysdperror(const char *s);\n%}\n\n// The %debug option causes a problem with the %destructor options later on.\n// The bison compilor generates undefined symbols:\n//\n//   parser.y: In function `void yysymprint(FILE*, int, yystype)':\n//   parser.y:0: error: `null' undeclared (first use this function)\n//\n// So if you need to debug, then outcomment the %destructor first. This will\n// do no harm to your debugging, it only will cause memory leaks during\n// error handling.\n//\n// %debug\n\n\n%expect 2\n/* See below for the expected shift/reduce conflicts. */\n\n%union {\n\tint\t\t\tyysdpt_int;\n\tstring\t\t\t*yysdpt_str;\n\tt_sdp_ntwk_type\t\tyysdpt_ntwk_type;\n\tt_sdp_addr_type\t\tyysdpt_addr_type;\n\tt_sdp_connection\t*yysdpt_connection;\n\tlist<t_sdp_attr>\t*yysdpt_attributes;\n\tt_sdp_attr\t\t*yysdpt_attribute;\n\tt_sdp_media\t\t*yysdpt_media;\n\tlist<string>\t\t*yysdpt_token_list;\n}\n\n%token <yysdpt_int>\tT_NUM\n%token <yysdpt_str>\tT_TOKEN\n%token <yysdpt_str>\tT_SAFE\n%token <yysdpt_str>\tT_LINE\n\n%token\t\t\tT_CRLF\n\n%token\t\t\tT_LINE_VERSION\n%token\t\t\tT_LINE_ORIGIN\n%token\t\t\tT_LINE_SESSION_NAME\n%token\t\t\tT_LINE_CONNECTION\n%token\t\t\tT_LINE_ATTRIBUTE\n%token\t\t\tT_LINE_MEDIA\n%token\t\t\tT_LINE_UNKNOWN\n\n// The token T_NULL is never returned by the scanner.\n%token\t\t\tT_NULL\n\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_TOKEN\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_SAFE\n%destructor { MEMMAN_DELETE($$); delete $$; }\tT_LINE\n\n%type <yysdpt_addr_type>\taddress_type\n%type <yysdpt_connection>\tconnection\n%type <yysdpt_ntwk_type>\tnetwork_type\n%type <yysdpt_attributes>\tattributes\n%type <yysdpt_attribute>\tattribute\n%type <yysdpt_attribute>\tattribute2\n%type <yysdpt_media>\t\tmedia\n%type <yysdpt_str>\t\ttransport\n%type <yysdpt_token_list>\tformats\n\n%destructor { MEMMAN_DELETE($$); delete $$; }\tconnection\n%destructor { MEMMAN_DELETE($$); delete $$; }\tattributes\n%destructor { MEMMAN_DELETE($$); delete $$; }\tattribute\n%destructor { MEMMAN_DELETE($$); delete $$; }\tattribute2\n%destructor { MEMMAN_DELETE($$); delete $$; }\tmedia\n%destructor { MEMMAN_DELETE($$); delete $$; }\ttransport\n%destructor { MEMMAN_DELETE($$); delete $$; }\tformats\n\n%%\n\n/* The unknown_lines cause an expected shift/reduce conflict */\nsdp_body:\t  version\n\t\t  origin\n\t\t  session_name\n\t\t  unknown_lines\n\t\t  sess_connection\n\t\t  unknown_lines\n\t\t  sess_attributes\n\t\t  media_list {\n\t\t  \t/* Parsing stops here. Remaining text is\n\t\t\t * not parsed.\n\t\t\t */\n\t\t  \tYYACCEPT; }\n\t\t| error T_NULL {\n\t\t\t/* KLUDGE to avoid memory leak in bison.\n\t\t\t * See the SIP parser for an explanation.\n\t\t\t */\n\t\t\tYYABORT;\n\t\t}\n;\n\nversion:\t  T_LINE_VERSION { CTX_NUM; } T_NUM { CTX_INITIAL; }\n\t\t  T_CRLF {\n\t\t\tSDP->version = $3; }\n;\n\norigin:\t\t  T_LINE_ORIGIN { CTX_SAFE; } T_SAFE { CTX_INITIAL; }\n\t\t  T_TOKEN T_TOKEN network_type address_type T_TOKEN T_CRLF {\n\t\t\tSDP->origin.username = *$3;\n\t\t\tSDP->origin.session_id = *$5;\n\t\t\tSDP->origin.session_version = *$6;\n\t\t\tSDP->origin.network_type = $7;\n\t\t\tSDP->origin.address_type = $8;\n\t\t\tSDP->origin.address = *$9;\n\t\t\tMEMMAN_DELETE($3); delete $3;\n\t\t\tMEMMAN_DELETE($5); delete $5;\n\t\t\tMEMMAN_DELETE($6); delete $6;\n\t\t\tMEMMAN_DELETE($9); delete $9; }\n;\n\nnetwork_type:\t  T_TOKEN { try {\n\t\t\t\t$$ = str2sdp_ntwk_type(*$1);\n\t\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\t    }\n\t\t\t    catch (t_sdp_syntax_error) {\n\t\t\t    \t// Invalid network type.\n\t\t\t\t// Set network type to NULL. This way the message\n\t\t\t\t// will not be discarded and the error can be\n\t\t\t\t// handled on the SIP level (error response or\n\t\t\t\t// call tear down).\n\t\t\t    \tMEMMAN_DELETE($1); delete $1;\n\t\t\t\t$$ = SDP_NTWK_NULL;\n\t\t\t    } }\n;\n\naddress_type:\t  T_TOKEN { try {\n\t\t\t\t$$ = str2sdp_addr_type(*$1);\n\t\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\t    }\n\t\t\t    catch (t_sdp_syntax_error) {\n\t\t\t    \t// Invalid address type\n\t\t\t    \tMEMMAN_DELETE($1); delete $1;\n\t\t\t\t$$ = SDP_ADDR_NULL;\n\t\t\t    } }\n;\n\nsession_name:\t  T_LINE_SESSION_NAME { CTX_LINE; } T_LINE\n\t\t  { CTX_INITIAL; } T_CRLF {\n\t\t\tSDP->session_name = *$3;\n\t\t\tMEMMAN_DELETE($3); delete $3; }\n;\n\nsess_connection:  connection {\n\t\t\tSDP->connection = *$1;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nconnection:\t  /* empty */\t{ $$ = new t_sdp_connection(); MEMMAN_NEW($$); }\n\t\t| T_LINE_CONNECTION network_type address_type T_TOKEN\n\t\t  T_CRLF {\n\t\t\t$$ = new t_sdp_connection();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t$$->network_type = $2;\n\t\t\t$$->address_type = $3;\n\t\t\t$$->address = *$4;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nsess_attributes:  attributes {\n\t\t\tSDP->attributes = *$1;\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n;\n\nattributes:\t  /* emtpy */\t{ $$ = new list<t_sdp_attr>; MEMMAN_NEW($$); }\n\t\t| attributes attribute {\n\t\t\t$$ = $1;\n\t\t\t$$->push_back(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\nattribute:\t  T_LINE_ATTRIBUTE attribute2 T_CRLF {\n\t\t\t$$ = $2; }\n;\n\nattribute2:\t  T_TOKEN {\n\t\t\t$$ = new t_sdp_attr(*$1);\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1; }\n\t\t| T_TOKEN ':' { CTX_LINE; } T_LINE { CTX_INITIAL; } {\n\t\t\t$$ = new t_sdp_attr(*$1, *$4);\n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1;\n\t\t\tMEMMAN_DELETE($4); delete $4; }\n;\n\nmedia_list:\t  /* empty */\n\t\t| media_list media {\n\t\t\tSDP->media.push_back(*$2);\n\t\t\tMEMMAN_DELETE($2); delete $2; }\n;\n\n/* The unknown_lines cause an expected shift/reduce conflict */\nmedia:\t\t  T_LINE_MEDIA T_TOKEN { CTX_NUM; } T_NUM { CTX_INITIAL; }\n\t\t  transport formats T_CRLF unknown_lines connection\n\t\t  unknown_lines attributes {\n\t\t  \t$$ = new t_sdp_media();\n\t\t\tMEMMAN_NEW($$);\n\t\t\t\n\t\t\tif ($4 > 65535) YYERROR;\n\t\t\t\n\t\t\t$$->media_type = tolower(*$2);\n\t\t\t$$->port = $4;\n\t\t\t$$->transport = *$6;\n\t\t\t\n\t\t\t/* The format depends on the media type */\n\t\t\tswitch($$->get_media_type()) {\n\t\t\tcase SDP_AUDIO:\n\t\t\tcase SDP_VIDEO:\n\t\t\t\t/* Numeric format */\n\t\t\t\tfor (list<string>::const_iterator it = $7->begin(); it != $7->end(); ++it) {\n\t\t\t\t\tif (is_number(*it)) $$->formats.push_back(atoi(it->c_str()));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t/* Alpha numeric format */\n\t\t\t\t$$->alpha_num_formats = *$7;\n\t\t\t}\n\t\t\t\n\t\t\t$$->connection = *$10;\n\t\t\t$$->attributes = *$12;\n\t\t\tMEMMAN_DELETE($2); delete $2;\n\t\t\tMEMMAN_DELETE($6); delete $6;\n\t\t\tMEMMAN_DELETE($7); delete $7;\n\t\t\tMEMMAN_DELETE($10); delete $10;\n\t\t\tMEMMAN_DELETE($12); delete $12; }\n;\n\ntransport:\t  T_TOKEN { \n\t\t\t$$ = $1; }\n\t\t| T_TOKEN '/' T_TOKEN {\n\t\t\t$$ = new string(*$1 + '/' + *$3); \n\t\t\tMEMMAN_NEW($$);\n\t\t\tMEMMAN_DELETE($1); delete $1; \n\t\t\tMEMMAN_DELETE($3); delete $3; }\n\n// For RTP/AVP a format is a number. For other transport protocols,\n// non-numerical formats are possible.\nformats:\t  /* empty */\t{ $$ = new list<string>; MEMMAN_NEW($$); }\n\t\t| formats T_TOKEN {\n\t\t\t$$ = $1;\n\t\t\t$$->push_back(*$2);\n\t\t\tMEMMAN_DELETE($2);\n\t\t\tdelete $2; }\n;\n\n/* Skip unknown lines */\nunknown_lines:\t  /* empty */\n\t\t| unknown_lines unknown_line\n;\n\nunknown_line:\t  T_LINE_UNKNOWN { CTX_LINE; } T_LINE { CTX_INITIAL; }\n\t\t  T_CRLF {\n\t\t  \tMEMMAN_DELETE($3); delete $3; }\n;\n\n%%\n\nvoid\nyysdperror (const char *s)  /* Called by yysdpparse on error */\n{\n  // printf (\"%s\\n\", s);\n}\n"
  },
  {
    "path": "src/sdp/sdp_scanner.lxx",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n%{\n#include <string>\n#include \"sdp_parse_ctrl.h\"\n#include \"sdp_parser.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\n%}\n\n%option noyywrap\n%option stack\n\nDIGIT\t\t[0-9]\nALPHA\t\t[a-zA-Z]\nALNUM\t\t[a-zA-Z0-9]\nTOKEN_SYM\t[[:alnum:]\\-\\.!%\\*_\\+\\'~]\nSAFE_SYM\t[[:alnum:]'`\\-\\.\\/:\\?\\\"#\\$&\\*;=@\\[\\]\\^_\\{|\\}\\+~\\\"]\n\n%x C_NUM\n%x C_LINE\n%x C_SAFE\n\n%%\n\tswitch (t_sdp_parser::context) {\n\tcase t_sdp_parser::X_NUM:\tBEGIN(C_NUM); break;\n\tcase t_sdp_parser::X_LINE:\tBEGIN(C_LINE); break;\n\tcase t_sdp_parser::X_SAFE:\tBEGIN(C_SAFE); break;\n\tdefault:\t\t\tBEGIN(INITIAL);\n\t}\n\n\t/* SDP lines */\n^v=\t\t{ return T_LINE_VERSION; }\n^o=\t\t{ return T_LINE_ORIGIN; }\n^s=\t\t{ return T_LINE_SESSION_NAME; }\n^c=\t\t{ return T_LINE_CONNECTION; }\n^a=\t\t{ return T_LINE_ATTRIBUTE; }\n^m=\t\t{ return T_LINE_MEDIA; }\n^{ALPHA}=\t{ return T_LINE_UNKNOWN; }\n\n\t/* Token as define in RFC 3261 */\n{TOKEN_SYM}+ \t{ yysdplval.yysdpt_str = new string(yysdptext);\n\t\t  MEMMAN_NEW(yysdplval.yysdpt_str);\n\t\t  return T_TOKEN; }\n\n\t/* End of line */\n\\r\\n\t\t{ return T_CRLF; }\n\\n\t\t{ return T_CRLF; }\n\n[[:blank:]]\t/* Skip white space */\n\n\t/* Single character token */\n.\t\t{ return yysdptext[0]; }\n\n\t/* Number */\n<C_NUM>{DIGIT}+\t\t{ yysdplval.yysdpt_int = atoi(yysdptext);\n\t\t\t  return T_NUM; }\n<C_NUM>[[:blank:]]\t/* Skip white space */\n<C_NUM>.\t\t{ return yysdptext[0]; }\n<C_NUM>\\r\\n\t\t{ return T_CRLF; }\n<C_NUM>\\n\t\t{ return T_CRLF; }\n\n\t/* Safe */\n<C_SAFE>{SAFE_SYM}+\t{ yysdplval.yysdpt_str = new string(yysdptext);\n\t\t\t  MEMMAN_NEW(yysdplval.yysdpt_str);\n\t\t\t  return T_SAFE; }\n<C_SAFE>[[:blank:]]\t/* Skip white space */\n<C_SAFE>.\t\t{ return yysdptext[0]; }\n<C_SAFE>\\r\\n\t\t{ return T_CRLF; }\n<C_SAFE>\\n\t\t{ return T_CRLF; }\n\n\t/* Get all text till end of line */\n<C_LINE>[^\\r\\n]+\t{ yysdplval.yysdpt_str = new string(yysdptext);\n\t\t\t  MEMMAN_NEW(yysdplval.yysdpt_str);\n\t\t\t  return T_LINE; }\n<C_LINE>\\r\\n\t\t{ return T_CRLF; }\n<C_LINE>\\n\t\t{ return T_CRLF; }\n<C_LINE>\\r\t\t{ return T_CRLF; }\n"
  },
  {
    "path": "src/sender.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include <ctime>\n#include <cstring>\n#include <cerrno>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include \"events.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"sender.h\"\n#include \"translator.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"sockets/connection_table.h\"\n#include \"sockets/ipaddr.h\"\n#include \"sockets/socket.h\"\n#include \"parser/parse_ctrl.h\"\n#include \"parser/sip_message.h\"\n#include \"audits/memman.h\"\n#include \"stun/stun.h\"\n\n#define MAX_TRANSMIT_RETRIES\t3\n\n// Maximum size of a message to be sent over an existing connection.\n// For larger message always a new connection is opened to avoid\n// head of line blocking.\n#define MAX_REUSE_CONN_SIZE\t10240\n\nextern t_socket_udp *sip_socket;\nextern t_connection_table *connection_table;\nextern t_event_queue *evq_sender;\nextern t_event_queue *evq_trans_mgr;\nextern t_event_queue *evq_trans_layer;\nextern t_phone *phone;\n\n// Number of consecutive non-icmp errors received\nstatic int num_non_icmp_errors = 0;\n\n// Check if the error is caused by an incoming ICMP error. If so, then deliver\n// the ICMP error to the transaction manager.\n//\n// err - error returned by sendto\n// dst_addr - destination IP address of packet that failed to be sent\n// dst_port - destination port of packet that failed to be sent\n//\n// Returns true if the packet that failed to be sent, should still be sent.\n// Returns false if the packet that failed to be sent, should be discarded.\nstatic bool handle_socket_err(int err, IPaddr dst_addr, unsigned short dst_port) {\n\tstring log_msg;\n\n\t// Check if an ICMP error has been received\n\tt_icmp_msg icmp;\n\tif (sip_socket->get_icmp(icmp)) {\n\t\tlog_msg = \"Received ICMP from: \";\n\t\tlog_msg += h_ip2str(icmp.icmp_src_ipaddr);\n\t\tlog_msg += \"\\nICMP type: \";\n\t\tlog_msg += int2str(icmp.type);\n\t\tlog_msg += \"\\nICMP code: \";\n\t\tlog_msg += int2str(icmp.code);\n\t\tlog_msg += \"\\nDestination of packet causing ICMP: \";\n\t\tlog_msg += h_ip2str(icmp.ipaddr);\n\t\tlog_msg += \":\";\n\t\tlog_msg += int2str(icmp.port);\n\t\tlog_msg += \"\\nSocket error: \";\n\t\tlog_msg += int2str(err);\n\t\tlog_msg += \" \";\n\t\tlog_msg += get_error_str(err);\n\t\tlog_file->write_report(log_msg, \"::hanlde_socket_err\", LOG_NORMAL);\n\n\t\tevq_trans_mgr->push_icmp(icmp);\n\t\t\n\t\tnum_non_icmp_errors = 0;\n\t\t\n\t\t// If the ICMP error comes from the same destination as the\n\t\t// destination of the packet that failed to be sent, then the\n\t\t// packet should be discarded as it can most likely not be\n\t\t// delivered and would cause an infinite loop of ICMP errors\n\t\t// otherwise.\n\t\tif (icmp.ipaddr == dst_addr && icmp.port == dst_port) {\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\t// Even if an ICMP message is received this code can get executed.\n\t\t// Sometimes the error is already present on the socket, but the ICMP\n\t\t// message is not yet queued.\n\t\tlog_msg = \"Failed to send to SIP UDP socket.\\n\";\n\t\tlog_msg += \"Error code: \";\n\t\tlog_msg += int2str(err);\n\t\tlog_msg += \"\\n\";\n\t\tlog_msg += get_error_str(err);\n\t\tlog_file->write_report(log_msg, \"::handle_socket_err\");\n\t\t\n\t\tnum_non_icmp_errors++;\n\t\t\n\t\t/*\n\t\t * non-ICMP errors occur when a destination on the same\n\t\t * subnet cannot be reached. So this code seems to be\n\t\t * harmful.\n\t\tif (num_non_icmp_errors > 100) {\n\t\t\tlog_msg = \"Excessive number of socket errors.\";\n\t\t\tlog_file->write_report(log_msg, \"::handle_socket_err\", \n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tlog_msg = TRANSLATE(\"Excessive number of socket errors.\");\n\t\t\tui->cb_show_msg(log_msg, MSG_CRITICAL);\n\t\t\texit(1);\n\t\t}\n\t\t*/\n\t}\n\t\n\treturn true;\n}\n\nstatic void send_sip_udp(t_event *event) {\n\tt_event_network\t*e;\n\t\n\te = (t_event_network *)event;\n\t\n\tassert(e->dst_addr != 0);\n\tassert(e->dst_port != 0);\n\n\t// Set correct transport in topmost Via header of a request.\n\t// For a response the Via header is copied from the incoming request.\n\tt_sip_message *sip_msg = e->get_msg();\n\tif (sip_msg->get_type() == MSG_REQUEST) {\n\t\tsip_msg->hdr_via.via_list.front().transport = \"UDP\";\n\t}\n\t\n\tstring m = sip_msg->encode();\n\tlog_file->write_header(\"::send_sip_udp\", LOG_SIP);\n\tlog_file->write_raw(\"Send to: udp:\");\n\tlog_file->write_raw(h_ip2str(e->dst_addr));\n\tlog_file->write_raw(\":\");\n\tlog_file->write_raw(e->dst_port);\n\tlog_file->write_endl();\n\tlog_file->write_raw(m);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\t\n\tbool msg_sent = false;\n\tint transmit_count = 0;\n\twhile (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {\n\t\ttry {\n\t\t\tsip_socket->sendto(e->dst_addr, e->dst_port, m.c_str(), m.size());\n\t\t\tnum_non_icmp_errors = 0;\n\t\t\tmsg_sent = true;\n\t\t} catch (int err) {\n\t\t\tif (!handle_socket_err(err, e->dst_addr, e->dst_port)) {\n\t\t\t\t// Discard packet.\n\t\t\t\tmsg_sent = true;\n\t\t\t} else {\n\t\t\t\tif (transmit_count <= MAX_TRANSMIT_RETRIES) {\n\t\t\t\t\t// Sleep 100 ms\n\t\t\t\t\tstruct timespec sleeptimer;\n\t\t\t\t\tsleeptimer.tv_sec = 0;\n\t\t\t\t\tsleeptimer.tv_nsec = 100000000;\n\t\t\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void send_sip_tcp(t_event *event) {\n\tt_event_network\t*e;\n\tbool new_connection = false;\n\t\n\te = (t_event_network *)event;\n\tIPaddr dst_addr = e->dst_addr;\n\tunsigned short dst_port = e->dst_port;\n\t\n\tassert(dst_addr != 0);\n\tassert(dst_port != 0);\n\t\n\t// Set correct transport in topmost Via header of a request.\n\t// For a response the Via header is copied from the incoming request.\n\tt_sip_message *sip_msg = e->get_msg();\n\tif (sip_msg->get_type() == MSG_REQUEST) {\n\t\tsip_msg->hdr_via.via_list.front().transport = \"TCP\";\n\t}\n\t\n\tt_connection *conn = NULL;\n\t\n\t// If a connection exists then re-use this connection. Otherwise a new connection\n\t// must be opened.\n\t// For a request a connection to the destination address and port of the event\n\t// must be opened.\n\t// For a response a connection to the sent-by address and port in the Via header\n\t// must be opened.\n\t\n\tif (sip_msg->get_encoded_size() <= MAX_REUSE_CONN_SIZE) {\n\t\t// Re-use a connection only for small messages. Large messages cause\n\t\t// head of line blocking.\n\t\tconn = connection_table->get_connection(dst_addr, dst_port);\n\t} else {\n\t\tlog_file->write_report(\n\t\t\t\"Open new connection for large message.\",\n\t\t\t\"::send_sip_tcp\", LOG_SIP, LOG_DEBUG);\n\t}\n\t\n\tif (!conn) {\n\t\tif (sip_msg->get_type() == MSG_RESPONSE) {\n\t\t\tt_ip_port dst_ip_port;\n\t\t\tsip_msg->hdr_via.get_response_dst(dst_ip_port);\n\t\t\tdst_addr = dst_ip_port.ipaddr;\n\t\t\tdst_port = dst_ip_port.port;\n\t\t}\n\t\t\n\t\tt_socket_tcp *tcp = new t_socket_tcp();\n\t\tMEMMAN_NEW(tcp);\n\t\t\n\t\tlog_file->write_header(\"::send_sip_tcp\", LOG_SIP, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Open connection to \");\n\t\tlog_file->write_raw(h_ip2str(dst_addr));\n\t\tlog_file->write_raw(\":\");\n\t\tlog_file->write_raw(dst_port);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\ttry {\n\t\t\ttcp->connect(dst_addr, dst_port);\n\t\t} catch (int err) {\n\t\t\tevq_trans_mgr->push_failure(FAIL_TRANSPORT,\n\t\t\t\tsip_msg->hdr_via.via_list.front().branch,\n\t\t\t\tsip_msg->hdr_cseq.method);\n\t\t\t\n\t\t\tlog_file->write_header(\"::send_sip_tcp\", LOG_SIP, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Failed to open connection to \");\n\t\t\tlog_file->write_raw(h_ip2str(dst_addr));\n\t\t\tlog_file->write_raw(\":\");\n\t\t\tlog_file->write_raw(dst_port);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tdelete tcp;\n\t\t\tMEMMAN_DELETE(tcp);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tconn = new t_connection(tcp);\n\t\tMEMMAN_NEW(conn);\n\t\t\n\t\t// For large messages always a new connection is established.\n\t\t// No other messages should be sent via this connection to avoid\n\t\t// head of line blocking.\n\t\tif (sip_msg->get_encoded_size() > MAX_REUSE_CONN_SIZE) {\n\t\t\tconn->set_reuse(false);\n\t\t}\n\t\t\n\t\tnew_connection = true;\n\t}\n\t\t\n\t// NOTE: if an existing connection was found, the connection table is now locked.\n\t\n\t// If persistent TCP connections are required, then\n\t// 1) If the SIP message is a registration request, then add the URI from the To-header \n\t//    to the registered URI set of the connection.\n\t// 2) If the SIP message is a de-registration request then remove the URI from the\n\t//    To-header from the registered URI set of the connection.\n\tif (sip_msg->get_type() == MSG_REQUEST) {\n\t\tt_request *req = dynamic_cast<t_request *>(sip_msg);\n\t\tif (req->method == REGISTER) \n\t\t{\n\t\t\tt_phone_user *pu = phone->find_phone_user(req->hdr_to.uri);\n\t\t\tif (pu) {\n\t\t\t\tt_user *user_config = pu->get_user_profile();\n\t\t\t\tassert(user_config);\n\t\t\t\t\n\t\t\t\tif (user_config->get_persistent_tcp()) {\n\t\t\t\t\tconn->update_registered_uri_set(req);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog_file->write_header(\"::send_sip_tcp\", LOG_NORMAL, LOG_WARNING);\n\t\t\t\tlog_file->write_raw(\"Cannot find phone user for \");\n\t\t\t\tlog_file->write_raw(req->hdr_to.uri.encode());\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t}\n\t\t}\n\t}\n\n\tstring m = sip_msg->encode();\n\tlog_file->write_header(\"::send_sip_tcp\", LOG_SIP);\n\tlog_file->write_raw(\"Send to: tcp:\");\n\tlog_file->write_raw(h_ip2str(dst_addr));\n\tlog_file->write_raw(\":\");\n\tlog_file->write_raw(dst_port);\n\tlog_file->write_endl();\n\tlog_file->write_raw(m);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\t\n\tconn->async_send(m.c_str(), m.size());\n\t\n\tif (new_connection) {\n\t\tconnection_table->add_connection(conn);\n\t} else {\n\t\tconnection_table->unlock();\n\t}\n}\n\nstatic void send_stun(t_event *event) {\n\tt_event_stun_request\t*e;\n\t\n\te = (t_event_stun_request *)event;\n\t\n\tassert(e->dst_addr != 0);\n\tassert(e->dst_port != 0);\n\t\n\tlog_file->write_header(\"::send_stun\", LOG_STUN);\n\tlog_file->write_raw(\"Send to: \");\n\tlog_file->write_raw(h_ip2str(e->dst_addr));\n\tlog_file->write_raw(\":\");\n\tlog_file->write_raw(e->dst_port);\n\tlog_file->write_endl();\n\tlog_file->write_raw(stunMsg2Str(*e->get_msg()));\n\tlog_file->write_footer();\n\t\n\tStunAtrString stun_pass;\n\tstun_pass.sizeValue = 0;\n\tchar m[STUN_MAX_MESSAGE_SIZE];\n\tint msg_size = stunEncodeMessage(*e->get_msg(), m, \n\t\tSTUN_MAX_MESSAGE_SIZE, stun_pass, false);\n\n\tbool msg_sent = false;\n\tint transmit_count = 0;\n\twhile (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {\t\n\t\ttry {\n\t\t\tsip_socket->sendto(e->dst_addr, e->dst_port, m, msg_size);\n\t\t\tnum_non_icmp_errors = 0;\n\t\t\tmsg_sent = true;\n\t\t} catch (int err) {\n\t\t\tif (!handle_socket_err(err, e->dst_addr, e->dst_port)) {\n\t\t\t\t// Discard packet.\n\t\t\t\tmsg_sent = true;\n\t\t\t} else {\n\t\t\t\tif (transmit_count <= MAX_TRANSMIT_RETRIES) {\n\t\t\t\t\t// Sleep 100 ms\n\t\t\t\t\tstruct timespec sleeptimer;\n\t\t\t\t\tsleeptimer.tv_sec = 0;\n\t\t\t\t\tsleeptimer.tv_nsec = 100000000;\n\t\t\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void send_nat_keepalive(t_event *event) {\n\tt_event_nat_keepalive *e = dynamic_cast<t_event_nat_keepalive *>(event);\n\tassert(e);\n\tassert(e->dst_addr != 0);\n\tassert(e->dst_port != 0);\t\n\t\n\tchar m[2] = { '\\r', '\\n' };\n\t\n\tbool msg_sent = false;\n\tint transmit_count = 0;\n\twhile (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {\n\t\ttry {\n\t\t\tsip_socket->sendto(e->dst_addr, e->dst_port, m, 2);\n\t\t\tnum_non_icmp_errors = 0;\n\t\t\tmsg_sent = true;\n\t\t} catch (int err) {\n\t\t\tif (!handle_socket_err(err, e->dst_addr, e->dst_port)) {\n\t\t\t\t// Discard packet.\n\t\t\t\tmsg_sent = true;\n\t\t\t} else {\n\t\t\t\tif (transmit_count <= MAX_TRANSMIT_RETRIES) {\n\t\t\t\t\t// Sleep 100 ms\n\t\t\t\t\tstruct timespec sleeptimer;\n\t\t\t\t\tsleeptimer.tv_sec = 0;\n\t\t\t\t\tsleeptimer.tv_nsec = 100000000;\n\t\t\t\t\tnanosleep(&sleeptimer, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void send_tcp_ping(t_event *event) {\n\tstring log_msg;\n\t\n\tt_event_tcp_ping *e = dynamic_cast<t_event_tcp_ping *>(event);\n\tassert(e);\n\t\n\tt_connection *conn = connection_table->get_connection(\n\t\t\te->get_dst_addr(), e->get_dst_port());\n\t\t\t\n\tif (!conn) {\n\t\t// There is no connection to send the ping.\n\t\tlog_msg = \"Connection to \";\n\t\tlog_msg += h_ip2str(e->get_dst_addr());\n\t\tlog_msg += \":\";\n\t\tlog_msg += int2str(e->get_dst_port());\n\t\tlog_msg += \" is gone.\";\n\t\tlog_file->write_report(log_msg, \"::send_tcp_ping\", LOG_SIP, LOG_WARNING);\n\t\t\t\t\t\n\t\t// Signal the transaction layer that the connection is gone.\n\t\tevq_trans_layer->push_broken_connection(e->get_user_uri());\n\t\t\n\t\treturn;\n\t}\n\t\n\tconn->async_send(TCP_PING_PACKET, strlen(TCP_PING_PACKET));\n\t\n\tconnection_table->unlock();\n}\n\nvoid *tcp_sender_loop(void *arg) {\n\tstring log_msg;\n\tlist<t_connection *> writable_connections;\n\t\n\twhile(true) {\n\t\twritable_connections.clear();\n\t\twritable_connections = connection_table->select_write(NULL);\n\t\t\n\t\tif (writable_connections.empty()) {\n\t\t\t// Another thread cancelled the select command.\n\t\t\t// Stop listening.\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\t// NOTE: The connection table is now locked.\n\t\t\n\t\tfor (list<t_connection *>::iterator it = writable_connections.begin();\n\t\t\tit != writable_connections.end(); ++it)\n\t\t{\n\t\t\ttry {\n\t\t\t\t(*it)->write();\n\t\t\t} catch (int err) {\n\t\t\t\tif (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tIPaddr remote_addr;\n\t\t\t\tunsigned short remote_port;\n\t\t\t\n\t\t\t\t(*it)->get_remote_address(remote_addr, remote_port);\n\t\t\t\t\n\t\t\t\tlog_msg = \"Got error on socket to \";\n\t\t\t\tlog_msg += h_ip2str(remote_addr);\n\t\t\t\tlog_msg += \":\";\n\t\t\t\tlog_msg += int2str(remote_port);\n\t\t\t\tlog_msg += \" - \";\n\t\t\t\tlog_msg += get_error_str(err);\n\t\t\t\tlog_file->write_report(log_msg, \"::tcp_sender_loop\", LOG_SIP, LOG_WARNING);\n\t\t\t\t\n\t\t\t\t// Connection is broken. \n\t\t\t\t// Signal the transaction layer that the connection is broken for\n\t\t\t\t// all associated registered URI's.\n\t\t\t\tconst list<t_url> &uris = (*it)->get_registered_uri_set();\n\t\t\t\tfor (list<t_url>::const_iterator it_uri = uris.begin(); \n\t\t\t\t     it_uri != uris.end(); ++it_uri)\n\t\t\t\t{\n\t\t\t\t\tevq_trans_layer->push_broken_connection(*it_uri);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Remove the broken connection.\n\t\t\t\tconnection_table->remove_connection(*it);\n\t\t\t\tMEMMAN_DELETE(*it);\n\t\t\t\tdelete *it;\n\t\t\t\t\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t\n\t\tconnection_table->unlock();\n\t}\n\t\n\tlog_file->write_report(\"TCP sender terminated.\", \"::tcp_sender_loop\");\n\treturn NULL;\n}\n\nvoid *sender_loop(void *arg) {\n\tt_event \t*event;\n\tt_event_network\t*ev_network;\n\tIPaddr\tlocal_ipaddr;\n\n\tbool quit = false;\n\twhile (!quit) {\n\t\tevent = evq_sender->pop();\n\t\t\n\t\tswitch(event->get_type()) {\n\t\tcase EV_NETWORK:\n\t\t\tev_network = dynamic_cast<t_event_network *>(event);\n\t\t\tlocal_ipaddr = get_src_ip4_address_for_dst(ev_network->dst_addr);\n\t\t\t\n\t\t\tif (local_ipaddr == 0) {\n\t\t\t\tlog_file->write_header(\"::sender_loop\", LOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tlog_file->write_raw(\"Cannot get source IP address for destination: \");\n\t\t\t\tlog_file->write_raw(h_ip2str(ev_network->dst_addr));\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\t\tevq_trans_mgr->push_failure(FAIL_TRANSPORT,\n\t\t\t\t\tev_network->get_msg()->hdr_via.via_list.front().branch,\n\t\t\t\t\tev_network->get_msg()->hdr_cseq.method);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tif (!ev_network->get_msg()->local_ip_check()) {\n\t\t\t\tlog_file->write_report(\"Local IP check failed\",\n\t\t\t\t\t\"::sender_loop\", LOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tif (ev_network->transport == \"udp\") {\n\t\t\t\tsend_sip_udp(event);\n\t\t\t} else if (ev_network->transport == \"tcp\") {\n\t\t\t\tsend_sip_tcp(event);\n\t\t\t} else {\n\t\t\t\tlog_file->write_header(\"::sender_loop\", LOG_NORMAL, LOG_WARNING);\n\t\t\t\tlog_file->write_raw(\"Received unsupported transport: \");\n\t\t\t\tlog_file->write_raw(ev_network->transport);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase EV_STUN_REQUEST:\n\t\t\tsend_stun(event);\n\t\t\tbreak;\n\t\tcase EV_NAT_KEEPALIVE:\n\t\t\tsend_nat_keepalive(event);\n\t\t\tbreak;\n\t\tcase EV_TCP_PING:\n\t\t\tsend_tcp_ping(event);\n\t\t\tbreak;\n\t\tcase EV_QUIT:\n\t\t\tquit = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\n\t\tMEMMAN_DELETE(event);\n\t\tdelete event;\n\t}\n\t\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/sender.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Network sender threads.\n */\n\n#ifndef _H_SENDER\n#define _H_SENDER\n\n/** Thread sending SIP messages */\nvoid *sender_loop(void *arg);\n\n/** Thread for sending asynchronously over TCP */\nvoid *tcp_sender_loop(void *arg);\n\n#endif\n"
  },
  {
    "path": "src/sequence_number.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Sequence number operations.\n */\n\n#ifndef _SEQUENCE_NUMBER_H\n#define _SEQUENCE_NUMBER_H\n\n\n/**\n * Sequence numbers.\n * Sequence numbers with comparison operators that deal with sequence number\n * roll-overs using serial number arithmetic.\n * See http://en.wikipedia.org/wiki/Serial_Number_Arithmetic\n *\n * @param U The unsigned int type for the sequence number.\n * @param S The corresponsing signed int type having the same width.\n */\ntemplate< typename U, typename S >\nclass sequence_number_t {\nprivate:\n\t/**\n\t * The sequence number.\n\t */\n\tU _number;\n\npublic:\n\t/**\n\t * Constructor.\n\t * @param number The sequence number.\n\t */\n\texplicit sequence_number_t(U number) : _number(number)\n\t{};\n\n\t/**\n\t * Get the sequence number.\n\t * @return The sequence number.\n\t */\n\tU get_number(void) const {\n\t\treturn _number;\n\t}\n\n\t/**\n\t * Cast to the sequence number.\n\t */\n\toperator U(void) const {\n\t\treturn get_number();\n\t}\n\n\t/**\n\t * Calculate the distance to another sequence number.\n\t * @param number The sequence number to which the distance must be calculated.\n\t * @return The distance.\n\t */\n\tS distance(const sequence_number_t &number) const {\n\t\treturn static_cast<S>(_number - number.get_number());\n\t}\n\n\t/**\n\t * Calculate the distance to another distance sequence number.\n\t * @param number The sequence number to which the distance must be calculated.\n\t * @return The distance.\n\t */\n\tS operator-(const sequence_number_t &number) const {\n\t\treturn distance(number);\n\t}\n\n\t/**\n\t * Less-than comparison.\n\t * @param number The sequence number to compare with.\n\t * @return true, if this sequence number is less than number.\n\t * @return false, otherwise.\n\t */\n\tbool operator<(const sequence_number_t &number) const {\n\t\treturn (distance(number) < 0);\n\t}\n\n\t/**\n\t * Less-than-equal comparison.\n\t * @param number The sequence number to compare with.\n\t * @return true, if this sequence number is less than or equal to number.\n\t * @return false, otherwise.\n\t */\n\tbool operator<=(const sequence_number_t &number) const {\n\t\treturn (distance(number) <= 0);\n\t}\n\n\t/**\n\t * Equality comparison.\n\t * @param number The sequence number to compare with.\n\t * @return true, if this sequence number is equal to number.\n\t * @return false, otherwise.\n\t */\n\tbool operator==(const sequence_number_t &number) const {\n\t\treturn (number.get_number() == _number);\n\t}\n\n\t/**\n\t * Greater-than comparison.\n\t * @param number The sequence number to compare with.\n\t * @return true, if this sequence number is greater than number.\n\t * @return false, otherwise.\n\t */\n\tbool operator>(const sequence_number_t &number) const {\n\t\treturn (distance(number) > 0);\n\t}\n\n\t/**\n\t * Greater-than-equal comparison.\n\t * @param number The sequence number to compare with.\n\t * @return true, if this sequence number is greater than or equal to number.\n\t * @return false, otherwise.\n\t */\n\tbool operator>=(const sequence_number_t &number) const {\n\t\treturn (distance(number) >= 0);\n\t}\n};\n\n/**\n * 16-bit sequence number\n */\ntypedef sequence_number_t<uint16, int16> seq16_t;\n\n/**\n * 32-bit sequence number\n */\ntypedef sequence_number_t<uint32, int32> seq32_t;\n\n#endif\n"
  },
  {
    "path": "src/service.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <fstream>\n#include <iostream>\n#include \"service.h\"\n#include \"log.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n\n#define FLD_CF_ALWAYS\t\t\t\"cf_always\"\n#define FLD_CF_BUSY\t\t\t\"cf_busy\"\n#define FLD_CF_NOANSWER\t\t\t\"cf_noanswer\"\n#define FLD_DND\t\t\t\t\"dnd\"\n#define FLD_AUTO_ANSWER\t\t\t\"auto_answer\"\n\nvoid t_service::lock() {\n\tmtx_service.lock();\n}\n\nvoid t_service::unlock() {\n\tmtx_service.unlock();\n}\n\nt_service::t_service(t_user *user) {\n\tuser_config = user;\n\n\t// Call redirection\n\tcf_always_active = false;\n\tcf_busy_active = false;\n\tcf_noanswer_active = false;\n\n\t// Do not disturb\n\tdnd_active = false;\n\t\n\t// Auto answer\n\tauto_answer_active = false;\n\t\n\tstring msg;\n\t(void)read_config(msg);\n}\n\nbool t_service::multiple_services_active(void) {\n\tint num_services = 0;\n\t\n\tif (is_cf_active()) num_services++;\n\tif (is_dnd_active()) num_services++;\n\tif (is_auto_answer_active()) num_services++;\n\t\n\tif (num_services > 1) return true;\n\t\n\treturn false;\n}\n\nvoid t_service::enable_cf(t_cf_type cf_type, const list<t_display_url> &cf_dest) {\n\tlock();\n\n\tswitch (cf_type) {\n\tcase CF_ALWAYS:\n\t\tcf_always_active = true;\n\t\tcf_always_dest = cf_dest;\n\t\tbreak;\n\tcase CF_BUSY:\n\t\tcf_busy_active = true;\n\t\tcf_busy_dest = cf_dest;\n\t\tbreak;\n\tcase CF_NOANSWER:\n\t\tcf_noanswer_active = true;\n\t\tcf_noanswer_dest = cf_dest;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tunlock();\n\t\n\tstring msg;\n\t(void)write_config(msg);\n}\n\nvoid t_service::disable_cf(t_cf_type cf_type) {\n\tlock();\n\n\tswitch (cf_type) {\n\tcase CF_ALWAYS:\n\t\tcf_always_active = false;\n\t\tcf_always_dest.clear();\n\t\tbreak;\n\tcase CF_BUSY:\n\t\tcf_busy_active = false;\n\t\tcf_busy_dest.clear();\n\t\tbreak;\n\tcase CF_NOANSWER:\n\t\tcf_noanswer_active = false;\n\t\tcf_noanswer_dest.clear();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tunlock();\n\t\n\tstring msg;\n\t(void)write_config(msg);\n}\n\nbool t_service::get_cf_active(t_cf_type cf_type, list<t_display_url> &dest) {\n\tbool active = false;\n\n\tlock();\n\n\tswitch (cf_type) {\n\tcase CF_ALWAYS:\n\t\tactive = cf_always_active;\n\t\tdest = cf_always_dest;\n\t\tbreak;\n\tcase CF_BUSY:\n\t\tactive = cf_busy_active;\n\t\tdest = cf_busy_dest;\n\t\tbreak;\n\tcase CF_NOANSWER:\n\t\tactive = cf_noanswer_active;\n\t\tdest = cf_noanswer_dest;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tunlock();\n\treturn active;\n}\n\nbool t_service::is_cf_active(void) {\n\tbool active = false;\n\n\tlock();\n\tactive = cf_always_active || cf_busy_active || cf_noanswer_active;\n\tunlock();\n\n\treturn active;\n}\n\nlist<t_display_url> t_service::get_cf_dest(t_cf_type cf_type) {\n\tlist<t_display_url> dest;\n\n\tlock();\n\n\tswitch (cf_type) {\n\tcase CF_ALWAYS:\n\t\tdest = cf_always_dest;\n\t\tbreak;\n\tcase CF_BUSY:\n\t\tdest = cf_busy_dest;\n\t\tbreak;\n\tcase CF_NOANSWER:\n\t\tdest = cf_noanswer_dest;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tunlock();\n\treturn dest;\n}\n\nvoid t_service::enable_dnd(void) {\n\tlock();\n\tdnd_active = true;\n\tunlock();\n\t\n\tstring msg;\n\t(void)write_config(msg);\n}\n\nvoid t_service::disable_dnd(void) {\n\tlock();\n\tdnd_active = false;\n\tunlock();\n\t\n\tstring msg;\n\t(void)write_config(msg);\n}\n\nbool t_service::is_dnd_active(void) const {\n\treturn dnd_active;\n}\n\nvoid t_service::enable_auto_answer(bool on) {\n\tlock();\n\tauto_answer_active = on;\n\tunlock();\n\t\n\tstring msg;\n\t(void)write_config(msg);\n}\n\nbool t_service::is_auto_answer_active(void) const {\n\treturn auto_answer_active;\n}\n\nbool t_service::read_config(string &error_msg) {\n\tstruct stat stat_buf;\n\t\n\tlock();\n\t\n\tstring filename = user_config->get_profile_name() + SVC_FILE_EXT;\n\tstring f = user_config->expand_filename(filename);\n\t\n\t// Check if config file exists\n\tif (stat(f.c_str(), &stat_buf) != 0) {\n\t\tunlock();\n\t\treturn true;\n\t}\n\t\n\t// Open file\n\tifstream config(f.c_str());\n\tif (!config) {\n\t\terror_msg = \"Cannot open file for reading: \";\n\t\terror_msg += f;\n\t\tlog_file->write_report(error_msg, \"t_service::read_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tunlock();\n\t\treturn false;\n\t}\n\n\tt_display_url display_url;\n\tcf_always_active = false;\n\tcf_always_dest.clear();\n\tcf_busy_active = false;\n\tcf_busy_dest.clear();\n\tcf_noanswer_active = false;\n\tcf_noanswer_dest.clear();\n\t\n\twhile (!config.eof()) {\n\t\tstring line;\n\t\tgetline(config, line);\n\n\t\t// Check if read operation succeeded\n\t\tif (!config.good() && !config.eof()) {\n\t\t\terror_msg = \"File system error while reading file \";\n\t\t\terror_msg += f;\n\t\t\tlog_file->write_report(error_msg, \"t_service::read_config\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tunlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tline = trim(line);\n\n\t\t// Skip empty lines\n\t\tif (line.size() == 0) continue;\n\n\t\t// Skip comment lines\n\t\tif (line[0] == '#') continue;\n\n\t\tvector<string> l = split_on_first(line, '=');\n\t\tif (l.size() != 2) {\n\t\t\terror_msg = \"Syntax error in file \";\n\t\t\terror_msg += f;\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += line;\n\t\t\tlog_file->write_report(error_msg, \"t_service::read_config\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tunlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tstring parameter = trim(l[0]);\n\t\tstring value = trim(l[1]);\n\t\t\n\t\tif (parameter == FLD_CF_ALWAYS) {\n\t\t\tui->expand_destination(user_config, value, display_url);\n\t\t\tif (display_url.is_valid()) {\n\t\t\t\tcf_always_active = true;\n\t\t\t\tcf_always_dest.push_back(display_url);\n\t\t\t}\n\t\t} else if (parameter == FLD_CF_BUSY) {\n\t\t\tui->expand_destination(user_config, value, display_url);\n\t\t\tif (display_url.is_valid()) {\n\t\t\t\tcf_busy_active = true;\n\t\t\t\tcf_busy_dest.push_back(display_url);\n\t\t\t}\n\t\t} else if (parameter == FLD_CF_NOANSWER) {\n\t\t\tui->expand_destination(user_config, value, display_url);\n\t\t\tif (display_url.is_valid()) {\n\t\t\t\tcf_noanswer_active = true;\n\t\t\t\tcf_noanswer_dest.push_back(display_url);\n\t\t\t}\n\t\t} else if (parameter == FLD_DND) {\n\t\t\tdnd_active = yesno2bool(value);\n\t\t} else if (parameter == FLD_AUTO_ANSWER) {\n\t\t\tauto_answer_active = yesno2bool(value);\n\t\t} else {\n\t\t\t// Ignore unknown parameters. Only report in log file.\n\t\t\tlog_file->write_header(\"t_service::read_config\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unknown parameter in service config: \");\n\t\t\tlog_file->write_raw(parameter);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\n\t\n\tunlock();\n\treturn true;\n}\n\nbool t_service::write_config(string &error_msg) {\n\tstruct stat stat_buf;\n\t\n\tlock();\n\n\tstring filename = user_config->get_profile_name() + SVC_FILE_EXT;\n\tstring f = user_config->expand_filename(filename);\n\n\t// Make a backup of the file if we are editing an existing file, so\n\t// that can be restored when writing fails.\n\tstring f_backup = f + '~';\n\tif (stat(f.c_str(), &stat_buf) == 0) {\n\t\tif (rename(f.c_str(), f_backup.c_str()) != 0) {\n\t\t\tstring err = get_error_str(errno);\n\t\t\terror_msg = \"Failed to backup \";\n\t\t\terror_msg += f;\n\t\t\terror_msg += \" to \";\n\t\t\terror_msg += f_backup;\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += err;\n\t\t\tlog_file->write_report(error_msg, \"t_service::write_config\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tunlock();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tofstream config(f.c_str());\n\tif (!config) {\n\t\terror_msg = \"Cannot open file for writing: \";\n\t\terror_msg += f;\n\t\tlog_file->write_report(error_msg, \"t_user::write_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tunlock();\n\t\treturn false;\n\t}\n\n\tfor (list<t_display_url>::iterator i = cf_always_dest.begin(); \n\t     i != cf_always_dest.end(); i++)\n\t{\n\t\tconfig << FLD_CF_ALWAYS << '=' << i->encode() << endl;\n\t}\n\t\n\tfor (list<t_display_url>::iterator i = cf_busy_dest.begin(); \n\t     i != cf_busy_dest.end(); i++)\n\t{\n\t\tconfig << FLD_CF_BUSY << '=' << i->encode() << endl;\n\t}\n\t\n\tfor (list<t_display_url>::iterator i = cf_noanswer_dest.begin(); \n\t     i != cf_noanswer_dest.end(); i++)\n\t{\n\t\tconfig << FLD_CF_NOANSWER << '=' << i->encode() << endl;\n\t}\n\t\n\tconfig << FLD_DND << '=' << bool2yesno(dnd_active) << endl;\n\tconfig << FLD_AUTO_ANSWER << '=' << bool2yesno(auto_answer_active) << endl;\n\t\n\t// Check if writing succeeded\n\tif (!config.good()) {\n\t\t// Restore backup\n\t\tconfig.close();\n\t\trename(f_backup.c_str(), f.c_str());\n\n\t\terror_msg = \"File system error while writing file \";\n\t\terror_msg += f;\n\t\tlog_file->write_report(error_msg, \"t_service::write_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tunlock();\n\t\treturn false;\n\t}\n\t\n\tunlock();\n\treturn true;\n}\n"
  },
  {
    "path": "src/service.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_SERVICE\n#define _H_SERVICE\n\n#include <list>\n#include \"user.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n\n#define SVC_FILE_EXT\t\".svc\"\n\nusing namespace std;\n\n// Call forwarding types\nenum t_cf_type {\n\tCF_ALWAYS,\n\tCF_BUSY,\n\tCF_NOANSWER\n};\n\nclass t_service {\nprivate:\n\t// Protect operations on the service\n\tt_mutex\t\tmtx_service;\n\t\n\tt_user\t\t*user_config;\n\n\t// Call redirection (call forwarding)\n\tbool\t\t\tcf_always_active;\n\tlist<t_display_url>\tcf_always_dest;\n\tbool\t\t\tcf_busy_active;\n\tlist<t_display_url>\tcf_busy_dest;\n\tbool\t\t\tcf_noanswer_active;\n\tlist<t_display_url>\tcf_noanswer_dest;\n\n\t// Do not disturb\n\t// Note: CF_ALWAYS takes precedence over DND\n\tbool\t\tdnd_active;\n\t\n\t// Auto answer\n\tbool\t\tauto_answer_active;\n\n\tvoid lock();\n\tvoid unlock();\n\t\n\tt_service() {};\n\npublic:\n\tt_service(t_user *user);\n\n\t// All methods first lock the mtx_service mutex before executing\n\t// and unlock on return to guarantee the service data does not\n\t// get changed by other threads during execution.\n\t\n\t// General\n\t// Is more than 1 service active?\n\t// Different types of call forwarding counts as 1.\n\tbool multiple_services_active(void);\n\n\t// Call forwarding\n\tvoid enable_cf(t_cf_type cf_type, const list<t_display_url> &cf_dest);\n\tvoid disable_cf(t_cf_type cf_type);\n\tbool get_cf_active(t_cf_type cf_type, list<t_display_url> &dest);\n\tbool is_cf_active(void); // is any cf active?\n\tlist<t_display_url> get_cf_dest(t_cf_type cf_type);\n\n\t// Do not disturb\n\tvoid enable_dnd(void);\n\tvoid disable_dnd(void);\n\tbool is_dnd_active(void) const;\n\t\n\t// Auto answer\n\tvoid enable_auto_answer(bool on);\n\tbool is_auto_answer_active(void) const;\n\t\n\t// Read/write service settings to file\n\tbool read_config(string &error_msg);\n\tbool write_config(string &error_msg);\n};\n\n#endif\n"
  },
  {
    "path": "src/session.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include \"line.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"phone_user.h\"\n#include \"session.h\"\n#include \"sockets/ipaddr.h\"\n#include \"util.h\"\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n\nextern string user_host;\nextern string local_hostname;\nextern t_phone *phone;\n\n///////////\n// PRIVATE\n///////////\n\nvoid t_session::set_recvd_codecs(t_sdp *sdp) {\n\trecvd_codecs.clear();\n\tsend_ac2payload.clear();\n\tsend_payload2ac.clear();\n\tlist<unsigned short> payloads = sdp->get_codecs(SDP_AUDIO);\n\tfor (list<unsigned short>::iterator i = payloads.begin(); \n\t     i != payloads.end(); i++)\n\t{\n\t\tt_audio_codec ac = sdp->get_codec(SDP_AUDIO, *i);\n\t\tif (ac > CODEC_UNSUPPORTED) {\n\t\t\trecvd_codecs.push_back(ac);\n\t\t\t// Don't overwrite any previous mapping for this codec\n\t\t\tif (!send_ac2payload.count(ac)) {\n\t\t\t\tsend_ac2payload[ac] = *i;\n\t\t\t}\n\t\t\tsend_payload2ac[*i] = ac;\n\t\t}\n\t}\n}\n\nbool t_session::is_3way(void) const {\n\tt_line *l = get_line();\n\tt_phone *p = l->get_phone();\n\n\treturn p->part_of_3way(l->get_line_number());\n}\n\nt_session *t_session::get_peer_3way(void) const {\n\tt_line *l = get_line();\n\tt_phone *p = l->get_phone();\n\n\tt_line *peer_line = p->get_3way_peer_line(l->get_line_number());\n\n\treturn peer_line->get_session();\n}\n\n///////////\n// PUBLIC\n///////////\n\nt_session::t_session(t_dialog *_dialog, string _receive_host,\n\t\t  unsigned short _receive_port)\n{\n\tdialog = _dialog;\n\t\n\tuser_config = dialog->get_line()->get_user();\n\tassert(user_config);\n\n\treceive_host = _receive_host;\n\tretrieve_host = _receive_host;\n\treceive_port = _receive_port;\n\tsrc_sdp_version = int2str(rand());\n\tsrc_sdp_id = int2str(rand());\n\tuse_codec = CODEC_NULL;\n\t\n\tswitch (user_config->get_dtmf_transport()) {\n\tcase DTMF_RFC2833:\n\tcase DTMF_AUTO:\n\t\trecv_dtmf_pt = user_config->get_dtmf_payload_type();\n\t\tbreak;\n\tdefault:\n\t\trecv_dtmf_pt = 0;\n\t}\n\t\n\tsend_dtmf_pt = 0;\n\n\toffer_codecs = user_config->get_codecs();\n\tptime = user_config->get_ptime();\n\tilbc_mode = user_config->get_ilbc_mode();\n\n\trecvd_offer = false;\n\trecvd_answer = false;\n\tsent_offer = false;\n\tdirection = SDP_SENDRECV;\n\n\taudio_rtp_session = NULL;\n\tis_on_hold = false;\n\tis_killed = false;\n\t\n\t// Initialize audio codec to payload mappings\n\trecv_ac2payload[CODEC_G711_ULAW] = SDP_FORMAT_G711_ULAW;\n\trecv_ac2payload[CODEC_G711_ALAW] = SDP_FORMAT_G711_ALAW;\n\trecv_ac2payload[CODEC_GSM] = SDP_FORMAT_GSM;\n\trecv_ac2payload[CODEC_G722] = SDP_FORMAT_G722;\n\trecv_ac2payload[CODEC_G729A] = SDP_FORMAT_G729;\n\trecv_ac2payload[CODEC_SPEEX_NB] = user_config->get_speex_nb_payload_type();\n\trecv_ac2payload[CODEC_SPEEX_WB] = user_config->get_speex_wb_payload_type();\n\trecv_ac2payload[CODEC_SPEEX_UWB] = user_config->get_speex_uwb_payload_type();\n\trecv_ac2payload[CODEC_ILBC] = user_config->get_ilbc_payload_type();\n\trecv_ac2payload[CODEC_G726_16] = user_config->get_g726_16_payload_type();\n\trecv_ac2payload[CODEC_G726_24] = user_config->get_g726_24_payload_type();\n\trecv_ac2payload[CODEC_G726_32] = user_config->get_g726_32_payload_type();\n\trecv_ac2payload[CODEC_G726_40] = user_config->get_g726_40_payload_type();\n\trecv_ac2payload[CODEC_TELEPHONE_EVENT] = user_config->get_dtmf_payload_type();\n\tsend_ac2payload.clear();\n\t\n\t// Initialize pauload to audio codec mappings\n\trecv_payload2ac[SDP_FORMAT_G711_ULAW] = CODEC_G711_ULAW;\n\trecv_payload2ac[SDP_FORMAT_G711_ALAW] = CODEC_G711_ALAW;\n\trecv_payload2ac[SDP_FORMAT_GSM] = CODEC_GSM;\n\trecv_payload2ac[SDP_FORMAT_G722] = CODEC_G722;\n\trecv_payload2ac[SDP_FORMAT_G729] = CODEC_G729A;\n\trecv_payload2ac[user_config->get_speex_nb_payload_type()] = CODEC_SPEEX_NB;\n\trecv_payload2ac[user_config->get_speex_wb_payload_type()] = CODEC_SPEEX_WB;\n\trecv_payload2ac[user_config->get_speex_uwb_payload_type()] = CODEC_SPEEX_UWB;\n\trecv_payload2ac[user_config->get_ilbc_payload_type()] = CODEC_ILBC;\n\trecv_payload2ac[user_config->get_g726_16_payload_type()] = CODEC_G726_16;\n\trecv_payload2ac[user_config->get_g726_24_payload_type()] = CODEC_G726_24;\n\trecv_payload2ac[user_config->get_g726_32_payload_type()] = CODEC_G726_32;\n\trecv_payload2ac[user_config->get_g726_40_payload_type()] = CODEC_G726_40;\n\trecv_payload2ac[user_config->get_dtmf_payload_type()] = CODEC_TELEPHONE_EVENT;\n\tsend_payload2ac.clear();\n}\n\nt_session::~t_session() {\n\tstop_rtp();\n}\n\nt_session *t_session::create_new_version(void) const {\n\tt_session *s = new t_session(*this);\n\tMEMMAN_NEW(s);\n\ts->src_sdp_version = int2str(atoi(src_sdp_version.c_str()) + 1);\n\ts->recvd_codecs.clear();\n\ts->recvd_offer = false;\n\ts->recvd_answer = false;\n\ts->sent_offer = false;\n\n\t// Do not copy the RTP session\n\ts->set_audio_session(NULL);\n\t\n\t// Clear the codec to payload mappings as a new response must\n\t// be received from the far end\n\ts->send_ac2payload.clear();\n\ts->send_payload2ac.clear();\n\n\treturn s;\n}\n\nt_session *t_session::create_call_hold(void) const {\n\tt_session *s = create_new_version();\n\n\tif (user_config->get_hold_variant() == HOLD_RFC2543) {\n\t\ts->receive_host = \"0.0.0.0\";\n\t} else if (user_config->get_hold_variant() == HOLD_RFC3264) {\n\t\t// RFC 3264 8.4\n\t\tif (direction == SDP_SENDRECV) {\n\t\t\ts->direction = SDP_SENDONLY;\n\t\t}\n\t\telse if (direction == SDP_RECVONLY) {\n\t\t\ts->direction = SDP_INACTIVE;\n\t\t}\n\t} else {\n\t\tassert(false);\n\t}\n\t\n\t// Prevent RTP from being started for this session as long\n\t// as the call is put on hold. Without this, the RTP sessions\n\t// will get started when a re-INVITE is received from the far-end\n\t// while the call is still locally on-hold.\n\ts->hold();\n\n\treturn s;\n}\n\nt_session *t_session::create_call_retrieve(void) const {\n\tt_session *s = create_new_version();\n\n\tif (user_config->get_hold_variant() == HOLD_RFC2543) {\n\t\ts->receive_host = retrieve_host;\n\t} else if (user_config->get_hold_variant() == HOLD_RFC3264) {\n\t\t// RFC 3264 8.4\n\t\tif (direction == SDP_SENDONLY) {\n\t\t\ts->direction = SDP_SENDRECV;\n\t\t}\n\t\telse if (direction == SDP_INACTIVE) {\n\t\t\ts->direction = SDP_RECVONLY;\n\t\t}\n\t} else {\n\t\tassert(false);\n\t}\n\n\treturn s;\n}\n\nt_session *t_session::create_session_refresh(void) const {\n\tt_session *s = new t_session(*this);\n\tMEMMAN_NEW(s);\n\n\t// Do not copy the RTP session\n\ts->set_audio_session(NULL);\n\n\treturn s;\n}\n\nt_session *t_session::create_clean_copy(void) const {\n\tt_session *s = new t_session(*this);\n\tMEMMAN_NEW(s);\n\ts->src_sdp_version = int2str(atoi(src_sdp_version.c_str()) + 1);\n\ts->dst_sdp_version = \"\";\n\ts->dst_sdp_id = \"\";\n\ts->dst_rtp_host = \"\";\n\ts->dst_rtp_port = 0;\n\ts->recvd_codecs.clear();\n\ts->recvd_offer = false;\n\ts->recvd_answer = false;\n\ts->sent_offer = false;\n\ts->direction = SDP_SENDRECV;\n\n\t// Do not copy the RTP session\n\ts->set_audio_session(NULL);\n\t\n\t// Clear the codec to payload mappings as a new response must\n\t// be received from the far end\n\ts->send_ac2payload.clear();\n\ts->send_payload2ac.clear();\n\n\treturn s;\n}\n\nbool t_session::process_sdp_offer(t_sdp *sdp, int &warn_code,\n\t\tstring &warn_text)\n{\n\tif (!sdp->is_supported(warn_code, warn_text)) return false;\n\n\tdst_sdp_version = sdp->origin.session_version;\n\tdst_sdp_id = sdp->origin.session_id;\n\trecvd_sdp_offer = *sdp;\n\t\n\t// RFC 3264 5\n\t// SDP may contain 0 m= lines\n\tif (sdp->media.empty()) return true;\n\t\n\tdst_rtp_host = sdp->get_rtp_host(SDP_AUDIO);\n\tdst_rtp_port = sdp->get_rtp_port(SDP_AUDIO);\n\tset_recvd_codecs(sdp);\n\tdst_zrtp_support = sdp->get_zrtp_support(SDP_AUDIO);\n\n\t// The direction in the SDP is from the point of view of the\n\t// far end. Swap the direction to store it as the point of view\n\t// from the near end.\n\tswitch(sdp->get_direction(SDP_AUDIO)) {\n\tcase SDP_INACTIVE:\n\t\tdirection = SDP_INACTIVE;\n\t\tbreak;\n\tcase SDP_SENDONLY:\n\t\tif (is_on_hold && user_config->get_hold_variant() == HOLD_RFC3264) {\n\t\t\t// The phone is put on-hold. We don't want to\n\t\t\t// receive media.\n\t\t\tdirection = SDP_INACTIVE;\n\t\t} else {\n\t\t\tdirection = SDP_RECVONLY;\n\t\t}\n\t\tbreak;\n\tcase SDP_RECVONLY:\n\t\tdirection = SDP_SENDONLY;\n\t\tbreak;\n\tcase SDP_SENDRECV:\n\t\tif (is_on_hold && user_config->get_hold_variant() == HOLD_RFC3264) {\n\t\t\t// The phone is put on-hold. We don't want to\n\t\t\t// receive media.\n\t\t\tdirection = SDP_SENDONLY;\n\t\t} else {\n\t\t\tdirection = SDP_SENDRECV;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\t// Check if the list of received codecs has at least 1 codec\n\t// in common with the list of codecs we can offer. If there\n\t// is no common codec, then no call can be established.\n\tlist<t_audio_codec>::iterator supported_codec_it = offer_codecs.end();\n\tfor (list<t_audio_codec>::const_iterator i = recvd_codecs.begin();\n\t     i != recvd_codecs.end(); i++)\n\t{\n\t\tlist<t_audio_codec>::iterator tmp_it;\n\t\tif ((supported_codec_it == offer_codecs.end() ||\n\t\t     !user_config->get_in_obey_far_end_codec_pref()) &&\n\t\t    (tmp_it = std::find(offer_codecs.begin(), supported_codec_it, *i)) !=\n\t\t\t\t\tsupported_codec_it)\n\t\t{\n\t\t\t// Codec supported\n\t\t\tsupported_codec_it = tmp_it;\n\t\t\tuse_codec = *i; // this codec goes into answer\n\t\t\t\n\t\t\t// Use the payload to codec bindings as signalled in the\n\t\t\t// offer by the far end.\n\t\t\trecv_payload2ac[send_ac2payload[use_codec]] = use_codec;\n\t\t\trecv_ac2payload[use_codec] = send_ac2payload[use_codec];\n\t\t} else if (*i == CODEC_TELEPHONE_EVENT) {\n\t\t\t// telephone-event payload is supported\n\t\t\tsend_dtmf_pt = send_ac2payload[*i];\n\t\t\t\n\t\t\t// When we support RFC 2833 events, then take the payload\n\t\t\t// type from the far end.\n\t\t\tif (recv_dtmf_pt > 0) {\n\t\t\t\trecv_dtmf_pt = send_dtmf_pt; // this goes into answer as well\n\t\t\t}\n\t\t}\n\t}\n\n\tif (supported_codec_it == offer_codecs.end()) {\n\t\twarn_code = W_305_INCOMPATIBLE_MEDIA_FORMAT;\n\t\twarn_text = \"None of the audio codecs is supported\";\n\t\treturn false;\n\t}\n\n\t// Overwrite ptime value with ptime from SDP\n\tunsigned short p = sdp->get_ptime(SDP_AUDIO);\n\tif (p > 0) ptime = p;\n\t\n\t// RFC 3952 5\n\t// Select the iLBC mode that needs the lowest bandwidth\n\tif (use_codec == CODEC_ILBC) {\n\t\tint recvd_mode = sdp->get_fmtp_int_param(SDP_AUDIO, \n\t\t\t\tsend_ac2payload[use_codec], \"mode\");\n\t\tif (recvd_mode == -1) recvd_mode = 30;\n\t\tif (VALID_ILBC_MODE(recvd_mode) && recvd_mode > ilbc_mode) {\n\t\t\tilbc_mode = static_cast<unsigned short>(recvd_mode);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool t_session::process_sdp_answer(t_sdp *sdp, int &warn_code,\n\t\tstring &warn_text)\n{\n\tif (!sdp->is_supported(warn_code, warn_text)) return false;\n\t\n\t// As our offer always contains an audio m= line, the answer\n\t// should contain one as well. If there are media lines, then\n\t// the sdp->is_supported already verified there is audio.\n\tif (sdp->media.empty()) {\n\t\twarn_code = W_304_MEDIA_TYPE_NOT_AVAILABLE;\n\t\twarn_text = \"Valid media stream for audio is missing\";\n\t\treturn false;\n\t}\n\n\tdst_sdp_version = sdp->origin.session_version;\n\tdst_sdp_id = sdp->origin.session_id;\n\tdst_rtp_host = sdp->get_rtp_host(SDP_AUDIO);\n\tdst_rtp_port = sdp->get_rtp_port(SDP_AUDIO);\n\tdst_zrtp_support = sdp->get_zrtp_support(SDP_AUDIO);\n\tset_recvd_codecs(sdp);\n\n\t// Find the first codec in the received codecs list that\n\t// is supported.\n\t// Per the offer/answer model all received codecs should be\n\t// supported! It seems that some applications put more codecs\n\t// in the answer though.\n\tlist<t_audio_codec>::iterator codec_found_it = offer_codecs.end();\n\n\tfor (list<t_audio_codec>::const_iterator i = recvd_codecs.begin();\n\t     i != recvd_codecs.end(); i++)\n\t{\n\t\tlist<t_audio_codec>::iterator tmp_it;\n\t\tif ((codec_found_it == offer_codecs.end() ||\n\t\t     !user_config->get_out_obey_far_end_codec_pref()) &&\n\t\t    (tmp_it = std::find(offer_codecs.begin(), codec_found_it, *i)) !=\n\t\t\t\tcodec_found_it)\n\t\t{\n\t\t\tcodec_found_it = tmp_it;\n\t\t\tuse_codec = *i;\n\t\t} else if (*i == CODEC_TELEPHONE_EVENT) {\n\t\t\t// telephone-event payload is supported\n\t\t\tsend_dtmf_pt = send_ac2payload[*i];\n\t\t}\n\t}\n\n\tif (codec_found_it == offer_codecs.end()) {\n\t\t// None of the answered codecs is supported\n\t\twarn_code = W_305_INCOMPATIBLE_MEDIA_FORMAT;\n\t\twarn_text = \"None of the codecs is supported\";\n\t\treturn false;\n\t}\n\n\t// Overwrite ptime value with ptime from SDP\n\tunsigned short p = sdp->get_ptime(SDP_AUDIO);\n\tif (p > 0) ptime = p;\n\t\n\t// RFC 3952 5\n\t// Select the iLBC mode that needs the lowest bandwidth\n\tif (use_codec == CODEC_ILBC) {\n\t\tint recvd_mode = sdp->get_fmtp_int_param(SDP_AUDIO, \n\t\t\t\tsend_ac2payload[use_codec], \"mode\");\n\t\tif (recvd_mode == -1) recvd_mode = 30;\n\t\tif (VALID_ILBC_MODE(recvd_mode) && recvd_mode > ilbc_mode) {\n\t\t\tilbc_mode = static_cast<unsigned short>(recvd_mode);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid t_session::create_sdp_offer(t_sip_message *m, const string &user) {\n\t// Delete old body if present\n\tif (m->body) {\n\t\tMEMMAN_DELETE(m->body);\n\t\tdelete m->body;\n\t}\n\t\n\t// Determine the IP address to receive the media streams\n\tif (receive_host == AUTO_IP4_ADDRESS) {\n\t\tIPaddr local_ip = m->get_local_ip();\n\t\tif (local_ip == 0) {\n\t\t\tlog_file->write_report(\"Cannot determine local IP address.\",\n\t\t\t\t\"t_session::create_sdp_offer\", LOG_NORMAL, LOG_CRITICAL);\n\t\t} else {\n\t\t\treceive_host = USER_HOST(user_config, h_ip2str(local_ip));\n\t\t\tretrieve_host = receive_host;\n\t\t}\n\t}\n\n\tm->body = new t_sdp(user, src_sdp_id, src_sdp_version, receive_host, \n\t\t\treceive_host, receive_port, offer_codecs, recv_dtmf_pt,\n\t\t\trecv_ac2payload);\n\tMEMMAN_NEW(m->body);\n\n\n\t// Set ptime for G.711/G.722/G.726 codecs\n\tlist<t_audio_codec>::iterator it_g7xx;\n\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G711_ALAW);\n\tif (it_g7xx == offer_codecs.end()) {\n\t\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G711_ULAW);\n\t}\n\tif (it_g7xx == offer_codecs.end()) {\n\t\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G722);\n\t}\n\tif (it_g7xx == offer_codecs.end()) {\n\t\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_16);\n\t}\n\tif (it_g7xx == offer_codecs.end()) {\n\t\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_24);\n\t}\n\tif (it_g7xx == offer_codecs.end()) {\n\t\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_32);\n\t}\n\tif (it_g7xx == offer_codecs.end()) {\n\t\tit_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_40);\n\t}\n\tif (it_g7xx != offer_codecs.end()) {\n\t\t((t_sdp *)m->body)->set_ptime(SDP_AUDIO, ptime);\n\t}\n\t\n\t// Set mode for iLBC codecs\n\tlist<t_audio_codec>::iterator it_ilbc;\n\tit_ilbc = find(offer_codecs.begin(), offer_codecs.end(), CODEC_ILBC);\n\tif (it_ilbc != offer_codecs.end() && ilbc_mode != 30) {\n\t\t((t_sdp *)m->body)->set_fmtp_int_param(SDP_AUDIO, recv_ac2payload[CODEC_ILBC],\n\t\t\t\t\"mode\", ilbc_mode);\n\t}\n\n\t// Set direction\n\tif (direction != SDP_SENDRECV) {\n\t\t((t_sdp *)m->body)->set_direction(SDP_AUDIO, direction);\n\t}\n\t\n\t// Set zrtp support\n\tif (user_config->get_zrtp_enabled() && user_config->get_zrtp_sdp()) {\n\t\t((t_sdp *)m->body)->set_zrtp_support(SDP_AUDIO);\n\t}\n\n\tm->hdr_content_type.set_media(t_media(\"application\", \"sdp\"));\n\n\tsent_offer = true;\n}\n\nvoid t_session::create_sdp_answer(t_sip_message *m, const string &user) {\n\t// Delete old body if present\n\tif (m->body) {\n\t\tMEMMAN_DELETE(m->body);\n\t\tdelete m->body;\n\t}\n\t\n\t// Determine the IP address to receive the media streams\n\tif (receive_host == AUTO_IP4_ADDRESS) {\n\t\tIPaddr local_ip = 0;\n\t\tIPaddr dst_ip = gethostbyname(dst_rtp_host);\n\t\t\n\t\tif (dst_ip != 0)\n\t\t{\n\t\t\t// Determine source IP address for RTP from the\n\t\t\t// destination RTP IP address.\n\t\t\tlog_file->write_report(\"Cannot determine local IP address from RTP destination.\",\n\t\t\t\t\"t_session::create_sdp_answer\", LOG_NORMAL, LOG_WARNING);\n\t\t\t\t\n\t\t\tlocal_ip = get_src_ip4_address_for_dst(dst_ip);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstring log_msg = \"Cannot determine IP address for: \";\n\t\t\tlog_msg += dst_rtp_host;\n\t\t\tlog_file->write_report(log_msg,\n\t\t\t\t\"t_session::create_sdp_answer\", LOG_NORMAL, LOG_WARNING);\n\t\t}\n\t\t\n\t\tif (local_ip == 0)\n\t\t{\n\t\t\t// Somehow the source IP address could not be determined\n\t\t\t// from the destination RTP address. Try to determine it\n\t\t\t// from the destination of the SIP message.\n\t\t\tlocal_ip = m->get_local_ip();\n\t\t}\n\t\t\t\n\t\tif (local_ip == 0) {\n\t\t\tlog_file->write_report(\"Cannot determine local IP address.\",\n\t\t\t\t\"t_session::create_sdp_answer\", LOG_NORMAL, LOG_CRITICAL);\n\t\t} else {\n\t\t\treceive_host = USER_HOST(user_config, h_ip2str(local_ip));\n\t\t\tretrieve_host = receive_host;\n\t\t}\n\t}\n\n\tlist<t_audio_codec> answer_codecs;\n\tanswer_codecs.push_back(use_codec);\n\n\t// RFC 3264 6\n\t// The answer must contain an m-line for each m-line in the offer in\n\t// the same order. Media can be rejected by setting the port to 0.\n\t// Only the first audio stream is accepted, all other media streams\n\t// will be rejected.\n\tm->body = new t_sdp(user, src_sdp_id, src_sdp_version, receive_host,\n\t\t\t\treceive_host);\n\tMEMMAN_NEW(m->body);\n\tbool audio_answered = false;\n\tfor (list<t_sdp_media>::const_iterator i = recvd_sdp_offer.media.begin();\n\t     i != recvd_sdp_offer.media.end(); i++)\n\t{\n\t\tif (!audio_answered && i->get_media_type() == SDP_AUDIO &&\n\t\t    i->port != 0)\n\t\t{\n\t\t\t// Accept the first audio stream\n\t\t\t((t_sdp *)m->body)->add_media(t_sdp_media(\n\t\t\t\tSDP_AUDIO, receive_port, answer_codecs, recv_dtmf_pt,\n\t\t\t\tsend_ac2payload));\n\t\t\taudio_answered = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Reject media stream by setting port to zero\n\t\t\tt_sdp_media reject_media(*i);\n\t\t\treject_media.port = 0;\n\t\t\t((t_sdp *)m->body)->add_media(reject_media);\n\t\t}\n\t}\n\t\n\tm->hdr_content_type.set_media(t_media(\"application\", \"sdp\"));\n\t\n\t// If there were no media lines in the offer, we sent no media\n\t// lines in the answer\n\tif (recvd_sdp_offer.media.empty()) return;\n\n\t// Set audio attributes\n\t\n\t// Set ptime for G711 codecs\n\tif (use_codec == CODEC_G711_ALAW ||\n\t    use_codec == CODEC_G711_ULAW)\n\t{\n\t\t((t_sdp *)m->body)->set_ptime(SDP_AUDIO, ptime);\n\t}\n\n\t// Set mode for iLBC codecs\n\tif (use_codec == CODEC_ILBC && ilbc_mode != 30) {\n\t\tunsigned short ilbc_payload = const_cast<t_session *>(this)->\n\t\t\t\trecv_ac2payload[CODEC_ILBC];\n\t\t((t_sdp *)m->body)->set_fmtp_int_param(SDP_AUDIO, ilbc_payload,\n\t\t\t\t\"mode\", ilbc_mode);\n\t}\n\t\n\t// Set direction\n\tif (direction != SDP_SENDRECV) {\n\t\t((t_sdp *)m->body)->set_direction(SDP_AUDIO, direction);\n\t}\n\t\n\t// Set zrtp support\n\tif (user_config->get_zrtp_enabled() && user_config->get_zrtp_sdp()) {\n\t\t((t_sdp *)m->body)->set_zrtp_support(SDP_AUDIO);\n\t}\n}\n\nvoid t_session::start_rtp(void) {\n\t// If a session is killed, it may not be started again.\n\tif (is_killed) {\n\t\tlog_file->write_report(\"Cannot start. The session is killed already.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t}\n\t\n\t// If a session is on-hold then do not start RTP.\n\tif (is_on_hold) {\n\t\tlog_file->write_report(\"Cannot start. The session is on hold.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t}\n\n\tif (receive_host.empty()) {\n\t\tlog_file->write_report(\"Cannot start. receive_host is empty.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t}\n\t\n\tif (dst_rtp_host.empty()) {\n\t\tlog_file->write_report(\"Cannot start. dst_rtp_host is empty.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t}\n\n\t// Local and remote hold\n\tif (((receive_host == \"0.0.0.0\" || receive_port == 0) &&\n\t     (dst_rtp_host == \"0.0.0.0\" || dst_rtp_port == 0)) ||\n \t    direction == SDP_INACTIVE)\n\t{\n\t\tlog_file->write_report(\"Cannot start. Local and remote on hold.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t}\n\n\t// Inform user about the codecs\n\tget_line()->ci_set_send_codec(use_codec);\n\tget_line()->ci_set_recv_codec(use_codec);\n\tui->cb_send_codec_changed(get_line()->get_line_number(), use_codec);\n\tui->cb_recv_codec_changed(get_line()->get_line_number(), use_codec);\n\t\n\t// Determine ptime\n\tunsigned short audio_ptime;\n\tif (use_codec == CODEC_ILBC) {\n\t\taudio_ptime = ilbc_mode;\n\t} else {\n\t\taudio_ptime = ptime;\n\t}\n\t\n\t// Determine if audio must be encrypted\n\tbool encrypt_audio = get_line()->get_try_to_encrypt();\n\tif (user_config->get_zrtp_send_if_supported()) {\n\t\tencrypt_audio = encrypt_audio && dst_zrtp_support;\n\t}\n\n\t// Start the RTP streams\n\tif (dst_rtp_host == \"0.0.0.0\" || dst_rtp_port == 0 ||\n\t    direction == SDP_RECVONLY)\n\t{\n\t\t// Local hold -> do not send RTP\n\t\tlog_file->write_report(\"Local hold. Do not send RTP.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\taudio_rtp_session = new t_audio_session(this,\n\t\t\t\t\"0.0.0.0\", get_line()->get_rtp_port(), \"\", 0, use_codec, \n\t\t\t\taudio_ptime, recv_payload2ac, send_ac2payload,\n\t\t\t\tencrypt_audio);\n\t\tMEMMAN_NEW(audio_rtp_session);\n\t}\n\telse if (receive_host == \"0.0.0.0\" || receive_port == 0 ||\n\t         direction == SDP_SENDONLY)\n\t{\n\t\t// Remote hold\n\t\t// For music on-hold music should be played here.\n\t\t// Without music on-hold do not send out RTP\n\t\t/*\n\t\taudio_rtp_session = new t_audio_session(this,\n\t\t\t\t\"\", 0, dst_rtp_host, dst_rtp_port, codec, ptime);\n\t\t*/\n\t\tlog_file->write_report(\"Do not start. Remote hold.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_DEBUG);\n\t\treturn;\n\t} else {\n\t\t// Bi-directional audio\n\t\taudio_rtp_session = new t_audio_session(this,\n\t\t\t\t\"0.0.0.0\", get_line()->get_rtp_port(),\n\t\t\t\tdst_rtp_host, dst_rtp_port, use_codec, audio_ptime,\n\t\t\t\trecv_payload2ac, send_ac2payload,\n\t\t\t\tencrypt_audio);\n\t\tMEMMAN_NEW(audio_rtp_session);\n\t}\n\n\t// Check if the created audio session is valid.\n\tif (!audio_rtp_session->is_valid()) {\n\t\tlog_file->write_report(\"Audio session is invalid.\",\n\t\t\t\"t_session::start_rtp\", LOG_NORMAL, LOG_CRITICAL);\n\t\tMEMMAN_DELETE(audio_rtp_session);\n\t\tdelete audio_rtp_session;\n\t\taudio_rtp_session = NULL;\n\t\treturn;\n\t}\n\n\t// Set dynamic payload type for DTMF events\n\tif (recv_dtmf_pt > 0) {\n\t\tunsigned short alt_dtmf_pt;\n\t\tif (recv_payload2ac.find(send_dtmf_pt) == recv_payload2ac.end()) {\n\t\t\t// Allow the payload type as signalled by the far end\n\t\t\t// as an alternative to the payload as signalled by Twinkle.\n\t\t\talt_dtmf_pt = send_dtmf_pt;\n\t\t} else {\n\t\t\t// The payload type as signalled by the far end for DTMF\n\t\t\t// is already in use by Twinkle for another codec, so it\n\t\t\t// cannot be used as an alternative.\n\t\t\talt_dtmf_pt = recv_dtmf_pt;\n\t\t}\n\t\taudio_rtp_session->set_pt_in_dtmf(recv_dtmf_pt, alt_dtmf_pt);\n\t}\n\n\tif (send_dtmf_pt > 0) {\n\t\taudio_rtp_session->set_pt_out_dtmf(send_dtmf_pt);\n\t\t\n\t\tswitch (user_config->get_dtmf_transport()) {\n\t\tcase DTMF_AUTO:\n\t\tcase DTMF_RFC2833:\n\t\t\tget_line()->ci_set_dtmf_supported(true, false);\n\t\t\tbreak;\n\t\tcase DTMF_INBAND:\n\t\t\tget_line()->ci_set_dtmf_supported(true, true);\n\t\t\tbreak;\n\t\tcase DTMF_INFO:\n\t\t\tget_line()->ci_set_dtmf_supported(true, false, true);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t\t\n\t\tui->cb_dtmf_supported(get_line()->get_line_number());\n\t} else {\n\t\tswitch (user_config->get_dtmf_transport()) {\n\t\tcase DTMF_AUTO:\n\t\tcase DTMF_INBAND:\n\t\t\tget_line()->ci_set_dtmf_supported(true, true);\n\t\t\tui->cb_dtmf_supported(get_line()->get_line_number());\n\t\t\tbreak;\n\t\tcase DTMF_RFC2833:\n\t\t\tget_line()->ci_set_dtmf_supported(false);\n\t\t\tui->cb_dtmf_not_supported(get_line()->get_line_number());\n\t\t\tbreak;\n\t\tcase DTMF_INFO:\n\t\t\tget_line()->ci_set_dtmf_supported(true, false, true);\n\t\t\tui->cb_dtmf_supported(get_line()->get_line_number());\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t}\n\n\taudio_rtp_session->run();\n}\n\nvoid t_session::stop_rtp(void) {\n\tif (audio_rtp_session) {\n\t\tMEMMAN_DELETE(audio_rtp_session);\n\t\tdelete audio_rtp_session;\n\t\taudio_rtp_session = NULL;\n\t\n\t\tget_line()->ci_set_dtmf_supported(false);\n\t\tui->cb_line_state_changed();\n\t}\n}\n\nvoid t_session::kill_rtp(void) {\n\tstop_rtp();\n\tis_killed = true;\n}\n\nt_audio_session *t_session::get_audio_session(void) const {\n\treturn audio_rtp_session;\n}\n\nvoid t_session::set_audio_session(t_audio_session *as) {\n\taudio_rtp_session = as;\n}\n\nbool t_session::equal_audio(const t_session &s) const {\n\t// According to RFC 3264 6, the SDP version in the o= line\n\t// must be updated when the SDP is changed.\n\t// We check for more changes to interoperate with SIP\n\t// devices that do not adhere fully to RFC 3264\n\treturn (receive_host == s.receive_host &&\n\t\treceive_port == s.receive_port &&\n\t\tdst_rtp_host == s.dst_rtp_host &&\n\t\tdst_rtp_port == s.dst_rtp_port &&\n\t\tdirection == s.direction &&\n\t\tsrc_sdp_version == s.src_sdp_version &&\n\t\tdst_sdp_version == s.dst_sdp_version &&\n\t\tsrc_sdp_id == s.src_sdp_id &&\n\t\tdst_sdp_id == s.dst_sdp_id);\n}\n\nvoid t_session::send_dtmf(char digit, bool inband) {\n\tif (audio_rtp_session) audio_rtp_session->send_dtmf(digit, inband);\n}\n\nt_line *t_session::get_line(void) const {\n\treturn dialog->get_line();\n}\n\nvoid t_session::set_owner(t_dialog *d) {\n\tdialog = d;\n}\n\nvoid t_session::hold(void) {\n\tis_on_hold = true;\n}\n\nvoid t_session::unhold(void) {\n\tis_on_hold = false;\n}\n\nbool t_session::is_rtp_active(void) const {\n\treturn (audio_rtp_session != NULL);\n}\n"
  },
  {
    "path": "src/session.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// Session description of an established session.\n// A session is the media part of a dialog.\n\n#ifndef _SESSION_H\n#define _SESSION_H\n\n#include <list>\n#include <map>\n#include <string>\n#include \"dialog.h\"\n#include \"user.h\"\n#include \"sdp/sdp.h\"\n#include \"parser/sip_message.h\"\n#include \"audio/audio_codecs.h\"\n#include \"audio/audio_session.h\"\n\n// Forward declarations\nclass t_dialog;\nclass t_line;\n\nusing namespace std;\n\nclass t_session {\nprivate:\n\t// The owning dialog\n\tt_dialog\t\t*dialog;\n\t\n\t// User profile of user for which the session is created.\n\t// This is a pointer to the user_config owned by a phone user.\n\t// So this pointer should never be deleted.\n\tt_user\t\t\t*user_config;\n\n\t// Copy of host needed for call-retrieve after call-hold\n\tstring\t\t\tretrieve_host;\n\n\t// Audio RTP session\n\tt_audio_session\t\t*audio_rtp_session;\n\n\t// Indicates if session is put on-hold, i.e. no RTP should be sent\n\t// or received for this session.\n\tbool\t\t\tis_on_hold;\n\t\n\t// Indicates if a session is killed, i.e. RTP will never be\n\t// sent or received anymore.\n\tbool\t\t\tis_killed;\n\t\n\t// Mapping from audio codecs to RTP payload numbers for receiving\n\t// and sending directions.\n\tmap<t_audio_codec, unsigned short>\trecv_ac2payload;\n\tmap<t_audio_codec, unsigned short>\tsend_ac2payload;\n\t\n\t// Mapping from RTP payload numbers to audio codecs for receiving\n\t// and sending directions.\n\tmap<unsigned short, t_audio_codec>\trecv_payload2ac;\n\tmap<unsigned short, t_audio_codec>\tsend_payload2ac;\n\t\n\t// Set the list of received codecs from the SDP.\n\t// Create the send_ac2paylaod and send_payload2ac mappings.\n\tvoid set_recvd_codecs(t_sdp *sdp);\n\t\n\t// Returns if this session is part of a 3-way conference\n\tbool is_3way(void) const;\n\t\n\t// Returns the peer session of a 3-way conference\n\tt_session *get_peer_3way (void) const;\n\npublic:\n\t// Audio session information\n\n\t// Near end information\n\tstring\t\t\tsrc_sdp_version;\n\tstring\t\t\tsrc_sdp_id;\n\tstring\t\t\treceive_host; // RTP receive host address\n\tunsigned short\t\treceive_port; // RTP receive port\n\n\t// Far end information\n\tstring\t\t\tdst_sdp_version;\n\tstring\t\t\tdst_sdp_id;\n\tstring\t\t\tdst_rtp_host;\n\tunsigned short\t\tdst_rtp_port;\n\tbool\t\t\tdst_zrtp_support;\n\n\t// Direction of the audio stream from this phone's point of view\n\tt_sdp_media_direction\tdirection;\n\n\tlist<t_audio_codec>\toffer_codecs;\t// codecs to offer in outgoing INVITE\n\tlist<t_audio_codec>\trecvd_codecs;\t// codecs received from far-end\n\tt_audio_codec\t\tuse_codec;\t// codec to be used\n\tunsigned short\t\tptime;\t\t// payload size (ms)\n\tunsigned short\t\tilbc_mode;\t// 20 or 30 ms\n\tbool\t\t\trecvd_offer;  \t// offer received?\n\tbool\t\t\trecvd_answer; \t// answer received?\n\tbool\t\t\tsent_offer;\t// offer sent?\n\tunsigned short\t\trecv_dtmf_pt;\t// payload type for DTMF receiving\n\tunsigned short\t\tsend_dtmf_pt;\t// payload type for DTMF sending\n\tt_sdp\t\t\trecvd_sdp_offer;\n\n\tt_session(t_dialog *_dialog, string _receive_host,\n\t\t  unsigned short _receive_port);\n\n\t// The destructor will destroy the RTP session and stop the\n\t// RTP streams\n\t~t_session();\n\n\t/** @name Clone a new session from an existing session. */\n\t//@{\n\t/** @note copies of a session do not copy the audio RTP session! */\n\n\t/**\n\t * Create a session based on an existing session, i.e.\n\t * same receive user and host. The source SDP version of the\n\t * new session will be increased by 1.\n\t * @return The new session.\n\t */\n\tt_session *create_new_version(void) const;\n\n\t/**\n\t * Create a copy of the session. The destination paramters\n\t * and recvd/offer and answer are erased in the copy.\n\t * The source SDP version of the new session will be increased by 1.\n\t * @return The new session.\n\t */\n\tt_session *create_clean_copy(void) const;\n\n\t/**\n\t * Create a session for call-hold.\n\t * @return The call-hold session.\n\t */\n\tt_session *create_call_hold(void) const;\n\n\t/**\n\t * Create a session for call-retrieve.\n\t * @return The call-retrieve session.\n\t */\n\tt_session *create_call_retrieve(void) const;\n\n\t/** Create an identical copy for a session refresh re-INVITE */\n\tt_session *create_session_refresh(void) const;\n\t//@}\n\n\t// Process incoming SDP offer. Return false if SDP is not\n\t// supported. If SDP is supported then use_codec will be\n\t// set to the first codec in the received offer that is\n\t// supported by this phone, i.e. this is the codec that should\n\t// be put in the answer.\n\tbool process_sdp_offer(t_sdp *sdp, int &warn_code, string &warn_text);\n\n\t// Process incoming SDP answer. Return false if SDP is not\n\t// supported. It is expected that the answer contains 1 codec\n\t// only. If more codecs are answered, then only the first supported\n\t// codec is considered.\n\tbool process_sdp_answer(t_sdp *sdp, int &warn_code, string &warn_text);\n\n\t// Create an SDP offer body for a SIP message\n\tvoid create_sdp_offer(t_sip_message *m, const string &user);\n\n\t// Create an SDP answer body for a SIP message\n\tvoid create_sdp_answer(t_sip_message *m, const string &user);\n\n\t// Start/stop the RTP streams\n\t// When a session is on-hold then start_rtp simply returns.\n\tvoid start_rtp(void);\n\tvoid stop_rtp(void);\n\t\n\t// Kill RTP streams. The difference with stopping an RTP stream\n\t// is that it cannot be started after being killed.\n\tvoid kill_rtp(void);\n\n\tt_audio_session *get_audio_session(void) const;\n\tvoid set_audio_session(t_audio_session *as);\n\n\t// Check if two session are equal wrt the audio parameters\n\tbool equal_audio(const t_session &s) const;\n\n\t// Send DTMF digit\n\tvoid send_dtmf(char digit, bool inband);\n\n\t// Get the line that belongs to this session\n\tt_line *get_line(void) const;\n\n\t// Transfer ownership of this session to a new dialog\n\tvoid set_owner(t_dialog *d);\n\n\t// Hold/un-hold a session\n\t// These methods only toggle the hold indicator. If you hold\n\t// a session, you must make sure that any running RTP is stopped.\n\t// If you unhold a session you have to call start_rtp to start the\n\t// RTP.\n\tvoid hold(void);\n\tvoid unhold(void);\n\t\n\t// Check if RTP session is acitve\n\tbool is_rtp_active(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/sockets/CMakeLists.txt",
    "content": "project(libtwinkle-sockets)\n\nset(LIBTWINKLE_SOCKETS-SRCS\n\tconnection.cpp\n\tconnection_table.cpp\n\tdnssrv.cpp\n        interfaces.cpp\n        socket.cpp\n        url.cpp\n)\n\nadd_library(libtwinkle-sockets OBJECT ${LIBTWINKLE_SOCKETS-SRCS})\n"
  },
  {
    "path": "src/sockets/connection.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"connection.h\"\n\n#include <algorithm>\n#include <iostream>\n\n#include \"connection_table.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"parser/parse_ctrl.h\"\n\nextern t_connection_table *connection_table;\n\nusing namespace std;\n\nt_connection::t_connection(t_socket *socket) : \n\tsocket_(socket), \n\tsip_msg_(NULL),\n\tpos_send_buf_(0),\n\tidle_time_(0),\n\tcan_reuse_(true)\n{}\n\nt_connection::~t_connection() {\n\tMEMMAN_DELETE(socket_);\n\tdelete socket_;\n}\n\nt_socket *t_connection::get_socket(void) {\n\treturn socket_;\n}\n\nt_connection::size_type t_connection::data_size(void) const {\n\treturn read_buf_.size();\n}\n\nvoid t_connection::read(bool &connection_closed) {\n\tconnection_closed = false;\n\tchar buf[READ_BLOCK_SIZE];\n\t\n\tssize_t nread = socket_->recv(buf, READ_BLOCK_SIZE);\n\t\n\tif (nread > 0) {\n\t\tread_buf_.append(buf, buf + nread);\n\t} else {\n\t\tconnection_closed = true;\n\t}\n\t\n\tidle_time_ = 0;\n}\n\nvoid t_connection::write(void) {\n\tif (send_buf_.empty()) return;\n\t\n\tssize_t nwrite = send_buf_.size() - pos_send_buf_;\n\tif ((ssize_t)WRITE_BLOCK_SIZE < nwrite) nwrite = WRITE_BLOCK_SIZE;\n\tssize_t nwritten = socket_->send(send_buf_.c_str() + pos_send_buf_, nwrite);\n\tpos_send_buf_ += nwritten;\n\t\n\tif (pos_send_buf_ >= send_buf_.size()) {\n\t\t// All data written\n\t\tsend_buf_.clear();\n\t\tpos_send_buf_ = 0;\n\t}\n}\n\nssize_t t_connection::send(const char *data, int data_size) {\n\tssize_t bytes_sent = socket_->send(data, data_size);\n\tidle_time_ = 0;\n\t\n\treturn bytes_sent;\n}\n\nvoid t_connection::async_send(const char *data, int data_size) {\n\tsend_buf_ += string(data, data_size);\n\tconnection_table->restart_write_select();\n}\n\nt_sip_message *t_connection::get_sip_msg(string &raw_headers, string &raw_body, bool &error,\n\t\tbool &msg_too_large) \n{\n\tstring log_msg;\n\t\n\traw_headers.clear();\n\traw_body.clear();\n\terror = false;\n\tmsg_too_large = false;\n\t\n\tif (!sip_msg_) {\n\t\t// RFC 3261 7.5\n\t\t// Ignore CRLF preceding the start-line of a SIP message\n\t\twhile (read_buf_.size() >= 2 && read_buf_.substr(0, 2) == string(CRLF)) {\n\t\t\tremove_data(2);\n\t\t}\n\t\n\t\t// A complete list of headers has not been read yet, try\n\t\t// to find the boundary between headers and body.\n\t\tstring seperator = string(CRLF) + string(CRLF);\n\t\tstring::size_type pos_body = read_buf_.find(seperator);\n\t\t\n\t\tif (pos_body == string::npos) {\n\t\t\t// Still no complete list of headers.\n\t\t\tif (read_buf_.size() > sys_config->get_sip_max_tcp_size()) {\n\t\t\t\tlog_file->write_report(\"Message too large\",\n\t\t\t\t\t\"t_connection::get_sip_msg\", LOG_SIP, LOG_WARNING);\n\t\t\t\terror = true;\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t\t\n\t\tpos_body += seperator.size();\n\t\t\n\t\t// Parse SIP headers\n\t\traw_sip_headers_ = read_buf_.substr(0, pos_body);\n\t\tlist<string> parse_errors;\n\t\ttry {\n\t\t\tsip_msg_ = t_parser::parse(raw_sip_headers_, parse_errors);\n\t\t}\n\t\tcatch (int) {\n\t\t\t// Discard malformed SIP messages.\n\t\t\tlog_msg = \"Invalid SIP message.\\n\";\n\t\t\tlog_msg += \"Fatal parse error in headers.\\n\\n\";\n\t\t\tlog_msg += to_printable(raw_sip_headers_);\n\t\t\tlog_file->write_report(log_msg, \"t_connection::get_sip_msg\", LOG_SIP, LOG_DEBUG);\n\t\t\t\n\t\t\terror = true;\n\t\t\treturn NULL;\n\t\t}\n\t\t\n\t\t// Log non-fatal parse errors.\n\t\tif (!parse_errors.empty()) {\n\t\t\tlog_msg = \"Parse errors:\\n\";\n\t\t\tlog_msg += \"\\n\";\n\t\t\tfor (list<string>::iterator i = parse_errors.begin(); \n\t\t\t     i != parse_errors.end(); i++) \n\t\t\t{\n\t\t\t\tlog_msg += *i;\n\t\t\t\tlog_msg += \"\\n\";\n\t\t\t}\n\t\t\tlog_msg += \"\\n\";\n\t\t\tlog_file->write_report(log_msg, \"t_connection::get_sip_msg\", LOG_SIP, LOG_DEBUG);\n\t\t}\n\t\t\n\t\tget_remote_address(sip_msg_->src_ip_port.ipaddr, sip_msg_->src_ip_port.port);\n\t\tsip_msg_->src_ip_port.transport = \"tcp\";\n\t\t\n\t\t// Remove the processed headers from the read buffer.\n\t\tremove_data(pos_body);\n\t}\n\t\n\t// RFC 3261 18.4\n\t// The Content-Length header field MUST be used with stream oriented transports.\n\tif (!sip_msg_->hdr_content_length.is_populated()) {\n\t\t// The transaction layer will send an error response.\n\t\tlog_file->write_report(\"Content-Length header is missing.\",\n\t\t\t\t\"t_connection::get_sip_msg\", LOG_SIP, LOG_WARNING);\n\t} else {\n\t\tif (read_buf_.size() < sip_msg_->hdr_content_length.length) {\n\t\t\t// No full body read yet.\n\t\t\tif (read_buf_.size() + raw_sip_headers_.size() <=\n\t\t\t\t\t\tsys_config->get_sip_max_tcp_size())\n\t\t\t{\n\t\t\t\treturn NULL;\n\t\t\t} else {\n\t\t\t\tlog_file->write_report(\"Message too large\",\n\t\t\t\t\t\"t_connection::get_sip_msg\", LOG_SIP, LOG_WARNING);\n\t\t\t\t\t\n\t\t\t\tmsg_too_large = true;\n\t\t\t}\n\t\t} else {\n\t\t\tif (sip_msg_->hdr_content_length.length > 0) {\n\t\t\t\traw_body = read_buf_.substr(0, sip_msg_->hdr_content_length.length);\n\t\t\t\tremove_data(sip_msg_->hdr_content_length.length);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Return data to caller. Clear internally cached data.\n\tt_sip_message *msg = sip_msg_;\n\tsip_msg_ = NULL;\n\traw_headers = raw_sip_headers_;\n\traw_sip_headers_.clear();\n\t\n\treturn msg;\n}\n\nstring t_connection::get_data(size_t nbytes) const {\n\tsize_t nread = min(nbytes, read_buf_.size());\n\n\treturn read_buf_.substr(0, nread);\n}\n\nvoid t_connection::remove_data(size_t nbytes) {\n\tif (nbytes == 0) return;\n\t\n\tif (nbytes >= read_buf_.size()) {\n\t\tread_buf_.clear();\n\t} else {\n\t\tread_buf_.erase(0, nbytes);\n\t}\n}\n\nvoid t_connection::get_remote_address(IPaddr &remote_addr, unsigned short &remote_port) {\n\tremote_addr = 0;\n\tremote_port = 0;\n\n\ttry {\n\t\tt_socket_tcp *tcp_socket = dynamic_cast<t_socket_tcp *>(socket_);\n\t\tif (tcp_socket) {\n\t\t\ttcp_socket->get_remote_address(remote_addr, remote_port);\n\t\t} else {\n\t\t\tlog_file->write_report(\"Socket is not connection oriented.\",\n\t\t\t\t\t\"t_connection::get_sip_msg\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t}\n\t}\n\tcatch (int err) {\n\t\t\tstring errmsg = get_error_str(err);\n\t\t\tstring log_msg = \"Cannot get remote address: \";\n\t\t\tlog_msg += errmsg;\n\t\t\tlog_file->write_report(log_msg, \"t_connection::get_sip_msg\", \n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t}\n}\n\nunsigned long t_connection::increment_idle_time(unsigned long interval) {\n\tidle_time_ += interval;\n\treturn idle_time_;\n}\n\nunsigned long t_connection::get_idle_time(void) const {\n\treturn idle_time_;\n}\n\nbool t_connection::has_data_to_send(void) const {\n\treturn !send_buf_.empty();\n}\n\nvoid t_connection::set_reuse(bool reuse) {\n\tcan_reuse_ = reuse;\n}\n\nbool t_connection::may_reuse(void) const {\n\treturn can_reuse_;\n}\n\nvoid t_connection::add_registered_uri(const t_url &uri) {\n\t// Add the URI if it is not in the set.\n\tif (find(registered_uri_set_.begin(), registered_uri_set_.end(), uri) == registered_uri_set_.end())\n\t{\n\t\tregistered_uri_set_.push_back(uri);\n\t}\n}\n\nvoid t_connection::remove_registered_uri(const t_url &uri) {\n\tregistered_uri_set_.remove(uri);\n}\n\nvoid t_connection::update_registered_uri_set(const t_request *req) {\n\tassert(req->method == REGISTER);\n\t\n\tif (req->is_registration_request()) {\n\t\tadd_registered_uri(req->hdr_to.uri);\n\t} else if (req->is_de_registration_request()) {\n\t\tremove_registered_uri(req->hdr_to.uri);\n\t}\n}\n\nconst list<t_url> &t_connection::get_registered_uri_set(void) const {\n\treturn registered_uri_set_;\n}\n\nbool t_connection::has_registered_uri(void) const {\n\treturn !registered_uri_set_.empty();\n}\n"
  },
  {
    "path": "src/sockets/connection.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Network connection\n */\n \n#ifndef _H_CONNECTION\n#define _H_CONNECTION\n\n#include <list>\n#include <string>\n\n#include \"socket.h\"\n#include \"sockets/ipaddr.h\"\n#include \"parser/request.h\"\n#include \"parser/sip_message.h\"\n\nusing namespace std;\n \n/** Abstract class for a network connection. */\nclass t_connection {\nprivate:\n\tstatic const unsigned int READ_BLOCK_SIZE = 1448;\n\tstatic const unsigned int WRITE_BLOCK_SIZE = 1448;\n\n\t/** Buffer with data already read from the network. */\n\tstring read_buf_;\n\t\n\t/** Socket for connection. */\n\tt_socket *socket_;\n\t\n\t/** SIP message parsed from available data (headers only) */\n\tt_sip_message *sip_msg_;\n\t\n\t/** Raw SIP headers that have been parsed already */\n\tstring raw_sip_headers_;\n\t\n\t/** Data to be sent on the connection. */\n\tstring send_buf_;\n\t\n\t/** Position in send buffer for next send action. */\n\tstring::size_type pos_send_buf_;\n\t\n\t/** \n\t * Time (ms) that the connection is idle .\n\t * This time is reset to zero by read and send actions.\n\t */\n\tunsigned long idle_time_;\n\t\n\t/** \n\t * Flag to indicate that a connection can be reused. \n\t * By default a connection is reusable.\n\t */\n\tbool can_reuse_;\n\t\n\t/**\n\t * A set of user URI's (AoR) that are registered via this connection.\n\t * If persistent connections are used for NAT traversal, then these are\n\t * the URI's that are impacted when the connection breaks.\n\t * A URI is only added to this set, if a persistent connection is required\n\t * for this user.\n\t * @note The set is implemented as a list as t_url has not less-than operator.\n\t */\n\tlist<t_url> registered_uri_set_;\n\t\npublic:\n\ttypedef string::size_type size_type;\n\n\tt_connection(t_socket *socket);\n\t\n\t/**\n\t * Destuctor.\n\t * @note The socket will be closed and destroyed.\n\t */\n\tvirtual ~t_connection();\n\t\n\t/**\n\t * Get a pointer to the socket.\n\t * @return The socket.\n\t */\n\tt_socket *get_socket(void);\n\t\n\t/**\n\t * Get the amount of data in the read buffer.\n\t * @return Number of bytes in read buffer.\n\t */\n\tsize_type data_size(void) const;\n\t\n\t/** \n\t * Read a block data from connection in to read buffer. \n\t * @param connection_closed [out] Indicates if the connection was closed.\n\t * @throw int errno as set by recv.\n\t */\n\tvoid read(bool &connection_closed);\n\t\n\t/**\n\t * Send a block of data from the send buffer on a connection.\n\t * @throw int errno.\n\t */\n\tvoid write(void);\n\t\n\t/**\n\t * Send data on a connection.\n\t * @param data [in] Data to send\n\t * @param data_size [in] Size of data in bytes\n\t * @return Number of bytes sent.\n\t * @throw int errno.\n\t */\n\tssize_t send(const char *data, int data_size);\n\t\n\t/**\n\t * Append data to the send buffer for asynchronous sending.\n\t * @param data [in] Data to send\n\t * @param data_size [in] Size of data in bytes\n\t */\n\tvoid async_send(const char *data, int data_size);\n\t\n\t/**\n\t * Get a SIP message from the connection.\n\t * @param raw_headers [out] Raw headers of SIP message\n\t * @param raw_body [out] Raw body of SIP message\n\t * @param error [out] Indicates if an error occurred (invalid SIP message)\n\t * @param msg_too_large [out] Indicates that the message is cutoff because it was too large\n\t * @return The SIP message if a message was received.\n\t * @return NULL, if no full SIP message has been received yet or an error occurred.\n\t * @post If error == true, then NULL is returned\n\t * @post If msg_too_large == true, then a message is returned (partial though)\n\t */\n\tt_sip_message *get_sip_msg(string &raw_headers, string &raw_body, bool &error,\n\t\t\tbool &msg_too_large);\n\t\n\t/**\n\t * Get read data from read buffer.\n\t * @param nbytes [in] Maximum number of bytes to get.\n\t * @return Data from the read buffer up to nbytes.\n\t * @note The data is still in the buffer after this operation.\n\t */\n\tstring get_data(size_t nbytes = 0) const;\n\t\n\t/**\n\t * Remove data from read buffer.\n\t * @param nbytes [in] Number of bytes to remove.\n\t */\n\tvoid remove_data(size_t nbytes);\n\t\n\t/**\n\t * Get the remote address of a connection.\n\t * @param remote_addr [out] Source IPv4 address of the connection.\n\t * @param remote_port [out] Source port of the connection.\n\t */\n\tvoid get_remote_address(IPaddr &remote_addr, unsigned short &remote_port);\n\t\n\t/**\n\t * Add an interval to the idle time.\n\t * @param interval [in] Interval in ms.\n\t * @return The new idle time.\n\t */\n\tunsigned long increment_idle_time(unsigned long interval);\n\t\n\t/**\n\t * Get idle time.\n\t * @return Idle time in ms.\n\t */\n\tunsigned long get_idle_time(void) const;\n\t\n\t/**\n\t * Check if there is data in the send buffer.\n\t * @return true if there is data, otherwise false.\n\t */\n\tbool has_data_to_send(void) const;\n\t\n\t/** Set re-use characteristic. */\n\tvoid set_reuse(bool reuse);\n\t\n\t/**\n\t * Check if this connection may be reused to send data.\n\t * @return true if the connection may be reused, otherwise false.\n\t */\n\tbool may_reuse(void) const;\n\t\n\t/**\n\t * Add a URI to the set of registered URI's.\n\t * @param uri [in] The URI to add.\n\t */\n\tvoid add_registered_uri(const t_url &uri);\n\t\n\t/**\n\t * Remove a URI from the set of registered URI's.\n\t * @param uri [in] The URI to remove.\n\t */\n\tvoid remove_registered_uri(const t_url &uri);\n\t\n\t/**\n\t * Update the set of registered URI based on a REGISTER request.\n\t * If the REGISTER is a registration, then add the To-header URI.\n\t * If the REGISTER is a de-registration, then remove the To-header URI.\n\t * If the REGISTER is a query, then do nothing.\n\t * @param req [in] A REGISTER request.\n\t * @pre req must be a REGISTER request.\n\t */\n\tvoid update_registered_uri_set(const t_request *req);\n\t\n\t/**\n\t * Get the set of registered URI's.\n\t * @return The set of registered URI's.\n\t */ \n\tconst list<t_url> &get_registered_uri_set(void) const;\n\t\n\t/**\n\t * Check if at least one registered URI is associated with this connection.\n\t * @return True if a URI is associated, false otherwise.\n\t */\n\tbool has_registered_uri(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/sockets/connection_table.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"connection_table.h\"\n\n#include <algorithm>\n#include <sys/select.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <cerrno>\n#include <iostream>\n#include <cstdlib>\n#include <fcntl.h>\n\n#include \"log.h\"\n#include \"protocol.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nusing namespace std;\n\nextern t_connection_table *connection_table;\n\nvoid t_connection_table::create_pipe(int p[2]) {\n\tif (pipe(p) == -1) {\n\t\tstring err = get_error_str(errno);\n\t\tcerr << \"FATAL: t_connection_table - Cannot create pipe.\\n\";\n\t\tcerr << err << endl;\n\t\texit(-1);\n\t}\n\t\n\tif (fcntl(p[0], F_SETFL, O_NONBLOCK) == -1) {\n\t\tstring err = get_error_str(errno);\n\t\tcerr << \"FATAL: t_connection_table - fcntl fails on read side of pipe.\\n\";\n\t\tcerr << err << endl;\n\t\texit(-1);\n\t}\n\t\n\tif (fcntl(p[1], F_SETFL, O_NONBLOCK) == -1) {\n\t\tstring err = get_error_str(errno);\n\t\tcerr << \"FATAL: t_connection_table - fcntl fails on write side of pipe.\\n\";\n\t\tcerr << err << endl;\n\t\texit(-1);\n\t}\n}\n\nvoid t_connection_table::signal_modification_read(void) {\n\tt_mutex_guard guard(mtx_connections_);\n\n\t// Write a byte to the modified pipe, so a select can be retried.\n\tchar x = 'x';\n\t(void)write(fd_pipe_modified_read_[1], &x, 1);\n}\n\nvoid t_connection_table::signal_modification_write(void) {\n\tt_mutex_guard guard(mtx_connections_);\n\n\t// Write a byte to the modified pipe, so a select can be retried.\n\tchar x = 'x';\n\t(void)write(fd_pipe_modified_write_[1], &x, 1);\n}\n\nvoid t_connection_table::signal_quit(void) {\n\tt_mutex_guard guard(mtx_connections_);\n\t\n\t// Write a byte to the quit pipe, so a select can be halted.\n\tchar x = 'x';\n\t(void)write(fd_pipe_quit_read_[1], &x, 1);\n\t(void)write(fd_pipe_quit_write_[1], &x, 1);\n\t\n\tterminated_ = true;\n}\n\nt_recursive_mutex t_connection_table::mtx_connections_;\n\nt_connection_table::t_connection_table() :\n\tterminated_(false)\n{\n\tcreate_pipe(fd_pipe_modified_read_);\n\tcreate_pipe(fd_pipe_modified_write_);\n\tcreate_pipe(fd_pipe_quit_read_);\n\tcreate_pipe(fd_pipe_quit_write_);\n}\n\nt_connection_table::~t_connection_table() {\n\tt_mutex_guard guard(mtx_connections_);\n\n\tfor (list<t_connection *>::iterator it = connections_.begin();\n\t     it != connections_.end(); ++it)\n\t{\n\t\tMEMMAN_DELETE(*it);\n\t\tdelete *it;\n\t}\n}\n\nvoid t_connection_table::unlock(void) const {\n\tmtx_connections_.unlock();\n}\n\nbool t_connection_table::empty(void) const {\n\tt_mutex_guard guard(mtx_connections_);\n\treturn connections_.empty();\n}\n\nt_connection_table::size_type t_connection_table::size(void) const {\n\tt_mutex_guard guard(mtx_connections_);\n\treturn connections_.size();\n}\n\nvoid t_connection_table::add_connection(t_connection *connection) {\n\tt_mutex_guard guard(mtx_connections_);\n\tconnections_.push_back(connection);\n\tsignal_modification_read();\n\tsignal_modification_write();\n}\n\nvoid t_connection_table::remove_connection(t_connection *connection) {\n\tt_mutex_guard guard(mtx_connections_);\n\tconnections_.remove(connection);\n\tsignal_modification_read();\n\tsignal_modification_write();\n}\n\nt_connection *t_connection_table::get_connection(IPaddr remote_addr, \n\t\tunsigned short remote_port)\n{\n\tmtx_connections_.lock();\n\t\n\tt_connection *found_connection = NULL;\n\tlist<t_connection *> broken_connections;\n\t\n\tfor (list<t_connection *>::iterator it = connections_.begin();\n\t     it != connections_.end(); ++it)\n\t{\n\t\tIPaddr addr;\n\t\tunsigned short port;\n\n\t\tif ((*it)->may_reuse()) {\n\t\t\ttry {\n\t\t\t\tt_socket *socket = (*it)->get_socket();\n\t\t\t\tt_socket_tcp *tcp_socket = dynamic_cast<t_socket_tcp *>(socket);\n\t\t\t\t\n\t\t\t\tif (tcp_socket) {\n\t\t\t\t\ttcp_socket->get_remote_address(addr, port);\n\t\t\t\t\tif (addr == remote_addr && port == remote_port) {\n\t\t\t\t\t\tfound_connection = *it;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (int err) {\n\t\t\t\t// This should never happen.\n\t\t\t\tcerr << \"Cannot get remote address of socket.\" << endl;\n\t\t\t\t\n\t\t\t\t// Destroy and remove connection as it is probably broken.\n\t\t\t\tbroken_connections.push_back(*it);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Clear broken connections\n\tfor (list<t_connection *>::iterator it = broken_connections.begin();\n\t     it != broken_connections.end(); ++it)\n\t{\n\t\tremove_connection(*it);\n\t\tMEMMAN_DELETE(*it);\n\t\tdelete *it;\n\t}\n\t\n\tif (!found_connection) mtx_connections_.unlock();\n\treturn found_connection;\n}\n\nlist<t_connection *> t_connection_table::select_read(struct timeval *timeout) const {\n\tfd_set read_fds;\n\tint nfds = 0;\n\tbool retry = true;\n\tlist<t_connection *> result;\n\t\n\t// Empty modification pipe\n\tchar pipe_buf;\n\twhile (read(fd_pipe_modified_read_[0], &pipe_buf, 1) > 0);\n\t\n\twhile (retry) {\n\t\tFD_ZERO(&read_fds);\n\t\t\n\t\t// Add modification pipe so select can be restarted when the\n\t\t// connection table modifies.\n\t\tFD_SET(fd_pipe_modified_read_[0], &read_fds);\n\t\tnfds = fd_pipe_modified_read_[0];\n\t\t\n\t\t// Add quit pipe so select can quit on demand.\n\t\tFD_SET(fd_pipe_quit_read_[0], &read_fds);\n\t\tnfds = max(nfds, fd_pipe_quit_read_[0]);\n\t\t\n\t\tmtx_connections_.lock();\n\t\t\n\t\tfor (list<t_connection *>::const_iterator it = connections_.begin();\n\t\t     it != connections_.end(); ++it)\n\t\t{\n\t\t\tt_socket *socket = (*it)->get_socket();\n\t\t\tint fd = socket->get_descriptor();\n\t\t\tFD_SET(fd, &read_fds);\n\t\t\tnfds = max(nfds, fd);\n\t\t}\n\t\t\n\t\tmtx_connections_.unlock();\n\t\t\n\t\tint ret = select(nfds + 1, &read_fds, NULL, NULL, timeout);\n\t\tif (ret < 0) throw errno;\n\t\t\n\t\tif (FD_ISSET(fd_pipe_quit_read_[0], &read_fds)) {\n\t\t\t// Quit was signalled, so stop immediately.\n\t\t\tbreak;\n\t\t}\n\t\n\t\tmtx_connections_.lock();\n\t\t\n\t\t// Determine which sockets have become readable\n\t\tfor (list<t_connection *>::const_iterator it = connections_.begin();\n\t\t     it != connections_.end(); ++it)\n\t\t{\n\t\t\tt_socket *socket = (*it)->get_socket();\n\t\t\tint fd = socket->get_descriptor();\n\t\t\tif (FD_ISSET(fd, &read_fds)) {\n\t\t\t\tresult.push_back(*it);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!result.empty()) {\n\t\t\t// Connections have become readable, so return to the caller.\n\t\t\tretry = false;\n\t\t} else {\n\t\t\tmtx_connections_.unlock();\n\t\t\t\n\t\t\t// No connections have become readable. Check signal descriptors\n\t\t\tif (FD_ISSET(fd_pipe_modified_read_[0], &read_fds)) {\n\t\t\t\t// The connection table is modified. Retry select.\n\t\t\t\tread(fd_pipe_modified_read_[0], &pipe_buf, 1);\n\t\t\t} else {\n\t\t\t\t// This should never happen.\n\t\t\t\tcerr << \"ERROR: select_read returned without any file descriptor.\" << endl;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn result;\n}\n\nlist<t_connection *> t_connection_table::select_write(struct timeval *timeout) const {\n\tfd_set read_fds;\n\tfd_set write_fds;\n\tint nfds = 0;\n\tbool retry = true;\n\tlist<t_connection *> result;\n\t\n\t// Empty modification pipe\n\tchar pipe_buf;\n\twhile (read(fd_pipe_modified_write_[0], &pipe_buf, 1) > 0);\n\t\n\twhile (retry) {\n\t\tFD_ZERO(&read_fds);\n\t\tFD_ZERO(&write_fds);\n\t\t\n\t\t// Add modification pipe so select can be restarted when the\n\t\t// connection table modifies.\n\t\tFD_SET(fd_pipe_modified_write_[0], &read_fds);\n\t\tnfds = fd_pipe_modified_write_[0];\n\t\t\n\t\t// Add quit pipe so select can quit on demand.\n\t\tFD_SET(fd_pipe_quit_write_[0], &read_fds);\n\t\tnfds = max(nfds, fd_pipe_quit_write_[0]);\n\t\t\n\t\tmtx_connections_.lock();\n\t\t\n\t\tfor (list<t_connection *>::const_iterator it = connections_.begin();\n\t\t     it != connections_.end(); ++it)\n\t\t{\n\t\t\tif ((*it)->has_data_to_send()) {\n\t\t\t\tt_socket *socket = (*it)->get_socket();\n\t\t\t\tint fd = socket->get_descriptor();\n\t\t\t\tFD_SET(fd, &write_fds);\n\t\t\t\tnfds = max(nfds, fd);\n\t\t\t}\n\t\t}\n\t\t\n\t\tmtx_connections_.unlock();\n\t\t\n\t\tint ret = select(nfds + 1, &read_fds, &write_fds, NULL, timeout);\n\t\tif (ret < 0) throw errno;\n\t\t\n\t\tif (FD_ISSET(fd_pipe_quit_write_[0], &read_fds)) {\n\t\t\t// Quit was signalled, so stop immediately.\n\t\t\tbreak;\n\t\t}\n\t\n\t\tmtx_connections_.lock();\n\t\t\n\t\t// Determine which sockets have become writable\n\t\tfor (list<t_connection *>::const_iterator it = connections_.begin();\n\t\t     it != connections_.end(); ++it)\n\t\t{\n\t\t\tt_socket *socket = (*it)->get_socket();\n\t\t\tint fd = socket->get_descriptor();\n\t\t\tif (FD_ISSET(fd, &write_fds)) {\n\t\t\t\tresult.push_back(*it);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!result.empty()) {\n\t\t\t// Connections have become writable, so return to the caller.\n\t\t\tretry = false;\n\t\t} else {\n\t\t\tmtx_connections_.unlock();\n\t\t\t\n\t\t\t// No connections have become writable. Check signal descriptors\n\t\t\tif (FD_ISSET(fd_pipe_modified_write_[0], &read_fds)) {\n\t\t\t\t// The connection table is modified. Retry select.\n\t\t\t\tread(fd_pipe_modified_write_[0], &pipe_buf, 1);\n\t\t\t} else {\n\t\t\t\t// This should never happen.\n\t\t\t\tcerr << \"ERROR: select_write returned without any file descriptor.\" << endl;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn result;\n}\n\nvoid t_connection_table::cancel_select(void) {\n\tsignal_quit();\n}\n\nvoid t_connection_table::restart_write_select(void) {\n\tsignal_modification_write();\n}\n\nvoid t_connection_table::close_idle_connections(unsigned long interval, bool &terminated) {\n\tt_mutex_guard guard(mtx_connections_);\n\t\n\tterminated = terminated_;\n\n\tlist<t_connection *> expired_connections;\n\t\n\t// Update idle times and find expired connections.\n\tfor (list<t_connection *>::iterator it = connections_.begin();\n\t     it != connections_.end(); ++it)\n\t{\n\t\tunsigned long idle_time = (*it)->increment_idle_time(interval);\n\t\tif (idle_time >= DUR_IDLE_CONNECTION || terminated) {\n\t\t\t// If a registered URI is associated with the connection, then\n\t\t\t// it is persistent and it should not be closed.\n\t\t\tif (!(*it)->has_registered_uri()) {\n\t\t\t\texpired_connections.push_back(*it);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Close expired connections.\n\tfor (list<t_connection *>::iterator it = expired_connections.begin();\n\t     it != expired_connections.end(); ++it)\n\t{\n\t\tIPaddr ipaddr;\n\t\tunsigned short port;\n\t\t\n\t\t(*it)->get_remote_address(ipaddr, port);\n\t\tlog_file->write_header(\"t_connection_table::close_idle_connections\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Close connection to \");\n\t\tlog_file->write_raw(h_ip2str(ipaddr));\n\t\tlog_file->write_raw(\":\");\n\t\tlog_file->write_raw(int2str(port));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\n\t\tremove_connection(*it);\n\t\tMEMMAN_DELETE(*it);\n\t\tdelete *it;\n\t}\n}\n\nvoid *connection_timeout_main(void *arg) {\n\tbool terminated = false;\n\t\n\twhile (!terminated) {\n\t\tstruct timespec sleep_timer;\n\t\t\n\t\tsleep_timer.tv_sec = 1;\n\t\tsleep_timer.tv_nsec = 0;\n\t\tnanosleep(&sleep_timer, NULL);\n\t\tconnection_table->close_idle_connections(1000, terminated);\n\t}\n\t\n\tlog_file->write_report(\"Connection timeout handler terminated.\",\n\t\t\t\"::connection_timeout_main\");\n\t\t\t\n\treturn NULL;\n};\n"
  },
  {
    "path": "src/sockets/connection_table.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/** \n * @file\n * Connection table\n */\n \n#ifndef _H_CONNECTION_TABLE\n#define _H_CONNECTION_TABLE\n\n#include <list>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"connection.h\"\n#include \"sockets/ipaddr.h\"\n#include \"threads/mutex.h\"\n\nusing namespace std;\n\n/** Table of established connections. */\nclass t_connection_table {\nprivate:\n\t/** Established connections */\n\tlist<t_connection *>\tconnections_;\n\t\n\t/** Mutex to protect concurrent access to the connections. */\n\tstatic t_recursive_mutex mtx_connections_;\n\t\n\t/** Indicates if the connection table is terminated. */\n\tbool\t\t\tterminated_;\n\t\n\t/** Pipe to signal modification of the list of readable connections. */\n\tint\t\t\tfd_pipe_modified_read_[2];\n\t\n\t/** Pipe to signal modification of the list of connections with data to send. */\n\tint\t\t\tfd_pipe_modified_write_[2];\n\t\n\t/** Pipe to signal the read select operation to quit. */\n\tint\t\t\tfd_pipe_quit_read_[2];\n\t\n\t/** Pipe to signal the write select operation to quit. */\n\tint\t\t\tfd_pipe_quit_write_[2];\n\t\n\t/** Create a pipe. */\n\tvoid create_pipe(int p[2]);\n\t\n\t/** Send a modification signal on the read modification pipe. */\n\tvoid signal_modification_read(void);\n\t\n\t/** Send a modification signal on the write modification pipe. */\n\tvoid signal_modification_write(void);\n\t\n\t/** Send a quit signal on the modification pipes. */\n\tvoid signal_quit(void);\n\t\npublic:\n\ttypedef list<t_connection *>::size_type size_type;\n\t\n\t/** Constructor */\n\tt_connection_table();\n\t\n\t/**\n\t * Destructor.\n\t * @note All connections in the table will be closed\n\t *       and destroyed.\n\t */\n\t~t_connection_table();\n\t\n\t/**\n\t * Unlock connection table.\n\t * @note After some operations, the table stays lock to avoid race\n\t *       conditions. The caller should unlock the table explicitly\n\t *       when it has finished working on the connections.\n\t */\n\tvoid unlock(void) const;\n\t\n\t/**\n\t * Check if connection table is empty.\n\t * @return true if empty, false if not empty.\n\t */\n\tbool empty(void) const;\n\t\n\t/**\n\t * Get number of connections in table.\n\t * @return number of connections.\n\t */\n\tsize_type size(void) const;\n\t\n\t/**\n\t * Add a connection to the table.\n\t * @param connection [in] Connection to add.\n\t */\n\tvoid add_connection(t_connection *connection);\n\t\n\t/**\n\t * Remove a TCP connection from the table.\n\t * @param connection [in] TCP connection to remove.\n\t */\n\tvoid remove_connection(t_connection *connection);\n\t\n\t/**\n\t * Get a connection to a particular destination.\n\t * @param remote_addr [in] IP address of destination.\n\t * @param remote_port [in] Port of destination.\n\t * @return The connection to the destination. If there is no\n\t *         connection to the destination, then NULL is returned.\n\t * @post If a connection is returned, the table is locked. The caller must\n\t *       unlock the tbale when it is finished with the connection.\n\t * @note Only re-usable connections are considered.\n\t */\n\tt_connection *get_connection(IPaddr remote_addr, unsigned short remote_port);\n\t\n\t/**\n\t * Wait for connections to become readable.\n\t * @param timeout [in] Maxmimum time to wait. If NULL, then wait indefinitely.\n\t * @return List of sockets that are readable.\n\t * @throw int Errno\n\t * @post The transaction table is locked if a non-empty list of sockets is returned.\n\t * @note The caller should unlock the table when processing of the sockets is finished.\n\t */\n\tlist<t_connection *> select_read(struct timeval *timeout) const;\n\t\n\t/**\n\t * Wait for connections to become writeable.\n\t * Only connections with data to send are waited for.\n\t * @param timeout [in] Maxmimum time to wait. If NULL, then wait indefinitely.\n\t * @return List of sockets that are writable.\n\t * @throw int Errno\n\t * @post The transaction table is locked if a non-empty list of sockets is returned.\n\t * @note The caller should unlock the table when processing of the sockets is finished.\n\t */\n\tlist<t_connection *> select_write(struct timeval *timeout) const;\n\t\n\t/** Cancel all selects. */\n\tvoid cancel_select(void);\n\t\n\t/** Restart write select, so new connections with data are picked up. */\n\tvoid restart_write_select(void);\n\t\n\t/**\n\t * Close all idle connections.\n\t * Increment the idle time of all connections with interval.\n\t * A persistent connection with associated registered URI's will not be closed.\n\t * @param interval [in] Interval to add to idle time (ms).\n\t * @param terminated [out] Indicates if the connection table has been terminated.\n\t */\n\tvoid close_idle_connections(unsigned long interval, bool &terminated);\n};\n\n/** Main for thread handling connection timeouts */\nvoid *connection_timeout_main(void *arg);\n\n#endif\n"
  },
  {
    "path": "src/sockets/dnssrv.cpp",
    "content": "/* \n   This software is copyrighted (c) 2002 Rick van Rein, the Netherlands.\n\n   This software has been modified by Michel de Boer. 2005\n*/ \n \n#include \"dnssrv.h\"\n\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/time.h>\n#include <netinet/in.h>\n#include <arpa/nameser.h>\n#include \"twinkle_config.h\"\n#include <resolv.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <netdb.h>\n#include <sys/socket.h>\n#include <string.h>\n#include <signal.h>\n\n\n/* Common offsets into an SRV RR */\n#define SRV_COST    (RRFIXEDSZ+0)\n#define SRV_WEIGHT  (RRFIXEDSZ+2)\n#define SRV_PORT    (RRFIXEDSZ+4)\n#define SRV_SERVER  (RRFIXEDSZ+6)\n#define SRV_FIXEDSZ (RRFIXEDSZ+6)\n\n\n/* Data structures */\ntypedef struct {\n\tunsigned char buf [PACKETSZ];\n\tint len;\n} iobuf;\ntypedef char name [MAXDNAME];\n#define MAXNUM_SRV PACKETSZ\n\n/* Local variable for SRV options */\nstatic unsigned long int srv_flags = 0L;\n\n\n/* Setup the SRV options when initialising -- invocation optional */\nvoid insrv_init (unsigned long flags) {\n#ifdef HAVE_RES_INIT\n\tsrv_flags = flags;\n\tres_init ();\n#endif\n}\n\n\n/* Test the given SRV options to see if all are set */\nint srv_testflag (unsigned long flags) {\n\treturn ((srv_flags & flags) == flags) ? 1 : 0;\n}\n\n\n/* Compare two SRV records by priority and by (scattered) weight */\nint srvcmp (const void *left, const void *right) {\n\tint lcost = ntohs (((unsigned short **) left ) [0][5]);\n\tint rcost = ntohs (((unsigned short **) right) [0][5]);\n\tif (lcost == rcost) {\n\t\tlcost = -ntohs (((unsigned short **) left ) [0][6]);\n\t\trcost = -ntohs (((unsigned short **) right) [0][6]);\n\t}\n\tif (lcost < rcost) {\n\t\treturn -1;\n\t} else if (lcost > rcost) {\n\t\treturn +1;\n\t} else {\n\t\treturn  0;\n\t}\n}\n\n\n/* Setup a client socket for the named service over the given protocol under\n * the given domain name.\n */\nint insrv_lookup (const char *service, const char *proto, const char *domain, \n\tlist<t_dns_result> &result) \n{\n\t// 1. convert service/proto to svcnm\n\t// 2. construct SRV query for _service._proto.domain\n\n\tiobuf names;\n\tname svcnm;\n\tint ctr;\n\tint rnd;\n\tHEADER *nameshdr;\n\tunsigned char *here, *srv[MAXNUM_SRV];\n\tint num_srv=0;\n\t// Storage for fallback SRV list, constructed when DNS gives no SRV\n\t//unsigned char fallbacksrv [2*(MAXCDNAME+SRV_FIXEDSZ+MAXCDNAME)];\n\n\t// srv_flags &= ~SRV_GOT_MASK;\n\t// srv_flags |=  SRV_GOT_SRV;\n\n\tstrcpy (svcnm, \"_\");\n\tstrcat (svcnm, service);\n\tstrcat (svcnm, \"._\");\n\tstrcat (svcnm, proto);\n\n\t// Note that SRV records are only defined for class IN\n\tif (domain) {\n\t\tnames.len=res_querydomain (svcnm, domain,\n\t\t\t\tC_IN, T_SRV,\n\t\t\t\tnames.buf, PACKETSZ);\n\t} else {\n\t\tnames.len=res_query (svcnm,\n\t\t\t\tC_IN, T_SRV,\n\t\t\t\tnames.buf, PACKETSZ);\n\t}\n\tif (names.len < 0) {\n\t\treturn -ENOENT;\n\t}\n\tnameshdr=(HEADER *) names.buf;\n\there=names.buf + HFIXEDSZ;\n\trnd=nameshdr->id; \t// Heck, gimme one reason why not!\n\n\tif ((names.len < HFIXEDSZ) || nameshdr->tc) {\n\t\treturn -EMSGSIZE;\n\t}\n\tswitch (nameshdr->rcode) {\n\t\tcase 1:\n\t\t\treturn -EFAULT;\n\t\tcase 2:\n\t\t\treturn -EAGAIN;\n\t\tcase 3:\n\t\t\treturn -ENOENT;\n\t\tcase 4:\n\t\t\treturn -ENOSYS;\n\t\tcase 5:\n\t\t\treturn -EPERM;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\tif (ntohs (nameshdr->ancount) == 0) {\n\t\treturn -ENOENT;\n\t}\n\tif (ntohs (nameshdr->ancount) > MAXNUM_SRV) {\n\t\treturn -ERANGE;\n\t}\n\tfor (ctr=ntohs (nameshdr->qdcount); ctr>0; ctr--) {\n\t\tint strlen=dn_skipname (here, names.buf+names.len);\n\t\there += strlen + QFIXEDSZ;\n\t}\n\tfor (ctr=ntohs (nameshdr->ancount); ctr>0; ctr--) {\n\t\tint strlen=dn_skipname (here, names.buf+names.len);\n\t\there += strlen;\n\t\tsrv [num_srv++] = here;\n\t\there += SRV_FIXEDSZ;\n\t\there += dn_skipname (here, names.buf+names.len);\n\t}\n\t\n\t// Overwrite weight with rnd-spread version to divide load over weights\n\tfor (ctr=0; ctr<num_srv; ctr++) {\n\t\t*(unsigned short *) (srv [ctr]+SRV_WEIGHT)\n\t\t\t= htons(rnd % (1+ns_get16 (srv [ctr]+SRV_WEIGHT)));\n\t}\n\tqsort (srv, num_srv, sizeof (*srv), srvcmp);\n\t\n\tresult.clear();\n\tfor (ctr=0; ctr<num_srv; ctr++) {\n\t\tname srvname;\n\t\tif (ns_name_ntop (srv [ctr]+SRV_SERVER, srvname, MAXDNAME) < 0) {\n\t\t\treturn -errno;\n\t\t}\n\t\t\n\t\tt_dns_result r;\n\t\tr.hostname = srvname;\n\t\tr.port = ns_get16(srv [ctr]+SRV_PORT);\n\t\tresult.push_back(r);\n\t}\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sockets/dnssrv.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _DNSSRV_H\n#define _DNSSRV_H\n\n#include <list>\n#include <string>\n\nusing namespace std;\n\ntypedef struct {\n\tstring\t\thostname;\n\tunsigned short\tport;\n} t_dns_result;\n\nvoid insrv_init (unsigned long flags);\nint insrv_lookup (const char *service, const char *proto, const char *domain, \n\tlist<t_dns_result> &result);\n\n#endif\n"
  },
  {
    "path": "src/sockets/interfaces.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstring>\n\n#include \"interfaces.h\"\n#include \"url.h\"\n\nusing namespace std;\n\nt_interface::t_interface(string _name) : name(_name) {}\n\nstring t_interface::get_ip_addr(void) const {\n\treturn inet_ntoa(address);\n}\n\nstring t_interface::get_ip_netmask(void) const {\n\treturn inet_ntoa(netmask);\n}\n\nlist <t_interface> *get_interfaces(bool include_loopback) {\n    \tstruct ifaddrs *ifa, *ifaddrs;\n    \tstruct sockaddr_in *sin;\n\tt_interface *intf;\n\n\tlist<t_interface> *result = new list<t_interface>;\n\n    \tif (getifaddrs(&ifaddrs)) {\n\t\t// No interfaces found\n\t\treturn result;\n    \t}\n\n    \tfor (ifa = ifaddrs; ifa ; ifa = ifa -> ifa_next) {\n\t\t// Skip interface without address\n\t\t// Skip interfaces marked DOWN and LOOPBACK.\n\t\tif (ifa->ifa_addr == NULL || !(ifa->ifa_flags & IFF_UP) ||\n\t    \t    ((ifa->ifa_flags & IFF_LOOPBACK) && !include_loopback)) {\n\t    \t\tcontinue;\n\t\t}\n\n\t\t// Add the interface to the list if it has an IP4 address\n\t\tswitch(ifa->ifa_addr->sa_family) {\n\t    \tcase AF_INET:\n\t\t\tintf = new t_interface(ifa->ifa_name);\n\t\t\tsin = (struct sockaddr_in *)ifa->ifa_addr;\n\t\t\tmemcpy(&intf->address, &sin->sin_addr,\n\t\t\t       sizeof(struct in_addr));\n\t\t\tsin = (struct sockaddr_in *)ifa->ifa_netmask;\n\t\t\tmemcpy(&intf->netmask, &sin->sin_addr,\n\t\t    \t       sizeof(struct in_addr));\n\n\t\t\tresult->push_back(*intf);\n\t\t\tdelete intf;\n\t\t\tbreak;\n\t\t}\n    \t}\n\n    \tfreeifaddrs(ifaddrs);\n\n\treturn result;\n}\n\nbool exists_interface(const string &hostname) {\n\tstruct hostent *h;\n\n\th = gethostbyname(hostname.c_str());\n\tif (h == NULL) return false;\n\tstring ipaddr = inet_ntoa(*((struct in_addr *)h->h_addr));\n\n\tlist<t_interface> *l = get_interfaces(true);\n\t\n\tfor (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {\n\t\tif (i->get_ip_addr() == ipaddr) {\n\t\t\tdelete l;\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tdelete l;\n\treturn false;\n}\n\n\n\nbool exists_interface_dev(const string &devname, string &ip_address) {\n\n\tlist<t_interface> *l = get_interfaces(true);\n\t\n\tfor (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {\n\t  if (i->name == devname) {\n\t    ip_address = i->get_ip_addr();\n\t    delete l;\n\t    return true;\n\t  }\n\t}\n\t\n\tdelete l;\n\treturn false;\n}\n"
  },
  {
    "path": "src/sockets/interfaces.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_INTERFACES\n#define _H_INTERFACES\n\n#include <list>\n#include <string>\n#include <netdb.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <ifaddrs.h>\n\nusing namespace std;\n\nclass t_interface {\npublic:\n\tstring name;\t\t// interface name, eg. eth0\n\tstruct in_addr address;\t// interface IP address\n\tstruct in_addr netmask; // interface netmask\n\n\tt_interface(string _name);\n\n\t// Get string representation of IP address\n\tstring get_ip_addr(void) const;\n\tstring get_ip_netmask(void) const;\n};\n\n// Return a list of all interfaces that are UP\n// If include_loopback == true, then the loopback interface is returned as well.\nlist<t_interface> *get_interfaces(bool include_loopback = false);\n\n// Check if an interface with a certain IP address exists\nbool exists_interface(const string &hostname);\n\n// Check if an interface exists and return its IP address\nbool exists_interface_dev(const string &devname, string &ip_address);\n\n#endif\n"
  },
  {
    "path": "src/sockets/ipaddr.h",
    "content": "#ifndef _H_IPADDR\n#define _H_IPADDR\n\n// IP address in host byte order\ntypedef unsigned long IPaddr;\n// IP address in network byte order\ntypedef unsigned long IPNaddr;\n\n#endif\n"
  },
  {
    "path": "src/sockets/socket.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdio>\n#include <cerrno>\n#include <cstring>\n#include <sys/un.h>\n#include \"twinkle_config.h\"\n#include \"socket.h\"\n#include \"audits/memman.h\"\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n\n#ifdef HAVE_LINUX_TYPES_H\n#include <linux/types.h>\n#endif\n\n#ifdef HAVE_LINUX_ERRQUEUE_H\n#include <linux/errqueue.h>\n#endif\n\n/////////////////\n// t_icmp_msg\n/////////////////\n\nt_icmp_msg::t_icmp_msg(short _type, short _code, IPaddr _icmp_src_ipaddr,\n\tIPaddr _ipaddr, unsigned short _port) :\n\t\ttype(_type), code(_code), icmp_src_ipaddr(_icmp_src_ipaddr),\n\t\tipaddr(_ipaddr), port(_port)\n{}\n\n/////////////////\n// t_socket\n/////////////////\n\nt_socket::~t_socket() {\n\tclose(sd);\n}\n\nt_socket::t_socket() : sd(0)\n{}\n\nt_socket::t_socket(int _sd) : sd(_sd)\n{}\n\nint t_socket::get_descriptor(void) const {\n\treturn sd;\n}\n\nint t_socket::getsockopt(int level, int optname, void *optval, socklen_t *optlen) {\n\treturn ::getsockopt(sd, level, optname, optval, optlen);\n}\n\nint t_socket::setsockopt(int level, int optname, const void *optval, socklen_t optlen) {\n\treturn ::setsockopt(sd, level, optname, optval, optlen);\n}\n\n/////////////////\n// t_socket_udp\n/////////////////\n\n\nt_socket_udp::t_socket_udp() {\n\tstruct sockaddr_in addr;\n\tint ret;\n\n\tsd = socket(AF_INET, SOCK_DGRAM, 0);\n\tif (sd < 0) throw errno;\n\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(INADDR_ANY);\n\taddr.sin_port = htons(0);\n\tret = ::bind(sd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) throw errno;\n}\n\nt_socket_udp::t_socket_udp(unsigned short port) {\n\tstruct sockaddr_in addr;\n\tint ret;\n\n\tsd = socket(AF_INET, SOCK_DGRAM, 0);\n\tif (sd < 0) throw errno;\n\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(INADDR_ANY);\n\taddr.sin_port = htons(port);\n\tret = ::bind(sd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) throw errno;\n}\n\nint t_socket_udp::connect(IPaddr dest_addr, unsigned short dest_port) {\n\tstruct sockaddr_in addr;\n\tint ret;\n\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(dest_addr);\n\taddr.sin_port = htons(dest_port);\n\tret = ::connect(sd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) throw errno;\n\t\n\treturn ret;\n}\n\nint t_socket_udp::sendto(IPaddr dest_addr, unsigned short dest_port,\n\t           const char *data, int data_size) {\n\tstruct sockaddr_in addr;\n\tint ret;\n\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(dest_addr);\n\taddr.sin_port = htons(dest_port);\n\tret = ::sendto(sd, data, data_size, 0,\n\t\t     (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) throw errno;\n\n\treturn ret;\n}\n\nssize_t t_socket_udp::send(const void *data, int data_size) {\n\tint ret = ::send(sd, data, data_size, 0);\n\tif (ret < 0) throw errno;\n\n\treturn ret;\n}\n\nint t_socket_udp::recvfrom(IPaddr &src_addr, unsigned short &src_port,\n\t\t     char *buf, int buf_size) {\n\tstruct sockaddr_in addr;\n\tint ret, len_addr;\n\n\tlen_addr = sizeof(addr);\n\tmemset(buf, 0, buf_size);\n\tret = ::recvfrom(sd, buf, buf_size - 1, 0,\n\t\t       (struct sockaddr *)&addr, (socklen_t *)&len_addr);\n\tif (ret < 0) throw errno;\n\n\tsrc_addr = ntohl(addr.sin_addr.s_addr);\n\tsrc_port = ntohs(addr.sin_port);\n\n\treturn ret;\n}\n\nssize_t t_socket_udp::recv(void *buf, int buf_size) {\n\tint ret;\n\n\tmemset(buf, 0, buf_size);\n\tret = ::recv(sd, buf, buf_size - 1, 0);\n\tif (ret < 0) throw errno;\n\n\treturn ret;\n}\n\nbool t_socket_udp::select_read(unsigned long timeout) {\n\tfd_set fds;\n\tstruct timeval t;\n\t\n\tFD_ZERO(&fds);\n\tFD_SET(sd, &fds);\n\n\tt.tv_sec = timeout / 1000;\n\tt.tv_usec = (timeout % 1000) * 1000;\n\t\n\tint ret = select(sd + 1, &fds, NULL, NULL, &t);\n\t\n\tif (ret < 0) throw errno;\n\tif (ret == 0) return false;\n\treturn true;\n}\n\nbool t_socket_udp::enable_icmp(void) {\n#ifdef HAVE_LINUX_ERRQUEUE_H\n\tint enable = 1;\n\tint ret = setsockopt(SOL_IP, IP_RECVERR, &enable, sizeof(int));\n\tif (ret < 0) return false;\n\treturn true;\n#else\n\treturn false;\n#endif\n}\n\nbool t_socket_udp::get_icmp(t_icmp_msg &icmp) {\n#ifdef HAVE_LINUX_ERRQUEUE_H\n\tint ret;\n\tchar buf[256];\n\t\n\t// The destination address of the packet causing the ICMP\n\tstruct sockaddr dest_addr;\n\t\n\tstruct msghdr msgh;\n\tstruct cmsghdr *cmsg;\n\t\n\t// Initialize message header to receive the ancillary data for\n\t// an ICMP message.\n\tmemset(&msgh, 0, sizeof(struct msghdr));\n\tmsgh.msg_control = buf;\n\tmsgh.msg_controllen = 256;\n\tmsgh.msg_name = &dest_addr;\n\tmsgh.msg_namelen = sizeof(struct sockaddr);\n\t\n\t// Get error from the socket error queue\n\tret = recvmsg(sd, &msgh, MSG_ERRQUEUE);\n\tif (ret < 0) return false;\n\t\n\t// Find ICMP message in returned controll messages\n\tfor (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; \n\t     cmsg = CMSG_NXTHDR(&msgh, cmsg))\n\t{\n\t\tif (cmsg->cmsg_level == SOL_IP &&\n\t\t    cmsg->cmsg_type == IP_RECVERR)\n\t\t{\n\t\t\t// ICMP message found\n\t\t\tsock_extended_err *err = (sock_extended_err *)CMSG_DATA(cmsg);\n\t\t\ticmp.type = err->ee_type;\n\t\t\ticmp.code = err->ee_code;\n\t\t\t\n\t\t\t// Get IP address of host that has sent the ICMP error\n\t\t\tsockaddr *sa_src_icmp = SO_EE_OFFENDER(err);\n\t\t\tif (sa_src_icmp->sa_family == AF_INET) {\n\t\t\t\tsockaddr_in *addr = (sockaddr_in *)sa_src_icmp;\n\t\t\t\ticmp.icmp_src_ipaddr = ntohl(addr->sin_addr.s_addr);\n\t\t\t} else {\n\t\t\t\t// Non supported address type\n\t\t\t\ticmp.icmp_src_ipaddr = 0;\n\t\t\t}\n\t\t\t\n\t\t\t// Get destinnation address/port of packet causing the error.\n\t\t\tif (dest_addr.sa_family == AF_INET) {\n\t\t\t\tsockaddr_in *addr = (sockaddr_in *)&dest_addr;\n\t\t\t\ticmp.ipaddr = ntohl(addr->sin_addr.s_addr);\n\t\t\t\ticmp.port = ntohs(addr->sin_port);\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t// Non supported address type\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\treturn false;\n}\n\nstring h_ip2str(IPaddr ipaddr) {\n\tchar buf[16];\n\tIPNaddr x = htonl(ipaddr);\n\tunsigned char *ipbuf = (unsigned char *)&x;\n\n\tsnprintf(buf, 16, \"%u.%u.%u.%u\", ipbuf[0], ipbuf[1], ipbuf[2],\n\t\tipbuf[3]);\n\n\treturn string(buf);\n}\n\n/////////////////\n// t_socket_tcp\n/////////////////\n\nt_socket_tcp::t_socket_tcp() {\n\tsd = socket(AF_INET, SOCK_STREAM, 0);\n\tif (sd < 0) throw errno;\n}\n\nt_socket_tcp::t_socket_tcp(unsigned short port) {\n\tstruct sockaddr_in addr;\n\tint ret;\n\n\tsd = socket(AF_INET, SOCK_STREAM, 0);\n\tif (sd < 0) throw errno;\n\t\n\tint enable = 1;\n\t\n\t// Allow server to connect to the socket immediately (disable TIME_WAIT)\n\t(void)setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));\n\t\n\tenable = 1;\n\t\n\t// Disable Nagle algorithm\n\t(void)setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));\n\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(INADDR_ANY);\n\taddr.sin_port = htons(port);\n\tret = ::bind(sd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) throw errno;\n}\n\nt_socket_tcp::t_socket_tcp(int _sd) : t_socket(_sd)\n{}\n\nvoid t_socket_tcp::listen(int backlog) {\n\tint ret = ::listen(sd, backlog);\n\tif (ret < 0) throw errno;\n}\n\nt_socket_tcp *t_socket_tcp::accept(IPaddr &src_addr, unsigned short &src_port) {\n\tstruct sockaddr_in addr;\n\tsocklen_t socklen = sizeof(addr);\n\tint ret = ::accept(sd, (struct sockaddr *)&addr, &socklen);\n\tif (ret < 0) throw errno;\n\t\n\tsrc_addr = ntohl(addr.sin_addr.s_addr);\n\tsrc_port = ntohs(addr.sin_port);\n\t\n\tt_socket_tcp *sock = new t_socket_tcp(ret);\n\tMEMMAN_NEW(sock);\n\treturn sock;\n}\n\nvoid t_socket_tcp::connect(IPaddr dest_addr, unsigned short dest_port) {\n\tstruct sockaddr_in addr;\n\tint ret;\n\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(dest_addr);\n\taddr.sin_port = htons(dest_port);\n\tret = ::connect(sd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) throw errno;\n}\n\nssize_t t_socket_tcp::send(const void *data, int data_size) {\n\tssize_t ret = ::send(sd, data, data_size, 0);\n\tif (ret < 0) throw errno;\n\n\treturn ret;\n}\n\nssize_t t_socket_tcp::recv(void *buf, int buf_size) {\n\tssize_t ret;\n\n\tret = ::recv(sd, buf, buf_size, 0);\n\tif (ret < 0) throw errno;\n\n\treturn ret;\n}\n\nvoid t_socket_tcp::get_remote_address(IPaddr &remote_addr, unsigned short &remote_port) {\n\tstruct sockaddr_in addr;\n\tsocklen_t namelen = sizeof(addr);\n\t\n\tint ret = getpeername(sd, (struct sockaddr *)&addr, &namelen);\n\tif (ret < 0) throw errno;\n\tif (addr.sin_family != AF_INET) throw EBADF;\n\t\n\tremote_addr = ntohl(addr.sin_addr.s_addr);\n\tremote_port = ntohs(addr.sin_port);\n};\n\n/////////////////\n// t_socket_local\n/////////////////\n\nt_socket_local::t_socket_local() {\n\tsd = socket(PF_LOCAL, SOCK_STREAM, 0);\n\tif (sd < 0) throw errno;\n}\n\nt_socket_local::t_socket_local(int _sd) {\n\tsd = _sd;\n}\n\nvoid t_socket_local::bind(const string &name) {\n\tint ret;\n\tstruct sockaddr_un sockname;\n\t\n\t// A name for a local socket can be at most 108 characters\n\t// including NULL at end of string.\n\tif (name.size() > 107) {\n\t\tthrow ENAMETOOLONG;\n\t}\n\t\n\tsockname.sun_family = AF_LOCAL;\n\tstrcpy(sockname.sun_path, name.c_str());\n\tret = ::bind(sd, (struct sockaddr *)&sockname, SUN_LEN(&sockname));\n\tif (ret < 0) throw errno;\n}\n\nvoid t_socket_local::listen(int backlog) {\n\tint ret;\n\tret = ::listen(sd, backlog);\n\tif (ret < 0) throw errno;\n}\n\nint t_socket_local::accept(void) {\n\tint ret;\n\tret = ::accept(sd, NULL, 0);\n\tif (ret < 0) throw errno;\n\treturn ret;\n}\n\nvoid t_socket_local::connect(const string &name) {\n\tint ret;\n\tstruct sockaddr_un sockname;\n\n\t// A name for a local socket can be at most 108 characters\n\t// including NULL at end of string.\n\tif (name.size() > 107) {\n\t\tthrow ENAMETOOLONG;\n\t}\n\t\n\tsockname.sun_family = AF_LOCAL;\n\tstrcpy(sockname.sun_path, name.c_str());\n\tret = ::connect(sd, (struct sockaddr *)&sockname, SUN_LEN(&sockname));\n\tif (ret < 0) throw errno;\n}\n\nint t_socket_local::read(void *buf, int count) {\n\tint ret;\n\t\n\tret = ::read(sd, buf, count);\n\tif (ret < 0) throw errno;\n\treturn ret;\n}\n\nssize_t t_socket_local::recv(void *buf, int buf_size) {\n\treturn read(buf, buf_size);\n}\n\nint t_socket_local::write(const void *buf, int count) {\n\tint ret;\n\t\n\tret = ::write(sd, buf, count);\n\tif (ret < 0) throw errno;\n\treturn ret;\t\n}\n\nssize_t t_socket_local::send(const void *buf, int count) {\n\treturn write(buf, count);\n}\n"
  },
  {
    "path": "src/sockets/socket.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/** \n * @file\n * Socket operations\n */\n\n#ifndef _H_SOCKET\n#define _H_SOCKET\n\n#include <string>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <arpa/inet.h>\n\n#include \"sockets/ipaddr.h\"\n\nusing namespace std;\n\n// ports and addresses should be in host order\n\n/** ICMP message */\nclass t_icmp_msg {\npublic:\n\tshort\t\ttype;\n\tshort\t\tcode;\n\t\n\t// ICMP source IP address\n\tIPaddr\t\ticmp_src_ipaddr;\n\t\n\t// Destination IP address/port of packet causing the ICMP message.\n\tIPaddr\t\tipaddr;\n\tunsigned short\tport;\n\t\n\tt_icmp_msg() {};\n\tt_icmp_msg(short _type, short _code, IPaddr _icmp_src_ipaddr,\n\t\tIPaddr _ipaddr, unsigned short _port);\n};\n\n/** Abstract socket */\nclass t_socket {\nprotected:\n\tint\tsd; /**< Socket descriptor. */\n\t\n\t/**\n\t * Constructor. This constructor does not create a valid socket.\n\t * The subclasses will create the real socket.\n\t */\n\tt_socket();\n\n\t/** \n\t * Constructor.\n\t * @param _sd Socket desciptor.\n\t */\n\tt_socket(int _sd);\n\t\npublic:\n\t/** Destructor */\n\tvirtual ~t_socket();\n\t\n\t/**\n\t * Get the socket descriptor.\n\t * @return The socket descriptor.\n\t */\n\tint get_descriptor(void) const;\n\t\n\t/** Get socket options */\n\tint getsockopt(int level, int optname, void *optval, socklen_t *optlen);\n\t\n\t/** Set socket options */\n\tint setsockopt(int level, int optname, const void *optval, socklen_t optlen);\n\t\n\t/** Receive data */\n\tvirtual ssize_t recv(void *buf, int buf_size) = 0;\n\t\n\t/** Send data */\n\tvirtual ssize_t send(const void *data, int data_size) = 0;\n};\n\n/** UDP socket */\nclass t_socket_udp : public t_socket {\npublic:\n\t// Create a socket and bind it to any port.\n\t// Throws an int exception if it fails. The int thrown is the value\n\t// of errno as set by 'socket' or 'bind'.\n\tt_socket_udp();\n\n\t// Create a socket and bind it to port.\n\t// Throws an int exception if it fails. The int thrown is the value\n\t// of errno as set by 'socket' or 'bind'.\n\tt_socket_udp(unsigned short port);\n\t\n\t// Connect a socket\n\t// Throws an int exception if it fails (errno as set by 'sendto')\n\tint connect(IPaddr dest_addr, unsigned short dest_port);\n\n\t// Throws an int exception if it fails (errno as set by 'sendto')\n\tint sendto(IPaddr dest_addr, unsigned short dest_port,\n\t           const char *data, int data_size);\n\tvirtual ssize_t send(const void *data, int data_size);\n\n\t// Throws an int exception if it fails (errno as set by 'recvfrom')\n\t// On success the length of the data in buf is returned. After the\n\t// data in buf there will be a 0.\n\tint recvfrom(IPaddr &src_addr, unsigned short &src_port,\n\t\t     char *buf, int buf_size);\n\t\n\tvirtual ssize_t recv(void *buf, int buf_size);\n\t\n\t// Do a select on the socket in read mode. timeout is in ms.\n\t// Returns true if the socket becomes unblocked. Returns false\n\t// on time out. Throws an int exception if select fails\n\t// (errno as set by 'select')\n\tbool select_read(unsigned long timeout);\n\t\n\t// Enable reception of ICMP errors on this socket.\n\t// Returns false if ICMP reception cannot be enabled.\n\tbool enable_icmp(void);\n\t\n\t// Get an ICMP message that was received on this socket.\n\t// Returns false if no ICMP message can be retrieved.\n\tbool get_icmp(t_icmp_msg &icmp);\n};\n\n/** TCP socket */\nclass t_socket_tcp : public t_socket {\npublic:\n\t/** \n\t * Constructor. Create a socket. \n\t * @throw int The errno value\n\t */\n\tt_socket_tcp();\n\t\n\t/** \n\t * Constructor. Create a socket and bind it to a port.\n\t * @throw int The errno value\n\t */\n\tt_socket_tcp(unsigned short port);\n\t\n\t/**\n\t * Constructor. Create a socket from an existing descriptor.\n\t */\n\tt_socket_tcp(int _sd);\n\t\n\t/**\n\t * Listen for a connection.\n\t * @throw int Errno\n\t */\n\tvoid listen(int backlog);\n\t \n\t\n\t/** \n\t * Accept a connection \n\t * @param src_addr [out] Source IPv4 address of the connection.\n\t * @param src_port [out] Source port of the connection.\n\t * @return A socket for the new connection\n\t * @throw int Errno\n\t */\n\tt_socket_tcp *accept(IPaddr &src_addr, unsigned short &src_port);\n\t\n\t/**\n\t * Connect to a destination\n\t * @param dest_addr [in] Destination IPv4 address.\n\t * @param dest_port [in] Destination port.\n\t * @throw int Errno\n\t */\n\tvoid connect(IPaddr dest_addr, unsigned short dest_port);\n\t\n\t/** Send data */\n\tvirtual ssize_t send(const void *data, int data_size);\n\t\n\t\n\t/** Receive data */\n\tvirtual ssize_t recv(void *buf, int buf_size);\n\t\n\t/**\n\t * Get the remote address of a connection.\n\t * @param remote_addr [out] Source IPv4 address of the connection.\n\t * @param remote_port [out] Source port of the connection.\n\t * @throw int Errno\n\t */\n\tvoid get_remote_address(IPaddr &remote_addr, unsigned short &remote_port);\n};\n\n/** Local socket */\nclass t_socket_local : public t_socket {\t\npublic:\n\t// Throws an int exception if it fails. The int thrown is the value\n\t// of errno as set by 'socket'\n\tt_socket_local();\n\t\n\tt_socket_local(int _sd);\n\t\n\tvoid bind(const string &name);\n\tvoid listen(int backlog);\n\tint accept(void);\n\tvoid connect(const string &name);\n\tint read(void *buf, int count);\n\tvirtual ssize_t recv(void *buf, int buf_size);\n\tint write(const void *buf, int count);\n\tvirtual ssize_t send(const void *buf, int count);\n};\n\n// Convert an IP address in host order to a string.\nstring h_ip2str(IPaddr ipaddr);\n\n#endif\n"
  },
  {
    "path": "src/sockets/url.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstdlib>\n#include <cstring>\n#include <iostream>\n#include <netdb.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include \"dnssrv.h\"\n#include \"log.h\"\n#include \"socket.h\"\n#include \"url.h\"\n#include \"user.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nunsigned short get_default_port(const string &protocol) {\n\tif (protocol == \"mailto\")\treturn 25;\n\tif (protocol == \"http\")\t\treturn 80;\n\tif (protocol == \"sip\")\t\treturn 5060;\n\tif (protocol == \"sips\")\t\treturn 5061;\n\tif (protocol == \"stun\")\t\treturn 3478;\n\n\treturn 0;\n}\n\nIPaddr gethostbyname(const string &name) {\n\tstruct hostent *h;\n\t\n\th = gethostbyname(name.c_str());\n\tif (h == NULL) return 0;\n\treturn ntohl(*((IPNaddr *)h->h_addr));\n}\n\nlist<IPaddr> gethostbyname_all(const string &name) {\n\tstruct hostent *h;\n\tlist<IPaddr> l;\n\t\n\th = gethostbyname(name.c_str());\n\tif (h == NULL) return l;\n\t\n\tchar **ipaddr = h->h_addr_list;\n\twhile (*ipaddr) {\n\t\tl.push_back(ntohl(*((IPNaddr *)(*ipaddr))));\n\t\tipaddr++;\n\t}\n\t\n\treturn l;\n}\n\nstring get_local_hostname(void) {\n\tchar name[256];\n\tint rc = gethostname(name, 256);\n\t\n\tif (rc < 0) {\n\t\treturn \"localhost\";\n\t}\n\t\n\tstruct hostent *h = gethostbyname(name);\n\t\n\tif (h == NULL) {\n\t\treturn \"localhost\";\n\t}\n\t\n\treturn h->h_name;\n}\n\nIPaddr get_src_ip4_address_for_dst(IPaddr dst_ip4) {\n\tstring log_msg;\n\tstruct sockaddr_in addr;\n\tint ret;\n\t\n\t// Create UDP socket\n\tint sd = socket(AF_INET, SOCK_DGRAM, 0);\n\tif (sd < 0) {\n\t\tstring err = get_error_str(errno);\n\t\tlog_msg = \"Cannot create socket: \";\n\t\tlog_msg += err;\n\t\tlog_file->write_report(log_msg, \"::get_src_ip4_address_for_dst\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\treturn 0;\n\t}\n\n\tmemset(&addr, 0, sizeof(addr));\n\taddr.sin_family = AF_INET;\n\taddr.sin_addr.s_addr = htonl(dst_ip4);\n\taddr.sin_port = htons(5060);\n\n\t// Connect to the destination. Note that no network traffic\n\t// is sent out as this is a UDP socket. The routing engine\n\t// will set the correct source address however.\n\tret = connect(sd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) {\n\t\tstring err = get_error_str(errno);\n\t\tlog_msg = \"Cannot connect socket: \";\n\t\tlog_msg += err;\n\t\tlog_file->write_report(log_msg, \"::get_src_ip4_address_for_dst\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\n\t\tclose(sd);\n\t\treturn 0;\n\t}\n\n\t// Get source address of socket\n\tmemset(&addr, 0, sizeof(addr));\n\tsocklen_t len_addr = sizeof(addr);\n\tret = getsockname(sd, (struct sockaddr *)&addr, &len_addr);\n\tif (ret < 0) {\n\t\tstring err = get_error_str(errno);\n\t\tlog_msg = \"Cannot get sockname: \";\n\t\tlog_msg += err;\n\t\tlog_file->write_report(log_msg, \"::get_src_ip4_address_for_dst\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\n\t\tclose(sd);\n\t\treturn 0;\n\t}\n\n\tclose(sd);\n\treturn ntohl(addr.sin_addr.s_addr);\n}\n\nstring display_and_url2str(const string &display, const string &url) {\n\tstring s;\n\t\n\tif (!display.empty()) {\n\t\tif (must_quote(display)) s += '\"';\n\t\ts += display;\n\t\tif (must_quote(display)) s += '\"';\n\t\ts += \" <\";\n\t}\n\t\n\ts += url;\n\t\n\tif (!display.empty()) s += '>';\n\t\n\treturn s;\n}\n\n// t_ip_port\n\nt_ip_port::t_ip_port(IPaddr _ipaddr, unsigned short _port) :\n\ttransport(\"udp\"), ipaddr(_ipaddr), port(_port) {}\n\t\nt_ip_port::t_ip_port(const string &proto, IPaddr _ipaddr, unsigned short _port) :\n\ttransport(proto), ipaddr(_ipaddr), port(_port) {}\n\nvoid t_ip_port::clear(void) {\n\ttransport.clear();\n\tipaddr = 0;\n\tport = 0;\n}\n\nbool t_ip_port::is_null(void) const {\n\treturn (ipaddr == 0 && port == 0);\n}\n\nbool t_ip_port::operator==(const t_ip_port &other) const {\n\treturn (transport == other.transport &&\n\t        ipaddr == other.ipaddr &&\n\t        port == other.port);\n}\n\nbool t_ip_port::operator!=(const t_ip_port &other) const {\n\treturn !operator==(other);\n}\n\nstring t_ip_port::tostring(void) const {\n\tstring s;\n\ts = transport;\n\ts += \":\";\n\ts += h_ip2str(ipaddr);\n\ts += \":\";\n\ts += int2str(port);\n\t\n\treturn s;\n}\n\n// Private\n\nvoid t_url::construct_user_url(const string &s) {\n\tstring::size_type i;\n\tstring r;\n\n\t// Determine user/password (both are optional)\n\ti = s.find('@');\n\tif (i != string::npos) {\n\t\tif (i == 0 || i == s.size()-1) return;\n\t\tstring userpass = s.substr(0, i);\n\t\tr = s.substr(i+1);\n\t\ti = userpass.find(':');\n\t\tif (i != string::npos) {\n\t\t\tif (i == 0 || i == userpass.size()-1) return;\n\t\t\tuser = unescape_hex(userpass.substr(0, i));\n\t\t\tpassword = unescape_hex(userpass.substr(i+1));\n\t\t\t\n\t\t\tif (escape_passwd_value(password) != password) {\n\t\t\t\tmodified = true;\n\t\t\t}\n\t\t} else {\n\t\t\tuser = unescape_hex(userpass);\n\t\t}\n\t\t\n\t\t// Set modified flag if user contains reserved symbols.\n\t\t// This enforces escaping these symbols when the url gets\n\t\t// encoded.\n\t\tif (escape_user_value(user) != user) {\n\t\t\tmodified = true;\n\t\t}\n\t} else {\n\t\tr = s;\n\t}\n\n\t// Determine host/port\n\tstring hostport;\n\n\ti = r.find_first_of(\";?\");\n\tif (i != string::npos) {\n\t\thostport = r.substr(0, i);\n\t\tif (!parse_params_headers(r.substr(i))) return;\n\t} else {\n\t\thostport = r;\n\t}\n\t\n\tif (hostport.empty()) return;\n\t\n\tif (hostport.at(0) == '[') {\n\t\t// Host contains an IPv6 reference\n\t\ti = hostport.find(']');\n\t\tif (i == string::npos) return;\n\t\t// TODO: check format of an IPv6 address\n\t\thost = hostport.substr(0, i+1);\n\t\tif (i < hostport.size()-1) {\n\t\t\tif (hostport.at(i+1) != ':') return; // wrong port separator\n\t\t\tif (i+1 == hostport.size()-1) return; // port missing\n\t\t\tunsigned long p = atol(hostport.substr(i+2).c_str());\n\t\t\tif (p > 65535) return; // illegal port value\n\t\t\tport = (unsigned short)p;\n\t\t}\n\t} else {\n\t\t// Host contains a host name or IPv4 address\n\t\ti = hostport.find(':');\n\t\tif (i != string::npos) {\n\t\t\tif (i == 0 || i == hostport.size()-1) return;\n\t\t\thost = hostport.substr(0, i);\n\t\t\tunsigned long p = atol(hostport.substr(i+1).c_str());\n\t\t\tif (p > 65535) return; // illegal port value\n\t\t\tport = (unsigned short)p;\n\t\t} else {\n\t\t\thost = hostport;\n\t\t}\n\t}\n\n\tuser_url = true;\n\tvalid = true;\n}\n\n\nvoid t_url::construct_machine_url(const string &s) {\n\tstring::size_type i;\n\n\t// Determine host\n\tstring hostport;\n\ti = s.find_first_of(\"/?;\");\n\tif ( i != string::npos) {\n\t\thostport = s.substr(0, i);\n\t\tif (!parse_params_headers(s.substr(i))) return;\n\t} else {\n\t\thostport = s;\n\t}\n\n\tif (hostport.empty()) return;\n\t\n\tif (hostport.at(0) == '[') {\n\t\t// Host contains an IPv6 reference\n\t\ti = hostport.find(']');\n\t\tif (i == string::npos) return;\n\t\t// TODO: check format of an IPv6 address\n\t\thost = hostport.substr(0, i+1);\n\t\tif (i < hostport.size()-1) {\n\t\t\tif (hostport.at(i+1) != ':') return; // wrong port separator\n\t\t\tif (i+1 == hostport.size()-1) return; // port missing\n\t\t\tunsigned long p = atol(hostport.substr(i+2).c_str());\n\t\t\tif (p > 65535) return; // illegal port value\n\t\t\tport = (unsigned short)p;\n\t\t}\n\t} else {\n\t\t// Host contains a host name or IPv4 address\n\t\ti = hostport.find(':');\n\t\tif (i != string::npos) {\n\t\t\tif (i == 0 || i == hostport.size()-1) return;\n\t\t\thost = hostport.substr(0, i);\n\t\t\tunsigned long p = atol(hostport.substr(i+1).c_str());\n\t\t\tif (p > 65535) return; // illegal port value\n\t\t\tport = (unsigned short)p;\n\t\t} else {\n\t\t\thost = hostport;\n\t\t}\n\t}\n\n\tuser_url = false;\n\tvalid = true;\n}\n\nbool t_url::parse_params_headers(const string &s) {\n\tstring param_str = \"\";\n\n\t// Find start of headers\n\t// Note: parameters will not contain / or ?-symbol\n\tstring::size_type header_start = s.find_first_of(\"/?\");\n\tif (header_start != string::npos) {\n\t\theaders = s.substr(header_start + 1);\n\n\t\tif (s[0] == ';') {\n\t\t\t// The first symbol of the parameter list is ;\n\t\t\t// Remove this.\n\t\t\tparam_str = s.substr(1, header_start - 1);\n\t\t}\n\t} else {\n\t\t// There are no headers\n\t\t// The first symbol of the parameter list is ;\n\t\t// Remove this.\n\t\tparam_str = s.substr(1);\n\t}\n\n\tif (param_str == \"\") return true;\n\n\t// Create a list of single parameters. Parameters are\n\t// seperated by semi-colons.\n\t// Note: parameters will not contain a semi-colon in the\n\t//       name or value.\n\tvector<string> param_lst = split(param_str, ';');\n\n\t// Parse the parameters\n\tfor (vector<string>::iterator i = param_lst.begin();\n\t     i != param_lst.end(); i++)\n\t{\n\t\tstring pname;\n\t\tstring pvalue;\n\n\t\tvector<string> param = split(*i, '=');\n\t\tif (param.size() > 2) return false;\n\n\t\tpname = tolower(unescape_hex(trim(param.front())));\n\t\tif (param.size() == 2) {\n\t\t\tpvalue = tolower(unescape_hex(trim(param.back())));\n\t\t}\n\n\t\tif (pname == \"transport\") {\n\t\t\ttransport = pvalue;\n\t\t} else if (pname == \"maddr\") {\n\t\t\tmaddr = pvalue;\n\t\t} else if (pname == \"lr\") {\n\t\t\tlr = true;\n\t\t} else if (pname == \"user\") {\n\t\t\tuser_param = pvalue;\n\t\t} else if (pname == \"method\") {\n\t\t\tmethod = pvalue;\n\t\t} else if (pname == \"ttl\") {\n\t\t\tttl = atoi(pvalue.c_str());\n\t\t} else {\n\t\t\tother_params += ';';\n\t\t\tother_params += *i;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n// Public static\n\nstring t_url::escape_user_value(const string &user_value) {\n\t// RFC 3261\n\t// user             =  1*( unreserved / escaped / user-unreserved )\n\t// user-unreserved  =  \"&\" / \"=\" / \"+\" / \"$\" / \",\" / \";\" / \"?\" / \"/\"\n\t// unreserved       =  alphanum / mark\n\t// mark             =  \"-\" / \"_\" / \".\" / \"!\" / \"~\" / \"*\" / \"'\" / \"(\" / \")\"\n\n\treturn escape_hex(user_value,\n\t\t\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\\\n\t\t\"-_.!~*'()&=+$,;?/\");\n}\n\nstring t_url::escape_passwd_value(const string &passwd_value) {\n\t// RFC 3261\n\t// password         = *( unreserved / escaped / \"&\" / \"=\" / \"+\" / \"$\" / \",\" )\n\t// unreserved       =  alphanum / mark\n\t// mark             =  \"-\" / \"_\" / \".\" / \"!\" / \"~\" / \"*\" / \"'\" / \"(\" / \")\"\n\t\n\treturn escape_hex(passwd_value,\n\t\t\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\\\n\t\t\"-_.!~*'()&=+$,\");\n}\n\nstring t_url::escape_hnv(const string &hnv) {\n\t// RFC 3261\n\t// hname           =  1*( hnv-unreserved / unreserved / escaped )\n\t// hvalue          =  *( hnv-unreserved / unreserved / escaped )\n\t// hnv-unreserved  =  \"[\" / \"]\" / \"/\" / \"?\" / \":\" / \"+\" / \"$\"\n\t// unreserved       =  alphanum / mark\n\t// mark             =  \"-\" / \"_\" / \".\" / \"!\" / \"~\" / \"*\" / \"'\" / \"(\" / \")\"\n\t\n\treturn escape_hex(hnv,\n\t\t\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\\\n\t\t\"-_.!~*'()[]/?:+$\");\n}\n\n// Public\n\nt_url::t_url(void) {\n\tmodified = false;\n\tvalid = false;\n\tport = 0;\n\tlr = false;\n\tttl = 0;\n}\n\nt_url::t_url(const string &s) {\n\tset_url(s);\n}\n\nt_url t_url::copy_without_headers(void) const {\n\tt_url u(*this);\n\tu.clear_headers();\n\treturn u;\n}\n\nvoid t_url::set_url(const string &s) {\n\tstring::size_type i;\n\tstring r;\n\n\tmodified = false;\n\tvalid = false;\n\tscheme.clear();\n\tuser.clear();\n\tpassword.clear();\n\thost.clear();\n\tport = 0;\n\ttransport.clear();\n\tmaddr.clear();\n\tlr = false;\n\tuser_param.clear();\n\tmethod.clear();\n\tttl = 0;\n\tother_params.clear();\n\theaders.clear();\n\tuser_url = false;\n\t\n\ttext_format = s;\n\n\t// Determine scheme. A scheme is mandatory. There should\n\t// be text following the scheme.\n\ti = s.find(':');\n\tif (i == string::npos || i == 0 || i == s.size()-1) return;\n\tscheme = tolower(s.substr(0, i));\n\tr = s.substr(i+1);\n\n\tif (r[0] == '/') {\n\t\tif (r.size() == 1) return;\n\t\tif (r[1] != '/') return;\n\t\tconstruct_machine_url(r.substr(2));\n\t} else {\n\t\tconstruct_user_url(r);\n\t}\n}\n\nstring t_url::get_scheme(void) const {\n\treturn scheme;\n}\n\nstring t_url::get_user(void) const {\n\treturn user;\n}\n\nstring t_url::get_password(void) const {\n\treturn password;\n}\n\nstring t_url::get_host(void) const {\n\treturn host;\n}\n\nint t_url::get_nport(void) const {\n\treturn htons(get_hport());\n}\n\nint t_url::get_hport(void) const {\n\tif (port != 0) return port;\n\treturn get_default_port(scheme);\n}\n\nint t_url::get_port(void) const {\n\treturn port;\n}\n\nIPNaddr t_url::get_n_ip(void) const {\n\tstruct hostent *h;\n\n\t// TODO: handle multiple A RR's\n\t\n\tif (scheme == \"tel\") return 0;\n\t\n\th = gethostbyname(host.c_str());\n\tif (h == NULL) return 0;\n\treturn *((IPNaddr *)h->h_addr);\n}\n\nIPaddr t_url::get_h_ip(void) const {\n\tif (scheme == \"tel\") return 0;\n\treturn gethostbyname(host);\n}\n\nlist<IPaddr> t_url::get_h_ip_all(void) const {\n\tif (scheme == \"tel\") return list<IPaddr>();\n\treturn gethostbyname_all(host);\n}\n\nstring t_url::get_ip(void) const {\n\tstruct hostent *h;\n\n\t// TODO: handle multiple A RR's\n\t\n\tif (scheme == \"tel\") return 0;\n\t\n\th = gethostbyname(host.c_str());\n\tif (h == NULL) return \"\";\n\treturn inet_ntoa(*((struct in_addr *)h->h_addr));\n}\n\nlist<t_ip_port> t_url::get_h_ip_srv(const string &transport) const {\n\tlist<t_ip_port> ip_list;\n\tlist<t_dns_result> srv_list;\n\tlist<IPaddr> ipaddr_list;\n\t\n\tif (scheme == \"tel\") return list<t_ip_port>();\n\t\t\n\t// RFC 3263 4.2\n\t// Only do an SRV lookup if host is a hostname and no port is specified.\n\tif (!is_ipaddr(host) && port == 0) {\n\t\tint ret = insrv_lookup(scheme.c_str(), transport.c_str(), \n\t\t\t\thost.c_str(), srv_list);\n\t\t\n\t\tif (ret >= 0 && !srv_list.empty()) {\n\t\t\t// SRV RR's found\n\t\t\tfor (list<t_dns_result>::iterator i = srv_list.begin();\n\t\t\ti != srv_list.end(); i++)\n\t\t\t{\n\t\t\t\t// Get A RR's\n\t\t\t\tt_ip_port ip_port;\n\t\t\t\tipaddr_list = gethostbyname_all(i->hostname);\n\t\t\t\tfor (list<IPaddr>::iterator j = ipaddr_list.begin();\n\t\t\t\t     j != ipaddr_list.end(); j++)\n\t\t\t\t{\n\t\t\t\t\tip_list.push_back(t_ip_port(transport, *j, i->port));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn ip_list;\n\t\t}\n\t}\n\t\n\t// No SRV RR's found, do an A RR lookup\n\tt_ip_port ip_port;\n\tipaddr_list = get_h_ip_all();\n\tfor (list<IPaddr>::iterator j = ipaddr_list.begin();\n\t\tj != ipaddr_list.end(); j++)\n\t{\n\t\tip_list.push_back(t_ip_port(transport, *j, get_hport()));\n\t}\n\t\n\treturn ip_list;\n}\n\nstring t_url::get_transport(void) const {\n\treturn transport;\n}\n\nstring t_url::get_maddr(void) const {\n\treturn maddr;\n}\n\nbool t_url::get_lr(void) const {\n\treturn lr;\n}\n\nstring t_url::get_user_param(void) const {\n\treturn user_param;\n}\n\nstring t_url::get_method(void) const {\n\treturn method;\n}\n\nint t_url::get_ttl(void) const {\n\treturn ttl;\n}\n\nstring t_url::get_other_params(void) const {\n\treturn other_params;\n}\n\nstring t_url::get_headers(void) const {\n\treturn headers;\n}\n\nvoid t_url::set_user(const string &u) {\n\tmodified = true;\n\tuser = u;\n}\n\nvoid t_url::set_host(const string &h) {\n\tmodified = true;\n\thost = h;\n}\n\nvoid t_url::add_header(const t_header &hdr) {\n\tif (!hdr.is_populated()) return;\n\t\n\tmodified = true;\n\t\n\tif (!headers.empty()) headers += ';';\n\theaders += escape_hnv(hdr.get_name());\n\theaders += '=';\n\theaders += escape_hnv(hdr.get_value());\n}\n\nvoid t_url::clear_headers(void) {\n\tif (headers.empty()) {\n\t\t// No headers to clear\n\t\treturn;\n\t}\n\t\n\tmodified = true;\n\theaders.clear();\n}\n\nbool t_url::is_valid(void) const {\n\treturn valid;\n}\n\n// RCF 3261 19.1.4\nbool t_url::sip_match(const t_url &u) const {\n\tif (!u.is_valid() || !is_valid()) return false;\n\n\t// Compare schemes\n\tif (scheme != \"sip\" && scheme != \"sips\") return false;\n\tif (u.get_scheme() != \"sip\" && u.get_scheme() != \"sips\") {\n\t\treturn false;\n\t}\n\tif (scheme != u.get_scheme()) return false;\n\n\t// Compare user info\n\tif (user != u.get_user()) return false;\n\tif (password != u.get_password()) return false;\n\n\t// Compare host/port\n\tif (cmp_nocase(host, u.get_host()) != 0) return false;\n\tif (port != u.get_port()) return false;\n\n\t// Compare parameters\n\tif (transport != \"\" && u.get_transport() != \"\" &&\n\t    cmp_nocase(transport, u.get_transport()) != 0)\n\t{\n\t\treturn false;\n\t}\n\n\tif (maddr != u.get_maddr()) return false;\n\tif (cmp_nocase(user_param, u.get_user_param()) != 0) return false;\n\tif (cmp_nocase(method, u.get_method()) != 0) return false;\n\tif (ttl != u.get_ttl()) return false;\n\n\t// TODO: compare other params and headers\n\n\treturn true;\n}\n\nbool t_url::operator==(const t_url &u) const {\n\treturn sip_match(u);\n}\n\nbool t_url::operator!=(const t_url &u) const {\n\treturn !sip_match(u);\n}\n\nbool t_url::user_host_match(const t_url &u, bool looks_like_phone, \n\t\tconst string &special_symbols) const\n{\n\tstring u1 = get_user();\n\tstring u2 = u.get_user();\n\t\n\t// For tel-URIs the phone number is in the host part.\n\tif (scheme == \"tel\") u1 = get_host();\n\tif (u.scheme == \"tel\") u2 = u.get_host();\n\t\n\tbool u1_is_phone = false;\n\tbool u2_is_phone = false;\n\t\n\tif (is_phone(looks_like_phone, special_symbols)) {\n\t\tu1 = remove_symbols(u1, special_symbols);\n\t\tu1_is_phone = true;\n\t}\n\t\n\tif (u.is_phone(looks_like_phone, special_symbols)) {\n\t\tu2 = remove_symbols(u2, special_symbols);\n\t\tu2_is_phone = true;\n\t}\n\t\n\tif (u1 != u2) return false;\n\t\n\tif (u1_is_phone && u2_is_phone) {\n\t\t// Both URLs are phone numbers. Do not compare\n\t\t// the host-part.\n\t\treturn true;\n\t}\n\t\n\treturn (get_host() == u.get_host());\n}\n\nbool t_url::user_looks_like_phone(const string &special_symbols) const {\n\treturn looks_like_phone(user, special_symbols);\n}\n\nbool t_url::is_phone(bool looks_like_phone, const string &special_symbols) const {\n\tif (scheme == \"tel\") return true;\n\n\t// RFC 3261 19.1.1\n\tif (user_param == \"phone\") return true;\n\treturn (looks_like_phone && user_looks_like_phone(special_symbols));\n}\n\nstring t_url::encode(void) const {\n\tif (modified) {\n\t\tif (!user_url) {\n\t\t\t// TODO: machine URL's are currently not used\n\t\t\treturn text_format;\n\t\t}\n\t\n\t\tstring s;\n\t\t\n\t\ts = scheme;\n\t\ts += ':';\n\t\t\n\t\tif (!user.empty()) {\n\t\t\ts += escape_user_value(user);\n\t\t\n\t\t\tif (!password.empty()) {\n\t\t\t\ts += ':';\n\t\t\t\ts += escape_passwd_value(password);\n\t\t\t}\n\t\t\t\n\t\t\ts += '@';\n\t\t}\n\t\t\n\t\ts += host;\n\t\t\n\t\tif (port > 0) {\n\t\t\ts += ':';\n\t\t\ts += int2str(port);\n\t\t}\n\t\t\n\t\tif (!transport.empty()) {\n\t\t\ts += \";transport=\";\n\t\t\ts += transport;\n\t\t}\n\t\n\t\tif (!maddr.empty()) {\n\t\t\ts += \";maddr=\";\n\t\t\ts += maddr;\n\t\t}\n\t\t\n\t\tif (lr) {\n\t\t\ts += \";lr\";\n\t\t}\n\t\t\n\t\tif (!user_param.empty()) {\n\t\t\ts += \";user=\";\n\t\t\ts += user_param;\n\t\t}\n\t\t\n\t\tif (!method.empty()) {\n\t\t\ts += \";method=\";\n\t\t\ts += method;\n\t\t}\n\t\t\n\t\tif (ttl > 0) {\n\t\t\ts += \";ttl=\";\n\t\t\ts += int2str(ttl);\n\t\t}\n\t\t\n\t\tif (!other_params.empty()) {\n\t\t\ts += other_params;\n\t\t}\n\t\t\n\t\tif (!headers.empty()) {\n\t\t\ts += \"?\";\n\t\t\ts += headers;\n\t\t}\n\t\t\n\t\treturn s;\n\t} else {\n\t\treturn text_format;\n\t}\n}\n\nstring t_url::encode_noscheme(void) const {\n\tstring s = encode();\n\tstring::size_type i = s.find(':');\n\n\tif (i != string::npos && i < s.size()) {\n\t\ts = s.substr(i + 1);\n\t}\n\n\treturn s;\n}\n\nstring t_url::encode_no_params_hdrs(bool escape) const {\n\tif (!user_url) {\n\t\t// TODO: machine URL's are currently not used\n\t\treturn text_format;\n\t}\n\n\tstring s;\n\t\n\ts = scheme;\n\ts += ':';\n\t\n\tif (!user.empty()) {\n\t\tif (escape) {\n\t\t\ts += escape_user_value(user);\n\t\t} else {\n\t\t\ts += user;\n\t\t}\n\t\t\n\t\tif (!password.empty()) {\n\t\t\ts += ':';\n\t\t\tif (escape) {\n\t\t\t\ts += escape_passwd_value(password);\n\t\t\t} else {\n\t\t\t\ts += password;\n\t\t\t}\n\t\t}\n\t\t\n\t\ts += '@';\n\t}\n\t\n\ts += host;\n\t\n\tif (port > 0) {\n\t\ts += ':';\n\t\ts += int2str(port);\n\t}\n\t\n\treturn s;\n}\n\nvoid t_url::apply_conversion_rules(t_user *user_config) {\n\tif (scheme == \"tel\") {\n\t\thost = user_config->convert_number(host);\n\t} else {\n\t\t// Convert user part for all other schemes\n\t\tuser = user_config->convert_number(user);\n\t}\n\t\n\tmodified = true;\n}\n\n\nt_display_url::t_display_url() {}\n\nt_display_url::t_display_url(const t_url &_url, const string &_display) :\n\turl(_url), display(_display) {}\n\t\nbool t_display_url::is_valid() {\n\treturn url.is_valid();\n}\n\t\nstring t_display_url::encode(void) const {\n\tstring s;\n\t\n\tif (!display.empty()) {\n\t\tif (must_quote(display)) s += '\"';\n\t\ts += display;\n\t\tif (must_quote(display)) s += '\"';\n\t\ts += \" <\";\n\t}\n\t\n\ts += url.encode();\n\t\n\tif (!display.empty()) s += '>';\n\t\n\treturn s;\n}\n"
  },
  {
    "path": "src/sockets/url.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_URL\n#define _H_URL\n\n#include <list>\n#include <string>\n#include \"parser/header.h\"\n#include \"sockets/ipaddr.h\"\n\n/** @name Forward declarations */\n//@{\nclass t_user;\n//@}\n\nusing namespace std;\n\nclass t_ip_port {\npublic:\n\tstring\t\t\ttransport;\n\tIPaddr\t\t\tipaddr;\n\tunsigned short\t\tport;\n\t\n\tt_ip_port() : transport(\"udp\") {};\n\tt_ip_port(IPaddr _ipaddr, unsigned short _port);\n\tt_ip_port(const string &proto, IPaddr _ipaddr, unsigned short _port);\n\t\n\tvoid clear(void);\n\tbool is_null(void) const;\n\t// NOTE: Equality is used only by t_phone_user::send_nat_keepalive(),\n\t// to avoid sending two keep-alives to the same host\n\tbool operator==(const t_ip_port &other) const;\n\tbool operator!=(const t_ip_port &other) const;\n\tstring tostring(void) const;\n};\n\n// Return the default port for a protocol (host order)\nunsigned short get_default_port(const string &protocol);\n\n// Return the first IP address of host name.\n// Return 0 if no IP address can be found.\nIPaddr gethostbyname(const string &name);\n\n// Return all IP address of host name\nlist<IPaddr> gethostbyname_all(const string &name);\n\n/**\n * Get local host name.\n * @return Local host name.\n */\nstring get_local_hostname(void);\n\n/**\n * Get the source IP address that will be used for sending\n * a packet to a certain destination.\n * @param dst_ip4 [in] The destination IPv4 address.\n * @return The source IPv4 address.\n * @return 0 if the source address cannot be determined.\n */\nIPaddr get_src_ip4_address_for_dst(IPaddr dst_ip4);\n\nclass t_url {\nprivate:\n\t/**\n\t * A t_url object is created with a string represnetation of\n\t * the URL. The encode method just returns this string.\n\t * If one of the components of the t_url object is modified\n\t * however, then encode will build a new string representation.\n\t * The modified flag indicates if the object was modified after\n\t * construction.\n\t */\n\tbool\t\tmodified;\n\n\t/** URL scheme. */\n\tstring\t\tscheme;\n\t\n\t/** The user part of a URL. For a tel URL this is empty. */\n\tstring\t\tuser;\n\t\n\t/** The user password. */\n\tstring\t\tpassword;\n\t\n\t/** \n\t * The host part of a URL. For a tel URL, it contains the part before\n\t * the first semi-colon.\n\t */\n\tstring\t\thost;\n\t\n\tunsigned short\tport; \t\t// host order\n\n\t// parameters\n\tstring\t\ttransport;\n\tstring\t\tmaddr;\n\tbool\t\tlr;\n\tstring\t\tuser_param;\n\tstring\t\tmethod;\n\tint\t\tttl;\n\tstring\t\tother_params;\t// unparsed other parameters\n\t\t\t\t\t// starting with a semi-colon\n\n\t// headers\n\tstring\t\theaders;\t// unparsed headers\n\n\tbool\t\tuser_url;\t// true -> user url\n\t\t\t\t\t// false -> machine\n\tbool\t\tvalid;\t\t// indicates if the url is valid\n\n\tstring\t\ttext_format;\t// url in string format\n\n\tvoid construct_user_url(const string &s);    // eg sip:, mailto:\n\tvoid construct_machine_url(const string &s); // eg http:, ftp:\n\n\t// Parse uri parameters and headers. Returns false if parsing\n\t// fails.\n\tbool parse_params_headers(const string &s);\n\t\npublic:\n\t// Escape reserved symbols in a user value\n\tstatic string escape_user_value(const string &user_value);\n\t\n\t// Escape reserved symbols in a password value\n\tstatic string escape_passwd_value(const string &passwd_value);\n\t\n\t// Escape reserved symbols in a header name or value\n\tstatic string escape_hnv(const string &hnv);\n\npublic:\n\tt_url();\n\tt_url(const string &s);\n\t\n\t// Return a copy of the URI without headers.\n\t// If the URI does not contain any headers, then the copy is\n\t// identical to the URI.\n\tt_url copy_without_headers(void) const;\n\n\tvoid set_url(const string &s);\n\n\t// Returns \"\" or 0 if item cannot be found\n\tstring get_scheme(void) const;\n\tstring get_user(void) const;\n\tstring get_password(void) const;\n\tstring get_host(void) const;\n\n\t// The following methods will return the default port if\n\t// no port is present in the url.\n\tint get_nport(void) const; // Get port in network order.\n\tint get_hport(void) const; // get port in host order.\n\n\t// The following method returns 0 if no port is present\n\t// in the url.\n\tint get_port(void) const;\n\n\t// ip address network order. Return 0 if address not found\n\t// DNS A RR lookup\n\tIPNaddr get_n_ip(void) const;\n\n\t// ip address host order. Return 0 if address not found\n\t// DNS A RR lookup\n\tIPaddr get_h_ip(void) const;\n\tlist<IPaddr> get_h_ip_all(void) const;\n\n\t// DNS A RR lookup\n\tstring get_ip(void) const; // ip address as string\n\t\n\t// Get list op IP address/ports in host order.\n\t// First do DNS SRV lookup. If no SRV RR's are found, then\n\t// do a DNS A RR lookup.\n\t// transport = the transport protocol for the service\n\tlist<t_ip_port> get_h_ip_srv(const string &transport) const;\n\t\n\t/** @name Getters */\n\t//@{\n\tstring get_transport(void) const;\n\tstring get_maddr(void) const;\n\tbool get_lr(void) const;\n\tstring get_user_param(void) const;\n\tstring get_method(void) const;\n\tint get_ttl(void) const;\n\tstring get_other_params(void) const;\n\tstring get_headers(void) const;\n\t//@}\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_user(const string &u);\n\tvoid set_host(const string &h);\n\t//@}\n\t\n\t/**\n\t * Add a header to the URI.\n\t * The encoded header will be concatenated to the headers field.\n\t * @param hdr [in] Header to be added.\n\t */\n\tvoid add_header(const t_header &hdr);\n\t\n\t/** Remove headers from the URI. */\n\tvoid clear_headers(void);\n\n\t/**\n\t * Check if the URI is valid.\n\t * @return True if valid, otherwise false.\n\t */\n\tbool is_valid(void) const;\n\n\t// Check if 2 sip or sips url's are equivalent\n\tbool sip_match(const t_url &u) const;\n\tbool operator==(const t_url &u) const;\n\tbool operator!=(const t_url &u) const;\n\t\n\t/**\n\t * Check if the user-host part of 2 url's are equal.\n\t * If the user-part is a phone number, then only compare\n\t * the user parts. If the url is a tel-url then the host\n\t * contains a telephone number for comparison.\n\t * @param u [in] Other URL to compare with.\n\t * @param looks_like_phone [in] Flag to indicate is a SIP URL\n\t *        that looks like a phone number must be treated as such.\n\t * @param special_symbols [in] Interpuction symbols in a phone number.\n\t * @return true if the URLs match, false otherwise.\n\t */\n\tbool user_host_match(const t_url &u, bool looks_like_phone, \n\t\tconst string &special_symbols) const;\n\n\t// Return true if the user part looks like a phone number, i.e.\n\t// consists of digits, *, # and special symbols\n\tbool user_looks_like_phone(const string &special_symbols) const;\n\n\t// Return true if the URI indicates a phone number, i.e.\n\t// - the user=phone parameter is present\n\t// or\n\t// - if looks_like_phone == true and the user part looks like\n\t//   a phone number\n\tbool is_phone(bool looks_like_phone, const string &special_symbols) const;\n\n\t// Return string encoding of url\n\tstring encode(void) const;\n\n\t// Return string encoding of url without scheme information\n\tstring encode_noscheme(void) const;\n\t\n\t// Return string encoding of url without parameters/headers\n\tstring encode_no_params_hdrs(bool escape = true) const;\n\t\n\t/**\n\t * Apply number conversion rules to modify the URL.\n\t * @param user_config [in] The user profile having the conversion\n\t *        rules to apply.\n\t */\n\tvoid apply_conversion_rules(t_user *user_config);\n};\n\n// Display name and url combined\n\nclass t_display_url {\npublic:\n\tt_url\t\turl;\n\tstring\t\tdisplay;\n\t\n\tt_display_url();\n\tt_display_url(const t_url &_url, const string &_display);\n\t\n\tbool is_valid();\n\tstring encode(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/stun/CMakeLists.txt",
    "content": "project(libtwinkle-stun)\n\nset(LIBTWINKLE_STUN-SRCS\n\tstun.cxx\n\tstun_transaction.cpp\n\tudp.cxx\n)\n\nadd_library(libtwinkle-stun OBJECT ${LIBTWINKLE_STUN-SRCS})\n"
  },
  {
    "path": "src/stun/stun.cxx",
    "content": "#include <cassert>\n#include <cstring>\n#include <iostream>\n#include <cstdlib>   \n#include <errno.h>\n\n#ifdef WIN32\n#include <winsock2.h>\n#include <stdlib.h>\n#include <io.h>\n#include <time.h>\n#else\n\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <sys/types.h> \n#include <arpa/inet.h>\n#include <fcntl.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <arpa/nameser.h>\n#include <resolv.h>\n#include <net/if.h>\n\n#endif\n\n\n#if defined(__sparc__) || defined(WIN32)\n#define NOSSL\n#endif\n#define NOSSL\n\n#include \"udp.h\"\n#include \"stun.h\"\n\n// Twinkle\n#include \"util.h\"\n#include \"sockets/socket.h\"\n#include \"audits/memman.h\"\n\n\nusing namespace std;\n\n\nstatic void\ncomputeHmac(char* hmac, const char* input, int length, const char* key, int keySize);\n\nstatic bool \nstunParseAtrAddress( char* body, unsigned int hdrLen,  StunAtrAddress4& result )\n{\n   if ( hdrLen != 8 )\n   {\n      clog << \"hdrLen wrong for Address\" <<endl;\n      return false;\n   }\n   result.pad = *body++;\n   result.family = *body++;\n   if (result.family == IPv4Family)\n   {\n      UInt16 nport;\n      memcpy(&nport, body, 2); body+=2;\n      result.ipv4.port = ntohs(nport);\n\t\t\n      UInt32 naddr;\n      memcpy(&naddr, body, 4); body+=4;\n      result.ipv4.addr = ntohl(naddr);\n      return true;\n   }\n   else if (result.family == IPv6Family)\n   {\n      clog << \"ipv6 not supported\" << endl;\n   }\n   else\n   {\n      clog << \"bad address family: \" << result.family << endl;\n   }\n\t\n   return false;\n}\n\nstatic bool \nstunParseAtrChangeRequest( char* body, unsigned int hdrLen,  StunAtrChangeRequest& result )\n{\n   if ( hdrLen != 4 )\n   {\n      clog << \"hdr length = \" << hdrLen << \" expecting \" << sizeof(result) << endl;\n\t\t\n      clog << \"Incorrect size for ChangeRequest\" << endl;\n      return false;\n   }\n   else\n   {\n      memcpy(&result.value, body, 4);\n      result.value = ntohl(result.value);\n      return true;\n   }\n}\n\nstatic bool \nstunParseAtrError( char* body, unsigned int hdrLen,  StunAtrError& result )\n{\n   if ( hdrLen >= sizeof(result) )\n   {\n      clog << \"head on Error too large\" << endl;\n      return false;\n   }\n   else\n   {\n      memcpy(&result.pad, body, 2); body+=2;\n      result.pad = ntohs(result.pad);\n      result.errorClass = *body++;\n      result.number = *body++;\n\t\t\n      result.sizeReason = hdrLen - 4;\n      memcpy(&result.reason, body, result.sizeReason);\n      result.reason[result.sizeReason] = 0;\n      return true;\n   }\n}\n\nstatic bool \nstunParseAtrUnknown( char* body, unsigned int hdrLen,  StunAtrUnknown& result )\n{\n   if ( hdrLen >= sizeof(result) )\n   {\n      return false;\n   }\n   else\n   {\n      if (hdrLen % 4 != 0) return false;\n      result.numAttributes = hdrLen / 4;\n      for (int i=0; i<result.numAttributes; i++)\n      {\n         memcpy(&result.attrType[i], body, 2); body+=2;\n         result.attrType[i] = ntohs(result.attrType[i]);\n      }\n      return true;\n   }\n}\n\n\nstatic bool \nstunParseAtrString( char* body, unsigned int hdrLen,  StunAtrString& result )\n{\n   if ( hdrLen >= STUN_MAX_STRING )\n   {\n      clog << \"String is too large\" << endl;\n      return false;\n   }\n   else\n   {\n      if (hdrLen % 4 != 0)\n      {\n         clog << \"Bad length string \" << hdrLen << endl;\n         return false;\n      }\n\t\t\n      result.sizeValue = hdrLen;\n      memcpy(&result.value, body, hdrLen);\n      result.value[hdrLen] = 0;\n      return true;\n   }\n}\n\n\nstatic bool \nstunParseAtrIntegrity( char* body, unsigned int hdrLen,  StunAtrIntegrity& result )\n{\n   if ( hdrLen != 20)\n   {\n      clog << \"MessageIntegrity must be 20 bytes\" << endl;\n      return false;\n   }\n   else\n   {\n      memcpy(&result.hash, body, hdrLen);\n      return true;\n   }\n}\n\n\nbool\nstunParseMessage( char* buf, unsigned int bufLen, StunMessage& msg, bool verbose)\n{\n   if (verbose) clog << \"Received stun message: \" << bufLen << \" bytes\" << endl;\n   memset(&msg, 0, sizeof(msg));\n\t\n   if (sizeof(StunMsgHdr) > bufLen)\n   {\n      return false;\n   }\n\t\n   memcpy(&msg.msgHdr, buf, sizeof(StunMsgHdr));\n   msg.msgHdr.msgType = ntohs(msg.msgHdr.msgType);\n   msg.msgHdr.msgLength = ntohs(msg.msgHdr.msgLength);\n\t\n   if (msg.msgHdr.msgLength + sizeof(StunMsgHdr) != bufLen)\n   {\n      clog << \"Message header length doesn't match message size: \" << msg.msgHdr.msgLength << \" - \" << bufLen << endl;\n      return false;\n   }\n\t\n   char* body = buf + sizeof(StunMsgHdr);\n   unsigned int size = msg.msgHdr.msgLength;\n\t\n   //clog << \"bytes after header = \" << size << endl;\n\t\n   while ( size > 0 )\n   {\n      // !jf! should check that there are enough bytes left in the buffer\n\t\t\n      StunAtrHdr* attr = reinterpret_cast<StunAtrHdr*>(body);\n\t\t\n      unsigned int attrLen = ntohs(attr->length);\n      int atrType = ntohs(attr->type);\n\t\t\n      //if (verbose) clog << \"Found attribute type=\" << AttrNames[atrType] << \" length=\" << attrLen << endl;\n      if ( attrLen+4 > size ) \n      {\n         clog << \"claims attribute is larger than size of message \" <<\"(attribute type=\"<<atrType<<\")\"<< endl;\n         return false;\n      }\n\t\t\n      body += 4; // skip the length and type in attribute header\n      size -= 4;\n\t\t\n      switch ( atrType )\n      {\n         case MappedAddress:\n            msg.hasMappedAddress = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.mappedAddress )== false )\n            {\n               clog << \"problem parsing MappedAddress\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"MappedAddress = \" << msg.mappedAddress.ipv4 << endl;\n            }\n\t\t\t\t\t\n            break;  \n\n         case ResponseAddress:\n            msg.hasResponseAddress = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.responseAddress )== false )\n            {\n               clog << \"problem parsing ResponseAddress\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"ResponseAddress = \" << msg.responseAddress.ipv4 << endl;\n            }\n            break;  \n\t\t\t\t\n         case ChangeRequest:\n            msg.hasChangeRequest = true;\n            if (stunParseAtrChangeRequest( body, attrLen, msg.changeRequest) == false)\n            {\n               clog << \"problem parsing ChangeRequest\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"ChangeRequest = \" << msg.changeRequest.value << endl;\n            }\n            break;\n\t\t\t\t\n         case SourceAddress:\n            msg.hasSourceAddress = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.sourceAddress )== false )\n            {\n               clog << \"problem parsing SourceAddress\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"SourceAddress = \" << msg.sourceAddress.ipv4 << endl;\n            }\n            break;  \n\t\t\t\t\n         case ChangedAddress:\n            msg.hasChangedAddress = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.changedAddress )== false )\n            {\n               clog << \"problem parsing ChangedAddress\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"ChangedAddress = \" << msg.changedAddress.ipv4 << endl;\n            }\n            break;  \n\t\t\t\t\n         case Username: \n            msg.hasUsername = true;\n            if (stunParseAtrString( body, attrLen, msg.username) == false)\n            {\n               clog << \"problem parsing Username\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"Username = \" << msg.username.value << endl;\n            }\n\t\t\t\t\t\n            break;\n\t\t\t\t\n         case Password: \n            msg.hasPassword = true;\n            if (stunParseAtrString( body, attrLen, msg.password) == false)\n            {\n               clog << \"problem parsing Password\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"Password = \" << msg.password.value << endl;\n            }\n            break;\n\t\t\t\t\n         case MessageIntegrity:\n            msg.hasMessageIntegrity = true;\n            if (stunParseAtrIntegrity( body, attrLen, msg.messageIntegrity) == false)\n            {\n               clog << \"problem parsing MessageIntegrity\" << endl;\n               return false;\n            }\n            else\n            {\n               //if (verbose) clog << \"MessageIntegrity = \" << msg.messageIntegrity.hash << endl;\n            }\n\t\t\t\t\t\n            // read the current HMAC\n            // look up the password given the user of given the transaction id \n            // compute the HMAC on the buffer\n            // decide if they match or not\n            break;\n\t\t\t\t\n         case ErrorCode:\n            msg.hasErrorCode = true;\n            if (stunParseAtrError(body, attrLen, msg.errorCode) == false)\n            {\n               clog << \"problem parsing ErrorCode\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"ErrorCode = \" << int(msg.errorCode.errorClass) \n                                 << \" \" << int(msg.errorCode.number) \n                                 << \" \" << msg.errorCode.reason << endl;\n            }\n\t\t\t\t\t\n            break;\n\t\t\t\t\n         case UnknownAttribute:\n            msg.hasUnknownAttributes = true;\n            if (stunParseAtrUnknown(body, attrLen, msg.unknownAttributes) == false)\n            {\n               clog << \"problem parsing UnknownAttribute\" << endl;\n               return false;\n            }\n            break;\n\t\t\t\t\n         case ReflectedFrom:\n            msg.hasReflectedFrom = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.reflectedFrom ) == false )\n            {\n               clog << \"problem parsing ReflectedFrom\" << endl;\n               return false;\n            }\n            break;  \n\t\t\t\t\n         case XorMappedAddress:\n            msg.hasXorMappedAddress = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.xorMappedAddress ) == false )\n            {\n               clog << \"problem parsing XorMappedAddress\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"XorMappedAddress = \" << msg.mappedAddress.ipv4 << endl;\n            }\n            break;  \n\n         case XorOnly:\n            msg.xorOnly = true;\n            if (verbose) \n            {\n               clog << \"xorOnly = true\" << endl;\n            }\n            break;  \n\t\t\t\t\n         case ServerName: \n            msg.hasServerName = true;\n            if (stunParseAtrString( body, attrLen, msg.serverName) == false)\n            {\n               clog << \"problem parsing ServerName\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"ServerName = \" << msg.serverName.value << endl;\n            }\n            break;\n\t\t\t\t\n         case SecondaryAddress:\n            msg.hasSecondaryAddress = true;\n            if ( stunParseAtrAddress(  body,  attrLen,  msg.secondaryAddress ) == false )\n            {\n               clog << \"problem parsing secondaryAddress\" << endl;\n               return false;\n            }\n            else\n            {\n               if (verbose) clog << \"SecondaryAddress = \" << msg.secondaryAddress.ipv4 << endl;\n            }\n            break;  \n\t\t\t\t\t\n         default:\n            if (verbose) clog << \"Unknown attribute: \" << atrType << endl;\n            if ( atrType <= 0x7FFF ) \n            {\n               return false;\n            }\n      }\n\t\t\n      body += attrLen;\n      size -= attrLen;\n   }\n    \n   return true;\n}\n\n\nstatic char* \nencode16(char* buf, UInt16 data)\n{\n   UInt16 ndata = htons(data);\n   memcpy(buf, reinterpret_cast<void*>(&ndata), sizeof(UInt16));\n   return buf + sizeof(UInt16);\n}\n\nstatic char* \nencode32(char* buf, UInt32 data)\n{\n   UInt32 ndata = htonl(data);\n   memcpy(buf, reinterpret_cast<void*>(&ndata), sizeof(UInt32));\n   return buf + sizeof(UInt32);\n}\n\n\nstatic char* \nencode(char* buf, const char* data, unsigned int length)\n{\n   memcpy(buf, data, length);\n   return buf + length;\n}\n\n\nstatic char* \nencodeAtrAddress4(char* ptr, UInt16 type, const StunAtrAddress4& atr)\n{\n   ptr = encode16(ptr, type);\n   ptr = encode16(ptr, 8);\n   *ptr++ = atr.pad;\n   *ptr++ = IPv4Family;\n   ptr = encode16(ptr, atr.ipv4.port);\n   ptr = encode32(ptr, atr.ipv4.addr);\n\t\n   return ptr;\n}\n\nstatic char* \nencodeAtrChangeRequest(char* ptr, const StunAtrChangeRequest& atr)\n{\n   ptr = encode16(ptr, ChangeRequest);\n   ptr = encode16(ptr, 4);\n   ptr = encode32(ptr, atr.value);\n   return ptr;\n}\n\nstatic char* \nencodeAtrError(char* ptr, const StunAtrError& atr)\n{\n   ptr = encode16(ptr, ErrorCode);\n   ptr = encode16(ptr, 6 + atr.sizeReason);\n   ptr = encode16(ptr, atr.pad);\n   *ptr++ = atr.errorClass;\n   *ptr++ = atr.number;\n   ptr = encode(ptr, atr.reason, atr.sizeReason);\n   return ptr;\n}\n\n\nstatic char* \nencodeAtrUnknown(char* ptr, const StunAtrUnknown& atr)\n{\n   ptr = encode16(ptr, UnknownAttribute);\n   ptr = encode16(ptr, 2+2*atr.numAttributes);\n   for (int i=0; i<atr.numAttributes; i++)\n   {\n      ptr = encode16(ptr, atr.attrType[i]);\n   }\n   return ptr;\n}\n\n\nstatic char* \nencodeXorOnly(char* ptr)\n{\n   ptr = encode16(ptr, XorOnly );\n   return ptr;\n}\n\n\nstatic char* \nencodeAtrString(char* ptr, UInt16 type, const StunAtrString& atr)\n{\n   assert(atr.sizeValue % 4 == 0);\n\t\n   ptr = encode16(ptr, type);\n   ptr = encode16(ptr, atr.sizeValue);\n   ptr = encode(ptr, atr.value, atr.sizeValue);\n   return ptr;\n}\n\n\nstatic char* \nencodeAtrIntegrity(char* ptr, const StunAtrIntegrity& atr)\n{\n   ptr = encode16(ptr, MessageIntegrity);\n   ptr = encode16(ptr, 20);\n   ptr = encode(ptr, atr.hash, sizeof(atr.hash));\n   return ptr;\n}\n\n\nunsigned int\nstunEncodeMessage( const StunMessage& msg, \n                   char* buf, \n                   unsigned int bufLen, \n                   const StunAtrString& password, \n                   bool verbose)\n{\n   assert(bufLen >= sizeof(StunMsgHdr));\n   char* ptr = buf;\n\t\n   ptr = encode16(ptr, msg.msgHdr.msgType);\n   char* lengthp = ptr;\n   ptr = encode16(ptr, 0);\n   ptr = encode(ptr, reinterpret_cast<const char*>(msg.msgHdr.id.octet), sizeof(msg.msgHdr.id));\n\t\n   if (verbose) clog << \"Encoding stun message: \" << endl;\n   if (msg.hasMappedAddress)\n   {\n      if (verbose) clog << \"Encoding MappedAddress: \" << msg.mappedAddress.ipv4 << endl;\n      ptr = encodeAtrAddress4 (ptr, MappedAddress, msg.mappedAddress);\n   }\n   if (msg.hasResponseAddress)\n   {\n      if (verbose) clog << \"Encoding ResponseAddress: \" << msg.responseAddress.ipv4 << endl;\n      ptr = encodeAtrAddress4(ptr, ResponseAddress, msg.responseAddress);\n   }\n   if (msg.hasChangeRequest)\n   {\n      if (verbose) clog << \"Encoding ChangeRequest: \" << msg.changeRequest.value << endl;\n      ptr = encodeAtrChangeRequest(ptr, msg.changeRequest);\n   }\n   if (msg.hasSourceAddress)\n   {\n      if (verbose) clog << \"Encoding SourceAddress: \" << msg.sourceAddress.ipv4 << endl;\n      ptr = encodeAtrAddress4(ptr, SourceAddress, msg.sourceAddress);\n   }\n   if (msg.hasChangedAddress)\n   {\n      if (verbose) clog << \"Encoding ChangedAddress: \" << msg.changedAddress.ipv4 << endl;\n      ptr = encodeAtrAddress4(ptr, ChangedAddress, msg.changedAddress);\n   }\n   if (msg.hasUsername)\n   {\n      if (verbose) clog << \"Encoding Username: \" << msg.username.value << endl;\n      ptr = encodeAtrString(ptr, Username, msg.username);\n   }\n   if (msg.hasPassword)\n   {\n      if (verbose) clog << \"Encoding Password: \" << msg.password.value << endl;\n      ptr = encodeAtrString(ptr, Password, msg.password);\n   }\n   if (msg.hasErrorCode)\n   {\n      if (verbose) clog << \"Encoding ErrorCode: class=\" \n\t\t\t<< int(msg.errorCode.errorClass)  \n\t\t\t<< \" number=\" << int(msg.errorCode.number) \n\t\t\t<< \" reason=\" \n\t\t\t<< msg.errorCode.reason \n\t\t\t<< endl;\n\t\t\n      ptr = encodeAtrError(ptr, msg.errorCode);\n   }\n   if (msg.hasUnknownAttributes)\n   {\n      if (verbose) clog << \"Encoding UnknownAttribute: ???\" << endl;\n      ptr = encodeAtrUnknown(ptr, msg.unknownAttributes);\n   }\n   if (msg.hasReflectedFrom)\n   {\n      if (verbose) clog << \"Encoding ReflectedFrom: \" << msg.reflectedFrom.ipv4 << endl;\n      ptr = encodeAtrAddress4(ptr, ReflectedFrom, msg.reflectedFrom);\n   }\n   if (msg.hasXorMappedAddress)\n   {\n      if (verbose) clog << \"Encoding XorMappedAddress: \" << msg.xorMappedAddress.ipv4 << endl;\n      ptr = encodeAtrAddress4 (ptr, XorMappedAddress, msg.xorMappedAddress);\n   }\n   if (msg.xorOnly)\n   {\n      if (verbose) clog << \"Encoding xorOnly: \" << endl;\n      ptr = encodeXorOnly( ptr );\n   }\n   if (msg.hasServerName)\n   {\n      if (verbose) clog << \"Encoding ServerName: \" << msg.serverName.value << endl;\n      ptr = encodeAtrString(ptr, ServerName, msg.serverName);\n   }\n   if (msg.hasSecondaryAddress)\n   {\n      if (verbose) clog << \"Encoding SecondaryAddress: \" << msg.secondaryAddress.ipv4 << endl;\n      ptr = encodeAtrAddress4 (ptr, SecondaryAddress, msg.secondaryAddress);\n   }\n\n   if (password.sizeValue > 0)\n   {\n      if (verbose) clog << \"HMAC with password: \" << password.value << endl;\n\t\t\n      StunAtrIntegrity integrity;\n      computeHmac(integrity.hash, buf, int(ptr-buf) , password.value, password.sizeValue);\n      ptr = encodeAtrIntegrity(ptr, integrity);\n   }\n   if (verbose) clog << endl;\n\t\n   encode16(lengthp, UInt16(ptr - buf - sizeof(StunMsgHdr)));\n   return int(ptr - buf);\n}\n\nint \nstunRand()\n{\n   // return 32 bits of random stuff\n   assert( sizeof(int) == 4 );\n   static bool init=false;\n   if ( !init )\n   { \n      init = true;\n\t\t\n      UInt64 tick;\n      \n// Twinkle: removed platform dependent code (except WIN32 code)\n\t\t\n#if defined(WIN32) \n      volatile unsigned int lowtick=0,hightick=0;\n      __asm\n         {\n            rdtsc \n               mov lowtick, eax\n               mov hightick, edx\n               }\n      tick = hightick;\n      tick <<= 32;\n      tick |= lowtick;\n#else\n      tick = time(NULL);\n#endif \n      int seed = int(tick);\n#ifdef WIN32\n      srand(seed);\n#else\n      srandom(seed);\n#endif\n   }\n\t\n#ifdef WIN32\n   assert( RAND_MAX == 0x7fff );\n   int r1 = rand();\n   int r2 = rand();\n\t\n   int ret = (r1<<16) + r2;\n\t\n   return ret;\n#else\n   return random(); \n#endif\n}\n\n\n/// return a random number to use as a port \nstatic int\nrandomPort()\n{\n   int min=0x4000;\n   int max=0x7FFF;\n\t\n   int ret = stunRand();\n   ret = ret|min;\n   ret = ret&max;\n\t\n   return ret;\n}\n\n\n#ifdef NOSSL\nstatic void\ncomputeHmac(char* hmac, const char* input, int length, const char* key, int sizeKey)\n{\n   strncpy(hmac,\"hmac-not-implemented\",20);\n}\n#else\n#include <openssl/hmac.h>\n\nstatic void\ncomputeHmac(char* hmac, const char* input, int length, const char* key, int sizeKey)\n{\n   unsigned int resultSize=0;\n   HMAC(EVP_sha1(), \n        key, sizeKey, \n        reinterpret_cast<const unsigned char*>(input), length, \n        reinterpret_cast<unsigned char*>(hmac), &resultSize);\n   assert(resultSize == 20);\n}\n#endif\n\n\nstatic void\ntoHex(const char* buffer, int bufferSize, char* output) \n{\n   static char hexmap[] = \"0123456789abcdef\";\n\t\n   const char* p = buffer;\n   char* r = output;\n   for (int i=0; i < bufferSize; i++)\n   {\n      unsigned char temp = *p++;\n\t\t\n      int hi = (temp & 0xf0)>>4;\n      int low = (temp & 0xf);\n\t\t\n      *r++ = hexmap[hi];\n      *r++ = hexmap[low];\n   }\n   *r = 0;\n}\n\nvoid\nstunCreateUserName(const StunAddress4& source, StunAtrString* username)\n{\n   UInt64 time = stunGetSystemTimeSecs();\n   time -= (time % 20*60);\n   //UInt64 hitime = time >> 32;\n   UInt64 lotime = time & 0xFFFFFFFF;\n\t\n   char buffer[1024];\n   sprintf(buffer,\n           \"%08x:%08x:%08x:\", \n           UInt32(source.addr),\n           UInt32(stunRand()),\n           UInt32(lotime));\n   assert( strlen(buffer) < 1024 );\n\t\n   assert(strlen(buffer) + 41 < STUN_MAX_STRING);\n\t\n   char hmac[20];\n   char key[] = \"Jason\";\n   computeHmac(hmac, buffer, strlen(buffer), key, strlen(key) );\n   char hmacHex[41];\n   toHex(hmac, 20, hmacHex );\n   hmacHex[40] =0;\n\t\n   strcat(buffer,hmacHex);\n\t\n   int l = strlen(buffer);\n   assert( l+1 < STUN_MAX_STRING );\n   assert( l%4 == 0 );\n   \n   username->sizeValue = l;\n   memcpy(username->value,buffer,l);\n   username->value[l]=0;\n\t\n   //if (verbose) clog << \"computed username=\" << username.value << endl;\n}\n\nvoid\nstunCreatePassword(const StunAtrString& username, StunAtrString* password)\n{\n   char hmac[20];\n   char key[] = \"Fluffy\";\n   //char buffer[STUN_MAX_STRING];\n   computeHmac(hmac, username.value, strlen(username.value), key, strlen(key));\n   toHex(hmac, 20, password->value);\n   password->sizeValue = 40;\n   password->value[40]=0;\n\t\n   //clog << \"password=\" << password->value << endl;\n}\n\n\nUInt64\nstunGetSystemTimeSecs()\n{\n   UInt64 time=0;\n#if defined(WIN32)  \n   SYSTEMTIME t;\n   // CJ TODO - this probably has bug on wrap around every 24 hours\n   GetSystemTime( &t );\n   time = (t.wHour*60+t.wMinute)*60+t.wSecond; \n#else\n   struct timeval now;\n   gettimeofday( &now , NULL );\n   //assert( now );\n   time = now.tv_sec;\n#endif\n   return time;\n}\n\n\nostream& operator<< ( ostream& strm, const UInt128& r )\n{\n   strm << int(r.octet[0]);\n   for ( int i=1; i<16; i++ )\n   {\n      strm << ':' << int(r.octet[i]);\n   }\n    \n   return strm;\n}\n\nostream& \noperator<<( ostream& strm, const StunAddress4& addr)\n{\n   UInt32 ip = addr.addr;\n   strm << ((int)(ip>>24)&0xFF) << \".\";\n   strm << ((int)(ip>>16)&0xFF) << \".\";\n   strm << ((int)(ip>> 8)&0xFF) << \".\";\n   strm << ((int)(ip>> 0)&0xFF) ;\n\t\n   strm << \":\" << addr.port;\n\t\n   return strm;\n}\n\n\n// returns true if it scucceeded\nbool \nstunParseHostName( char* peerName,\n               UInt32& ip,\n               UInt16& portVal,\n               UInt16 defaultPort )\n{\n   in_addr sin_addr;\n    \n   char host[512];\n   strncpy(host,peerName,512);\n   host[512-1]='\\0';\n   char* port = NULL;\n\t\n   int portNum = defaultPort;\n\t\n   // pull out the port part if present.\n   char* sep = strchr(host,':');\n\t\n   if ( sep == NULL )\n   {\n      portNum = defaultPort;\n   }\n   else\n   {\n      *sep = '\\0';\n      port = sep + 1;\n      // set port part\n\t\t\n      char* endPtr=NULL;\n\t\t\n      portNum = strtol(port,&endPtr,10);\n\t\t\n      if ( endPtr != NULL )\n      {\n         if ( *endPtr != '\\0' )\n         {\n            portNum = defaultPort;\n         }\n      }\n   }\n    \n   if ( portNum < 1024 ) return false;\n   if ( portNum >= 0xFFFF ) return false;\n\t\n   // figure out the host part \n   struct hostent* h;\n\t\n#ifdef WIN32\n   assert( strlen(host) >= 1 );\n   if ( isdigit( host[0] ) )\n   {\n      // assume it is a ip address \n      unsigned long a = inet_addr(host);\n      //cerr << \"a=0x\" << hex << a << dec << endl;\n\t\t\n      ip = ntohl( a );\n   }\n   else\n   {\n      // assume it is a host name \n      h = gethostbyname( host );\n\t\t\n      if ( h == NULL )\n      {\n         int err = getErrno();\n         std::cerr << \"error was \" << err << std::endl;\n         assert( err != WSANOTINITIALISED );\n\t\t\t\n         ip = ntohl( 0x7F000001L );\n\t\t\t\n         return false;\n      }\n      else\n      {\n         sin_addr = *(struct in_addr*)h->h_addr;\n         ip = ntohl( sin_addr.s_addr );\n      }\n   }\n\t\n#else\n   h = gethostbyname( host );\n   if ( h == NULL )\n   {\n      int err = getErrno();\n      std::cerr << \"error was \" << err << std::endl;\n      ip = ntohl( 0x7F000001L );\n      return false;\n   }\n   else\n   {\n      sin_addr = *(struct in_addr*)h->h_addr;\n      ip = ntohl( sin_addr.s_addr );\n   }\n#endif\n\t\n   portVal = portNum;\n\t\n   return true;\n}\n\n\nbool\nstunParseServerName( char* name, StunAddress4& addr)\n{\n   assert(name);\n\t\n   // TODO - put in DNS SRV stuff.\n\t\n   bool ret = stunParseHostName( name, addr.addr, addr.port, 3478); \n   if ( ret != true ) \n   {\n       addr.port=0xFFFF;\n   }\t\n   return ret;\n}\n\n\nstatic void\nstunCreateErrorResponse(StunMessage& response, int cl, int number, const char* msg)\n{\n   response.msgHdr.msgType = BindErrorResponseMsg;\n   response.hasErrorCode = true;\n   response.errorCode.errorClass = cl;\n   response.errorCode.number = number;\n   strcpy(response.errorCode.reason, msg);\n}\n\n// Twinkle\nStunMessage *stunBuildError(const StunMessage &m, int code, const char *reason) {\n\tStunMessage *err = new StunMessage();\n\tMEMMAN_NEW(err);\n\t\n\tstunCreateErrorResponse(*err, code / 100, code % 100, reason);\n\n        for ( int i=0; i<16; i++ )\n        {\n\t\terr->msgHdr.id.octet[i] = m.msgHdr.id.octet[i];\n        }\n        \n        return err;\n}\n\nstring stunMsg2Str(const StunMessage &m) {\n\tstring s = \"STUN \";\n\t\n\t// Message type\n\tif (m.msgHdr.msgType == BindRequestMsg) {\n\t\ts += \"Bind Request\";\n\t} else if (m.msgHdr.msgType == BindResponseMsg) {\n\t\ts += \"Bind Response\";\n\t} else if (m.msgHdr.msgType == BindErrorResponseMsg) {\n\t\ts += \"Bind Error Response\";\n\t} else if (m.msgHdr.msgType == SharedSecretRequestMsg) {\n\t\ts += \"Shared Secret Request\";\n\t} else if (m.msgHdr.msgType == SharedSecretResponseMsg) {\n\t\ts += \"Shared Secret Response\";\n\t} else if (m.msgHdr.msgType == SharedSecretErrorResponseMsg) {\n\t\ts += \"Shared Secret Error Response\";\n\t}\n\ts += \"\\n\";\n\t\n\t// Message ID\n\ts += \"ID = \";\n        for ( int i=0; i<16; i++ ) {\n        \tchar buf[3];\n        \tsnprintf(buf, 3, \"%X\", m.msgHdr.id.octet[i]);\n        \ts += buf;\n        }\n        s += \"\\n\";\n\t\n\tif (m.hasMappedAddress) {\n\t\ts += \"Mapped Address = \";\n\t\ts += h_ip2str(m.mappedAddress.ipv4.addr);\n\t\ts += ':';\n\t\ts += int2str(m.mappedAddress.ipv4.port);\n\t\ts += \"\\n\";\n\t}\n\t\n\t\n\tif (m.hasResponseAddress) {\n\t\ts += \"Response Address = \";\n\t\ts += h_ip2str(m.responseAddress.ipv4.addr);\n\t\ts += ':';\n\t\ts += int2str(m.responseAddress.ipv4.port);\n\t\ts += \"\\n\";\n\t}\n\t\n\tif (m.hasChangeRequest) {\n\t\ts += \"Change Request = \";\n\t\tbool change_flags = false;\n\t\tif (m.changeRequest.value & ChangeIpFlag) {\n\t\t\ts += \"change IP\";\n\t\t\tchange_flags = true;\n\t\t}\n\t\tif (m.changeRequest.value & ChangePortFlag) {\n\t\t\tif (change_flags) s += \", \";\n\t\t\ts += \"change port\";\n\t\t\tchange_flags = true;\n\t\t}\n\t\tif (!change_flags) s += \"none\";\n\t\ts += \"\\n\";\n\t}\n\t\n\tif (m.hasSourceAddress) {\n\t\ts += \"Source Address = \";\n\t\ts += h_ip2str(m.sourceAddress.ipv4.addr);\n\t\ts += ':';\n\t\ts += int2str(m.sourceAddress.ipv4.port);\n\t\ts += \"\\n\";\n\t}\n\n\tif (m.hasChangedAddress) {\n\t\ts += \"Changed Address = \";\n\t\ts += h_ip2str(m.changedAddress.ipv4.addr);\n\t\ts += ':';\n\t\ts += int2str(m.changedAddress.ipv4.port);\n\t\ts += \"\\n\";\n\t}\n\t\n\tif (m.hasErrorCode) {\n\t\ts += \"Error Code = \";\n\t\ts += int2str(m.errorCode.errorClass * 100 + m.errorCode.number);\n\t\ts += ' ';\n\t\ts += m.errorCode.reason;\n\t\ts += \"\\n\";\n\t}\n\t\n\treturn s;\n}\n\nbool stunEqualId(const StunMessage &m1, const StunMessage &m2) {\n        for ( int i=0; i<16; i++ )\n        {\n\t\tif (m1.msgHdr.id.octet[i] != m2.msgHdr.id.octet[i]) {\n\t\t\treturn false;\n\t\t}\n        }\n        \n        return true;\n}\n\nstring stunNatType2Str(NatType t) {\n\tswitch (t) {\n\tcase StunTypeUnknown:\n\t\treturn \"Unknow\";\n\tcase StunTypeOpen:\n\t\treturn \"Open\";\n\tcase StunTypeConeNat:\n\t\treturn \"Full cone\";\n\tcase StunTypeRestrictedNat:\n\t\treturn \"Restriced cone\";\n\tcase StunTypePortRestrictedNat:\n\t\treturn \"Port restricted cone\";\n\tcase StunTypeSymNat:\n\t\treturn \"Symmetric\";\n\tcase StunTypeSymFirewall:\n\t\treturn \"Symmetric firewall;\";\n\tcase StunTypeBlocked:\n\t\treturn \"Blocked.\";\n\tcase StunTypeFailure:\n\t\treturn \"Failed\";\n\tdefault:\n\t\treturn \"NULL\";\n\t}\n}\n// Twinkle\n\n#if 0\nstatic void\nstunCreateSharedSecretErrorResponse(StunMessage& response, int cl, int number, const char* msg)\n{\n   response.msgHdr.msgType = SharedSecretErrorResponseMsg;\n   response.hasErrorCode = true;\n   response.errorCode.errorClass = cl;\n   response.errorCode.number = number;\n   strcpy(response.errorCode.reason, msg);\n}\n#endif\n\nstatic void\nstunCreateSharedSecretResponse(const StunMessage& request, const StunAddress4& source, StunMessage& response)\n{\n   response.msgHdr.msgType = SharedSecretResponseMsg;\n   response.msgHdr.id = request.msgHdr.id;\n\t\n   response.hasUsername = true;\n   stunCreateUserName( source, &response.username);\n\t\n   response.hasPassword = true;\n   stunCreatePassword( response.username, &response.password);\n}\n\n\n// This funtion takes a single message sent to a stun server, parses\n// and constructs an apropriate repsonse - returns true if message is\n// valid\nbool\nstunServerProcessMsg( char* buf,\n                      unsigned int bufLen,\n                      StunAddress4& from, \n                      StunAddress4& secondary,\n                      StunAddress4& myAddr,\n                      StunAddress4& altAddr, \n                      StunMessage* resp,\n                      StunAddress4* destination,\n                      StunAtrString* hmacPassword,\n                      bool* changePort,\n                      bool* changeIp,\n                      bool verbose)\n{\n    \n   // set up information for default response \n\t\n   memset( resp, 0 , sizeof(*resp) );\n\t\n   *changeIp = false;\n   *changePort = false;\n\t\n   StunMessage req;\n   bool ok = stunParseMessage( buf,bufLen, req, verbose);\n\t\n   if (!ok)      // Complete garbage, drop it on the floor\n   {\n      if (verbose) clog << \"Request did not parse\" << endl;\n      return false;\n   }\n   if (verbose) clog << \"Request parsed ok\" << endl;\n\t\n   StunAddress4 mapped = req.mappedAddress.ipv4;\n   StunAddress4 respondTo = req.responseAddress.ipv4;\n   UInt32 flags = req.changeRequest.value;\n\t\n   switch (req.msgHdr.msgType)\n   {\n      case SharedSecretRequestMsg:\n         if(verbose) clog << \"Received SharedSecretRequestMsg on udp. send error 433.\" << endl;\n         // !cj! - should fix so you know if this came over TLS or UDP\n         stunCreateSharedSecretResponse(req, from, *resp);\n         //stunCreateSharedSecretErrorResponse(*resp, 4, 33, \"this request must be over TLS\");\n         return true;\n\t\t\t\n      case BindRequestMsg:\n         if (!req.hasMessageIntegrity)\n         {\n            if (verbose) clog << \"BindRequest does not contain MessageIntegrity\" << endl;\n\t\t\t\t\n            if (0) // !jf! mustAuthenticate\n            {\n               if(verbose) clog << \"Received BindRequest with no MessageIntegrity. Sending 401.\" << endl;\n               stunCreateErrorResponse(*resp, 4, 1, \"Missing MessageIntegrity\");\n               return true;\n            }\n         }\n         else\n         {\n            if (!req.hasUsername)\n            {\n               if (verbose) clog << \"No UserName. Send 432.\" << endl;\n               stunCreateErrorResponse(*resp, 4, 32, \"No UserName and contains MessageIntegrity\");\n               return true;\n            }\n            else\n            {\n               if (verbose) clog << \"Validating username: \" << req.username.value << endl;\n               // !jf! could retrieve associated password from provisioning here\n               if (strcmp(req.username.value, \"test\") == 0)\n               {\n                  if (0)\n                  {\n                     // !jf! if the credentials are stale \n                     stunCreateErrorResponse(*resp, 4, 30, \"Stale credentials on BindRequest\");\n                     return true;\n                  }\n                  else\n                  {\n                     if (verbose) clog << \"Validating MessageIntegrity\" << endl;\n                     // need access to shared secret\n\t\t\t\t\t\t\t\n                     unsigned char hmac[20];\n#ifndef NOSSL\n                     unsigned int hmacSize=20;\n\n                     HMAC(EVP_sha1(), \n                          \"1234\", 4, \n                          reinterpret_cast<const unsigned char*>(buf), bufLen-20-4, \n                          hmac, &hmacSize);\n                     assert(hmacSize == 20);\n#endif\n\t\t\t\t\t\t\t\n                     if (memcmp(buf, hmac, 20) != 0)\n                     {\n                        if (verbose) clog << \"MessageIntegrity is bad. Sending \" << endl;\n                        stunCreateErrorResponse(*resp, 4, 3, \"Unknown username. Try test with password 1234\");\n                        return true;\n                     }\n\t\t\t\t\t\t\t\n                     // need to compute this later after message is filled in\n                     resp->hasMessageIntegrity = true;\n                     assert(req.hasUsername);\n                     resp->hasUsername = true;\n                     resp->username = req.username; // copy username in\n                  }\n               }\n               else\n               {\n                  if (verbose) clog << \"Invalid username: \" << req.username.value << \"Send 430.\" << endl; \n               }\n            }\n         }\n\t\t\t\n         // TODO !jf! should check for unknown attributes here and send 420 listing the\n         // unknown attributes. \n\t\t\t\n         if ( respondTo.port == 0 ) respondTo = from;\n         if ( mapped.port == 0 ) mapped = from;\n\t\t\t\t\n         *changeIp   = ( flags & ChangeIpFlag )?true:false;\n         *changePort = ( flags & ChangePortFlag )?true:false;\n\t\t\t\n         if (verbose)\n         {\n            clog << \"Request is valid:\" << endl;\n            clog << \"\\t flags=\" << flags << endl;\n            clog << \"\\t changeIp=\" << *changeIp << endl;\n            clog << \"\\t changePort=\" << *changePort << endl;\n            clog << \"\\t from = \" << from << endl;\n            clog << \"\\t respond to = \" << respondTo << endl;\n            clog << \"\\t mapped = \" << mapped << endl;\n         }\n\t\t\t\t\n         // form the outgoing message\n         resp->msgHdr.msgType = BindResponseMsg;\n         for ( int i=0; i<16; i++ )\n         {\n            resp->msgHdr.id.octet[i] = req.msgHdr.id.octet[i];\n         }\n\t\t\n         if ( req.xorOnly == false )\n         {\n            resp->hasMappedAddress = true;\n            resp->mappedAddress.ipv4.port = mapped.port;\n            resp->mappedAddress.ipv4.addr = mapped.addr;\n         }\n\n         if (1) // do xorMapped address or not \n         {\n            resp->hasXorMappedAddress = true;\n            UInt16 id16 = req.msgHdr.id.octet[7]<<8 \n               | req.msgHdr.id.octet[6];\n            UInt32 id32 = req.msgHdr.id.octet[7]<<24 \n               |  req.msgHdr.id.octet[6]<<16 \n               |  req.msgHdr.id.octet[5]<<8 \n               | req.msgHdr.id.octet[4];;\n            resp->xorMappedAddress.ipv4.port = mapped.port^id16;\n            resp->xorMappedAddress.ipv4.addr = mapped.addr^id32;\n         }\n         \n         resp->hasSourceAddress = true;\n         resp->sourceAddress.ipv4.port = (*changePort) ? altAddr.port : myAddr.port;\n         resp->sourceAddress.ipv4.addr = (*changeIp)   ? altAddr.addr : myAddr.addr;\n\t\t\t\n         resp->hasChangedAddress = true;\n         resp->changedAddress.ipv4.port = altAddr.port;\n         resp->changedAddress.ipv4.addr = altAddr.addr;\n\t\n         if ( secondary.port != 0 )\n         {\n            resp->hasSecondaryAddress = true;\n            resp->secondaryAddress.ipv4.port = secondary.port;\n            resp->secondaryAddress.ipv4.addr = secondary.addr;\n         }\n         \n         if ( req.hasUsername && req.username.sizeValue > 0 ) \n         {\n            // copy username in\n            resp->hasUsername = true;\n            assert( req.username.sizeValue % 4 == 0 );\n            assert( req.username.sizeValue < STUN_MAX_STRING );\n            memcpy( resp->username.value, req.username.value, req.username.sizeValue );\n            resp->username.sizeValue = req.username.sizeValue;\n         }\n\t\t\n         if (1) // add ServerName \n         {\n            resp->hasServerName = true;\n            const char serverName[] = \"Vovida.org \" STUN_VERSION; // must pad to mult of 4\n            \n            assert( sizeof(serverName) < STUN_MAX_STRING );\n            //cerr << \"sizeof serverName is \"  << sizeof(serverName) << endl;\n            assert( sizeof(serverName)%4 == 0 );\n            memcpy( resp->serverName.value, serverName, sizeof(serverName));\n            resp->serverName.sizeValue = sizeof(serverName);\n         }\n         \n         if ( req.hasMessageIntegrity & req.hasUsername )  \n         {\n            // this creates the password that will be used in the HMAC when then\n            // messages is sent\n            stunCreatePassword( req.username, hmacPassword );\n         }\n\t\t\t\t\n         if (req.hasUsername && (req.username.sizeValue > 64 ) )\n         {\n            UInt32 source;\n            assert( sizeof(int) == sizeof(UInt32) );\n\t\t\t\t\t\n            sscanf(req.username.value, \"%x\", &source);\n            resp->hasReflectedFrom = true;\n            resp->reflectedFrom.ipv4.port = 0;\n            resp->reflectedFrom.ipv4.addr = source;\n         }\n\t\t\t\t\n         destination->port = respondTo.port;\n         destination->addr = respondTo.addr;\n\t\t\t\n         return true;\n\t\t\t\n      default:\n         if (verbose) clog << \"Unknown or unsupported request \" << endl;\n         return false;\n   }\n\t\n   assert(0);\n   return false;\n}\n\nbool\nstunInitServer(StunServerInfo& info, const StunAddress4& myAddr, const StunAddress4& altAddr, int startMediaPort, bool verbose )\n{\n   assert( myAddr.port != 0 );\n   assert( altAddr.port!= 0 );\n   assert( myAddr.addr  != 0 );\n   //assert( altAddr.addr != 0 );\n\t\n   info.myAddr = myAddr;\n   info.altAddr = altAddr;\n\t\n   info.myFd = INVALID_SOCKET;\n   info.altPortFd = INVALID_SOCKET;\n   info.altIpFd = INVALID_SOCKET;\n   info.altIpPortFd = INVALID_SOCKET;\n\n   memset(info.relays, 0, sizeof(info.relays));\n   if (startMediaPort > 0)\n   {\n      info.relay = true;\n\n      for (int i=0; i<MAX_MEDIA_RELAYS; ++i)\n      {\n         StunMediaRelay* relay = &info.relays[i];\n         relay->relayPort = startMediaPort+i;\n         relay->fd = 0;\n         relay->expireTime = 0;\n      }\n   }\n   else\n   {\n      info.relay = false;\n   }\n   \n   if ((info.myFd = openPort(myAddr.port, myAddr.addr,verbose)) == INVALID_SOCKET)\n   {\n      clog << \"Can't open \" << myAddr << endl;\n      stunStopServer(info);\n\n      return false;\n   }\n   //if (verbose) clog << \"Opened \" << myAddr.addr << \":\" << myAddr.port << \" --> \" << info.myFd << endl;\n\n   if ((info.altPortFd = openPort(altAddr.port,myAddr.addr,verbose)) == INVALID_SOCKET)\n   {\n      clog << \"Can't open \" << myAddr << endl;\n      stunStopServer(info);\n      return false;\n   }\n   //if (verbose) clog << \"Opened \" << myAddr.addr << \":\" << altAddr.port << \" --> \" << info.altPortFd << endl;\n   \n   \n   info.altIpFd = INVALID_SOCKET;\n   if (  altAddr.addr != 0 )\n   {\n      if ((info.altIpFd = openPort( myAddr.port, altAddr.addr,verbose)) == INVALID_SOCKET)\n      {\n         clog << \"Can't open \" << altAddr << endl;\n         stunStopServer(info);\n         return false;\n      }\n      //if (verbose) clog << \"Opened \" << altAddr.addr << \":\" << myAddr.port << \" --> \" << info.altIpFd << endl;;\n   }\n   \n   info.altIpPortFd = INVALID_SOCKET;\n   if (  altAddr.addr != 0 )\n   {  if ((info.altIpPortFd = openPort(altAddr.port, altAddr.addr,verbose)) == INVALID_SOCKET)\n      {\n         clog << \"Can't open \" << altAddr << endl;\n         stunStopServer(info);\n         return false;\n      }\n      //if (verbose) clog << \"Opened \" << altAddr.addr << \":\" << altAddr.port << \" --> \" << info.altIpPortFd << endl;;\n   }\n   \n   return true;\n}\n\nvoid\nstunStopServer(StunServerInfo& info)\n{\n   if (info.myFd > 0) closesocket(info.myFd);\n   if (info.altPortFd > 0) closesocket(info.altPortFd);\n   if (info.altIpFd > 0) closesocket(info.altIpFd);\n   if (info.altIpPortFd > 0) closesocket(info.altIpPortFd);\n   \n   if (info.relay)\n   {\n      for (int i=0; i<MAX_MEDIA_RELAYS; ++i)\n      {\n         StunMediaRelay* relay = &info.relays[i];\n         if (relay->fd)\n         {\n            closesocket(relay->fd);\n            relay->fd = 0;\n         }\n      }\n   }\n}\n\n\nbool\nstunServerProcess(StunServerInfo& info, bool verbose)\n{\n   char msg[STUN_MAX_MESSAGE_SIZE];\n   int msgLen = sizeof(msg);\n   \t\n   bool ok = false;\n   bool recvAltIp =false;\n   bool recvAltPort = false;\n\t\n   fd_set fdSet; \n#ifdef WIN32\n   unsigned int maxFd=0;\n#else\n   int maxFd=0;\n#endif\n   FD_ZERO(&fdSet); \n   FD_SET(info.myFd,&fdSet); \n   if ( info.myFd >= maxFd ) maxFd=info.myFd+1;\n   FD_SET(info.altPortFd,&fdSet); \n   if ( info.altPortFd >= maxFd ) maxFd=info.altPortFd+1;\n\n   if ( info.altIpFd != INVALID_SOCKET )\n   {\n      FD_SET(info.altIpFd,&fdSet);\n      if (info.altIpFd>=maxFd) maxFd=info.altIpFd+1;\n   }\n   if ( info.altIpPortFd != INVALID_SOCKET )\n   {\n      FD_SET(info.altIpPortFd,&fdSet);\n      if (info.altIpPortFd>=maxFd) maxFd=info.altIpPortFd+1;\n   }\n\n   if (info.relay)\n   {\n      for (int i=0; i<MAX_MEDIA_RELAYS; ++i)\n      {\n         StunMediaRelay* relay = &info.relays[i];\n         if (relay->fd)\n         {\n            FD_SET(relay->fd, &fdSet);\n            if (relay->fd >= maxFd) maxFd=relay->fd+1;\n         }\n      }\n   }\n   \n   if ( info.altIpFd != INVALID_SOCKET )\n   {\n      FD_SET(info.altIpFd,&fdSet);\n      if (info.altIpFd>=maxFd) maxFd=info.altIpFd+1;\n   }\n   if ( info.altIpPortFd != INVALID_SOCKET )\n   {\n      FD_SET(info.altIpPortFd,&fdSet);\n      if (info.altIpPortFd>=maxFd) maxFd=info.altIpPortFd+1;\n   }\n   \n   struct timeval tv;\n   tv.tv_sec = 0;\n   tv.tv_usec = 1000;\n\t\n   int e = select( maxFd, &fdSet, NULL,NULL, &tv );\n   if (e < 0)\n   {\n      int err = getErrno();\n      clog << \"Error on select: \" << strerror(err) << endl;\n   }\n   else if (e >= 0)\n   {\n      StunAddress4 from;\n\n      // do the media relaying\n      if (info.relay)\n      {\n         time_t now = time(0);\n         for (int i=0; i<MAX_MEDIA_RELAYS; ++i)\n         {\n            StunMediaRelay* relay = &info.relays[i];\n            if (relay->fd)\n            {\n               if (FD_ISSET(relay->fd, &fdSet))\n               {\n                  char msg[MAX_RTP_MSG_SIZE];\n                  int msgLen = sizeof(msg);\n                  \n                  StunAddress4 rtpFrom;\n                  ok = getMessage( relay->fd, msg, &msgLen, &rtpFrom.addr, &rtpFrom.port ,verbose);\n                  if (ok)\n                  {\n                     sendMessage(info.myFd, msg, msgLen, relay->destination.addr, relay->destination.port, verbose);\n                     relay->expireTime = now + MEDIA_RELAY_TIMEOUT;\n                     if ( verbose ) clog << \"Relay packet on \" \n                                         << relay->fd \n                                         << \" from \" << rtpFrom \n                                         << \" -> \" << relay->destination \n                                         << endl;\n                  }\n               }\n               else if (now > relay->expireTime)\n               {\n                  closesocket(relay->fd);\n                  relay->fd = 0;\n               }\n            }\n         }\n      }\n      \n     \n      if (FD_ISSET(info.myFd,&fdSet))\n      {\n         if (verbose) clog << \"received on A1:P1\" << endl;\n         recvAltIp = false;\n         recvAltPort = false;\n         ok = getMessage( info.myFd, msg, &msgLen, &from.addr, &from.port,verbose );\n      }\n      else if (FD_ISSET(info.altPortFd, &fdSet))\n      {\n         if (verbose) clog << \"received on A1:P2\" << endl;\n         recvAltIp = false;\n         recvAltPort = true;\n         ok = getMessage( info.altPortFd, msg, &msgLen, &from.addr, &from.port,verbose );\n      }\n      else if ( (info.altIpFd!=INVALID_SOCKET) && FD_ISSET(info.altIpFd,&fdSet))\n      {\n         if (verbose) clog << \"received on A2:P1\" << endl;\n         recvAltIp = true;\n         recvAltPort = false;\n         ok = getMessage( info.altIpFd, msg, &msgLen, &from.addr, &from.port ,verbose);\n      }\n      else if ( (info.altIpPortFd!=INVALID_SOCKET) && FD_ISSET(info.altIpPortFd, &fdSet))\n      {\n         if (verbose) clog << \"received on A2:P2\" << endl;\n         recvAltIp = true;\n         recvAltPort = true;\n         ok = getMessage( info.altIpPortFd, msg, &msgLen, &from.addr, &from.port,verbose );\n      }\n      else\n      {\n         return true;\n      }\n\n      int relayPort = 0;\n      if (info.relay)\n      {\n         for (int i=0; i<MAX_MEDIA_RELAYS; ++i)\n         {\n            StunMediaRelay* relay = &info.relays[i];\n            if (relay->destination.addr == from.addr && \n                relay->destination.port == from.port)\n            {\n               relayPort = relay->relayPort;\n               relay->expireTime = time(0) + MEDIA_RELAY_TIMEOUT;\n               break;\n            }\n         }\n\n         if (relayPort == 0)\n         {\n            for (int i=0; i<MAX_MEDIA_RELAYS; ++i)\n            {\n               StunMediaRelay* relay = &info.relays[i];\n               if (relay->fd == 0)\n               {\n                  if ( verbose ) clog << \"Open relay port \" << relay->relayPort << endl;\n                  \n                  relay->fd = openPort(relay->relayPort, info.myAddr.addr, verbose);\n                  relay->destination.addr = from.addr;\n                  relay->destination.port = from.port;\n                  relay->expireTime = time(0) + MEDIA_RELAY_TIMEOUT;\n                  relayPort = relay->relayPort;\n                  break;\n               }\n            }\n         }\n      }\n         \n      if ( !ok ) \n      {\n         if ( verbose ) clog << \"Get message did not return a valid message\" <<endl;\n         return true;\n      }\n\t\t\n      if ( verbose ) clog << \"Got a request (len=\" << msgLen << \") from \" << from << endl;\n\t\t\n      if ( msgLen <= 0 )\n      {\n         return true;\n      }\n\t\t\n      bool changePort = false;\n      bool changeIp = false;\n\t\t\n      StunMessage resp;\n      StunAddress4 dest;\n      StunAtrString hmacPassword;  \n      hmacPassword.sizeValue = 0;\n\n      StunAddress4 secondary;\n      secondary.port = 0;\n      secondary.addr = 0;\n               \n      if (info.relay && relayPort)\n      {\n         secondary = from;\n         \n         from.addr = info.myAddr.addr;\n         from.port = relayPort;\n      }\n      \n      ok = stunServerProcessMsg( msg, msgLen, from, secondary,\n                                 recvAltIp ? info.altAddr : info.myAddr,\n                                 recvAltIp ? info.myAddr : info.altAddr, \n                                 &resp,\n                                 &dest,\n                                 &hmacPassword,\n                                 &changePort,\n                                 &changeIp,\n                                 verbose );\n\t\t\n      if ( !ok )\n      {\n         if ( verbose ) clog << \"Failed to parse message\" << endl;\n         return true;\n      }\n\t\t\n      char buf[STUN_MAX_MESSAGE_SIZE];\n      int len = sizeof(buf);\n      \t\t\n      len = stunEncodeMessage( resp, buf, len, hmacPassword,verbose );\n\t\t\n      if ( dest.addr == 0 )  ok=false;\n      if ( dest.port == 0 ) ok=false;\n\t\t\n      if ( ok )\n      {\n         assert( dest.addr != 0 );\n         assert( dest.port != 0 );\n\t\t\t\n         StunSocket sendFd;\n\t\t\t\n         bool sendAltIp   = recvAltIp;   // send on the received IP address \n         bool sendAltPort = recvAltPort; // send on the received port\n\t\t\t\n         if ( changeIp )   sendAltIp   = !sendAltIp;   // if need to change IP, then flip logic \n         if ( changePort ) sendAltPort = !sendAltPort; // if need to change port, then flip logic \n\t\t\t\n         if ( !sendAltPort )\n         {\n            if ( !sendAltIp )\n            {\n               sendFd = info.myFd;\n            }\n            else\n            {\n               sendFd = info.altIpFd;\n            }\n         }\n         else\n         {\n            if ( !sendAltIp )\n            {\n               sendFd = info.altPortFd;\n            }\n            else\n            {\n               sendFd = info.altIpPortFd;\n            }\n         }\n\t\n         if ( sendFd != INVALID_SOCKET )\n         {\n            sendMessage( sendFd, buf, len, dest.addr, dest.port, verbose );\n         }\n      }\n   }\n\t\n   return true;\n}\n\nint \nstunFindLocalInterfaces(UInt32* addresses,int maxRet)\n{\n#if defined(WIN32) || defined(__sparc__)\n   return 0;\n#else\n   struct ifconf ifc;\n\t\n   int s = socket( AF_INET, SOCK_DGRAM, 0 );\n   int len = 100 * sizeof(struct ifreq);\n\t\n   char buf[ len ];\n\t\n   ifc.ifc_len = len;\n   ifc.ifc_buf = buf;\n\t\n   int e = ioctl(s,SIOCGIFCONF,&ifc);\n   char *ptr = buf;\n   int tl = ifc.ifc_len;\n   int count=0;\n\t\n   while ( (tl > 0) && ( count < maxRet) )\n   {\n      struct ifreq* ifr = (struct ifreq *)ptr;\n\t\t\n      int si = sizeof(ifr->ifr_name) + sizeof(struct sockaddr);\n      tl -= si;\n      ptr += si;\n      //char* name = ifr->ifr_ifrn.ifrn_name;\n      //cerr << \"name = \" << name << endl;\n\t\t\n      struct ifreq ifr2;\n      ifr2 = *ifr;\n\t\t\n      e = ioctl(s,SIOCGIFADDR,&ifr2);\n      if ( e == -1 )\n      {\n         break;\n      }\n\t\t\n      //cerr << \"ioctl addr e = \" << e << endl;\n\t\t\n      struct sockaddr a = ifr2.ifr_addr;\n      struct sockaddr_in* addr = (struct sockaddr_in*) &a;\n\t\t\n      UInt32 ai = ntohl( addr->sin_addr.s_addr );\n      if (int((ai>>24)&0xFF) != 127)\n      {\n         addresses[count++] = ai;\n      }\n\t\t\n#if 0\n      cerr << \"Detected interface \"\n           << int((ai>>24)&0xFF) << \".\" \n           << int((ai>>16)&0xFF) << \".\" \n           << int((ai>> 8)&0xFF) << \".\" \n           << int((ai    )&0xFF) << endl;\n#endif\n   }\n\t\n   closesocket(s);\n\t\n   return count;\n#endif\n}\n\n\nvoid\nstunBuildReqSimple( StunMessage* msg,\n                    const StunAtrString& username,\n                    bool changePort, bool changeIp, unsigned int id )\n{\n   assert( msg );\n   memset( msg , 0 , sizeof(*msg) );\n\t\n   msg->msgHdr.msgType = BindRequestMsg;\n\t\n   for ( int i=0; i<16; i=i+4 )\n   {\n      assert(i+3<16);\n      int r = stunRand();\n      msg->msgHdr.id.octet[i+0]= r>>0;\n      msg->msgHdr.id.octet[i+1]= r>>8;\n      msg->msgHdr.id.octet[i+2]= r>>16;\n      msg->msgHdr.id.octet[i+3]= r>>24;\n   }\n\t\n   if ( id != 0 )\n   {\n      msg->msgHdr.id.octet[0] = id; \n   }\n\t\n   msg->hasChangeRequest = true;\n   msg->changeRequest.value =(changeIp?ChangeIpFlag:0) | \n      (changePort?ChangePortFlag:0);\n\t\n   if ( username.sizeValue > 0 )\n   {\n      msg->hasUsername = true;\n      msg->username = username;\n   }\n}\n\n\nstatic void \nstunSendTest( StunSocket myFd, StunAddress4& dest, \n              const StunAtrString& username, const StunAtrString& password, \n              int testNum, bool verbose )\n{ \n   assert( dest.addr != 0 );\n   assert( dest.port != 0 );\n\t\n   bool changePort=false;\n   bool changeIP=false;\n   bool discard=false;\n\t\n   switch (testNum)\n   {\n      case 1:\n      case 10:\n      case 11:\n         break;\n      case 2:\n         //changePort=true;\n         changeIP=true;\n         break;\n      case 3:\n         changePort=true;\n         break;\n      case 4:\n         changeIP=true;\n         break;\n      case 5:\n         discard=true;\n         break;\n      default:\n         cerr << \"Test \" << testNum <<\" is unknown\\n\";\n         assert(0);\n   }\n\t\n   StunMessage req;\n   memset(&req, 0, sizeof(StunMessage));\n\t\n   stunBuildReqSimple( &req, username, \n                       changePort , changeIP , \n                       testNum );\n\t\n   char buf[STUN_MAX_MESSAGE_SIZE];\n   int len = STUN_MAX_MESSAGE_SIZE;\n\t\n   len = stunEncodeMessage( req, buf, len, password,verbose );\n\t\n   if ( verbose )\n   {\n      clog << \"About to send msg of len \" << len << \" to \" << dest << endl;\n   }\n\t\n   sendMessage( myFd, buf, len, dest.addr, dest.port, verbose );\n\t\n   // add some delay so the packets don't get sent too quickly \n#ifdef WIN32 // !cj! TODO - should fix this up in windows\n\t\t clock_t now = clock();\n\t\t assert( CLOCKS_PER_SEC == 1000 );\n\t\t while ( clock() <= now+10 ) { };\n#else\n\t\t usleep(10*1000);\n#endif\n\n}\n\n\nvoid \nstunGetUserNameAndPassword(  const StunAddress4& dest, \n                             StunAtrString* username,\n                             StunAtrString* password)\n{ \n   // !cj! This is totally bogus - need to make TLS connection to dest and get a\n   // username and password to use \n   stunCreateUserName(dest, username);\n   stunCreatePassword(*username, password);\n}\n\n\nvoid \nstunTest( StunAddress4& dest, int testNum, bool verbose, StunAddress4* sAddr )\n{ \n   assert( dest.addr != 0 );\n   assert( dest.port != 0 );\n\t\n   int port = randomPort();\n   UInt32 interfaceIp=0;\n   if (sAddr)\n   {\n      interfaceIp = sAddr->addr;\n      if ( sAddr->port != 0 )\n      {\n        port = sAddr->port;\n      }\n   }\n   StunSocket myFd = openPort(port,interfaceIp,verbose);\n\t\n   StunAtrString username;\n   StunAtrString password;\n\t\n   username.sizeValue = 0;\n   password.sizeValue = 0;\n\t\n#ifdef USE_TLS\n   stunGetUserNameAndPassword( dest, username, password );\n#endif\n\t\n   stunSendTest( myFd, dest, username, password, testNum, verbose );\n    \n   char msg[STUN_MAX_MESSAGE_SIZE];\n   int msgLen = STUN_MAX_MESSAGE_SIZE;\n\t\n   StunAddress4 from;\n   getMessage( myFd,\n               msg,\n               &msgLen,\n               &from.addr,\n               &from.port,verbose );\n\t\n   StunMessage resp;\n   memset(&resp, 0, sizeof(StunMessage));\n\t\n   if ( verbose ) clog << \"Got a response\" << endl;\n   bool ok = stunParseMessage( msg,msgLen, resp,verbose );\n\t\n   if ( verbose )\n   {\n      clog << \"\\t ok=\" << ok << endl;\n      clog << \"\\t id=\" << resp.msgHdr.id << endl;\n      clog << \"\\t mappedAddr=\" << resp.mappedAddress.ipv4 << endl;\n      clog << \"\\t changedAddr=\" << resp.changedAddress.ipv4 << endl;\n      clog << endl;\n   }\n\t\n   if (sAddr)\n   {\n      sAddr->port = resp.mappedAddress.ipv4.port;\n      sAddr->addr = resp.mappedAddress.ipv4.addr;\n   }\n}\n\n\nNatType\nstunNatType( StunAddress4& dest, \n             bool verbose,\n             bool* preservePort, // if set, is return for if NAT preservers ports or not\n             bool* hairpin,  // if set, is the return for if NAT will hairpin packets\n             int port, // port to use for the test, 0 to choose random port\n             StunAddress4* sAddr // NIC to use \n   )\n{ \n   assert( dest.addr != 0 );\n   assert( dest.port != 0 );\n\t\n   if ( hairpin ) \n   {\n      *hairpin = false;\n   }\n\t\n   if ( port == 0 )\n   {\n      port = randomPort();\n   }\n   UInt32 interfaceIp=0;\n   if (sAddr)\n   {\n      interfaceIp = sAddr->addr;\n   }\n   StunSocket myFd1 = openPort(port,interfaceIp,verbose);\n   StunSocket myFd2 = openPort(port+1,interfaceIp,verbose);\n\n   if ( ( myFd1 == INVALID_SOCKET) || ( myFd2 == INVALID_SOCKET) )\n   {\n        cerr << \"Some problem opening port/interface to send on\" << endl;\n       return StunTypeFailure; \n   }\n\n   assert( myFd1 != INVALID_SOCKET );\n   assert( myFd2 != INVALID_SOCKET );\n    \n   bool respTestI=false;\n   bool isNat=true;\n   StunAddress4 testIchangedAddr;\n   StunAddress4 testImappedAddr;\n   bool respTestI2=false; \n   bool mappedIpSame = true;\n   StunAddress4 testI2mappedAddr;\n   StunAddress4 testI2dest=dest;\n   bool respTestII=false;\n   bool respTestIII=false;\n\n   bool respTestHairpin=false;\n\t\n   memset(&testImappedAddr,0,sizeof(testImappedAddr));\n\t\n   StunAtrString username;\n   StunAtrString password;\n\t\n   username.sizeValue = 0;\n   password.sizeValue = 0;\n\t\n#ifdef USE_TLS \n   stunGetUserNameAndPassword( dest, username, password );\n#endif\n\t\n   //stunSendTest( myFd1, dest, username, password, 1, verbose );\n   int count=0;\n   while ( count < 7 )\n   {\n      struct timeval tv;\n      fd_set fdSet; \n#ifdef WIN32\n      unsigned int fdSetSize;\n#else\n      int fdSetSize;\n#endif\n      FD_ZERO(&fdSet); fdSetSize=0;\n      FD_SET(myFd1,&fdSet); fdSetSize = (myFd1+1>fdSetSize) ? myFd1+1 : fdSetSize;\n      FD_SET(myFd2,&fdSet); fdSetSize = (myFd2+1>fdSetSize) ? myFd2+1 : fdSetSize;\n      tv.tv_sec=0;\n      tv.tv_usec=150*1000; // 150 ms \n      if ( count == 0 ) tv.tv_usec=0;\n\t\t\n      int  err = select(fdSetSize, &fdSet, NULL, NULL, &tv);\n      int e = getErrno();\n      if ( err == SOCKET_ERROR )\n      {\n        // error occured\n        cerr << \"Error \" << e << \" \" << strerror(e) << \" in select\" << endl;\n        closesocket(myFd1);\n        closesocket(myFd2);\n        return StunTypeFailure; \n     }\n      else if ( err == 0 )\n      {\n         // timeout occured \n         count++;\n\t\t\t\n         if ( !respTestI ) \n         {\n            stunSendTest( myFd1, dest, username, password, 1 ,verbose );\n         }         \n\t\t\t\n         if ( (!respTestI2) && respTestI ) \n         {\n            // check the address to send to if valid \n            if (  ( testI2dest.addr != 0 ) &&\n                  ( testI2dest.port != 0 ) )\n            {\n               stunSendTest( myFd1, testI2dest, username, password, 10  ,verbose);\n            }\n         }\n\t\t\t\n         if ( !respTestII )\n         {\n            stunSendTest( myFd2, dest, username, password, 2 ,verbose );\n         }\n\t\t\t\n         if ( !respTestIII )\n         {\n            stunSendTest( myFd2, dest, username, password, 3 ,verbose );\n         }\n\t\t\t\n         if ( respTestI && (!respTestHairpin) )\n         {\n            if (  ( testImappedAddr.addr != 0 ) &&\n                  ( testImappedAddr.port != 0 ) )\n            {\n               stunSendTest( myFd1, testImappedAddr, username, password, 11 ,verbose );\n            }\n         }\n      }\n      else\n      {\n         //if (verbose) clog << \"-----------------------------------------\" << endl;\n         assert( err>0 );\n         // data is avialbe on some fd \n\t\t\t\n         for ( int i=0; i<2; i++)\n         {\n            StunSocket myFd;\n            if ( i==0 ) \n            {\n               myFd=myFd1;\n            }\n            else\n            {\n               myFd=myFd2;\n            }\n\t\t\t\t\n            if ( myFd!=INVALID_SOCKET ) \n            {\t\t\t\t\t\n               if ( FD_ISSET(myFd,&fdSet) )\n               {\n                  char msg[STUN_MAX_MESSAGE_SIZE];\n                  int msgLen = sizeof(msg);\n                  \t\t\t\t\t\t\n                  StunAddress4 from;\n\t\t\t\t\t\t\n                  getMessage( myFd,\n                              msg,\n                              &msgLen,\n                              &from.addr,\n                              &from.port,verbose );\n\t\t\t\t\t\t\n                  StunMessage resp;\n                  memset(&resp, 0, sizeof(StunMessage));\n\t\t\t\t\t\t\n                  stunParseMessage( msg,msgLen, resp,verbose );\n\t\t\t\t\t\t\n                  if ( verbose )\n                  {\n                     clog << \"Received message of type \" << resp.msgHdr.msgType \n                          << \"  id=\" << (int)(resp.msgHdr.id.octet[0]) << endl;\n                  }\n\t\t\t\t\t\t\n                  switch( resp.msgHdr.id.octet[0] )\n                  {\n                     case 1:\n                     {\n                        if ( !respTestI )\n                        {\n\t\t\t\t\t\t\t\t\t\n                           testIchangedAddr.addr = resp.changedAddress.ipv4.addr;\n                           testIchangedAddr.port = resp.changedAddress.ipv4.port;\n                           testImappedAddr.addr = resp.mappedAddress.ipv4.addr;\n                           testImappedAddr.port = resp.mappedAddress.ipv4.port;\n\t\t\t\t\t\t\t\t\t\n                           if ( preservePort )\n                           {\n                              *preservePort = ( testImappedAddr.port == port );\n                           }\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n                           testI2dest.addr = resp.changedAddress.ipv4.addr;\n\t\t\t\t\t\t\t\t\t\n                           if (sAddr)\n                           {\n                              sAddr->port = testImappedAddr.port;\n                              sAddr->addr = testImappedAddr.addr;\n                           }\n\t\t\t\t\t\t\t\t\t\n                           count = 0;\n                        }\t\t\n                        respTestI=true;\n                     }\n                     break;\n                     case 2:\n                     {  \n                        respTestII=true;\n                     }\n                     break;\n                     case 3:\n                     {\n                        respTestIII=true;\n                     }\n                     break;\n                     case 10:\n                     {\n                        if ( !respTestI2 )\n                        {\n                           testI2mappedAddr.addr = resp.mappedAddress.ipv4.addr;\n                           testI2mappedAddr.port = resp.mappedAddress.ipv4.port;\n\t\t\t\t\t\t\t\t\n                           mappedIpSame = false;\n                           if ( (testI2mappedAddr.addr  == testImappedAddr.addr ) &&\n                                (testI2mappedAddr.port == testImappedAddr.port ))\n                           { \n                              mappedIpSame = true;\n                           }\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n                        }\n                        respTestI2=true;\n                     }\n                     break;\n                     case 11:\n                     {\n\t\t\t\t\t\t\t\n                        if ( hairpin ) \n                        {\n                           *hairpin = true;\n                        }\n                        respTestHairpin = true;\n                     }\n                     break;\n                  }\n               }\n            }\n         }\n      }\n   }\n\t\n   // see if we can bind to this address \n   //cerr << \"try binding to \" << testImappedAddr << endl;\n   StunSocket s = openPort( 0/*use ephemeral*/, testImappedAddr.addr, false );\n   if ( s != INVALID_SOCKET )\n   {\n      closesocket(s);\n      isNat = false;\n      //cerr << \"binding worked\" << endl;\n   }\n   else\n   {\n      isNat = true;\n      //cerr << \"binding failed\" << endl;\n   }\n   \n   closesocket(myFd1);\n   closesocket(myFd2);\n\t\n   if (verbose)\n   {\n      clog << \"test I = \" << respTestI << endl;\n      clog << \"test II = \" << respTestII << endl;\n      clog << \"test III = \" << respTestIII << endl;\n      clog << \"test I(2) = \" << respTestI2 << endl;\n      clog << \"is nat  = \" << isNat <<endl;\n      clog << \"mapped IP same = \" << mappedIpSame << endl;\n   }\n\t\n   // implement logic flow chart from draft RFC\n   if ( respTestI )\n   {\n      if ( isNat )\n      {\n         if (respTestII)\n         {\n            return StunTypeConeNat;\n         }\n         else\n         {\n            if ( mappedIpSame )\n            {\n               if ( respTestIII )\n               {\n                  return StunTypeRestrictedNat;\n               }\n               else\n               {\n                  return StunTypePortRestrictedNat;\n               }\n            }\n            else\n            {\n               return StunTypeSymNat;\n            }\n         }\n      }\n      else\n      {\n         if (respTestII)\n         {\n            return StunTypeOpen;\n         }\n         else\n         {\n            return StunTypeSymFirewall;\n         }\n      }\n   }\n   else\n   {\n      return StunTypeBlocked;\n   }\n\t\n   return StunTypeUnknown;\n}\n\n\nint\nstunOpenStunSocket( StunAddress4& dest, StunAddress4* mapAddr, \n                int port, StunAddress4* srcAddr, \n                bool verbose )\n{\n   assert( dest.addr != 0 );\n   assert( dest.port != 0 );\n   assert( mapAddr );\n   \n   if ( port == 0 )\n   {\n      port = randomPort();\n   }\n   unsigned int interfaceIp = 0;\n   if ( srcAddr )\n   {\n      interfaceIp = srcAddr->addr;\n   }\n   \n   StunSocket myFd = openPort(port,interfaceIp,verbose);\n   if (myFd == INVALID_SOCKET)\n   {\n      return myFd;\n   }\n   \n   char msg[STUN_MAX_MESSAGE_SIZE];\n   int msgLen = sizeof(msg);\n\t\n   StunAtrString username;\n   StunAtrString password;\n\t\n   username.sizeValue = 0;\n   password.sizeValue = 0;\n\t\n#ifdef USE_TLS\n   stunGetUserNameAndPassword( dest, username, password );\n#endif\n\t\n   stunSendTest(myFd, dest, username, password, 1, 0/*false*/ );\n\t\n   StunAddress4 from;\n\t\n   getMessage( myFd, msg, &msgLen, &from.addr, &from.port,verbose );\n\t\n   StunMessage resp;\n   memset(&resp, 0, sizeof(StunMessage));\n\t\n   bool ok = stunParseMessage( msg, msgLen, resp,verbose );\n   if (!ok)\n   {\n      return -1;\n   }\n\t\n   StunAddress4 mappedAddr = resp.mappedAddress.ipv4;\n   StunAddress4 changedAddr = resp.changedAddress.ipv4;\n\t\n   //clog << \"--- stunOpenStunSocket --- \" << endl;\n   //clog << \"\\treq  id=\" << req.id << endl;\n   //clog << \"\\tresp id=\" << id << endl;\n   //clog << \"\\tmappedAddr=\" << mappedAddr << endl;\n\t\n   *mapAddr = mappedAddr;\n\t\n   return myFd;\n}\n\n\nbool\nstunOpenStunSocketPair( StunAddress4& dest, StunAddress4* mapAddr, \n                    int* fd1, int* fd2, \n                    int port, StunAddress4* srcAddr, \n                    bool verbose )\n{\n   assert( dest.addr!= 0 );\n   assert( dest.port != 0 );\n   assert( mapAddr );\n   \n   const int NUM=3;\n\t\n   if ( port == 0 )\n   {\n      port = randomPort();\n   }\n\t\n   *fd1=-1;\n   *fd2=-1;\n\t\n   char msg[STUN_MAX_MESSAGE_SIZE];\n   int msgLen =sizeof(msg);\n\t\n   StunAddress4 from;\n   int fd[NUM];\n   int i;\n\t\n   unsigned int interfaceIp = 0;\n   if ( srcAddr )\n   {\n      interfaceIp = srcAddr->addr;\n   }\n\n   for( i=0; i<NUM; i++)\n   {\n      fd[i] = openPort( (port == 0) ? 0 : (port + i), \n                        interfaceIp, verbose);\n      if (fd[i] < 0) \n      {\n         while (i > 0)\n         {\n            closesocket(fd[--i]);\n         }\n         return false;\n      }\n   }\n\t\n   StunAtrString username;\n   StunAtrString password;\n\t\n   username.sizeValue = 0;\n   password.sizeValue = 0;\n\t\n#ifdef USE_TLS\n   stunGetUserNameAndPassword( dest, username, password );\n#endif\n\t\n   for( i=0; i<NUM; i++)\n   {\n      stunSendTest(fd[i], dest, username, password, 1/*testNum*/, verbose );\n   }\n\t\n   StunAddress4 mappedAddr[NUM];\n   for( i=0; i<NUM; i++)\n   {\n      msgLen = sizeof(msg)/sizeof(*msg);\n      getMessage( fd[i],\n                  msg,\n                  &msgLen,\n                  &from.addr,\n                  &from.port ,verbose);\n\t\t\n      StunMessage resp;\n      memset(&resp, 0, sizeof(StunMessage));\n\t\t\n      bool ok = stunParseMessage( msg, msgLen, resp, verbose );\n      if (!ok) \n      {\n         return false;\n      }\n\t\t\n      mappedAddr[i] = resp.mappedAddress.ipv4;\n      StunAddress4 changedAddr = resp.changedAddress.ipv4;\n   }\n\t\n   if (verbose)\n   {               \n      clog << \"--- stunOpenStunSocketPair --- \" << endl;\n      for( i=0; i<NUM; i++)\n      {\n         clog << \"\\t mappedAddr=\" << mappedAddr[i] << endl;\n      }\n   }\n\t\n   if ( mappedAddr[0].port %2 == 0 )\n   {\n      if (  mappedAddr[0].port+1 ==  mappedAddr[1].port )\n      {\n         *mapAddr = mappedAddr[0];\n         *fd1 = fd[0];\n         *fd2 = fd[1];\n         closesocket( fd[2] );\n         return true;\n      }\n   }\n   else\n   {\n      if (( mappedAddr[1].port %2 == 0 )\n          && (  mappedAddr[1].port+1 ==  mappedAddr[2].port ))\n      {\n         *mapAddr = mappedAddr[1];\n         *fd1 = fd[1];\n         *fd2 = fd[2];\n         closesocket( fd[0] );\n         return true;\n      }\n   }\n\n   // something failed, close all and return error\n   for( i=0; i<NUM; i++)\n   {\n      closesocket( fd[i] );\n   }\n\t\n   return false;\n}\n\n/* ====================================================================\n * The Vovida Software License, Version 1.0 \n * \n * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * \n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n * \n * 3. The names \"VOCAL\", \"Vovida Open Communication Application Library\",\n *    and \"Vovida Open Communication Application Library (VOCAL)\" must\n *    not be used to endorse or promote products derived from this\n *    software without prior written permission. For written\n *    permission, please contact vocal@vovida.org.\n *\n * 4. Products derived from this software may not be called \"VOCAL\", nor\n *    may \"VOCAL\" appear in their name, without prior written\n *    permission of Vovida Networks, Inc.\n * \n * THIS SOFTWARE IS PROVIDED \"AS IS\" AND ANY EXPRESSED OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND\n * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA\n * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES\n * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n * DAMAGE.\n * \n * ====================================================================\n * \n * This software consists of voluntary contributions made by Vovida\n * Networks, Inc. and many individuals on behalf of Vovida Networks,\n * Inc.  For more information on Vovida Networks, Inc., please see\n * <http://www.vovida.org/>.\n *\n */\n\n// Local Variables:\n// mode:c++\n// c-file-style:\"ellemtel\"\n// c-file-offsets:((case-label . +))\n// indent-tabs-mode:nil\n// End:\n\n\n"
  },
  {
    "path": "src/stun/stun.h",
    "content": "#ifndef STUN_H\n#define STUN_H\n\n#include <iostream>\n#include <time.h>\n#include <string>\n\nusing namespace std;\n\n// if you change this version, change in makefile too \n#define STUN_VERSION \"0.94\"\n\n#define STUN_MAX_STRING 256\n#define STUN_MAX_UNKNOWN_ATTRIBUTES 8\n#define STUN_MAX_MESSAGE_SIZE 2048\n\n#define STUN_PORT 3478\n\n// define some basic types\ntypedef unsigned char  UInt8;\ntypedef unsigned short UInt16;\ntypedef unsigned int   UInt32;\n#if defined( WIN32 )\ntypedef unsigned __int64 UInt64;\n#else\ntypedef unsigned long long UInt64;\n#endif\ntypedef struct { unsigned char octet[16]; }  UInt128;\n\n/// define a structure to hold a stun address \nconst UInt8  IPv4Family = 0x01;\nconst UInt8  IPv6Family = 0x02;\n\n// define  flags  \nconst UInt32 ChangeIpFlag   = 0x04;\nconst UInt32 ChangePortFlag = 0x02;\n\n// define  stun attribute\nconst UInt16 MappedAddress    = 0x0001;\nconst UInt16 ResponseAddress  = 0x0002;\nconst UInt16 ChangeRequest    = 0x0003;\nconst UInt16 SourceAddress    = 0x0004;\nconst UInt16 ChangedAddress   = 0x0005;\nconst UInt16 Username         = 0x0006;\nconst UInt16 Password         = 0x0007;\nconst UInt16 MessageIntegrity = 0x0008;\nconst UInt16 ErrorCode        = 0x0009;\nconst UInt16 UnknownAttribute = 0x000A;\nconst UInt16 ReflectedFrom    = 0x000B;\nconst UInt16 XorMappedAddress = 0x0020;\nconst UInt16 XorOnly          = 0x0021;\nconst UInt16 ServerName       = 0x0022;\nconst UInt16 SecondaryAddress = 0x0050; // Non standard extention\n\n// define types for a stun message \nconst UInt16 BindRequestMsg               = 0x0001;\nconst UInt16 BindResponseMsg              = 0x0101;\nconst UInt16 BindErrorResponseMsg         = 0x0111;\nconst UInt16 SharedSecretRequestMsg       = 0x0002;\nconst UInt16 SharedSecretResponseMsg      = 0x0102;\nconst UInt16 SharedSecretErrorResponseMsg = 0x0112;\n\ntypedef struct \n{\n      UInt16 msgType;\n      UInt16 msgLength;\n      UInt128 id;\n} StunMsgHdr;\n\n\ntypedef struct\n{\n      UInt16 type;\n      UInt16 length;\n} StunAtrHdr;\n\ntypedef struct\n{\n      UInt16 port;\n      UInt32 addr;\n} StunAddress4;\n\ntypedef struct\n{\n      UInt8 pad;\n      UInt8 family;\n      StunAddress4 ipv4;\n} StunAtrAddress4;\n\ntypedef struct\n{\n      UInt32 value;\n} StunAtrChangeRequest;\n\ntypedef struct\n{\n      UInt16 pad; // all 0\n      UInt8 errorClass;\n      UInt8 number;\n      char reason[STUN_MAX_STRING];\n      UInt16 sizeReason;\n} StunAtrError;\n\ntypedef struct\n{\n      UInt16 attrType[STUN_MAX_UNKNOWN_ATTRIBUTES];\n      UInt16 numAttributes;\n} StunAtrUnknown;\n\ntypedef struct\n{\n      char value[STUN_MAX_STRING];      \n      UInt16 sizeValue;\n} StunAtrString;\n\ntypedef struct\n{\n      char hash[20];\n} StunAtrIntegrity;\n\ntypedef enum \n{\n   HmacUnkown=0,\n   HmacOK,\n   HmacBadUserName,\n   HmacUnkownUserName,\n   HmacFailed,\n} StunHmacStatus;\n\ntypedef struct\n{\n      StunMsgHdr msgHdr;\n\t\n      bool hasMappedAddress;\n      StunAtrAddress4  mappedAddress;\n\t\n      bool hasResponseAddress;\n      StunAtrAddress4  responseAddress;\n\t\n      bool hasChangeRequest;\n      StunAtrChangeRequest changeRequest;\n\t\n      bool hasSourceAddress;\n      StunAtrAddress4 sourceAddress;\n\t\n      bool hasChangedAddress;\n      StunAtrAddress4 changedAddress;\n\t\n      bool hasUsername;\n      StunAtrString username;\n\t\n      bool hasPassword;\n      StunAtrString password;\n\t\n      bool hasMessageIntegrity;\n      StunAtrIntegrity messageIntegrity;\n\t\n      bool hasErrorCode;\n      StunAtrError errorCode;\n\t\n      bool hasUnknownAttributes;\n      StunAtrUnknown unknownAttributes;\n\t\n      bool hasReflectedFrom;\n      StunAtrAddress4 reflectedFrom;\n\n      bool hasXorMappedAddress;\n      StunAtrAddress4  xorMappedAddress;\n\t\n      bool xorOnly;\n\n      bool hasServerName;\n      StunAtrString serverName;\n      \n      bool hasSecondaryAddress;\n      StunAtrAddress4 secondaryAddress;\n} StunMessage; \n\n\n// Define enum with different types of NAT \ntypedef enum \n{\n   StunTypeUnknown=0,\n   StunTypeOpen,\n   StunTypeConeNat,\n   StunTypeRestrictedNat,\n   StunTypePortRestrictedNat,\n   StunTypeSymNat,\n   StunTypeSymFirewall,\n   StunTypeBlocked,\n   StunTypeFailure\n} NatType;\n\n#ifdef WIN32\ntypedef SOCKET StunSocket;\n#else\ntypedef int StunSocket;\n#endif\n\n#define MAX_MEDIA_RELAYS 500\n#define MAX_RTP_MSG_SIZE 1500\n#define MEDIA_RELAY_TIMEOUT 3*60\n\ntypedef struct \n{\n      int relayPort;       // media relay port\n      int fd;              // media relay file descriptor\n      StunAddress4 destination; // NAT IP:port\n      time_t expireTime;      // if no activity after time, close the socket \n} StunMediaRelay;\n\ntypedef struct\n{\n      StunAddress4 myAddr;\n      StunAddress4 altAddr;\n      StunSocket myFd;\n      StunSocket altPortFd;\n      StunSocket altIpFd;\n      StunSocket altIpPortFd;\n      bool relay; // true if media relaying is to be done\n      StunMediaRelay relays[MAX_MEDIA_RELAYS];\n} StunServerInfo;\n\nbool\nstunParseMessage( char* buf, \n                  unsigned int bufLen, \n                  StunMessage& message, \n                  bool verbose );\n\nvoid\nstunBuildReqSimple( StunMessage* msg,\n                    const StunAtrString& username,\n                    bool changePort, bool changeIp, unsigned int id=0 );\n\nunsigned int\nstunEncodeMessage( const StunMessage& message, \n                   char* buf, \n                   unsigned int bufLen, \n                   const StunAtrString& password,\n                   bool verbose);\n\nvoid\nstunCreateUserName(const StunAddress4& addr, StunAtrString* username);\n\nvoid \nstunGetUserNameAndPassword(  const StunAddress4& dest, \n                             StunAtrString* username,\n                             StunAtrString* password);\n\nvoid\nstunCreatePassword(const StunAtrString& username, StunAtrString* password);\n\n// Twinkle\n// Build an error response\nStunMessage *stunBuildError(const StunMessage &m, int code, const char *reason);\n\n// Create a string representation of a STUN message\nstring stunMsg2Str(const StunMessage &m);\n\nbool stunEqualId(const StunMessage &m1, const StunMessage &m2);\n\nstring stunNatType2Str(NatType t);\n// Twinkle\n\nint \nstunRand();\n\nUInt64\nstunGetSystemTimeSecs();\n\n/// find the IP address of a the specified stun server - return false is fails parse \nbool  \nstunParseServerName( char* serverName, StunAddress4& stunServerAddr);\n\nbool \nstunParseHostName( char* peerName,\n                   UInt32& ip,\n                   UInt16& portVal,\n                   UInt16 defaultPort );\n\n/// return true if all is OK\n/// Create a media relay and do the STERN thing if startMediaPort is non-zero\nbool\nstunInitServer(StunServerInfo& info, \n               const StunAddress4& myAddr, \n               const StunAddress4& altAddr,\n               int startMediaPort,\n               bool verbose);\n\nvoid\nstunStopServer(StunServerInfo& info);\n\n/// return true if all is OK \nbool\nstunServerProcess(StunServerInfo& info, bool verbose);\n\n/// returns number of address found - take array or addres \nint \nstunFindLocalInterfaces(UInt32* addresses, int maxSize );\n\nvoid \nstunTest( StunAddress4& dest, int testNum, bool verbose, StunAddress4* srcAddr=0 );\n\nNatType\nstunNatType( StunAddress4& dest, bool verbose, \n             bool* preservePort=0, // if set, is return for if NAT preservers ports or not\n             bool* hairpin=0 ,  // if set, is the return for if NAT will hairpin packets\n             int port=0, // port to use for the test, 0 to choose random port\n             StunAddress4* sAddr=0 // NIC to use \n   );\n\n/// prints a StunAddress\nstd::ostream& \noperator<<( std::ostream& strm, const StunAddress4& addr);\n\nstd::ostream& \noperator<< ( std::ostream& strm, const UInt128& );\n\n\nbool\nstunServerProcessMsg( char* buf,\n                      unsigned int bufLen,\n                      StunAddress4& from, \n                      StunAddress4& myAddr,\n                      StunAddress4& altAddr, \n                      StunMessage* resp,\n                      StunAddress4* destination,\n                      StunAtrString* hmacPassword,\n                      bool* changePort,\n                      bool* changeIp,\n                      bool verbose);\n\nint\nstunOpenStunSocket( StunAddress4& dest, \n                StunAddress4* mappedAddr, \n                int port=0, \n                StunAddress4* srcAddr=0, \n                bool verbose=false );\n\nbool\nstunOpenStunSocketPair( StunAddress4& dest, StunAddress4* mappedAddr, \n                    int* fd1, int* fd2, \n                    int srcPort=0,  StunAddress4* srcAddr=0,\n                    bool verbose=false);\n\n#endif\n\n\n/* ====================================================================\n * The Vovida Software License, Version 1.0 \n * \n * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * \n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n * \n * 3. The names \"VOCAL\", \"Vovida Open Communication Application Library\",\n *    and \"Vovida Open Communication Application Library (VOCAL)\" must\n *    not be used to endorse or promote products derived from this\n *    software without prior written permission. For written\n *    permission, please contact vocal@vovida.org.\n *\n * 4. Products derived from this software may not be called \"VOCAL\", nor\n *    may \"VOCAL\" appear in their name, without prior written\n *    permission of Vovida Networks, Inc.\n * \n * THIS SOFTWARE IS PROVIDED \"AS IS\" AND ANY EXPRESSED OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND\n * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA\n * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES\n * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n * DAMAGE.\n * \n * ====================================================================\n * \n * This software consists of voluntary contributions made by Vovida\n * Networks, Inc. and many individuals on behalf of Vovida Networks,\n * Inc.  For more information on Vovida Networks, Inc., please see\n * <http://www.vovida.org/>.\n *\n */\n\n// Local Variables:\n// mode:c++\n// c-file-style:\"ellemtel\"\n// c-file-offsets:((case-label . +))\n// indent-tabs-mode:nil\n// End:\n\n"
  },
  {
    "path": "src/stun/stun_transaction.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cstring>\n#include \"stun_transaction.h\"\n#include \"events.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"sys_settings.h\"\n#include \"transaction_mgr.h\"\n#include \"translator.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nextern t_transaction_mgr\t*transaction_mgr;\nextern t_event_queue\t\t*evq_trans_layer;\nextern t_event_queue\t\t*evq_trans_mgr;\nextern t_event_queue\t\t*evq_sender;\nextern t_phone\t\t\t*phone;\n\n\nbool get_stun_binding(t_user *user_config, unsigned short src_port, IPaddr &mapped_ip,\n\tunsigned short &mapped_port, int &err_code, string &err_reason)\n{\n\tlist<t_ip_port> destinations = \n\t\tuser_config->get_stun_server().get_h_ip_srv(\"udp\");\n\t\n\tif (destinations.empty()) {\n\t\t// Cannot resolve STUN server address.\n\t\tlog_file->write_header(\"::get_stun_binding\", LOG_NORMAL, LOG_CRITICAL);\n\t\tlog_file->write_raw(\"Failed to resolve: \");\n\t\tlog_file->write_raw(user_config->get_stun_server().encode());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"Return internal STUN bind error: 404 Not Found\");\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\terr_code = 404;\n\t\terr_reason = \"Not Found\";\n\t\treturn false;\n\t}\n\t\n\tint num_transmissions = 0;\n\tint wait_intval = DUR_STUN_START_INTVAL;\n\n\tt_socket_udp sock(src_port);\n\tsock.connect(destinations.front().ipaddr, destinations.front().port);\n\t\t\n\t// Build STUN request\n\tchar buf[STUN_MAX_MESSAGE_SIZE + 1];\n\tStunMessage req_bind;\n\tStunAtrString stun_null_str;\n\tstun_null_str.sizeValue = 0;\t\n\tstunBuildReqSimple(&req_bind, stun_null_str, false, false);\t\n\tchar req_msg[STUN_MAX_MESSAGE_SIZE];\n\tint req_msg_size = stunEncodeMessage(req_bind, req_msg, \n\t\tSTUN_MAX_MESSAGE_SIZE, stun_null_str, false);\n\t\t\n\t// Send STUN request and retransmit till a response is received.\n\twhile (num_transmissions < STUN_MAX_TRANSMISSIONS) {\n\t\tbool ret;\n\n\t\ttry {\n\t\t\tsock.send(req_msg, req_msg_size);\n\t\t}\n\t\tcatch (int err) {\n\t\t\t// Socket error (probably ICMP error)\n\t\t\t// Failover to next destination\n\t\t\tlog_file->write_report(\"Send failed. Failover to next destination.\",\n\t\t\t\t\t\"::get_stun_binding\");\t\n\t\t\t\t\t\t\n\t\t\tdestinations.pop_front();\n\t\t\tif (destinations.empty()) {\n\t\t\t\tlog_file->write_report(\"No next destination for failover.\",\n\t\t\t\t\t\"::get_stun_binding\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tnum_transmissions = 0;\n\t\t\twait_intval = DUR_STUN_START_INTVAL;\n\t\t\tsock.connect(destinations.front().ipaddr, destinations.front().port);\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tlog_file->write_header(\"::get_stun_binding\", LOG_STUN);\n\t\tlog_file->write_raw(\"Send to: \");\n\t\tlog_file->write_raw(h_ip2str(destinations.front().ipaddr));\n\t\tlog_file->write_raw(\":\");\n\t\tlog_file->write_raw(destinations.front().port);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(stunMsg2Str(req_bind));\n\t\tlog_file->write_footer();\n\t\t\t\n\t\ttry {\n\t\t\tret = sock.select_read(wait_intval);\n\t\t}\n\t\tcatch (int err) {\n\t\t\t// Socket error (probably ICMP error)\n\t\t\t// Failover to next destination\n\t\t\tlog_file->write_report(\"Select failed. Failover to next destination.\",\n\t\t\t\t\t\"::get_stun_binding\");\t\n\t\t\t\t\t\t\n\t\t\tdestinations.pop_front();\n\t\t\tif (destinations.empty()) {\n\t\t\t\tlog_file->write_report(\"No next destination for failover.\",\n\t\t\t\t\t\"::get_stun_binding\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tnum_transmissions = 0;\n\t\t\twait_intval = DUR_STUN_START_INTVAL;\n\t\t\tsock.connect(destinations.front().ipaddr, destinations.front().port);\n\t\t\tcontinue;\n\t\t}\n\t\t\t\n\t\tif (!ret) {\n\t\t\t// Time out\n\t\t\tnum_transmissions++;\n\t\t\tif (wait_intval < DUR_STUN_MAX_INTVAL) {\n\t\t\t\twait_intval *= 2;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t\t\n\t\t// A message has been received\n\t\tint resp_msg_size;\n\t\ttry {\n\t\t\tresp_msg_size = sock.recv(buf, STUN_MAX_MESSAGE_SIZE + 1);\n\t\t}\n\t\tcatch (int err) {\n\t\t\t// Socket error (probably ICMP error)\n\t\t\t// Failover to next destination\n\t\t\tlog_file->write_report(\"Recv failed. Failover to next destination.\",\n\t\t\t\t\t\"::get_stun_binding\");\t\n\t\t\t\t\t\t\n\t\t\tdestinations.pop_front();\n\t\t\tif (destinations.empty()) {\n\t\t\t\tlog_file->write_report(\"No next destination for failover.\",\n\t\t\t\t\t\"::get_stun_binding\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tnum_transmissions = 0;\n\t\t\twait_intval = DUR_STUN_START_INTVAL;\n\t\t\tsock.connect(destinations.front().ipaddr, destinations.front().port);\n\t\t\tcontinue;\n\t\t}\n\t\t\t\n\t\tStunMessage resp_bind;\n\t\t\n\t\tif (!stunParseMessage(buf, resp_msg_size, resp_bind, false)) {\n\t\t\tlog_file->write_report(\n\t\t\t\t\"Received faulty STUN message\", \"::get_stun_binding\", \n\t\t\t\t\tLOG_STUN);\n\t\t\tnum_transmissions++;\n\t\t\tif (wait_intval < DUR_STUN_MAX_INTVAL) {\n\t\t\t\twait_intval *= 2;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tlog_file->write_header(\"::get_stun_binding\", LOG_STUN);\n\t\tlog_file->write_raw(\"Received from: \");\n\t\tlog_file->write_raw(h_ip2str(destinations.front().ipaddr));\n\t\tlog_file->write_raw(\":\");\n\t\tlog_file->write_raw(destinations.front().port);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(stunMsg2Str(resp_bind));\n\t\tlog_file->write_footer();\n\t\t\n\t\t// Check if id in msgHdr matches\n\t\tif (!stunEqualId(resp_bind, req_bind)) {\n\t\t\tnum_transmissions++;\n\t\t\tif (wait_intval < DUR_STUN_MAX_INTVAL) {\n\t\t\t\twait_intval *= 2;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t\t\t\n\t\tif (resp_bind.msgHdr.msgType == BindResponseMsg && \n\t\t    resp_bind.hasMappedAddress) {\n\t\t    \t// Bind response received\n\t\t\tmapped_ip = resp_bind.mappedAddress.ipv4.addr;\n\t\t\tmapped_port = resp_bind.mappedAddress.ipv4.port;\n\t\t\treturn true;\n\t\t}\n\t\t\t\n\t\tif (resp_bind.msgHdr.msgType == BindErrorResponseMsg &&\n\t\t    resp_bind.hasErrorCode) \n\t\t{\n\t\t\t// Bind error received\n\t\t\terr_code = resp_bind.errorCode.errorClass * 100 +\n\t\t\t\t   resp_bind.errorCode.number;\n\t\t\tchar s[STUN_MAX_STRING + 1];\n\t\t\tstrncpy(s, resp_bind.errorCode.reason, STUN_MAX_STRING);\n\t\t\ts[STUN_MAX_STRING] = 0;\n\t\t\terr_reason = s;\n\t\t\treturn false;\n\t\t}\n\t\t\t\n\t\t// A wrong response has been received.\n\t\tlog_file->write_report(\n\t\t\t\"Invalid STUN response received\", \"::get_stun_binding\", \n\t\t\t\tLOG_NORMAL);\n\n\t\terr_code = 500;\n\t\terr_reason = \"Server Error\";\n\t\treturn false;\n\t}\n\t\t\n\t// Request timed out\n\tlog_file->write_report(\"STUN request timeout\", \"::get_stun_binding\", \n\t\t\tLOG_NORMAL);\n\t\t\t\t\n\terr_code = 408;\n\terr_reason = \"Request Timeout\";\n\treturn false;\n}\n\nbool stun_discover_nat(t_phone_user *pu, string &err_msg) {\n\tt_user *user_config = pu->get_user_profile();\n\n\t// By default enable STUN. If for some reason we cannot perform\n\t// NAT discovery, then enable STUN. It will not harm, but only\n\t// create non-needed STUN transactions if we are not behind a NAT.\n\tpu->use_stun = true;\n\tpu->use_nat_keepalive = true;\n\n\tlist<t_ip_port> destinations = \n\t\tuser_config->get_stun_server().get_h_ip_srv(\"udp\");\n\n\tif (destinations.empty()) {\n\t\t// Cannot resolve STUN server address.\n\t\tlog_file->write_header(\"::main\", LOG_NORMAL, LOG_CRITICAL);\n\t\tlog_file->write_raw(\"Failed to resolve: \");\n\t\tlog_file->write_raw(user_config->get_stun_server().encode());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\n\t\terr_msg = TRANSLATE(\"Cannot resolve STUN server: %1\");\n\t\terr_msg = replace_first(err_msg, \"%1\", user_config->get_stun_server().encode());\n\t\treturn false;\n\t}\n\n\twhile (!destinations.empty()) {\n\t\tStunAddress4 stun_ip4;\n\t\tstun_ip4.addr = destinations.front().ipaddr;\n\t\tstun_ip4.port = destinations.front().port;\n\t\t\n\t\tNatType nat_type = stunNatType(stun_ip4, false);\n\t\tlog_file->write_header(\"::main\");\n\t\tlog_file->write_raw(\"STUN NAT type discovery for \");\n\t\tlog_file->write_raw(user_config->get_profile_name());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"NAT type: \");\n\t\tlog_file->write_raw(stunNatType2Str(nat_type));\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tswitch (nat_type) {\n\t\tcase StunTypeOpen:\n\t\t\t// STUN is not needed.\n\t\t\tpu->use_stun = false;\n\t\t\tpu->use_nat_keepalive = false;\n\t\t\treturn true;\n\t\tcase StunTypeSymNat:\n\t\t\terr_msg = TRANSLATE(\"You are behind a symmetric NAT.\\nSTUN will not work.\\nConfigure a public IP address in the user profile\\nand create the following static bindings (UDP) in your NAT.\");\n\t\t\terr_msg += \"\\n\\n\";\n\t\t\terr_msg += TRANSLATE(\"public IP: %1 --> private IP: %2 (SIP signaling)\");\n\t\t\terr_msg = replace_first(err_msg, \"%1\", int2str(sys_config->get_sip_port()));\n\t\t\terr_msg = replace_first(err_msg, \"%2\", int2str(sys_config->get_sip_port()));\n\t\t\terr_msg += \"\\n\";\n\t\t\terr_msg += TRANSLATE(\"public IP: %1-%2 --> private IP: %3-%4 (RTP/RTCP)\");\n\t\t\terr_msg = replace_first(err_msg, \"%1\", int2str(sys_config->get_rtp_port()));\n\t\t\terr_msg = replace_first(err_msg, \"%2\", int2str(sys_config->get_rtp_port() + 5));\n\t\t\terr_msg = replace_first(err_msg, \"%3\", int2str(sys_config->get_rtp_port()));\n\t\t\terr_msg = replace_first(err_msg, \"%4\", int2str(sys_config->get_rtp_port() + 5));\n\t\t\t\n\t\t\tpu->use_stun = false;\n\t\t\tpu->use_nat_keepalive = false;\n\t\t\treturn false;\n\t\tcase StunTypeSymFirewall:\n\t\t\t// STUN is not needed as we are on a pubic IP.\n\t\t\t// NAT keep alive is needed however to keep the firewall open.\n\t\t\tpu->use_stun = false;\n\t\t\treturn true;\n\t\tcase StunTypeBlocked:\n\t\t\tdestinations.pop_front();\n\t\t\t\n\t\t\t// The code for NAT type discovery does not handle\n\t\t\t// ICMP errors. So if the conclusion is that the network\n\t\t\t// connection is blocked, it might be due to a down STUN\n\t\t\t// server. Try alternative destination if avaliable.\n\t\t\n\t\t\tif (destinations.empty()) {\n\t\t\t\terr_msg = TRANSLATE(\"Cannot reach the STUN server: %1\");\n\t\t\t\terr_msg = replace_first(err_msg, \"%1\",\n\t\t\t\t\t\tuser_config->get_stun_server().encode());\n\t\t\t\terr_msg += \"\\n\\n\";\n\t\t\t\terr_msg += TRANSLATE(\"If you are behind a firewall then you need to open the following UDP ports.\");\n\t\t\t\terr_msg += \"\\n\";\n\t\t\t\terr_msg += TRANSLATE(\"Port %1 (SIP signaling)\");\n\t\t\t\terr_msg = replace_first(err_msg, \"%1\",\n\t\t\t\t\t\tint2str(sys_config->get_sip_port()));\n\t\t\t\terr_msg += \"\\n\";\n\t\t\t\terr_msg += TRANSLATE(\"Ports %1-%2 (RTP/RTCP)\");\n\t\t\t\terr_msg = replace_first(err_msg, \"%1\",\n\t\t\t\t\t\tint2str(sys_config->get_rtp_port()));\n\t\t\t\terr_msg = replace_first(err_msg, \"%2\",\n\t\t\t\t\t\tint2str(sys_config->get_rtp_port() + 5));\n\t\t\t\t\t\t\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\t\"::stun_discover_nat\");\t\t\t\n\t\t\tbreak;\n\t\tcase StunTypeFailure:\n\t\t\tdestinations.pop_front();\n\t\t\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\t\"::stun_discover_nat\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Use STUN.\n\t\t\treturn true;\n\t\t}\n\t}\n\n\terr_msg = TRANSLATE(\"NAT type discovery via STUN failed.\");\t\n\treturn false;\n}\n\n\n// Main function for STUN listener thread for media STUN requests.\nvoid *stun_listen_main(void *arg) {\n\tchar\t\tbuf[STUN_MAX_MESSAGE_SIZE + 1];\n\tint\t\tdata_size;\n\t\n\tt_socket_udp *sock = (t_socket_udp *)arg;\n\t\n\twhile(true) {\n\t\ttry {\n\t\t\tdata_size = sock->recv(buf, STUN_MAX_MESSAGE_SIZE + 1);\n\t\t} catch (int err) {\n\t\t\tstring msg(\"Failed to receive STUN response for media.\\n\");\n\t\t\tmsg += get_error_str(err);\n\t\t\tlog_file->write_report(msg, \"::stun_listen_main\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\t\n\t\t\t// The request will timeout, no need to send a response now.\n\t\t\t\t\n\t\t\treturn NULL;\n\t\t}\n\t\t\n\t\tStunMessage m;\n\t\t\n\t\tif (!stunParseMessage(buf, data_size, m, false)) {\n\t\t\tlog_file->write_report(\"Faulty STUN message\", \"::stun_listen_main\");\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tlog_file->write_header(\"::stun_listen_main\", LOG_STUN);\n\t\tlog_file->write_raw(\"Received: \");\n\t\tlog_file->write_raw(stunMsg2Str(m));\n\t\tlog_file->write_footer();\n\t\n\t\tevq_trans_mgr->push_stun_response(&m, 0, 0);\n\t}\n}\n\n//////////////////////////////////////////////\n// Base STUN transaction\n//////////////////////////////////////////////\n\nt_mutex t_stun_transaction::mtx_class;\nt_tid t_stun_transaction::next_id = 1;\n\nt_stun_transaction::t_stun_transaction(t_user *user, StunMessage *r,\n\t\t\t   unsigned short _tuid, const list<t_ip_port> &dst) \n{\n\tmtx_class.lock();\n\tid = next_id++;\n\tif (next_id == 65535) next_id = 1;\n\tmtx_class.unlock();\n\t\n\tstate = TS_NULL;\n\trequest = new StunMessage(*r);\n\tMEMMAN_NEW(request);\n\ttuid = _tuid;\n\t\n\tdur_req_timeout = DUR_STUN_START_INTVAL;\n\tnum_transmissions = 0;\n\t\n\tdestinations = dst;\n\t\n\tuser_config = user->copy();\n}\n\nt_stun_transaction::~t_stun_transaction() {\n\tMEMMAN_DELETE(request);\n\tdelete request;\n\tMEMMAN_DELETE(user_config);\n\tdelete user_config;\n}\n\nt_tid t_stun_transaction::get_id(void) const {\n\treturn id;\n}\n\nt_trans_state t_stun_transaction::get_state(void) const {\n\treturn state;\n}\n\nvoid t_stun_transaction::start_timer_req_timeout(void) {\n\ttimer_req_timeout = transaction_mgr->start_stun_timer(dur_req_timeout,\n\t\tSTUN_TMR_REQ_TIMEOUT, id);\n\t\t\n\t// RFC 3489 9.3\n\t// Double the retransmision interval till a maximum\n\tif (dur_req_timeout < DUR_STUN_MAX_INTVAL) {\n\t\tdur_req_timeout = 2 * dur_req_timeout;\n\t}\n}\n\nvoid t_stun_transaction::stop_timer_req_timeout(void) {\n\tif (timer_req_timeout) {\n\t\ttransaction_mgr->stop_timer(timer_req_timeout);\n\t\ttimer_req_timeout = 0;\n\t}\n}\n\nvoid t_stun_transaction::process_response(StunMessage *r) {\n\tstop_timer_req_timeout();\n\tevq_trans_layer->push_stun_response(r, tuid, id);\n\tstate = TS_TERMINATED;\n}\n\nvoid t_stun_transaction::process_icmp(const t_icmp_msg &icmp) {\n\tstop_timer_req_timeout();\n\t\n\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\t\"t_stun_transaction::process_icmp\");\t\n\t\t\t\t\n\tdestinations.pop_front();\n\tif (destinations.empty()) {\n\t\tlog_file->write_report(\"No next destination for failover.\",\n\t\t\t\t\"t_stun_transaction::process_icmp\");\n\t\t\t\t\t\t\n\t\tlog_file->write_header(\"t_stun_transaction::process_icmp\",\n\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"ICMP error received.\\n\\n\");\n\t\tlog_file->write_raw(\"Send internal: 500 Server Error\\n\");\n\t\tlog_file->write_footer();\t\n\t\n\t\t// No server could be reached, Notify the TU with 500 Server\n\t\t// Error.\n\t\tStunMessage *resp = stunBuildError(*request, 500, \"Server Error\");\n\t\tevq_trans_layer->push_stun_response(resp, tuid, id);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\tstate = TS_TERMINATED;\n\t\treturn;\n\t}\n\t\n\t// Failover to next destination\n\tevq_sender->push_stun_request(user_config, request, TYPE_STUN_SIP, tuid, id,\n\t\tdestinations.front().ipaddr, destinations.front().port);\n\tnum_transmissions = 1;\n\tdur_req_timeout = DUR_STUN_START_INTVAL;\n\tstart_timer_req_timeout();\n}\n\nvoid t_stun_transaction::timeout(t_stun_timer t) {\n\t// RFC 3489 9.3\n\tif (num_transmissions < STUN_MAX_TRANSMISSIONS) {\n\t\tretransmit();\n\t\tstart_timer_req_timeout();\n\t\treturn;\n\t}\n\t\n\t// Report timeout to TU\n\tStunMessage *timeout_resp;\n\ttimeout_resp = stunBuildError(*request, 408, \"Request Timeout\");\n\tlog_file->write_report(\"STUN request timeout\", \"t_stun_transaction::timeout\", \n\t\t\tLOG_NORMAL);\n\t\n\tevq_trans_layer->push_stun_response(timeout_resp, tuid, id);\n\tMEMMAN_DELETE(timeout_resp);\n\tdelete timeout_resp;\n\t\n\tstate = TS_TERMINATED;\n}\n\nbool t_stun_transaction::match(StunMessage *resp) const {\n\treturn stunEqualId(*resp, *request);\n}\n\n// An ICMP error matches a transaction when the destination IP address/port\n// of the packet that caused the ICMP error equals the destination \n// IP address/port of the transaction. Other information of the packet causing\n// the ICMP error is not available.\n// In theory when multiple transactions are open for the same destination, the\n// wrong transaction may process the ICMP error. In practice this should rarely\n// happen as the destination will be unreachable for all those transactions.\n// If it happens a transaction gets aborted.\nbool t_stun_transaction::match(const t_icmp_msg &icmp) const {\n\treturn (destinations.front().ipaddr == icmp.ipaddr && \n\t        destinations.front().port == icmp.port);\n}\n\n//////////////////////////////////////////////\n// SIP STUN transaction\n//////////////////////////////////////////////\n\nvoid t_sip_stun_trans::retransmit(void) {\n\t// The SIP UDP sender will send out the STUN request.\n\tevq_sender->push_stun_request(user_config, request, TYPE_STUN_SIP, tuid, id,\n\t\tdestinations.front().ipaddr, destinations.front().port);\n\tnum_transmissions++;\n}\n\nt_sip_stun_trans::t_sip_stun_trans(t_user *user, StunMessage *r,\n\t\t\tunsigned short _tuid, const list<t_ip_port> &dst) :\n\t\tt_stun_transaction(user, r, _tuid, dst)\n{\n\t// The SIP UDP sender will send out the STUN request.\n\tevq_sender->push_stun_request(user_config, request, TYPE_STUN_SIP, tuid, id,\n\t\tdestinations.front().ipaddr, destinations.front().port);\n\tnum_transmissions++;\n\tstart_timer_req_timeout();\t\n\tstate = TS_PROCEEDING;\n}\n\n//////////////////////////////////////////////\n// Media STUN transaction\n//////////////////////////////////////////////\n\n// TODO: this code is not used anymore. Remove?\n\nvoid t_media_stun_trans::retransmit(void) {\n\t// Retransmit the STUN request\n\tStunAtrString stun_pass;\n\tstun_pass.sizeValue = 0;\n\tchar m[STUN_MAX_MESSAGE_SIZE];\n\tint msg_size = stunEncodeMessage(*request, m, STUN_MAX_MESSAGE_SIZE, stun_pass, false);\n\t\n\ttry {\n\t\tsock->sendto(destinations.front().ipaddr, destinations.front().port, \n\t\t\tm, msg_size);\n\t} catch (int err) {\n\t\tstring msg(\"Failed to send STUN request for media.\\n\");\n\t\tmsg += get_error_str(err);\n\t\tlog_file->write_report(msg, \"::t_media_stun_trans::retransmit\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\n\t\tStunMessage *resp;\n\t\tresp = stunBuildError(*request, 500, \"Could not send request\");\n\n\t\tevq_trans_layer->push_stun_response(resp, tuid, id);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\t\n\t\treturn;\n\t}\n\t\n\tnum_transmissions++;\n}\n\nt_media_stun_trans::t_media_stun_trans(t_user *user, StunMessage *r,\n\t\t\t unsigned short _tuid, const list<t_ip_port> &dst, \n\t\t\t unsigned short src_port) :\n\t\tt_stun_transaction(user, r, _tuid, dst)\n{\n\tthr_listen = NULL;\n\t\n\ttry {\n\t\tsock = new t_socket_udp(src_port);\n\t\tMEMMAN_NEW(sock);\n\t\tsock->connect(destinations.front().ipaddr, destinations.front().port);\n\t} catch (int err) {\n\t\tstring msg(\"Failed to create a UDP socket (STUN) on port \");\n\t\tmsg += int2str(src_port);\n\t\tmsg += \"\\n\";\n\t\tmsg += get_error_str(err);\n\t\tlog_file->write_report(msg, \"t_media_stun_trans::t_media_stun_trans\", LOG_NORMAL, \n\t\t\tLOG_CRITICAL);\n\t\tdelete sock;\n\t\tsock = NULL;\n\t\t\n\t\tStunMessage *resp;\n\t\tresp = stunBuildError(*request, 500, \"Could not create socket\");\n\n\t\tevq_trans_layer->push_stun_response(resp, tuid, id);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\t// Send STUN request\n\tStunAtrString stun_pass;\n\tstun_pass.sizeValue = 0;\n\tchar m[STUN_MAX_MESSAGE_SIZE];\n\tint msg_size = stunEncodeMessage(*r, m, STUN_MAX_MESSAGE_SIZE, stun_pass, false);\n\t\n\ttry {\n\t\tsock->send(m, msg_size);\n\t} catch (int err) {\n\t\tstring msg(\"Failed to send STUN request for media.\\n\");\n\t\tmsg += get_error_str(err);\n\t\tlog_file->write_report(msg, \"::t_media_stun_trans::t_media_stun_trans\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\n\t\tStunMessage *resp;\n\t\tresp = stunBuildError(*request, 500, \"Failed to send request\");\n\n\t\tevq_trans_layer->push_stun_response(resp, tuid, id);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\t\n\t\treturn;\n\t}\n\t\n\tnum_transmissions++;\n\t\n\ttry {\n\t\tthr_listen = new t_thread(stun_listen_main, sock);\n\t\tMEMMAN_NEW(thr_listen);\n\t} catch (int) {\n\t\tlog_file->write_report(\"Failed to create STUN listener thread.\",\n\t\t\t\"::t_media_stun_trans::t_media_stun_trans\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tdelete thr_listen;\n\t\tthr_listen = NULL;\n\n\t\tStunMessage *resp;\n\t\tresp = stunBuildError(*request, 500, \"Failed to create STUN listen thread\");\n\n\t\tevq_trans_layer->push_stun_response(resp, tuid, id);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\t\t\n\t\t\n\t\treturn;\n\t}\n\t\n\tstart_timer_req_timeout();\n\tstate = TS_PROCEEDING;\n}\n\nt_media_stun_trans::~t_media_stun_trans() {\n\tif (sock) {\n\t\tMEMMAN_DELETE(sock);\n\t\tdelete sock;\n\t}\n\t\n\tif (thr_listen) {\n\t\tthr_listen->cancel();\n\t\tthr_listen->join();\n\t\tMEMMAN_DELETE(thr_listen);\n\t\tdelete thr_listen;\n\t}\n}\n\n\n"
  },
  {
    "path": "src/stun/stun_transaction.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _STUN_TRANSACTION_H\n#define _STUN_TRANSACTION_H\n\n#include \"stun.h\"\n#include \"phone_user.h\"\n#include \"protocol.h\"\n#include \"user.h\"\n#include \"transaction.h\"\n#include \"threads/mutex.h\"\n#include \"threads/thread.h\"\n#include \"sockets/ipaddr.h\"\n#include \"sockets/socket.h\"\n#include \"sockets/url.h\"\n\n// Create a binding in a NAT.\n// Returns true on success. Returns false when the STUN server returned\n// an error. Throws an int exception (containing errno) when some\n// socket operation fails.\nbool get_stun_binding(t_user *user_config, unsigned short src_port, IPaddr &mapped_ip,\n\tunsigned short &mapped_port, int &err_code, string &err_reason);\n\t\n\t\n// Discover the type of NAT and determine is STUN should be used or\n// wether STUN is useless.\n// It sets the use_stun attribute of phone if STUN should be used.\n// Return false if STUN cannot be used. err_msg will contain an\n// error message that can be displayed to the user.\nbool stun_discover_nat(t_phone_user *pu, string &err_msg);\n\n\n//////////////////////////////////////////////\n// Base STUN transaction\n//////////////////////////////////////////////\n\nclass t_stun_transaction {\nprivate:\n\tstatic t_mutex\t\tmtx_class; // protect static members\n\tstatic t_tid\t\tnext_id; // next id to be issued\n\nprotected:\n\tt_tid\t\t\tid; \t// transaction id\n\tunsigned short\t\ttuid;\t// TU id\n\tt_trans_state\t\tstate;\n\t\n\t// Timer for retransmissions\n\tunsigned short\t\ttimer_req_timeout;\n\t\n\t// Duration for the next timer start in msec\n\tunsigned long\t\tdur_req_timeout;\n\t\n\t// Number of transmissions of the request\n\tunsigned short\t\tnum_transmissions;\n\n\t// Destinations for the request\t\n\tlist<t_ip_port> \tdestinations;\n\t\n\t// User profile of user that created this transaction\n\tt_user\t\t\t*user_config;\n\t\n\tvoid start_timer_req_timeout(void);\n\tvoid stop_timer_req_timeout(void);\n\t\n\t// Retransmit STUN request\n\tvirtual void retransmit(void) = 0;\n\npublic:\n\tStunMessage\t\t*request;\n\n\tt_tid get_id(void) const;\n\t\n\t// Get state of the transaction\n\tt_trans_state get_state(void) const;\n\n\t// The transaction will keep a copy of the request\n\tt_stun_transaction(t_user *user, StunMessage *r,\n\t\t\t   unsigned short _tuid, const list<t_ip_port> &dst);\n\n\t// All request and response pointers contained by the\n\t// transaction will be deleted.\n\tvirtual ~t_stun_transaction();\n\n\t// Process STUN response\n\tvirtual void process_response(StunMessage *r);\n\t\n\t// Process ICMP error\n\tvirtual void process_icmp(const t_icmp_msg &icmp);\n\t\n\t// Process timeout\n\tvirtual void timeout(t_stun_timer t);\t\n\t\n\t// Match response with transaction\n\tbool match(StunMessage *resp) const;\n\t\n\t// Match ICMP error with transaction\n\tbool match(const t_icmp_msg &icmp) const;\n};\n\n//////////////////////////////////////////////\n// SIP STUN transaction\n//////////////////////////////////////////////\n\n// A SIP STUN transaction is a STUN request to get a binding\n// for the SIP port. Such a request must be sent from the SIP port.\n// So it must be sent out via the SIP sender thread.\n\nclass t_sip_stun_trans : public t_stun_transaction {\nprotected:\n\tvirtual void retransmit(void);\n\t\npublic:\n\t// Create transaction and send out STUN request\t\t\n\tt_sip_stun_trans(t_user *user, StunMessage *r,\n\t\t\t unsigned short _tuid, const list<t_ip_port> &dst);\n};\n\n//////////////////////////////////////////////\n// Media STUN transaction\n//////////////////////////////////////////////\n\n// TODO: this code is not used anymore. Remove?\n\n// A media STUN transaction is a STUN request to get a binding\n// for a media port. Such a request must be sent from the media\n// port.\n\nclass t_media_stun_trans : public t_stun_transaction {\nprivate:\n\tt_socket_udp\t*sock;\t\t// UDP socket for STUN\n\tt_thread\t*thr_listen;\t// Listener thread\n\t\nprotected:\n\tvirtual void retransmit(void);\n\t\npublic:\n\t// Create transaction and send out STUN request\t\t\n\tt_media_stun_trans(t_user *user, StunMessage *r,\n\t\t\t unsigned short _tuid, const list<t_ip_port> &dst, \n\t\t\t unsigned short src_port);\n\t~t_media_stun_trans();\n};\n\n#endif\n"
  },
  {
    "path": "src/stun/udp.cxx",
    "content": "#include <cassert>\n#include <cstdio>\n#include <cstring>\n#include <errno.h>\n#include <iostream>\n#include <cstdlib>\n#include <time.h>\n\n#ifdef WIN32\n\n#include <winsock2.h>\n#include <stdlib.h>\n#include <io.h>\n\n#else\n\n#include <arpa/inet.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <netdb.h>\n#include <string.h>\n#include <unistd.h>\n\n#endif\n\n#include <string.h>\n\n#include \"udp.h\"\n\nusing namespace std;\n\n\nStunSocket\nopenPort( unsigned short port, unsigned int interfaceIp, bool verbose )\n{\n   StunSocket fd;\n    \n   fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);\n   if ( fd == INVALID_SOCKET )\n   {\n      int err = getErrno();\n      cerr << \"Could not create a UDP socket:\" << err << endl;\n      return INVALID_SOCKET;\n   }\n    \n   struct sockaddr_in addr;\n   memset((char*) &(addr),0, sizeof((addr)));\n   addr.sin_family = AF_INET;\n   addr.sin_addr.s_addr = htonl(INADDR_ANY);\n   addr.sin_port = htons(port);\n    \n   if ( (interfaceIp != 0) && \n        ( interfaceIp != 0x100007f ) )\n   {\n      addr.sin_addr.s_addr = htonl(interfaceIp);\n      if (verbose )\n      {\n         clog << \"Binding to interface \" \n              << hex << \"0x\" << htonl(interfaceIp) << dec << endl;\n      }\n   }\n\t\n   if ( ::bind( fd,(struct sockaddr*)&addr, sizeof(addr)) != 0 )\n   {\n      int e = getErrno();\n        \n      switch (e)\n      {\n         case 0:\n         {\n            cerr << \"Could not bind socket\" << endl;\n            return INVALID_SOCKET;\n         }\n         case EADDRINUSE:\n         {\n            cerr << \"Port \" << port << \" for receiving UDP is in use\" << endl;\n            return INVALID_SOCKET;\n         }\n         break;\n         case EADDRNOTAVAIL:\n         {\n            if ( verbose ) \n            {\n               cerr << \"Cannot assign requested address\" << endl;\n            }\n            return INVALID_SOCKET;\n         }\n         break;\n         default:\n         {\n            cerr << \"Could not bind UDP receive port\"\n                 << \"Error=\" << e << \" \" << strerror(e) << endl;\n            return INVALID_SOCKET;\n         }\n         break;\n      }\n   }\n   if ( verbose )\n   {\n      clog << \"Opened port \" << port << \" with fd \" << fd << endl;\n   }\n   \n   assert( fd != INVALID_SOCKET  );\n\t\n   return fd;\n}\n\n\nbool \ngetMessage( StunSocket fd, char* buf, int* len,\n            unsigned int* srcIp, unsigned short* srcPort,\n            bool verbose)\n{\n   assert( fd != INVALID_SOCKET );\n\t\n   int originalSize = *len;\n   assert( originalSize > 0 );\n   \n   struct sockaddr_in from;\n   int fromLen = sizeof(from);\n\t\n   *len = recvfrom(fd,\n                   buf,\n                   originalSize,\n                   0,\n                   (struct sockaddr *)&from,\n                   (socklen_t*)&fromLen);\n\t\n   if ( *len == SOCKET_ERROR )\n   {\n      int err = getErrno();\n\t\t\n      switch (err)\n      {\n         case ENOTSOCK:\n            cerr << \"Error fd not a socket\" <<   endl;\n            break;\n         case ECONNRESET:\n            cerr << \"Error connection reset - host not reachable\" <<   endl;\n            break;\n\t\t\t\t\n         default:\n            cerr << \"StunSocket Error=\" << err << endl;\n      }\n\t\t\n      return false;\n   }\n\t\n   if ( *len < 0 )\n   {\n      clog << \"socket closed? negative len\" << endl;\n      return false;\n   }\n    \n   if ( *len == 0 )\n   {\n      clog << \"socket closed? zero len\" << endl;\n      return false;\n   }\n    \n   *srcPort = ntohs(from.sin_port);\n   *srcIp = ntohl(from.sin_addr.s_addr);\n\t\n   if ( (*len)+1 >= originalSize )\n   {\n      if (verbose)\n      {\n         clog << \"Received a message that was too large\" << endl;\n      }\n      return false;\n   }\n   buf[*len]=0;\n    \n   return true;\n}\n\n\nbool \nsendMessage( StunSocket fd, char* buf, int l, \n             unsigned int dstIp, unsigned short dstPort,\n             bool verbose)\n{\n   assert( fd != INVALID_SOCKET );\n    \n   int s;\n   if ( dstPort == 0 )\n   {\n      // sending on a connected port \n      assert( dstIp == 0 );\n\t\t\n      s = send(fd,buf,l,0);\n   }\n   else\n   {\n      assert( dstIp != 0 );\n      assert( dstPort != 0 );\n        \n      struct sockaddr_in to;\n      int toLen = sizeof(to);\n      memset(&to,0,toLen);\n        \n      to.sin_family = AF_INET;\n      to.sin_port = htons(dstPort);\n      to.sin_addr.s_addr = htonl(dstIp);\n        \n      s = sendto(fd, buf, l, 0,(sockaddr*)&to, toLen);\n   }\n    \n   if ( s == SOCKET_ERROR )\n   {\n      int e = getErrno();\n      switch (e)\n      {\n         case ECONNREFUSED:\n         case EHOSTDOWN:\n         case EHOSTUNREACH:\n         {\n            // quietly ignore this \n         }\n         break;\n         case EAFNOSUPPORT:\n         {\n            cerr << \"err EAFNOSUPPORT in send\" << endl;\n         }\n         break;\n         default:\n         {\n            cerr << \"err \" << e << \" \"  << strerror(e) << \" in send\" << endl;\n         }\n      }\n      return false;\n   }\n    \n   if ( s == 0 )\n   {\n      cerr << \"no data sent in send\" << endl;\n      return false;\n   }\n    \n   if ( s != l )\n   {\n      if (verbose)\n      {\n         cerr << \"only \" << s << \" out of \" << l << \" bytes sent\" << endl;\n      }\n      return false;\n   }\n    \n   return true;\n}\n\n\nvoid\ninitNetwork()\n{\n#ifdef WIN32 \n   WORD wVersionRequested = MAKEWORD( 2, 2 );\n   WSADATA wsaData;\n   int err;\n\t\n   err = WSAStartup( wVersionRequested, &wsaData );\n   if ( err != 0 ) \n   {\n      // could not find a usable WinSock DLL\n      cerr << \"Could not load winsock\" << endl;\n      assert(0); // is this is failing, try a different version that 2.2, 1.0 or later will likely work \n      exit(1);\n   }\n    \n   /* Confirm that the WinSock DLL supports 2.2.*/\n   /* Note that if the DLL supports versions greater    */\n   /* than 2.2 in addition to 2.2, it will still return */\n   /* 2.2 in wVersion since that is the version we      */\n   /* requested.                                        */\n    \n   if ( LOBYTE( wsaData.wVersion ) != 2 ||\n        HIBYTE( wsaData.wVersion ) != 2 ) \n   {\n      /* Tell the user that we could not find a usable */\n      /* WinSock DLL.                                  */\n      WSACleanup( );\n      cerr << \"Bad winsock verion\" << endl;\n      assert(0); // is this is failing, try a different version that 2.2, 1.0 or later will likely work \n      exit(1);\n   }    \n#endif\n}\n\n\n/* ====================================================================\n * The Vovida Software License, Version 1.0 \n * \n * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * \n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n * \n * 3. The names \"VOCAL\", \"Vovida Open Communication Application Library\",\n *    and \"Vovida Open Communication Application Library (VOCAL)\" must\n *    not be used to endorse or promote products derived from this\n *    software without prior written permission. For written\n *    permission, please contact vocal@vovida.org.\n *\n * 4. Products derived from this software may not be called \"VOCAL\", nor\n *    may \"VOCAL\" appear in their name, without prior written\n *    permission of Vovida Networks, Inc.\n * \n * THIS SOFTWARE IS PROVIDED \"AS IS\" AND ANY EXPRESSED OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND\n * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA\n * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES\n * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n * DAMAGE.\n * \n * ====================================================================\n * \n * This software consists of voluntary contributions made by Vovida\n * Networks, Inc. and many individuals on behalf of Vovida Networks,\n * Inc.  For more information on Vovida Networks, Inc., please see\n * <http://www.vovida.org/>.\n *\n */\n\n// Local Variables:\n// mode:c++\n// c-file-style:\"ellemtel\"\n// c-file-offsets:((case-label . +))\n// indent-tabs-mode:nil\n// End:\n"
  },
  {
    "path": "src/stun/udp.h",
    "content": "#ifndef udp_h\n#define udp_h\n\n\n#if defined(__APPLE__) && defined(__MACH__)\ntypedef int socklen_t;\n#endif\n\n#include <errno.h>\n\n#ifdef WIN32\n\n#include <winsock2.h>\n#include <io.h>\n\ntypedef int socklen_t;\ntypedef SOCKET StunSocket;\n\n#define EWOULDBLOCK             WSAEWOULDBLOCK\n#define EINPROGRESS             WSAEINPROGRESS\n#define EALREADY                WSAEALREADY\n#define ENOTSOCK                WSAENOTSOCK\n#define EDESTADDRREQ            WSAEDESTADDRREQ\n#define EMSGSIZE                WSAEMSGSIZE\n#define EPROTOTYPE              WSAEPROTOTYPE\n#define ENOPROTOOPT             WSAENOPROTOOPT\n#define EPROTONOSUPPORT         WSAEPROTONOSUPPORT\n#define ESOCKTNOSUPPORT         WSAESOCKTNOSUPPORT\n#define EOPNOTSUPP              WSAEOPNOTSUPP\n#define EPFNOSUPPORT            WSAEPFNOSUPPORT\n#define EAFNOSUPPORT            WSAEAFNOSUPPORT\n#define EADDRINUSE              WSAEADDRINUSE\n#define EADDRNOTAVAIL           WSAEADDRNOTAVAIL\n#define ENETDOWN                WSAENETDOWN\n#define ENETUNREACH             WSAENETUNREACH\n#define ENETRESET               WSAENETRESET\n#define ECONNABORTED            WSAECONNABORTED\n#define ECONNRESET              WSAECONNRESET\n#define ENOBUFS                 WSAENOBUFS\n#define EISCONN                 WSAEISCONN\n#define ENOTCONN                WSAENOTCONN\n#define ESHUTDOWN               WSAESHUTDOWN\n#define ETOOMANYREFS            WSAETOOMANYREFS\n#define ETIMEDOUT               WSAETIMEDOUT\n#define ECONNREFUSED            WSAECONNREFUSED\n#define ELOOP                   WSAELOOP\n#define EHOSTDOWN               WSAEHOSTDOWN\n#define EHOSTUNREACH            WSAEHOSTUNREACH\n#define EPROCLIM                WSAEPROCLIM\n#define EUSERS                  WSAEUSERS\n#define EDQUOT                  WSAEDQUOT\n#define ESTALE                  WSAESTALE\n#define EREMOTE                 WSAEREMOTE\n\ntypedef LONGLONG Int64; \ninline int getErrno() { return WSAGetLastError(); }\n\n#else\n\ntypedef int StunSocket;\nstatic const StunSocket INVALID_SOCKET = -1;\nstatic const int SOCKET_ERROR = -1;\n\ninline int closesocket( StunSocket fd ) { return close(fd); };\n\ninline int getErrno() { return errno; }\n\n#define WSANOTINITIALISED  EPROTONOSUPPORT\n\n#endif\n\n/// Open a UDP socket to receive on the given port - if port is 0, pick a a\n/// port, if interfaceIp!=0 then use ONLY the interface specified instead of\n/// all of them  \nStunSocket\nopenPort( unsigned short port, unsigned int interfaceIp,\n          bool verbose);\n\n\n/// recive a UDP message \nbool \ngetMessage( StunSocket fd, char* buf, int* len,\n            unsigned int* srcIp, unsigned short* srcPort,\n            bool verbose);\n\n\n/// send a UDP message \nbool \nsendMessage( StunSocket fd, char* msg, int len, \n             unsigned int dstIp, unsigned short dstPort,\n             bool verbose);\n\n\n/// set up network - does nothing in unix but needed for windows\nvoid\ninitNetwork();\n\n\n/* ====================================================================\n * The Vovida Software License, Version 1.0 \n * \n * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * \n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n * \n * 3. The names \"VOCAL\", \"Vovida Open Communication Application Library\",\n *    and \"Vovida Open Communication Application Library (VOCAL)\" must\n *    not be used to endorse or promote products derived from this\n *    software without prior written permission. For written\n *    permission, please contact vocal@vovida.org.\n *\n * 4. Products derived from this software may not be called \"VOCAL\", nor\n *    may \"VOCAL\" appear in their name, without prior written\n *    permission of Vovida Networks, Inc.\n * \n * THIS SOFTWARE IS PROVIDED \"AS IS\" AND ANY EXPRESSED OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND\n * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA\n * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES\n * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n * DAMAGE.\n * \n * ====================================================================\n * \n * This software consists of voluntary contributions made by Vovida\n * Networks, Inc. and many individuals on behalf of Vovida Networks,\n * Inc.  For more information on Vovida Networks, Inc., please see\n * <http://www.vovida.org/>.\n *\n */\n\n// Local Variables:\n// mode:c++\n// c-file-style:\"ellemtel\"\n// c-file-offsets:((case-label . +))\n// indent-tabs-mode:nil\n// End:\n\n#endif\n"
  },
  {
    "path": "src/sub_refer.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"sub_refer.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"phone_user.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"audits/memman.h\"\n\nt_dialog *t_sub_refer::get_dialog(void) const {\n\treturn dynamic_cast<t_dialog *>(dialog);\n}\n\nt_sub_refer::t_sub_refer(t_dialog *_dialog, t_subscription_role _role) :\n\t\tt_subscription(_dialog, _role, SIP_EVENT_REFER)\n{\n\t// A refer subscription is implicitly defined by the REFER\n\t// transaction\n\tstate = SS_ESTABLISHED;\n\n\t// Start the subscription timer only for a notifier.\n\t// The subscriber will start a timer when it receives NOTIFY.\n\tif (role == SR_NOTIFIER) {\n\t\tunsigned long dur;\n\t\tif (user_config->get_ask_user_to_refer()) {\n\t\t\tdur = DUR_REFER_SUB_INTERACT * 1000;\n\t\t} else {\n\t\t\tdur = DUR_REFER_SUBSCRIPTION * 1000;\n\t\t}\n\t\tstart_timer(STMR_SUBSCRIPTION, dur);\n\t}\n\n\tauto_refresh = user_config->get_auto_refresh_refer_sub();\n\tsubscription_expiry = DUR_REFER_SUBSCRIPTION;\n\tsr_result = SRR_INPROG;\n\t\n\tlast_response = NULL;\n\n\tlog_file->write_header(\"t_sub_refer::t_sub_refer\");\n\tlog_file->write_raw(\"Refer \");\n\tif (role == SR_SUBSCRIBER) {\n\t\tlog_file->write_raw(\"subscriber\");\n\t} else {\n\t\tlog_file->write_raw(\"notifier\");\n\t}\n\tlog_file->write_raw(\" created: event = \");\n\tlog_file->write_raw(event_type);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n}\n\nt_sub_refer::t_sub_refer(t_dialog *_dialog, t_subscription_role _role,\n\t\tconst string &_event_id) :\n\t\t\tt_subscription(_dialog, _role, SIP_EVENT_REFER, _event_id)\n{\n\tstate = SS_ESTABLISHED;\n\n\tif (role == SR_NOTIFIER) {\n\t\tunsigned long dur;\n\t\tif (user_config->get_ask_user_to_refer()) {\n\t\t\tdur = DUR_REFER_SUB_INTERACT * 1000;\n\t\t} else {\n\t\t\tdur = DUR_REFER_SUBSCRIPTION * 1000;\n\t\t}\n\t\tstart_timer(STMR_SUBSCRIPTION, dur);\n\t}\n\n\tauto_refresh = user_config->get_auto_refresh_refer_sub();\n\tsubscription_expiry = DUR_REFER_SUBSCRIPTION;\n\tsr_result = SRR_INPROG;\n\n\tlast_response = NULL;\n\n\tlog_file->write_header(\"t_sub_refer::t_sub_refer\");\n\tlog_file->write_raw(\"Refer \");\n\tif (role == SR_SUBSCRIBER) {\n\t\tlog_file->write_raw(\"subscriber\");\n\t} else {\n\t\tlog_file->write_raw(\"notifier\");\n\t}\n\tlog_file->write_raw(\" created: event = \");\n\tlog_file->write_raw(event_type);\n\tlog_file->write_raw(\";id=\");\n\tlog_file->write_raw(event_id);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n}\n\nt_sub_refer::~t_sub_refer() {\n\tif (last_response) {\n\t\tMEMMAN_DELETE(last_response);\n\t\tdelete last_response;\n\t}\n\n\tlog_file->write_header(\"t_sub_refer::~t_sub_refer\");\n\tlog_file->write_raw(\"Refer \");\n\tif (role == SR_SUBSCRIBER) {\n\t\tlog_file->write_raw(\"subscriber\");\n\t} else {\n\t\tlog_file->write_raw(\"notifier\");\n\t}\n\tlog_file->write_raw(\" destroyed: event = \");\n\tlog_file->write_raw(event_type);\n\tif (!event_id.empty()) {\n\t\tlog_file->write_raw(\";id=\");\n\t\tlog_file->write_raw(event_id);\n\t}\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n}\n\nvoid t_sub_refer::send_notify(t_response *r, const string &substate,\n\t\tconst string reason)\n{\n\tt_request *notify;\n\t\n\tif (substate == SUBSTATE_TERMINATED) {\n\t\t// RFC 3515 2.4.7\n\t\tnotify = create_notify(substate, reason);\n\t\tstop_timer(STMR_SUBSCRIPTION);\n\t} else {\n\t\tnotify = create_notify(substate);\n\t}\n\n\t// RFC 3515 2.4.4\n\t// Create message/sipfrag body containing only the status line\n\t// of the response.\n\tt_response sipfrag(r->code, r->reason);\n\tnotify->body = new t_sip_body_sipfrag(&sipfrag);\n\tMEMMAN_NEW(notify->body);\n\tnotify->hdr_content_type.set_media(t_media(\"message\", \"sipfrag\"));\n\n\t// If an outgoing NOTIFY is still pending, then store this\n\t// NOTIFY in the queue\n\tif (req_out) {\n\t\tqueue_notify.push(notify);\n\t} else {\n\t\t// Send NOTIFY\n\t\treq_out = new t_client_request(user_config, notify,0);\n\t\tMEMMAN_NEW(req_out);\n\t\tsend_request(user_config, notify, req_out->get_tuid());\n\t\tMEMMAN_DELETE(notify);\n\t\tdelete notify;\n\t}\n\n\t// Keep response and state such that it can be resend when\n\t// a SUBSCRIBE is received.\n\tif (last_response && last_response != r) {\n\t\tMEMMAN_DELETE(last_response);\n\t\tdelete last_response;\n\t\tlast_response = NULL;\n\t}\n\t\n\tif (!last_response) last_response = (t_response *)r->copy();\n\tcurrent_substate = substate;\n}\n\nbool t_sub_refer::recv_notify(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (t_subscription::recv_notify(r, tuid, tid)) return true;\n\t\n\t// RFC 3515 2.4.5.\n\t// NOTIFY must have a sipfrag body\n\tif (!r->body || r->body->get_type() != BODY_SIPFRAG) {\n\t\tt_response *resp = r->create_response(R_400_BAD_REQUEST,\n\t\t\t\t\"message/sipfrag body missing\");\n\t\tsend_response(user_config, resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn true;\n\t}\n\n\t// RFC 3515 2.4.5\n\t// The sipfrag body must start with a Status-Line\n\tif (((t_sip_body_sipfrag *)r->body)->sipfrag->get_type() != MSG_RESPONSE) {\n\t\tt_response *resp = r->create_response(R_400_BAD_REQUEST,\n\t\t\t\t\"sipfrag body does not begin with Status-Line\");\n\t\tsend_response(user_config, resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn true;\n\t}\n\n\tt_response *sipfrag = (t_response *)((t_sip_body_sipfrag *)r->body)->sipfrag;\n\n\t// Determine state of reference\n\tif (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {\n\t\tif (r->hdr_subscription_state.reason == EV_REASON_REJECTED) {\n\t\t\t// Referee rejected to refer\n\t\t\tsr_result = SRR_FAILED;\n\t\t} else if (r->hdr_subscription_state.reason == EV_REASON_NORESOURCE) {\n\t\t\t// Reference is finished. The sipfrag body indicates\n\t\t\t// success or failure.\n\t\t\tif (sipfrag->is_success()) {\n\t\t\t\tsr_result = SRR_SUCCEEDED;\n\t\t\t} else {\n\t\t\t\tsr_result = SRR_FAILED;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t// Inform user about progress\n\tui->cb_notify_recvd(get_dialog()->get_line()->get_line_number(), r);\n\n\tt_response *resp = r->create_response(R_200_OK);\n\tsend_response(user_config, resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\treturn true;\n}\n\nbool t_sub_refer::recv_subscribe(t_request *r, t_tuid tuid, t_tid tid) {\n\tunsigned long expires;\n\n\tif (t_subscription::recv_subscribe(r, tuid, tid)) return true;\n\t\n\t// Determine value for Expires header\n\tif (!r->hdr_expires.is_populated() ||\n\t    r->hdr_expires.time > 2 * DUR_REFER_SUBSCRIPTION)\n\t{\n\t\t// User did not indicate an expiry time for subscription\n\t\t// refresh or a time larger then 2 times the default.\n\t\t// Just use the Twinkle default.\n\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\tstart_timer(STMR_SUBSCRIPTION, DUR_REFER_SUBSCRIPTION * 1000);\n\t\texpires = DUR_REFER_SUBSCRIPTION;\n\t} else {\n\t\texpires = r->hdr_expires.time;\n\t}\n\n\tt_response *resp = r->create_response(R_200_OK);\n\n\t// RFC 3265 7.1\n\t// Contact header is mandatory\n\tt_contact_param contact;\n\tcontact.uri.set_url(get_dialog()->get_line()->create_user_contact(\n\t\t\th_ip2str(resp->get_local_ip())));\n\tresp->hdr_contact.add_contact(contact);\n\n\t// Expires header is mandatory\n\tresp->hdr_expires.set_time(expires);\n\n\tsend_response(user_config, resp, 0, tid);\n\tMEMMAN_DELETE(resp);\n\tdelete resp;\n\n\t// RFC 3265 3.2.2\n\t// After a successful SUBSCRIBE the notifier must immediately\n\t// send a NOTIFY.\n\t// If no last response has been kept then this is probably a\n\t// first SUBSCRIBE. The dialog has to send the initial NOTIFY.\n\tif (last_response) {\n\t\tif (expires == 0) {\n\t\t\tsend_notify(last_response, SUBSTATE_TERMINATED,\n\t\t\t\tEV_REASON_TIMEOUT);\n\t\t} else {\n\t\t\tsend_notify(last_response, current_substate);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool t_sub_refer::timeout(t_subscribe_timer timer) {\n\tif (t_subscription::timeout(timer)) return true;\n\n\tswitch (timer) {\n\tcase STMR_SUBSCRIPTION:\n\t\tswitch (role) {\n\t\tcase SR_NOTIFIER:\n\t\t\t// RFC 3265 3.1.6.4\n\t\t\t// The subscription has expired\n\t\t\t// RFC 2.4.5\n\t\t\t// Each NOTIFY MUST contain a body\n\t\t\tif (last_response) {\n\t\t\t\t// Repeat last response as body\n\t\t\t\tsend_notify(last_response, SUBSTATE_TERMINATED,\n\t\t\t\t\tEV_REASON_TIMEOUT);\n\t\t\t} else {\n\t\t\t\t// This should never happen. Create a timeout\n\t\t\t\t// response for the body.\n\t\t\t\tt_response resp(R_408_REQUEST_TIMEOUT);\n\t\t\t\tsend_notify(&resp, SUBSTATE_TERMINATED,\n\t\t\t\t\tEV_REASON_TIMEOUT);\n\t\t\t}\n\n\t\t\tlog_file->write_report(\"Refer notifier timed out.\",\n\t\t\t\t\"t_sub_refer::timeout\");\n\n\t\t\treturn true;\n\t\tcase SR_SUBSCRIBER:\n\t\t\t// Should have been handled by parent class\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn false;\n}\n\nt_sub_refer_result t_sub_refer::get_sr_result(void) const {\n\treturn sr_result;\n}\n"
  },
  {
    "path": "src/sub_refer.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// RFC 3515\n// Refer event package\n\n#ifndef _SUB_REFER_H\n#define _SUB_REFER_H\n\n#include \"subscription.h\"\n#include \"dialog.h\"\n\n// State of reference as seen by the referrer\nenum t_sub_refer_result {\n\tSRR_INPROG,\t// Referee is referring call\n\tSRR_FAILED,\t// Refer failed\n\tSRR_SUCCEEDED,\t// Refer succeeded\n};\n\nclass t_sub_refer : public t_subscription {\nprivate:\n\t// Result of the reference as seen by the referrer.\n\tt_sub_refer_result\tsr_result;\n\n\t// Last response received from the refer-target\n\tt_response\t\t*last_response;\n\n\t// Current substate of the notification\n\tstring\t\t\tcurrent_substate;\n\t\n\tt_dialog *get_dialog(void) const;\n\npublic:\n\tt_sub_refer(t_dialog *_dialog, t_subscription_role _role);\n\tt_sub_refer(t_dialog *_dialog, t_subscription_role _role,\n\t\tconst string &_event_id);\n\tvirtual ~t_sub_refer();\n\n\t// Send a NOTIFY with the status line of the response as body\n\t// substate indicates the subscription state of refer\n\t// A reason should be given if substate == TERMINATED\n\tvoid send_notify(t_response *r, const string &substate,\n\t\tconst string reason = \"\");\n\n\tbool recv_notify(t_request *r, t_tuid tuid, t_tid tid);\n\tbool recv_subscribe(t_request *r, t_tuid tuid, t_tid tid);\n\n\tbool timeout(t_subscribe_timer timer);\n\n\tt_sub_refer_result get_sr_result(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/subscription.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"subscription.h\"\n\n#include \"dialog.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"phone_user.h\"\n#include \"audits/memman.h\"\n#include \"parser/hdr_event.h\"\n\nextern t_event_queue\t*evq_trans_mgr;\nextern t_event_queue\t*evq_timekeeper;\nextern t_timekeeper\t*timekeeper;\n\nstring t_subscription_state2str(t_subscription_state state) {\n\tswitch (state) {\n\tcase SS_NULL:\t\treturn \"SS_NULL\";\n\tcase SS_ESTABLISHED:\treturn \"SS_ESTABLISHED\";\n\tcase SS_UNSUBSCRIBING:\treturn \"SS_UNSUBSCRIBING\";\n\tcase SS_UNSUBSCRIBED:\treturn \"SS_UNSUBSCRIBED\";\n\tcase SS_TERMINATED:\treturn \"SS_TERMINATED\";\n\t}\n\t\n\treturn \"UNKNOWN\";\n}\n\n/////////////\n// PROTECTED\n/////////////\n\nvoid t_subscription::log_event() const {\n\tlog_file->write_raw(\"Event:    \");\n\tlog_file->write_raw(event_type);\n\tlog_file->write_endl();\n\tlog_file->write_raw(\"Event id: \");\n\tlog_file->write_raw(event_id);\n\tlog_file->write_endl();\n}\n\nvoid t_subscription::remove_client_request(t_client_request **cr) {\n\tif ((*cr)->dec_ref_count() == 0) {\n\t\tMEMMAN_DELETE(*cr);\n\t\tdelete *cr;\n\t}\n\n\t*cr = NULL;\n}\n\nt_request *t_subscription::create_subscribe(unsigned long expires) const {\n\t// RFC 3265 3.1.4\n\tt_request *r = dialog->create_request(SUBSCRIBE);\n\tr->hdr_expires.set_time(expires);\n\tr->hdr_event.set_event_type(event_type);\n\tif (event_id.size() > 0) r->hdr_event.set_id(event_id);\n\t\n\t// Re-calculate the destination as the event type may\n\t// influence the route to be taken.\n\t// The destination has been calculated already at the\n\t// dialog level.\n\tr->calc_destinations(*user_config);\n\n\treturn r;\n}\n\nt_request *t_subscription::create_notify(const string &sub_state,\n\t\tconst string &reason) const\n{\n\t// RFC 3265 3.2.2\n\tt_request *r = dialog->create_request(NOTIFY);\n\tr->hdr_event.set_event_type(event_type);\n\tif (event_id.size() > 0) r->hdr_event.set_id(event_id);\n\tr->hdr_subscription_state.set_substate(sub_state);\n\n\t// Subscription state specific parameters\n\tif (sub_state == SUBSTATE_ACTIVE || sub_state == SUBSTATE_PENDING) {\n\t\t// Add expires parameter with remaining time\n\t\tif (id_subscription_timeout) {\n\t\t\tlong remaining = timekeeper->\n\t\t\t\tget_remaining_time(id_subscription_timeout);\n\t\t\tr->hdr_subscription_state.set_expires(remaining / 1000);\n\t\t}\n\t} else if (sub_state == SUBSTATE_TERMINATED) {\n\t\t// Add reason parameter\n\t\tif (reason.size() > 0) {\n\t\t\tr->hdr_subscription_state.set_reason(reason);\n\t\t}\n\t}\n\n\treturn r;\n}\n\nvoid t_subscription::send_request(t_user *user_config, t_request *r, t_tuid tuid) const {\n\tevq_trans_mgr->push_user(user_config, (t_sip_message *)r, tuid, 0);\n}\n\nvoid t_subscription::send_response(t_user *user_config, t_response *r, \n\t\tt_tuid tuid, t_tid tid) const \n{\n\tevq_trans_mgr->push_user(user_config, (t_sip_message *)r, tuid, tid);\n}\n\nvoid t_subscription::start_timer(t_subscribe_timer timer, long duration) {\n\tt_tmr_subscribe\t\t*t;\n\tt_object_id\t\toid_line = 0;\n\n\tswitch(timer) {\n\tcase STMR_SUBSCRIPTION:\n\t\tif (dynamic_cast<t_dialog *>(dialog) != NULL) {\n\t\t\toid_line = dynamic_cast<t_dialog *>(dialog)->get_line()->get_object_id();\n\t\t}\n\t\tt = new t_tmr_subscribe(duration, timer, oid_line,\n\t\t\t\tdialog->get_object_id(), event_type, event_id);\n\t\tMEMMAN_NEW(t);\n\t\tid_subscription_timeout = t->get_object_id();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tevq_timekeeper->push_start_timer(t);\n\tMEMMAN_DELETE(t);\n\tdelete t;\n}\n\nvoid t_subscription::stop_timer(t_subscribe_timer timer) {\n\tunsigned short\t*id;\n\n\tswitch(timer) {\n\tcase STMR_SUBSCRIPTION:\n\t\tid = &id_subscription_timeout;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tif (*id != 0) evq_timekeeper->push_stop_timer(*id);\n\t*id = 0;\n}\n\n//////////\n// PUBLIC\n//////////\n\nt_subscription::t_subscription(t_abstract_dialog *_dialog, t_subscription_role _role,\n\t\t\tconst string &_event_type) \n{\n\tdialog = _dialog;\n\t\n\tuser_config = dialog->get_user();\n\tassert(user_config);\n\t\n\trole = _role;\n\tstate = SS_NULL;\n\tresubscribe_after = 0;\n\tmay_resubscribe = false;\n\tpending = true;\n\tid_subscription_timeout = 0;\n\treq_out = NULL;\n\tevent_type = _event_type;\n\tauto_refresh = true;\n\tsubscription_expiry = 3600;\n\tdefault_duration = 3600;\n}\n\nt_subscription::t_subscription(t_abstract_dialog *_dialog, t_subscription_role _role,\n\t\t\tconst string &_event_type, const string &_event_id)\n{\n\tdialog = _dialog;\n\t\n\tuser_config = dialog->get_user();\n\tassert(user_config);\n\t\n\trole = _role;\n\tstate = SS_NULL;\n\tresubscribe_after = 0;\n\tmay_resubscribe = false;\n\tpending = true;\n\tid_subscription_timeout = 0;\n\treq_out = NULL;\n\tevent_type = _event_type;\n\tevent_id = _event_id;\n\tauto_refresh = true;\n\tsubscription_expiry = 3600;\n\tdefault_duration = 3600;\n}\n\nt_subscription::~t_subscription() {\n\tif (req_out) remove_client_request(&req_out);\n\tif (id_subscription_timeout) stop_timer(STMR_SUBSCRIPTION);\n\n\t// Cleanup list of unsent NOTIFY messages\n\twhile (!queue_notify.empty()) {\n\t\tt_request *r = queue_notify.front();\n\t\tqueue_notify.pop();\n\t\tMEMMAN_DELETE(r);\n\t\tdelete r;\n\t}\n}\n\nt_subscription_role t_subscription::get_role(void) const {\n\treturn role;\n}\n\nt_subscription_state t_subscription::get_state(void) const {\n\treturn state;\n}\n\nstring t_subscription::get_reason_termination(void) const {\n\treturn reason_termination;\n}\n\nunsigned long t_subscription::get_resubscribe_after(void) const {\n\treturn resubscribe_after;\n}\n\nbool t_subscription::get_may_resubscribe(void) const {\n\treturn may_resubscribe;\n}\n\nstring t_subscription::get_event_type(void) const {\n\treturn event_type;\n}\n\nstring t_subscription::get_event_id(void) const {\n\treturn event_id;\n}\n\nunsigned long t_subscription::get_expiry(void) const {\n\treturn subscription_expiry;\n}\n\nbool t_subscription::recv_subscribe(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (role != SR_NOTIFIER) {\n\t\t// Reject a SUBSCRIBE coming in for a SUBSCRIBER\n\t\t// TODO: is this ok??\n\t\tlog_file->write_header(\"t_subscription::recv_subscribe\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"SUBSCRIBER receives SUBSCRIBE.\\n\");\n\t\tlog_event();\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(r->encode());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tt_response *resp = r->create_response(R_603_DECLINE);\n\t\tsend_response(user_config, resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn true;\n\t}\n\n\t// If the subscription is already in the terminated state\n\t// then a SUBSCRIBE is not allowed anymore.\n\tif (state == SS_TERMINATED) {\n\t\tt_response *resp = r->create_response(R_481_TRANSACTION_NOT_EXIST,\n\t\t\tREASON_481_SUBSCRIPTION_NOT_EXIST);\n\t\tsend_response(user_config, resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn true;\n\t}\n\n\tif (state == SS_NULL) state = SS_ESTABLISHED;\n\n\t// If there is no expires header, then the implementation of\n\t// the event specific package must deal with this subscribe\n\tif (!r->hdr_expires.is_populated()) {\n\t\treturn false;\n\t}\n\n\t// An expiry time of 0 is an unsubscribe\n\tif (r->hdr_expires.time == 0) {\n\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\tstate = SS_TERMINATED;\n\t\treturn false;\n\t}\n\n\t// Check if the requested expiry is not too small\n\tif (r->hdr_expires.time < MIN_DUR_SUBSCRIPTION) {\n\t\tt_response *resp = r->create_response(\n\t\t\t\t\tR_423_INTERVAL_TOO_BRIEF);\n\t\tresp->hdr_min_expires.set_time(MIN_DUR_SUBSCRIPTION);\n\t\tsend_response(user_config, resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn true;\n\t}\n\n\t// Restart subscription timer\n\tstop_timer(STMR_SUBSCRIPTION);\n\tstart_timer(STMR_SUBSCRIPTION, r->hdr_expires.time * 1000);\n\treturn false;\n}\n\nbool t_subscription::recv_notify(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (role != SR_SUBSCRIBER) {\n\t\t// Reject a NOTIFY coming in for a NOTIFIER\n\t\t// TODO: is this ok??\n\t\tlog_file->write_header(\"t_subscription::recv_notify\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"NOTIFIER receives NOTIFY.\\n\");\n\t\tlog_event();\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(r->encode());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tt_response *resp = r->create_response(R_603_DECLINE);\n\t\tsend_response(user_config, resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\treturn true;\n\t}\n\t\n\tif (state == SS_NULL) {\n\t\tlog_file->write_header(\"t_subscription::recv_notify\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"NOTIFY establishes subscription.\\n\");\n\t\tlog_event();\n\t\tlog_file->write_footer();\n\t\t\n\t\tstate = SS_ESTABLISHED;\n\t}\n\n\tif (r->hdr_subscription_state.substate == SUBSTATE_ACTIVE && pending) {\n\t\tlog_file->write_header(\"t_subscription::recv_notify\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"NOTIFY ends pending state.\\n\");\n\t\tlog_event();\n\t\tlog_file->write_footer();\n\t\t\n\t\tpending = false;\n\t}\n\n\tif (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {\n\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\tstate = SS_TERMINATED;\n\t\treason_termination = r->hdr_subscription_state.reason;\n\t\tresubscribe_after = r->hdr_subscription_state.retry_after;\n\t\t\n\t\t// RFC 3264 3.2.4\n\t\tif (resubscribe_after > 0) {\n\t\t\tmay_resubscribe = true;\n\t\t} else {\n\t\t\tif (reason_termination == EV_REASON_DEACTIVATED ||\n\t\t\t    reason_termination == EV_REASON_TIMEOUT)\n\t\t\t{\n\t\t\t\tmay_resubscribe = true;\n\t\t\t} else if (reason_termination == EV_REASON_PROBATION ||\n\t\t\t           reason_termination == EV_REASON_GIVEUP)\n\t\t\t{\n\t\t\t\tmay_resubscribe = true;\n\t\t\t\tresubscribe_after = DUR_RESUBSCRIBE;\n\t\t\t}\n\t\t}\n\t\t\n\t\tlog_file->write_header(\"t_subscription::recv_notify\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"NOTIFY terminates subscription.\\n\");\n\t\tlog_event();\n\t\tlog_file->write_raw(\"Termination reason: \");\n\t\tlog_file->write_raw(reason_termination);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"May resubscribe:    \");\n\t\tlog_file->write_bool(may_resubscribe);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"Resubscribe after:  \");\n\t\tlog_file->write_raw(resubscribe_after);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t}\n\n\tif (r->hdr_subscription_state.expires > 0 && state == SS_ESTABLISHED) {\n\t\tlog_file->write_header(\"t_subscription::recv_notify\", LOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Received NOTIFY on established subscription.\\n\");\n\t\tlog_event();\n\t\tlog_file->write_footer();\n\t\t\n\t\tunsigned long dur = r->hdr_subscription_state.expires;\n\t\tif (auto_refresh) {\n\t\t\tif (!id_subscription_timeout ||\n\t\t\t    timekeeper->get_remaining_time(id_subscription_timeout) >=\n\t\t\t    \tdur * 1000)\n\t\t\t{\n\t\t\t\t// Adjust timer to expiry duration indicated\n\t\t\t\t// in NOTIFY.\n\t\t\t\tdur -= dur / 10;\n\t\t\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\t\t\tstart_timer(STMR_SUBSCRIPTION, dur * 1000);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!id_subscription_timeout ||\n\t\t\t    timekeeper->get_remaining_time(id_subscription_timeout) <\n\t\t\t    \tdur * 1000)\n\t\t\t{\n\t\t\t\t// Adjust timer to expiry duration indicated\n\t\t\t\t// in NOTIFY.\n\t\t\t\tdur += dur / 10;\n\t\t\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\t\t\tstart_timer(STMR_SUBSCRIPTION, dur * 1000);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\nbool t_subscription::recv_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tswitch (r->hdr_cseq.method) {\n\tcase NOTIFY:\n\t\treturn recv_notify_response(r, tuid, tid);\n\t\tbreak;\n\tcase SUBSCRIBE:\n\t\treturn recv_subscribe_response(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\treturn false;\n}\n\nbool t_subscription::recv_notify_response(t_response *r, t_tuid tuid, t_tid tid) {\n\t// Discard response if it does not match a pending request\n\tif (!req_out) return true;\n\tif (r->hdr_cseq.method != req_out->get_request()->method) return true;\n\n\t// Ignore provisional responses\n\tif (r->is_provisional()) return true;\n\t\n\t// Successful response\n\tif (r->is_success()) {\n\t\tif (req_out->get_request()->hdr_subscription_state.substate ==\n\t\t\t\tSUBSTATE_TERMINATED)\n\t\t{\n\t\t\t// This is a 2XX respsone on a NOTIFY that terminates the\n\t\t\t// subscription.\n\t\t\tstate = SS_TERMINATED;\n\t\t\t\n\t\t\tlog_file->write_header(\"t_subscription::recv_notify_response\", \n\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Subscription terminated by 2XX NOTIFY.\\n\");\n\t\t\tlog_file->write_raw(r->code);\n\t\t\tlog_file->write_raw(\" \" + r->reason + \"\\n\");\n\t\t\tlog_event();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t\tremove_client_request(&req_out);\n\t} else {\n\t\t// RFC 3265 3.2.2\n\t\t// NOTIFY failed, terminate subscription\n\t\tremove_client_request(&req_out);\n\t\tstate = SS_TERMINATED;\n\t\t\n\t\tlog_file->write_header(\"t_subscription::recv_notify_response\", \n\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Subscription terminated by NOTIFY failure response.\\n\");\n\t\tlog_file->write_raw(r->code);\n\t\tlog_file->write_raw(\" \" + r->reason + \"\\n\");\n\t\tlog_event();\n\t\tlog_file->write_footer();\n\t\treturn true;\n\t}\n\n\t// If there is a NOTIFY in the queue, then send it\n\tif (!queue_notify.empty()) {\n\t\tlog_file->write_header(\"t_subscription::recv_notify_response\", \n\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\tlog_file->write_raw(\"Get NOTIFY from queue.\");\n\t\tlog_event();\n\t\tlog_file->write_footer();\n\t\n\t\tt_request *notify = queue_notify.front();\n\t\tqueue_notify.pop();\n\t\treq_out = new t_client_request(user_config, notify,0);\n\t\tMEMMAN_NEW(req_out);\n\t\tsend_request(user_config, notify, req_out->get_tuid());\n\t\tMEMMAN_DELETE(notify);\n\t\tdelete notify;\n\t}\n\n\treturn true;\n}\n\nbool t_subscription::recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid) {\n\t// Discard response if it does not match a pending request\n\tif (!req_out) return true;\n\tif (r->hdr_cseq.method != req_out->get_request()->method) return true;\n\n\t// Ignore provisional responses\n\tif (r->is_provisional()) return true;\n\t\n\t// Successful response\n\tif (r->is_success()) {\n\t\tif (state == SS_NULL) {\n\t\t\tlog_file->write_header(\"t_subscription::recv_subscribe_response\", \n\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Subscription established by 2XX SUBSCRIBE.\\n\");\n\t\t\tlog_file->write_raw(r->code);\n\t\t\tlog_file->write_raw(\" \" + r->reason + \"\\n\");\n\t\t\tlog_event();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tstate = SS_ESTABLISHED;\n\t\t}\n\t\t\n\t\t// RFC 3265 7.1, 7.2 says that the Expires header is mandatory\n\t\t// in a 2XX response. Some SIP servers do not include this\n\t\t// however. To interoperate with such servers, assume that\n\t\t// the granted expiry time equals the requested expiry time.\n\t\tif (!r->hdr_expires.is_populated()) {\n\t\t\tr->hdr_expires.set_time(\n\t\t\t\treq_out->get_request()->hdr_expires.time);\n\t\t\t\t\n\t\t\tlog_file->write_header(\n\t\t\t\t\"t_subscription::recv_subscribe_response\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Mandatory Expires header missing.\\n\");\n\t\t\tlog_file->write_raw(\"Assuming expires = \");\n\t\t\tlog_file->write_raw(r->hdr_expires.time);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_event();\n\t\t\tlog_file->write_footer();\n\t\t}\n\n\t\t// If some faulty server sends a non-zero expiry time in\n\t\t// a response on an unsubscribe request, then ignore\n\t\t// the expiry time.\n\t\tif (r->hdr_expires.time == 0 || state == SS_UNSUBSCRIBING) {\n\t\t\t// Unsubscription succeeded.\n\t\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\t\t\n\t\t\t// Start the unsubscribe guard. If the triggered\n\t\t\t// NOTIFY is never received, this guard assures, that\n\t\t\t// the subscription will be cleaned up at timeout.\n\t\t\tstart_timer(STMR_SUBSCRIPTION, DUR_UNSUBSCRIBE_GUARD);\n\n\t\t\t// The subscription will only\n\t\t\t// terminate after a NOTIFY triggered by the unsubscribe\n\t\t\t// has been received or this guard timer expires.\n\t\t\tstate = SS_UNSUBSCRIBED;\n\t\t\tauto_refresh = false;\n\t\t\t\n\t\t\tlog_file->write_header(\"t_subscription::recv_subscribe_response\", \n\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\tlog_file->write_raw(\"Unsubcription successful.\\n\");\n\t\t\tlog_event();\n\t\t\tlog_file->write_footer();\n\t\t} else {\n\t\t\t// Start/refresh subscribe timer\n\t\t\tstop_timer(STMR_SUBSCRIPTION);\n\t\t\tunsigned long dur = r->hdr_expires.time;\n\t\t\tif (auto_refresh) {\n\t\t\t\tdur -= dur / 10;\n\t\t\t} else {\n\t\t\t\tdur += dur / 10;\n\t\t\t}\n\t\t\tstart_timer(STMR_SUBSCRIPTION, dur * 1000);\n\t\t}\n\n\t\tremove_client_request(&req_out);\n\t\treturn true;\n\t}\n\n\t// RFC 3265 3.1.4.1\n\t// SUBSCRIBE failed, terminate subscription\n\t// NOTE: redirection and authentication responses should have\n\t// been handled already (eg. on line or phone user level).\n\tremove_client_request(&req_out);\n\tstate = SS_TERMINATED;\n\t\n\tlog_file->write_header(\"t_subscription::recv_subscribe_response\", \n\t\tLOG_NORMAL, LOG_DEBUG);\n\tlog_file->write_raw(\"Subscription terminated by SUBSCRIBE failure response.\\n\");\n\tlog_file->write_raw(r->code);\n\tlog_file->write_raw(\" \" + r->reason + \"\\n\");\n\tlog_event();\n\tlog_file->write_footer();\n\n\treturn true;\n}\n\nbool t_subscription::timeout(t_subscribe_timer timer) {\n\tswitch (timer) {\n\tcase STMR_SUBSCRIPTION:\n\t\tid_subscription_timeout = 0;\n\t\t\n\t\tif (role == SR_SUBSCRIBER) {\n\t\t\tlog_file->write_header(\"t_subcription::timeout\");\n\t\t\tlog_file->write_raw(\"Subscriber timed out.\\n\");\n\t\t\tlog_event();\n\t\t\tlog_file->write_footer();\n\n\t\t\tif (auto_refresh) {\n\t\t\t\t// Refresh subscription\n\t\t\t\trefresh_subscribe();\n\t\t\t} else {\n\t\t\t\t// The cause for timeout may be temporary.\n\t\t\t\t// Allow resubscription to overcome a transient problem.\n\t\t\t\tmay_resubscribe = true;\n\t\t\t\tstate = SS_TERMINATED;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn false;\n}\n\nbool t_subscription::match_timer(t_subscribe_timer timer, t_object_id id_timer) const {\n\treturn id_timer == id_subscription_timeout;\n}\n\nbool t_subscription::match(t_request *r) const {\n\tif (!r->hdr_event.is_populated()) return false;\n\t\n\t// If the subscription has been terminated, then do not match\n\t// any request.\n\tif (state == SS_TERMINATED) return false;\n\n\treturn (r->hdr_event.event_type == event_type &&\n\t        r->hdr_event.id == event_id);\n}\n\nbool t_subscription::is_pending(void) const {\n\treturn pending;\n}\n\nvoid t_subscription::subscribe(unsigned long expires) {\n\tif (req_out) {\n\t\t// Delete previous outgoing request\n\t\tMEMMAN_DELETE(req_out);\n\t\tdelete req_out;\n\t}\n\t\n\tif (expires > 0) {\n\t\tsubscription_expiry = expires;\n\t} else {\n\t\tsubscription_expiry = default_duration;\n\t}\n\t\n\tt_request *r = create_subscribe(subscription_expiry);\n\treq_out = new t_client_request(user_config, r ,0);\n\tMEMMAN_NEW(req_out);\n\tsend_request(user_config, r, req_out->get_tuid());\n\tMEMMAN_DELETE(r);\n\tdelete r;\n}\n\nvoid t_subscription::unsubscribe(void) {\n\tif (state != SS_ESTABLISHED) {\n\t\tstate = SS_TERMINATED;\n\t\treturn;\n\t}\n\t\n\tif (req_out) {\n\t\t// Delete previous outgoing request\n\t\tMEMMAN_DELETE(req_out);\n\t\tdelete req_out;\n\t}\n\n\tstop_timer(STMR_SUBSCRIPTION);\n\t\n\tt_request *r = create_subscribe(0);\n\treq_out = new t_client_request(user_config, r ,0);\n\tMEMMAN_NEW(req_out);\n\tsend_request(user_config, r, req_out->get_tuid());\n\tMEMMAN_DELETE(r);\n\tdelete r;\n\t\n\t// NOTE: the subscription is only ended when the response is received.\n\tstate = SS_UNSUBSCRIBING;\n}\n\nvoid t_subscription::refresh_subscribe(void) {\n\tif (state != SS_ESTABLISHED) return;\n\tstop_timer(STMR_SUBSCRIPTION);\n\tsubscribe(subscription_expiry);\n}\n"
  },
  {
    "path": "src/subscription.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Subscription (RFC 3265)\n */\n\n#ifndef _SUBSCRIPTION_H\n#define _SUBSCRIPTION_H\n\n#include <queue>\n#include <string>\n#include \"abstract_dialog.h\"\n\n/** Subscription role */\nenum t_subscription_role {\n\tSR_SUBSCRIBER,\t\t/**< Subscriber */\n\tSR_NOTIFIER\t\t/**< Notifier */\n};\n\n/** Subscription state */\nenum t_subscription_state {\n\tSS_NULL,\t\t/**< Initial state */\n\tSS_ESTABLISHED,\t\t/**< Subscription is in place */\n\tSS_UNSUBSCRIBING,\t/**< A request to unsubscribe has been sent */\n\tSS_UNSUBSCRIBED,\t/**< An outoging unsubscribe was successful. Waiting for the final NOTIFY. */\n\tSS_TERMINATED,\t\t/**< Subscription ended */\n};\n\n/**\n * Convert a subscription state to string.\n * @param state [in] Subscription state.\n * @return String representation of state.\n */\nstring t_subscription_state2str(t_subscription_state state);\n\n/**\n * RFC 3265\n * Generic subscription state for subscribers and notifiers\n * For each event type this class should be subclassed.\n */\nclass t_subscription {\nprotected:\n\tt_subscription_role\trole;\n\tt_subscription_state\tstate;\n\t\n\t/**\n\t *  When a subscriber subscription is terminated, this reason indicates\n\t * the reason conveyed in the NOTIFY, if any.\n\t */\n\tstring\t\t\treason_termination;\n\t\n\t/**\n\t * If the NOTIFY indicated that the subscriber may retry subscription at\n\t * a later time, then resubscribe_after indicates the number of seconds to wait.\n\t */\n\tunsigned long\t\tresubscribe_after;\n\t\n\t/** Indicates if a re-subscribe may be done after a failure. */\n\tbool\t\t\tmay_resubscribe;\n\t\n\tt_abstract_dialog\t*dialog; /**< Dialog owning the subscription */\n\tstring\t\t\tevent_type;\n\tstring\t\t\tevent_id;\n\t\n\t/**\n\t * User profile of user using the line\n\t * This is a pointer to the user_config owned by a phone user.\n\t * So this pointer should never be deleted.\n\t */\n\tt_user\t\t\t*user_config;\n\n\tbool\t\t\tpending; /**< Indicates if not active yet. */\n\n\t/** @name Timers */\n\t//@{\n\t/**\n\t * For a subscriber the subscription_timeout timer indicates when\n\t * the subscription must be refreshed.\n\t * For a notifier it indicates when the subscription expires.\n\t */\n\tunsigned short\t\tid_subscription_timeout;\n\n\t/**\n\t * Indicates if a subscriber automatically refreshes the subscritption\n\t * when the subscription timer expires. If not, then the subscription\n\t * terminates at expiry.\n\t */\n\tbool\t\t\tauto_refresh;\n\t\n\t/** Subcription expiry for a SUBSCRIBE request */\n\tunsigned long\t\tsubscription_expiry;\n\t\n\t/** Default duration for a subscription */\n\tunsigned long\t\tdefault_duration;\n\t//@}\n\n\t/** Protect constructor from being used */\n\tt_subscription() {};\n\t\n\t/** Write event type and id to log file */\n\tvoid log_event() const;\n\n\t/**\n\t * Remove a pending request. Pass one of the client request pointers.\n\t * @param cr [in] Client request to remove.\n\t */\n\tvoid remove_client_request(t_client_request **cr);\n\n\t/** @name Create requests based on the event type */\n\t//@{\n\t/**\n\t * Create a SUBSCRIBE request.\n\t * Creating a SUBSCRIBE is for subscription refreshment/unsubscribe.\n\t * @param expires [in] Expiry time in seconds.\n\t */\n\tvirtual t_request *create_subscribe(unsigned long expires) const;\n\t\n\t/**\n\t * Create a NOTIFY request.\n\t * @param sub_state [in] Subscription state to be put in the Subscription-State header.\n\t * @param reason [in] The reason parameter of the Subscription-State header.\n\t */\n\tvirtual t_request *create_notify(const string &sub_state,\n\t\tconst string &reason = \"\") const;\n\t//@}\n\n\t/**\n\t * Send request.\n\t * @param user_config [in] User profile of user sending the request.\n\t * @param r [in] Request to send.\n\t * @param tuid [in] Transaction user id.\n\t */\n\tvoid send_request(t_user *user_config, t_request *r, t_tuid tuid) const;\n\t\n\t/**\n\t * Send response.\n\t * @param user_config [in] User profile of user sending the response.\n\t * @param r [in] Response to send.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvoid send_response(t_user *user_config, t_response *r, t_tuid tuid, t_tid tid) const;\n\n\t/** \n\t * Start a subscription timer.\n\t * @param timer [in] Type of subscription timer.\n\t * @param duration [in] Duration of timer in ms\n\t */\n\tvoid start_timer(t_subscribe_timer timer, long duration);\n\t\n\t/**\n\t * Stop a subscription timer.\n\t * @param timer [in] Type of subscription timer.\n\t */\n\tvoid stop_timer(t_subscribe_timer timer);\n\npublic:\n\t/** Pending request */\n\tt_client_request\t*req_out;\n\n\t/**\n\t * Queue of pending outgoing NOTIFY requests. A next NOTIFY\n\t * will only be sent after the previous NOTIFY has been\n\t * answered.\n\t */\n\tqueue<t_request *>\tqueue_notify;\n\n\t/**\n\t * Constructor\n\t * @param _dialog [in] Dialog owning this subscription. SUBSCRIBE and NOTIFY\n\t * requests are sent within this dialog.\n\t * @param _role [in] Role\n\t * @param _event_type [in] Event type of the subscription.\n\t */\n\tt_subscription(t_abstract_dialog *_dialog, t_subscription_role _role,\n\t\t\tconst string &_event_type);\n\t\t\t\n\t/**\n\t * Constructor\n\t * @param _dialog [in] Dialog owning this subscription. SUBSCRIBE and NOTIFY\n\t * requests are sent within this dialog.\n\t * @param _role [in] Role\n\t * @param _event_type [in] Event type of the subscription.\n\t * @param _event_id [in] Event id.\n\t */\n\tt_subscription(t_abstract_dialog *_dialog, t_subscription_role _role,\n\t\t\tconst string &_event_type, const string &_event_id);\n\t\t\t\n\t/** Destructor */\n\tvirtual ~t_subscription();\n\n\t/** @name Getters */\n\t//@{\n\tt_subscription_role get_role(void) const;\n\tt_subscription_state get_state(void) const;\n\tstring get_reason_termination(void) const;\n\tunsigned long get_resubscribe_after(void) const;\n\tbool get_may_resubscribe(void) const;\n\tstring get_event_type(void) const;\n\tstring get_event_id(void) const;\n\tunsigned long get_expiry(void) const;\n\t//@}\n\n\t/** @name Receive requests */\n\t//@{\n\t/**\n\t * Reveive SUBSCRIBE request\n\t * @param r [in] Received request.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return The return value indicates if processing is finished.\n\t * This way a subclass can first call the parent class method.\n\t * If the parent indicates that process is finished, then the child\n\t * does not need to further process.\n\t * Note that recv_subscribe returns false if the SUBSCRIBE is valid. The\n\t * subscription timer will be started, but no response is sent. The subclass\n\t * MUST further handle the SUBSCRIBE, i.e. send a response and a NOTIFY.\n\t */\n\tvirtual bool recv_subscribe(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Receive NOTIFY request.\n\t * @param r [in] Received request.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return When the NOTIFY is valid, false is returned. The subclass MUST further\n\t * handle the NOTIFY, i.e. send a response.\n\t */\n\tvirtual bool recv_notify(t_request *r, t_tuid tuid, t_tid tid);\n\t//@}\n\n\t/** @name Receive responses */\n\t//@{\n\t/**\n\t * Receive NOTIFY/SUBSCRIBE response.\n\t * @param r [in] Received response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return The return value indicates if processing is finished.\n\t */\n\tvirtual bool recv_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Receive NOTIFY response.\n\t * @param r [in] Received response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return The return value indicates if processing is finished.\n\t */\n\tvirtual bool recv_notify_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Receive SUBSCRIBE response.\n\t * @param r [in] Received response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return The return value indicates if processing is finished.\n\t */\n\tvirtual bool recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid);\n\t//@}\n\n\t/**\n\t * Process timeouts\n\t * @param timer [in] Type of subscription timer.\n\t * @return The return value indicates if processing is finished.\n\t */\n\tvirtual bool timeout(t_subscribe_timer timer);\n\t\n\t/**\n\t * Match timer id with a running timer.\n\t * @param timer [in] Type of subscription timer.\n\t * @return True, if id matches, otherwise false.\n\t */\n\tvirtual bool match_timer(t_subscribe_timer timer, t_object_id id_timer) const;\n\t\n\t/**\n\t * Does incoming request match with event type and id?\n\t * @param r [in] Request to match.\n\t * @return True if request matches, otherwise false.\n\t */\n\tvirtual bool match(t_request *r) const;\n\n\t/**\n\t * Check if subscription is pending.\n\t * @return True if subscription is pending, otherwise false.\n\t */\n\tbool is_pending(void) const;\n\n\t/**\n\t * Subscribe to an event. \n\t * @param expires [in] Expiry in seconds. If expires == 0, then the default duration is used.\n\t */\n\tvirtual void subscribe(unsigned long expires);\n\t\n\t/** Unsubscribe from an event. */\n\tvirtual void unsubscribe(void);\n\t\n\t/** Refresh subscription. */\n\tvirtual void refresh_subscribe(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/subscription_dialog.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"subscription_dialog.h\"\n\n#include <cassert>\n\n#include \"log.h\"\n#include \"phone.h\"\n#include \"phone_user.h\"\n#include \"protocol.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nextern t_phone *phone;\nextern string local_hostname;\n\nt_subscription_dialog::t_subscription_dialog(t_phone_user *_phone_user) :\n\t\tt_abstract_dialog(_phone_user),\n\t\tsubscription(NULL)\n{}\n\nvoid t_subscription_dialog::send_request(t_request *r, t_tuid tuid) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tphone->send_request(user_config, r, tuid);\n}\n\nvoid t_subscription_dialog::process_subscribe(t_request *r, t_tuid tuid, t_tid tid) {\n\tif (get_subscription_state() == SS_NULL) {\n\t\t// Process initial incoming SUBSCRIBE. Create dialog state.\n\t\t// Set local tag\n\t\tif (r->hdr_to.tag.size() == 0) {\n\t\t\tlocal_tag = NEW_TAG;\n\t\t} else {\n\t\t\tlocal_tag = r->hdr_to.tag;\n\t\t}\n\t\t\n\t\tcall_id = r->hdr_call_id.call_id;\n\t\n\t\t// Initialize local seqnr\n\t\tlocal_seqnr = NEW_SEQNR;\n\t\tlocal_resp_nr = NEW_SEQNR;\n\t\n\t\tremote_tag = r->hdr_from.tag;\n\t\tlocal_uri = r->hdr_to.uri;\n\t\tlocal_display = r->hdr_to.display;\n\t\tremote_uri = r->hdr_from.uri;\n\t\tremote_display = r->hdr_from.display;\n\t\n\t\t// Set remote target URI and display name\n\t\tremote_target_uri = r->hdr_contact.contact_list.front().uri;\n\t\tremote_target_display = r->\n\t\t\t\thdr_contact.contact_list.front().display;\n\t\n\t\t// Set route set\n\t\tif (r->hdr_record_route.is_populated()) {\n\t\t\troute_set = r->hdr_record_route.route_list;\n\t\t}\n\t}\n\t\n\t(void)subscription->recv_subscribe(r, tuid, tid);\n}\n\nvoid t_subscription_dialog::process_notify(t_request *r, t_tuid tuid, t_tid tid) {\n\t// RFC 3265 3.1.4.4\n\t// A NOTIFY may be received before a 2XX response on the SUBSCRIBE.\n\t// If this happens, the remote information must be set using the\n\t// NOTIFY from header.\n\tif (remote_tag.empty()) {\n\t\tremote_tag = r->hdr_from.tag;\n\t\tremote_uri = r->hdr_from.uri;\n\t\tremote_display = r->hdr_from.display;\n\t}\n\t\n\t(void)subscription->recv_notify(r, tuid, tid);\n}\n\nbool t_subscription_dialog::process_initial_subscribe_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tswitch (r->get_class()) {\n\tcase R_2XX:\n\t\tremote_tag = r->hdr_to.tag;\n\t\tremote_uri = r->hdr_to.uri;\n\t\tremote_display = r->hdr_to.display;\n\t\tcreate_route_set(r);\n\t\tcreate_remote_target(r);\n\t\tbreak;\n\tcase R_4XX:\n\t\tif (r->code == R_423_INTERVAL_TOO_BRIEF) {\n\t\t\tif (!r->hdr_min_expires.is_populated()) {\n\t\t\t\t// Violation of RFC 3261 10.3 item 7\t\t\n\t\t\t\tlog_file->write_report(\"Expires header missing from 423 response.\",\n\t\t\t\t\t\"t_subscription_dialog::process_initial_subscribe_response\",\n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (r->hdr_min_expires.time <= subscription->get_expiry()) {\n\t\t\t\t// Wrong Min-Expires time\n\t\t\t\tstring s = \"Min-Expires (\";\n\t\t\t\ts += ulong2str(r->hdr_min_expires.time);\n\t\t\t\ts += \") is smaller than the requested \";\n\t\t\t\ts += \"time (\";\n\t\t\t\ts += ulong2str(subscription->get_expiry());\n\t\t\t\ts += \")\";\n                                log_file->write_report(s,\n                                \t\"t_subscription_dialog::process_initial_subscribe_response\",\n                                \tLOG_NORMAL, LOG_WARNING);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\t// Subscribe with the advised interval\n\t\t\tsubscribe(r->hdr_min_expires.time, remote_target_uri, \n\t\t\t\tremote_uri, remote_display);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\treturn false;\n}\n\nt_subscription_dialog::~t_subscription_dialog() {\n\tif (subscription) {\n\t\tMEMMAN_DELETE(subscription);\n\t\tdelete subscription;\n\t}\n}\n\nt_request *t_subscription_dialog::create_request(t_method m) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_request *r = t_abstract_dialog::create_request(m);\n\t\n\t// Contact header\n\tt_contact_param contact;\n\tswitch (m) {\n\tcase REFER:\n\tcase SUBSCRIBE:\n\tcase NOTIFY:\n\t\t// RFC 3265 7.1, RFC 3515 2.2\n\t\t// Contact header is mandatory\n\t\tcontact.uri.set_url(user_config->create_user_contact(false,\n\t\t\t\th_ip2str(r->get_local_ip())));\n\t\tr->hdr_contact.add_contact(contact);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\treturn r;\n}\n\nbool t_subscription_dialog::resend_request_auth(t_response *resp) {\n\tt_client_request **current_cr = &(subscription->req_out);\n\tif (!*current_cr) return false;\n\tt_request *req = (*current_cr)->get_request();\n\t\n\tif (phone_user->authorize(req, resp)) {\n\t\tresend_request(*current_cr);\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nbool t_subscription_dialog::redirect_request(t_response *resp) {\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tt_client_request **current_cr = &(subscription->req_out);\n\tif (!*current_cr) return false;\n\tt_request *req = (*current_cr)->get_request();\n\n\t// If the response is a 3XX response then add redirection\n\t// contacts\n\tif (resp->get_class() == R_3XX  &&\n\t\tresp->hdr_contact.is_populated())\n\t{\n\t\t(*current_cr)->redirector.add_contacts(\n\t\t\t\tresp->hdr_contact.contact_list);\n\t}\n\n\t// Get next destination\n\tt_contact_param contact;\n\tif ((*current_cr)->redirector.get_next_contact(contact)) {\n\t\t// Ask user for permission to redirect if indicated\n\t\t// by user config\n\t\tbool permission = true;\n\t\tif (user_config->get_ask_user_to_redirect()) {\n\t\t\tpermission = ui->cb_ask_user_to_redirect_request(\n\t\t\t\t\t\tuser_config,\n\t\t\t\t\t\tcontact.uri, contact.display,\n\t\t\t\t\t\tresp->hdr_cseq.method);\n\t\t}\n\n\t\tif (permission) {\n\t\t\treq->uri = contact.uri;\n\t\t\treq->calc_destinations(*user_config);\n\t\t\tui->cb_redirecting_request(user_config, contact);\n\t\t\tresend_request(*current_cr);\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\treturn false;\n}\n\nbool t_subscription_dialog::failover_request(t_response *resp) {\n\tt_client_request **current_cr = &(subscription->req_out);\n\tif (!*current_cr) return false;\n\tt_request *req = (*current_cr)->get_request();\n\t\n\tif (req->next_destination()) {\n\t\tlog_file->write_report(\"Failover to next destination.\",\n\t\t\t\"t_subscription_dialog::handle_response_out_of_dialog\");\n\t\tresend_request(*current_cr);\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nvoid t_subscription_dialog::recvd_response(t_response *r, t_tuid tuid, t_tid tid) {\n\tt_user *user_config = phone_user->get_user_profile();\n\tt_abstract_dialog::recvd_response(r, tuid ,tid);\n\n\tt_client_request *cr = subscription->req_out;\n\tif (!cr) return;\n\t\n\t// Check cseq\n\tif (r->hdr_cseq.method != cr->get_request()->method) {\n\t\treturn;\n\t}\n\tif (r->hdr_cseq.seqnr != cr->get_request()->hdr_cseq.seqnr) return;\n\t\n\t// Authentication\n\tif (r->must_authenticate()) {\n\t\tif (resend_request_auth(r)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Authentication failed\n\t\t// Handle the 401/407 as a normal failure response\n\t}\n\t\n\t// RFC 3263 4.3\n\t// Failover\n\tif (r->code == R_503_SERVICE_UNAVAILABLE) {\n\t\tif (failover_request(r)) {\n\t\t\treturn;\n\t\t}\t\t\t\n\t}\n\t\n\t// Redirect failed request if there is another destination\n\tif (r->get_class() > R_2XX && user_config->get_allow_redirection()) {\n\t\tif (redirect_request(r)) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Set the transaction identifier. This identifier is needed if the\n\t// transaction must be aborted at a later time.\n\tcr->set_tid(tid);\n\t\n\tswitch (r->hdr_cseq.method) {\n\tcase SUBSCRIBE:\n\t\t// Process response to initial SUBSCRIBE\n\t\tif (get_subscription_state() == SS_NULL) {\n\t\t\tif (process_initial_subscribe_response(r, tuid, tid)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\t\n\t(void)subscription->recv_response(r, tuid, tid);\n}\n\nvoid t_subscription_dialog::recvd_request(t_request *r, t_tuid tuid, t_tid tid) {\n\tt_response *resp;\n\t\n\tt_abstract_dialog::recvd_request(r, tuid, tid);\n\t\n\t// Check cseq\n\t// RFC 3261 12.2.2\n\tif (remote_seqnr_set && r->hdr_cseq.seqnr <= remote_seqnr) {\n\t\t// Request received out of order.\t\t\n\t\tlog_file->write_header(\"t_subscription_dialog::recvd_request\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"CSeq seqnr is out of sequence.\\n\");\n\t\tlog_file->write_raw(\"Reveived seqnr: \");\n\t\tlog_file->write_raw(r->hdr_cseq.seqnr);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_raw(\"Remote seqnr: \");\n\t\tlog_file->write_raw(remote_seqnr);\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR,\n\t\t\t\"Request received out of order\");\n\t\tphone->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\n\t\treturn;\n\t}\n\t\n\tremote_seqnr = r->hdr_cseq.seqnr;\n\tremote_seqnr_set = true;\n\n\tswitch (r->method) {\n\tcase SUBSCRIBE:\t\n\t\tprocess_subscribe(r, tuid, tid);\n\t\tbreak;\n\tcase NOTIFY:\n\t\tprocess_notify(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\t// Other requests are not supported in a subscription dialog.\n\t\tresp = r->create_response(R_500_INTERNAL_SERVER_ERROR);\n\t\tphone->send_response(resp, tuid, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n}\n\nbool t_subscription_dialog::match_request(t_request *r, bool &partial) {\n\tif (!subscription->match(r)) return false;\n\n\tif (t_abstract_dialog::match_request(r)) {\n\t\treturn true;\n\t}\n\t\n\tpartial = t_abstract_dialog::match_partial_request(r);\n\treturn false; \n}\n\nt_subscription_state t_subscription_dialog::get_subscription_state(void) const {\n\treturn subscription->get_state();\n}\n\nstring t_subscription_dialog::get_reason_termination(void) const {\n\treturn subscription->get_reason_termination();\n}\n\nunsigned long t_subscription_dialog::get_resubscribe_after(void) const {\n\treturn subscription->get_resubscribe_after();\n}\n\nbool t_subscription_dialog::get_may_resubscribe(void) const {\n\treturn subscription->get_may_resubscribe();\n}\n\nbool t_subscription_dialog::timeout(t_subscribe_timer timer) {\n\treturn subscription->timeout(timer);\n}\n\nbool t_subscription_dialog::match_timer(t_subscribe_timer timer, t_object_id id_timer) const {\n\treturn subscription->match_timer(timer, id_timer);\n}\n\nvoid t_subscription_dialog::subscribe(unsigned long expires, const t_url &req_uri,\n\t\tconst t_url &to_uri, const string &to_display) \n{\n\tt_user *user_config = phone_user->get_user_profile();\n\t\n\tassert (get_subscription_state() == SS_NULL);\n\tcall_id = NEW_CALL_ID(user_config);\n\tcall_id_owner = true;\n\tlocal_tag = NEW_TAG;\n\tlocal_display = user_config->get_display(false);\n\tlocal_uri = user_config->create_user_uri(false);\n\tlocal_seqnr = rand() % 1000 + 1;\n\tremote_uri = to_uri;\n\tremote_display = to_display;\n\tremote_tag.clear();\n\tremote_target_uri = req_uri;\n\troute_set = phone_user->get_service_route();\n\t\n\tsubscription->subscribe(expires);\n}\n\nvoid t_subscription_dialog::unsubscribe(void) {\n\tsubscription->unsubscribe();\n}\n\nvoid t_subscription_dialog::refresh_subscribe(void) {\n\tsubscription->refresh_subscribe();\n}\n"
  },
  {
    "path": "src/subscription_dialog.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Subscription dialog (RFC 3265)\n */\n\n#ifndef _SUBSCRIPTION_DIALOG_H\n#define _SUBSCRIPTION_DIALOG_H\n\n#include \"abstract_dialog.h\"\n#include \"subscription.h\"\n\n// Forward declaration\nclass t_phone_user;\n\n/**\n * RFC 3265\n * Generic subscription dialog state for subscribers and notifiers.\n * For each event type this class should be subclassed.\n */\nclass t_subscription_dialog : public t_abstract_dialog {\nprotected:\n\t/**\n\t * The subscription belonging to this dialog. Subclasses must\n\t * create the proper subscription.\n\t */\n\tt_subscription\t\t*subscription;\n\t\n\t/**\n\t * Constructor. This class must be subclassed. The subclass must provide\n\t * a public constructor.\n\t */\n\tt_subscription_dialog(t_phone_user *_phone_user);\n\n\tvirtual void send_request(t_request *r, t_tuid tuid);\n\t\n\t/**\n\t * Process a received SUBSCRIBE request.\n\t * @param r [in] The request.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvirtual void process_subscribe(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process a received NOTIFY request.\n\t * @param r [in] The request.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t */\n\tvirtual void process_notify(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Process the response to the initial SUBSCRIBE.\n\t * @param r [in] The response.\n\t * @param tuid [in] Transaction user id.\n\t * @param tid [in] Transaction id.\n\t * @return true, if no further processing is needed. This happens, when a\n\t * 423 Interval too brief response is received. Then this method sends a\n\t * new SUBSCRIBE.\n\t * @return false, subcalss must do further processing.\n\t */\n\tvirtual bool process_initial_subscribe_response(t_response *r, t_tuid tuid, t_tid tid);\n\npublic:\n\t/** Destructor. */\n\tvirtual ~t_subscription_dialog();\n\t\n\tvirtual t_request *create_request(t_method m);\n\t\n\tvirtual t_subscription_dialog *copy(void) = 0;\n\t\n\tvirtual bool resend_request_auth(t_response *resp);\n\n\tvirtual bool redirect_request(t_response *resp);\n\t\n\tvirtual bool failover_request(t_response *resp);\n\n\tvirtual void recvd_response(t_response *r, t_tuid tuid, t_tid tid);\n\t\n\tvirtual void recvd_request(t_request *r, t_tuid tuid, t_tid tid);\n\t\n\t/**\n\t * Match request with dialog and subscription.\n\t * @param r [in] The request.\n\t * @param partial [out] Indicates if there is a partial match on return.\n\t * @return true, if the request matches.\n\t * @return false, if the request does not match. In this case the request\n\t * may match partially, i.e. the from-tag matches, but the to-tag does not.\n\t * In case of a partial match, partial is set to true.\n\t */\n\tvirtual bool match_request(t_request *r, bool &partial);\n\t\n\t/**\n\t * Get the state of the subscription.\n\t * @return The subscription state.\n\t */\n\tt_subscription_state get_subscription_state(void) const;\n\t\n\t/**\n\t * Get the reason for termination of the subscription.\n\t * @return The termination reason.\n\t */\n\tstring get_reason_termination(void) const;\n\t\n\t/**\n\t * Get the time after which a resubscription may be tried.\n\t * @return The time in seconds.\n\t */\n\tunsigned long get_resubscribe_after(void) const;\n\t\n\t/**\n\t * Check if a resubscription may be tried.\n\t * @return true, if a resubscription may be tried.\n\t * @return false, otherwise.\n\t */\n\tbool get_may_resubscribe(void) const;\n\t\n\t/**\n\t * Process timeout.\n\t * @param timer [in] The timer that expired.\n\t * @return true, if processing is finished.\n\t * @return false, if subsclass needs to do further processing.\n\t */\n\tvirtual bool timeout(t_subscribe_timer timer);\n\t\n\t/**\n\t * Match a timer id with a running timer.\n\t * @param timer [in] The running timer.\n\t * @param id_timer [in] The timer id.\n\t * @return true, if timer id matches with timer.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool match_timer(t_subscribe_timer timer, t_object_id id_timer) const;\n\t\n\t/**\n\t * Subscribe to an event (send SUBSCRIBE).\n\t * @param epxires [in] The subscription interval in seconds.\n\t * @param req_uri [in] The request-URI for the SUBSCRIBE.\n\t * @param to_uri [in] The URI for the To header in the SUBSCRIBE.\n\t * @param to_display [in] The display name for the To header in the SUBSCRIBE.\n\t */\n\tvirtual void subscribe(unsigned long expires, const t_url &req_uri, \n\t\t\tconst t_url &to_uri, const string &to_display);\n\t\t\t\n\t/** Unsubscribe to an event (send SUBSCRIBE). */\n\tvirtual void unsubscribe(void);\n\t\n\t/** Refresh subscription. */\n\tvirtual void refresh_subscribe(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/sys_settings.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"twinkle_config.h\"\n\n\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/soundcard.h>\n#include <dirent.h>\n#include <unistd.h>\n#include <fstream>\n#include <iostream>\n#include <cstring>\n\n#include \"sys_settings.h\"\n\n#include \"log.h\"\n#include \"translator.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n#include \"utils/file_utils.h\"\n\nusing namespace utils;\n\n// Share directory containing files applicable to all users\n#define DIR_SHARE\tDATADIR\n\n// Lock file to guarantee that a user is running the application only once\n#define LOCK_FILENAME\t\"twinkle.lck\"\n\n// System config file\n#define SYS_CONFIG_FILE\t\"twinkle.sys\"\n\n// Default location of the shared mime database\n#define DFLT_SHARED_MIME_DB\t\"/usr/share/mime/globs\"\n\n// Field names in the config file\n// AUDIO fields\n#define FLD_DEV_RINGTONE\t\"dev_ringtone\"\n#define FLD_DEV_SPEAKER\t\t\"dev_speaker\"\n#define FLD_DEV_MIC\t\t\"dev_mic\"\n#define FLD_VALIDATE_AUDIO_DEV\t\"validate_audio_dev\"\n#define FLD_ALSA_PLAY_PERIOD_SIZE\t\"alsa_play_period_size\"\n#define FLD_ALSA_CAPTURE_PERIOD_SIZE\t\"alsa_capture_period_size\"\n#define FLD_OSS_FRAGMENT_SIZE\t\"oss_fragment_size\"\n\n// LOG fields\n#define FLD_LOG_MAX_SIZE\t\"log_max_size\"\n#define FLD_LOG_SHOW_SIP\t\"log_show_sip\"\n#define FLD_LOG_SHOW_STUN\t\"log_show_stun\"\n#define FLD_LOG_SHOW_MEMORY\t\"log_show_memory\"\n#define FLD_LOG_SHOW_DEBUG\t\"log_show_debug\"\n\n// GUI settings\n#define FLD_GUI_USE_SYSTRAY\t\"gui_use_systray\"\n#define FLD_GUI_HIDE_ON_CLOSE\t\"gui_hide_on_close\"\n#define FLD_GUI_SHOW_INCOMING_POPUP\t\"gui_show_incoming_popup\"\n#define FLD_GUI_AUTO_SHOW_INCOMING\t\"gui_auto_show_incoming\"\n#define FLD_GUI_AUTO_SHOW_TIMEOUT\t\"gui_auto_show_timeout\"\n#define FLD_GUI_BROWSER_CMD\t\"gui_browser_cmd\"\n#define FLD_GUI_SHOW_CALL_OSD\t\"gui_show_call_osd\"\n\n// Inhibit idle session\n#define FLD_INHIBIT_IDLE_SESSION\t\"inhibit_idle_session\"\n\n// Address book settings\n#define FLD_AB_SHOW_SIP_ONLY\t\"ab_show_sip_only\"\n#define FLD_AB_LOOKUP_NAME\t\"ab_lookup_name\"\n#define FLD_AB_OVERRIDE_DISPLAY\t\"ab_override_display\"\n#define FLD_AB_LOOKUP_PHOTO\t\"ab_lookup_photo\"\n\n// Call history fields\n#define FLD_CH_MAX_SIZE\t\t\"ch_max_size\"\n\n// Service settings\n#define FLD_CALL_WAITING\t\"call_waiting\"\n#define FLD_HANGUP_BOTH_3WAY\t\"hangup_both_3way\"\n\n// Startup settings\n#define FLD_START_USER_PROFILE\t\"start_user_profile\"\n#define FLD_START_HIDDEN\t\"start_hidden\"\n\n// Network settings\n#define FLD_sip_udp_port\t\"sip_udp_port\"\n#define FLD_sip_port\t\t\"sip_port\"\n#define FLD_RTP_PORT\t\t\"rtp_port\"\n#define FLD_SIP_MAX_UDP_SIZE\t\"sip_max_udp_size\"\n#define FLD_SIP_MAX_TCP_SIZE\t\"sip_max_tcp_size\"\n\n// Ring tone settings\n#define FLD_PLAY_RINGTONE\t\"play_ringtone\"\n#define FLD_RINGTONE_FILE\t\"ringtone_file\"\n#define FLD_PLAY_RINGBACK\t\"play_ringback\"\n#define FLD_RINGBACK_FILE\t\"ringback_file\"\n\n// Persistent storage for user interface state\n#define FLD_LAST_USED_PROFILE\t\"last_used_profile\"\n#define FLD_REDIAL_URL\t\t\"redial_url\"\n#define FLD_REDIAL_DISPLAY\t\"redial_display\"\n#define FLD_REDIAL_SUBJECT\t\"redial_subject\"\n#define FLD_REDIAL_PROFILE\t\"redial_profile\"\n#define FLD_REDIAL_HIDE_USER\t\"redial_hide_user\"\n#define FLD_DIAL_HISTORY\t\"dial_history\"\n#define FLD_SHOW_DISPLAY\t\"show_display\"\n#define FLD_COMPACT_LINE_STATUS\t\"compact_line_status\"\n#define FLD_SHOW_BUDDY_LIST\t\"show_buddy_list\"\n#define FLD_WARN_HIDE_USER\t\"warn_hide_user\"\n\n// Settings to restore session after shutdown\n#define FLD_UI_SESSION_ID\t\t\"ui_session_id\"\n#define FLD_UI_SESSION_ACTIVE_PROFILE\t\"ui_session_active_profile\"\n#define FLD_UI_SESSION_MAIN_GEOMETRY\t\"ui_session_main_geometry\"\n#define FLD_UI_SESSION_MAIN_HIDDEN\t\"ui_session_main_hidden\"\n#define FLD_UI_SESSION_MAIN_STATE\t\"ui_session_main_state\"\n\n// Mime settings\n#define FLD_MIME_SHARED_DATABASE\t\"mime_shared_database\"\n\n/////////////////////////\n// class t_audio_device\n/////////////////////////\n\nstring t_audio_device::get_description(void) const {\n\tstring s = device;\n\tif (type == OSS) {\n\t\ts = \"OSS: \" + s;\n\t\tif (sym_link.size() > 0) {\n\t\t\ts += \" -> \";\n\t\t\ts += sym_link;\n\t\t}\n\t\t\n\t\tif (name.size() > 0) {\n\t\t\ts += \": \";\n\t\t\ts += name;\n\t\t}\n\t} else if (type == ALSA) {\n\t\ts = \"ALSA: \" + s;\n\t\tif (!name.empty()) {\n\t\t\ts += \": \";\n\t\t\ts += name;\n\t\t}\n\t} else {\n\t\ts = \"Unknown: \" + s;\n\t}\n\t\n\treturn s;\n}\n\nstring t_audio_device::get_settings_value(void) const {\n\tstring s;\n\t\n\tswitch (type) {\n\tcase OSS:\n\t\ts = PFX_OSS;\n\t\tbreak;\n\tcase ALSA:\n\t\ts = PFX_ALSA;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\t\n\ts += device;\n\treturn s;\n}\n\n/////////////////////////\n// class t_win_geometry\n/////////////////////////\n\nt_win_geometry::t_win_geometry(int x_, int y_, int width_, int height_) :\n\t\tx(x_), y(y_), width(width_), height(height_)\n{}\n\nt_win_geometry::t_win_geometry() :\n\t\tx(0), y(0), width(0), height(0)\n{}\n\nt_win_geometry::t_win_geometry(const string &value) {\n\tvector<string> v = split(value, ',');\n\t\n\tif (v.size() == 4) {\n\t\tx = std::stoi(v[0]);\n\t\ty = std::stoi(v[1]);\n\t\twidth = std::stoi(v[2]);\n\t\theight = std::stoi(v[3]);\n\t}\n}\n\nstring t_win_geometry::encode(void) const {\n\tstring s;\n\t\n\ts = int2str(x);\n\ts += ',';\n\ts += int2str(y);\n\ts += ',';\n\ts += int2str(width);\n\ts += ',';\n\ts += int2str(height);\n\t\n\treturn s;\n}\n\n\n/////////////////////////\n// class t_sys_settings\n/////////////////////////\n\nt_sys_settings::t_sys_settings() {\n\tfd_lock_file = -1;\n\tdir_share = DIR_SHARE;\n\tfilename = string(DIR_HOME);\n\tfilename += \"/\";\n\tfilename += USER_DIR;\n\tfilename += \"/\";\n\tfilename += SYS_CONFIG_FILE;\n\t\n\t// Audio device default settings\n#ifdef HAVE_LIBASOUND\n\tdev_ringtone = audio_device(DEV_ALSA_DFLT);\n\tdev_speaker = audio_device(DEV_ALSA_DFLT);\n\tdev_mic = audio_device(DEV_ALSA_DFLT);\n#else\n\tdev_ringtone = audio_device();\n\tdev_speaker = audio_device();\n\tdev_mic = audio_device();\n#endif\n\tvalidate_audio_dev = true;\n\talsa_play_period_size = 128;\n\talsa_capture_period_size = 32;\n\toss_fragment_size = 128;\n\t\n\tlog_max_size = 5;\n\tlog_show_sip = true;\n\tlog_show_stun = true;\n\tlog_show_memory = true;\n\tlog_show_debug = false;\n\t\n\tgui_use_systray = true;\n\tgui_hide_on_close = true;\n\tgui_show_incoming_popup = true;\n\tgui_auto_show_incoming = false;\n\tgui_auto_show_timeout = 10;\n\tgui_show_call_osd = true;\n\n\tinhibit_idle_session = false;\n\t\n\tab_show_sip_only = false;\n\tab_lookup_name = true;\n\tab_override_display = true;\n\tab_lookup_photo = true;\n\t\n\tch_max_size = 50;\n\t\n\tcall_waiting = true;\n\thangup_both_3way = true;\n\t\n\tstart_user_profiles.clear();\n\tstart_hidden = false;\n\t\n\tconfig_sip_port = 5060;\n\tactive_sip_port = 0;\n\toverride_sip_port = 0;\n\trtp_port = 8000;\n\toverride_rtp_port = 0;\n\tsip_max_udp_size = 65535;\n\tsip_max_tcp_size = 1000000;\n\t\n\tplay_ringtone = true;\n\tringtone_file.clear();\n\tplay_ringback = true;\n\tringback_file.clear();\n\t\n\tlast_used_profile.clear();\n\tredial_url.set_url(\"\");\n\tredial_display.clear();\n\tredial_subject.clear();\n\tredial_profile.clear();\n\tredial_hide_user = false;\n\tdial_history.clear();\n\tshow_display = true;\n\tcompact_line_status = false;\n\tshow_buddy_list = true;\n\twarn_hide_user = true;\n\t\n\tui_session_id.clear();\n\t\n\tmime_shared_database = DFLT_SHARED_MIME_DB;\n}\n\n// Getters\nt_audio_device t_sys_settings::get_dev_ringtone(void) const {\n\tt_audio_device result;\n\tmtx_sys.lock();\n\tresult = dev_ringtone;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nt_audio_device t_sys_settings::get_dev_speaker(void) const {\n\tt_audio_device result;\n\tmtx_sys.lock();\n\tresult = dev_speaker;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nt_audio_device t_sys_settings::get_dev_mic(void) const {\n\tt_audio_device result;\n\tmtx_sys.lock();\n\tresult = dev_mic;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_validate_audio_dev(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = validate_audio_dev;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nint t_sys_settings::get_alsa_play_period_size(void) const {\n\tint result;\n\tmtx_sys.lock();\n\tresult = alsa_play_period_size;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nint t_sys_settings::get_alsa_capture_period_size(void) const {\n\tint result;\n\tmtx_sys.lock();\n\tresult = alsa_capture_period_size;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nint t_sys_settings::get_oss_fragment_size(void) const {\n\tint result;\n\tmtx_sys.lock();\n\tresult = oss_fragment_size;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nunsigned short t_sys_settings::get_log_max_size(void) const {\n\tunsigned short result;\n\tmtx_sys.lock();\n\tresult = log_max_size;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_log_show_sip(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = log_show_sip;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_log_show_stun(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = log_show_stun;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_log_show_memory(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = log_show_memory;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_log_show_debug(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = log_show_debug;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_gui_use_systray(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_use_systray;\n}\n\nbool t_sys_settings::get_gui_show_incoming_popup(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_show_incoming_popup;\n}\n\nbool t_sys_settings::get_gui_auto_show_incoming(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_auto_show_incoming;\n}\n\nint t_sys_settings::get_gui_auto_show_timeout(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_auto_show_timeout;\n}\n\nbool t_sys_settings::get_gui_hide_on_close(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_hide_on_close;\n}\n\nstring t_sys_settings::get_gui_browser_cmd(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_browser_cmd;\n}\n\nbool t_sys_settings::get_inhibit_idle_session(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn inhibit_idle_session;\n}\n\nbool t_sys_settings::get_ab_show_sip_only(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = ab_show_sip_only;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_ab_lookup_name(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = ab_lookup_name;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_ab_override_display(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = ab_override_display;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_ab_lookup_photo(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = ab_lookup_photo;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nint t_sys_settings::get_ch_max_size(void) const {\n\tint result;\n\tmtx_sys.lock();\n\tresult = ch_max_size;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_call_waiting(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = call_waiting;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_hangup_both_3way(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = hangup_both_3way;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nlist<string> t_sys_settings::get_start_user_profiles(void) const {\n\tlist<string> result;\n\tmtx_sys.lock();\n\tresult = start_user_profiles;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_start_hidden(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = start_hidden;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nunsigned short t_sys_settings::get_config_sip_port(void) const {\n\tunsigned short result;\n\tmtx_sys.lock();\n\tresult = config_sip_port;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nunsigned short t_sys_settings::get_rtp_port(void) const {\n\tunsigned short result;\n\tmtx_sys.lock();\n\tif (override_rtp_port > 0) {\n\t\tresult = override_rtp_port;\n\t} else {\n\t\tresult = rtp_port;\n\t}\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nunsigned short t_sys_settings::get_sip_max_udp_size(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn sip_max_udp_size;\n}\n\nunsigned long t_sys_settings::get_sip_max_tcp_size(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn sip_max_tcp_size;\n}\n\nbool t_sys_settings::get_play_ringtone(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = play_ringtone;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nstring t_sys_settings::get_ringtone_file(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = ringtone_file;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_play_ringback(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = play_ringback;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nstring t_sys_settings::get_ringback_file(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = ringback_file;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nstring t_sys_settings::get_last_used_profile(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = last_used_profile;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nt_url t_sys_settings::get_redial_url(void) const {\n\tt_url result;\n\tmtx_sys.lock();\n\tresult = redial_url;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nstring t_sys_settings::get_redial_display(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = redial_display;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nstring t_sys_settings::get_redial_subject(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = redial_subject;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nstring t_sys_settings::get_redial_profile(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = redial_profile;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_redial_hide_user(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = redial_hide_user;\n\tmtx_sys.unlock();\n\treturn result;\n}\n\nlist<string> t_sys_settings::get_dial_history(void) const {\n\tlist<string> result;\n\tmtx_sys.lock();\n\tresult = dial_history;\n\tmtx_sys.unlock();\n\treturn result;\t\n}\n\nbool t_sys_settings::get_show_display(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = show_display;\n\tmtx_sys.unlock();\n\treturn result;\n}\n\nbool t_sys_settings::get_compact_line_status(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = compact_line_status;\n\tmtx_sys.unlock();\n\treturn result;\n}\n\nbool t_sys_settings::get_show_buddy_list(void) const {\n\tbool result;\n\tmtx_sys.lock();\n\tresult = show_buddy_list;\n\tmtx_sys.unlock();\n\treturn result;\n}\n\nbool t_sys_settings::get_gui_show_call_osd() const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn gui_show_call_osd;\n}\n\nstring t_sys_settings::get_ui_session_id(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn ui_session_id;\n}\n\nlist<string> t_sys_settings::get_ui_session_active_profiles(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn ui_session_active_profiles;\n}\n\nt_win_geometry t_sys_settings::get_ui_session_main_geometry(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn ui_session_main_geometry;\n}\n\nbool t_sys_settings::get_ui_session_main_hidden(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn ui_session_main_hidden;\n}\n\nunsigned int t_sys_settings::get_ui_session_main_state(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn ui_session_main_state;\n}\n\nbool t_sys_settings::get_warn_hide_user(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn warn_hide_user;\n}\n\nstring t_sys_settings::get_mime_shared_database(void) const {\n\tt_mutex_guard guard(mtx_sys);\n\treturn mime_shared_database;\n}\n\n// Setters\nvoid t_sys_settings::set_dev_ringtone(const t_audio_device &dev) {\n\tmtx_sys.lock();\n\tdev_ringtone = dev;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_dev_speaker(const t_audio_device &dev) {\n\tmtx_sys.lock();\n\tdev_speaker = dev;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_dev_mic(const t_audio_device &dev) {\n\tmtx_sys.lock();\n\tdev_mic = dev;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_validate_audio_dev(bool b) {\n\tmtx_sys.lock();\n\tvalidate_audio_dev = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_alsa_play_period_size(int size) {\n\tmtx_sys.lock();\n\talsa_play_period_size = size;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_alsa_capture_period_size(int size) {\n\tmtx_sys.lock();\n\talsa_capture_period_size = size;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_oss_fragment_size(int size) {\n\tmtx_sys.lock();\n\toss_fragment_size = size;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_log_max_size(unsigned short size) {\n\tmtx_sys.lock();\n\tlog_max_size = size;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_log_show_sip(bool b) {\n\tmtx_sys.lock();\n\tlog_show_sip = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_log_show_stun(bool b) {\n\tmtx_sys.lock();\n\tlog_show_stun = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_log_show_memory(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tlog_show_memory = b;\n}\n\nvoid t_sys_settings::set_log_show_debug(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tlog_show_debug = b;\n}\n\nvoid t_sys_settings::set_gui_use_systray(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_use_systray = b;\n}\n\nvoid t_sys_settings::set_gui_hide_on_close(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_hide_on_close = b;\n}\n\nvoid t_sys_settings::set_gui_show_incoming_popup(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_show_incoming_popup = b;\n}\n\nvoid t_sys_settings::set_gui_auto_show_incoming(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_auto_show_incoming = b;\n}\n\nvoid t_sys_settings::set_gui_auto_show_timeout(int timeout) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_auto_show_timeout = timeout;\n}\n\nvoid t_sys_settings::set_gui_browser_cmd(const string &s) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_browser_cmd = s;\n}\n\nvoid t_sys_settings::set_inhibit_idle_session(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tinhibit_idle_session = b;\n}\n\nvoid t_sys_settings::set_ab_show_sip_only(bool b) {\n\tmtx_sys.lock();\n\tab_show_sip_only = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ab_lookup_name(bool b) {\n\tmtx_sys.lock();\n\tab_lookup_name = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ab_override_display(bool b) {\n\tmtx_sys.lock();\n\tab_override_display = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ab_lookup_photo(bool b) {\n\tmtx_sys.lock();\n\tab_lookup_photo = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ch_max_size(int size) {\n\tmtx_sys.lock();\n\tch_max_size = size;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_call_waiting(bool b) {\n\tmtx_sys.lock();\n\tcall_waiting = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_hangup_both_3way(bool b) {\n\tmtx_sys.lock();\n\thangup_both_3way = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_start_user_profiles(const list<string> &profiles) {\n\tmtx_sys.lock();\n\tstart_user_profiles = profiles;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_start_hidden(bool b) {\n\tmtx_sys.lock();\n\tstart_hidden = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_config_sip_port(unsigned short port) {\n\tmtx_sys.lock();\n\tconfig_sip_port = port;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_override_sip_port(unsigned short port) {\n\tmtx_sys.lock();\n\toverride_sip_port = port;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_rtp_port(unsigned short port) {\n\tmtx_sys.lock();\n\trtp_port = port;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_override_rtp_port(unsigned short port) {\n\tmtx_sys.lock();\n\toverride_rtp_port = port;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_sip_max_udp_size(unsigned short size) {\n\tt_mutex_guard guard(mtx_sys);\n\tsip_max_udp_size = size;\n}\n\nvoid t_sys_settings::set_sip_max_tcp_size(unsigned long size) {\n\tt_mutex_guard guard(mtx_sys);\n\tsip_max_tcp_size = size;\n}\n\nvoid t_sys_settings::set_play_ringtone(bool b) {\n\tmtx_sys.lock();\n\tplay_ringtone = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ringtone_file(const string &file) {\n\tmtx_sys.lock();\n\tringtone_file = file;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_play_ringback(bool b) {\n\tmtx_sys.lock();\n\tplay_ringback = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ringback_file(const string &file) {\n\tmtx_sys.lock();\n\tringback_file = file;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_last_used_profile(const string &profile) {\n\tmtx_sys.lock();\n\tlast_used_profile = profile;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_redial_url(const t_url &url) {\n\tmtx_sys.lock();\n\tredial_url = url;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_redial_display(const string &display) {\n\tmtx_sys.lock();\n\tredial_display = display;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_redial_subject(const string &subject) {\n\tmtx_sys.lock();\n\tredial_subject = subject;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_redial_profile(const string &profile) {\n\tmtx_sys.lock();\n\tredial_profile = profile;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_redial_hide_user(const bool b) {\n\tmtx_sys.lock();\n\tredial_hide_user = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_dial_history(const list<string> &history) {\n\tmtx_sys.lock();\n\tdial_history = history;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_show_display(bool b) {\n\tmtx_sys.lock();\n\tshow_display = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_compact_line_status(bool b) {\n\tmtx_sys.lock();\n\tcompact_line_status = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_show_buddy_list(bool b) {\n\tmtx_sys.lock();\n\tshow_buddy_list = b;\n\tmtx_sys.unlock();\n}\n\nvoid t_sys_settings::set_ui_session_id(const string &id) {\n\tt_mutex_guard guard(mtx_sys);\n\tui_session_id = id;\n}\n\nvoid t_sys_settings::set_ui_session_active_profiles(const list<string> &profiles) {\n\tt_mutex_guard guard(mtx_sys);\n\tui_session_active_profiles = profiles;\n}\n\nvoid t_sys_settings::set_ui_session_main_geometry(const t_win_geometry &geometry) {\n\tt_mutex_guard guard(mtx_sys);\n\tui_session_main_geometry = geometry;\n}\n\nvoid t_sys_settings::set_ui_session_main_hidden(bool hidden) {\n\tt_mutex_guard guard(mtx_sys);\n\tui_session_main_hidden = hidden;\n}\n\nvoid t_sys_settings::set_ui_session_main_state(unsigned int state) {\n\tt_mutex_guard guard(mtx_sys);\n\tui_session_main_state = state;\n}\n\nvoid t_sys_settings::set_warn_hide_user(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\twarn_hide_user = b;\n}\n\nvoid t_sys_settings::set_gui_show_call_osd(bool b) {\n\tt_mutex_guard guard(mtx_sys);\n\tgui_show_call_osd = b;\n}\n\nvoid t_sys_settings::set_mime_shared_database(const string &filename) {\n\tt_mutex_guard guard(mtx_sys);\n\tmime_shared_database = filename;\n}\n\nstring t_sys_settings::about(bool html) const {\n\tstring s = PRODUCT_NAME;\n\ts += ' ';\n\ts += PRODUCT_VERSION;\n\ts += \" - \";\n\ts += get_product_date();\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"Copyright (C) 2005-2015  \";\n\ts += PRODUCT_AUTHOR;\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\n\ts += \"http://twinkle.dolezel.info\";\n\tif (html) s += \"<BR><BR>\";\n\ts += \"\\n\\n\";\n\t\n\tstring options_built = get_options_built();\n\tif (!options_built.empty()) {\n\t\ts += TRANSLATE(\"Built with support for:\");\n\t\ts += \" \";\n\t\ts += options_built;\n\t\tif (html) s += \"<BR><BR>\";\n\t\ts += \"\\n\\n\";\n\t}\n\t\n\ts += TRANSLATE(\"Contributions:\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"* Werner Dittmann (ZRTP/SRTP)\\n\";\n\tif (html) s += \"<BR>\";\n\t\n\ts += \"* Bogdan Harjoc (AKAv1-MD5, Service-Route)\\n\";\n\tif (html) s += \"<BR>\";\n\t\n\ts += \"* Roman Imankulov (command line editing)\\n\";\n\tif (html) s += \"<BR>\";\n\t\n\tif (html) {\n\t\ts += \"* Ondrej Mori&scaron; (codec preprocessing)<BR>\\n\";\n\t} else {\n\t\ts += \"* Ondrej Moris (codec preprocessing)\\n\";\n\t}\n\t\n\tif (html) {\n\t\ts += \"* Rickard Petz&auml;ll (ALSA)<BR>\\n\";\n\t} else {\n\t\ts += \"* Rickard Petzall (ALSA)\\n\";\n\t}\n\t\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\n\ts += TRANSLATE(\"This software contains the following software from 3rd parties:\");\t\t\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\n\ts += TRANSLATE(\"* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\n\ts += TRANSLATE(\"* G.711/G.726 codecs from Sun Microsystems (public domain)\");\t\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += TRANSLATE(\"* G.722 codec from Steve Underwood (public domain)\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\n#ifdef HAVE_ILBC\n\ts += TRANSLATE(\"* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)\");\t\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n#endif\n#ifdef HAVE_BCG729\n\ts += TRANSLATE(\"* G729A codec (http://www.linphone.org/technical-corner/bcg729/overview\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n#endif\n\t\n\ts += TRANSLATE(\"* Parts of the STUN project at http://sourceforge.net/projects/stun\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += TRANSLATE(\"* Parts of libsrv at http://libsrv.sourceforge.net/\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += TRANSLATE(\"For RTP the following dynamic libraries are linked:\");\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"* GNU ccRTP - http://www.gnu.org/software/ccrtp\";\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\n\ts += \"* GNU uCommon C++ - https://www.gnu.org/software/commoncpp/\";\n\tif (html) s += \"<BR><BR>\";\n\ts += \"\\n\\n\";\n\t\n\t// Display information about translator only on non-english version.\n\tstring translated_by = TRANSLATE(\"Translated to english by <your name>\");\n\tif (translated_by != \"Translated to english by <your name>\") {\n\t\ts += translated_by;\n\t\tif (html) s += \"<BR><BR>\";\n\t\ts += \"\\n\\n\";\n\t}\n\t\n\ts += PRODUCT_NAME;\n\ts += \" comes with ABSOLUTELY NO WARRANTY.\";\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"This program is free software; you can redistribute it and/or modify\";\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"it under the terms of the GNU General Public License as published by\";\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"the Free Software Foundation; either version 2 of the License, or\";\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\ts += \"(at your option) any later version.\";\n\tif (html) s += \"<BR>\";\n\ts += \"\\n\";\n\t\n\treturn s;\n}\n\nstring t_sys_settings::get_product_date(void) const {\n\tstruct tm t;\n\tt.tm_sec = 0;\n\tt.tm_min = 0;\n\tt.tm_hour = 0;\n\t\n\tvector<string> l = split(PRODUCT_DATE, ' ');\n\tassert(l.size() == 3);\n\tt.tm_mon = str2month_full(l[0]);\n\tt.tm_mday = std::stoi(l[1]);\n\tt.tm_year = std::stoi(l[2]) - 1900;\n\t\n\tchar buf[64];\n\tstrftime(buf, 64, \"%d %B %Y\", &t);\n\treturn string(buf);\n}\n\nstring t_sys_settings::get_options_built(void) const {\n\tstring options_built;\n#ifdef HAVE_LIBASOUND\n\tif (!options_built.empty()) options_built += \", \";\n\toptions_built += \"ALSA\";\n#endif\n#ifdef HAVE_KDE\n\tif (!options_built.empty()) options_built += \", \";\n\toptions_built += \"KDE\";\n#endif\n#ifdef HAVE_SPEEX\n\tif (!options_built.empty()) options_built += \", \";\n\toptions_built += \"Speex\";\n#endif\n#ifdef HAVE_ILBC\n\tif (!options_built.empty()) options_built += \", \";\n\toptions_built += \"iLBC\";\n#endif\n#ifdef HAVE_BCG729\n\tif (!options_built.empty()) options_built += \", \";\n\toptions_built += \"G729\";\n#endif\n#ifdef HAVE_ZRTP\n\tif (!options_built.empty()) options_built += \", \";\n\toptions_built += \"ZRTP\";\n#endif\n\n\treturn options_built;\n}\n\nbool t_sys_settings::check_environment(string &error_msg) const {\n\tstruct stat stat_buf;\n\tstring filename, dirname;\n\t\n\tmtx_sys.lock();\n\n\t// Check if share directory exists\n\tif (stat(dir_share.c_str(), &stat_buf) != 0) {\n\t\terror_msg = TRANSLATE(\"Directory %1 does not exist.\");\n\t\terror_msg = replace_first(error_msg, \"%1\", dir_share);\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\n\t// Check if audio file for ring tone exist\n\tfilename = dir_share;\n\tfilename += '/';\n\tfilename += FILE_RINGTONE;\n\tifstream f_ringtone(filename.c_str());\n\tif (!f_ringtone) {\n\t\terror_msg = TRANSLATE(\"Cannot open file %1 .\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\n\t// Check if audio file for ring back exist\n\tfilename = dir_share;\n\tfilename += '/';\n\tfilename += FILE_RINGBACK;\n\tifstream f_ringback(filename.c_str());\n\tif (!f_ringback) {\n\t\terror_msg = TRANSLATE(\"Cannot open file %1 .\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\n\t// Check if $HOME is set correctly\n\tif (string(DIR_HOME) == \"\") {\n\t\terror_msg = TRANSLATE(\"%1 is not set to your home directory.\");\n\t\terror_msg = replace_first(error_msg, \"%1\", \"$HOME\");\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\tif (stat(DIR_HOME, &stat_buf) != 0) {\n\t\terror_msg = TRANSLATE(\"Directory %1 (%2) does not exist.\");\n\t\terror_msg = replace_first(error_msg, \"%1\", DIR_HOME);\n\t\terror_msg = replace_first(error_msg, \"%2\", \"$HOME\");\t\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\n\t// Check if user directory exists\n\tdirname = get_dir_user();\n\tif (stat(dirname.c_str(), &stat_buf) != 0) {\n\t\t// User directory does not exist. Create it now.\n\t\tif (mkdir(dirname.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {\n\t\t\t// Failed to create the user directory\n\t\t\terror_msg = TRANSLATE(\"Cannot create directory %1 .\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", dirname);\n\t\t\tmtx_sys.unlock();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Check if tmp file directory exists\n\tdirname = get_dir_tmpfile();\n\tif (stat(dirname.c_str(), &stat_buf) != 0) {\n\t\t// Tmp file directory does not exist. Create it now.\n\t\tif (mkdir(dirname.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {\n\t\t\t// Failed to create the tmp file directory\n\t\t\terror_msg = TRANSLATE(\"Cannot create directory %1 .\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", dirname);\n\t\t\tmtx_sys.unlock();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tmtx_sys.unlock();\n\treturn true;\n}\n\nvoid t_sys_settings::set_dir_share(const string &dir) {\n\tmtx_sys.lock();\n\tdir_share = dir;\n\tmtx_sys.unlock();\n}\n\nstring t_sys_settings::get_dir_share(void) const {\n\tstring result;\n\tmtx_sys.lock();\n\tresult = dir_share;\n\tmtx_sys.unlock();\n\treturn result;\n}\n\nstring t_sys_settings::get_dir_lang(void) const {\n\tstring result = get_dir_share();\n\tresult += \"/lang\";\n\treturn result;\n}\n\nstring t_sys_settings::get_dir_user(void) const {\n\tstring dir = DIR_HOME;\n\tdir += \"/\";\n\tdir += DIR_USER;\n\t\n\treturn dir;\n}\n\nstring t_sys_settings::get_history_file(void) const {\n\tstring dir = get_dir_user();\n\tdir += \"/\";\n\tdir += FILE_CLI_HISTORY;\n\t\n\treturn dir;\n}\n\nstring t_sys_settings::get_dir_tmpfile(void) const {\n\tstring dir = get_dir_user();\n\tdir += \"/\";\n\tdir += DIR_TMPFILE;\n\t\n\treturn dir;\n}\n\nbool t_sys_settings::is_tmpfile(const string &filename) const {\n\tstring tmpdir = get_dir_tmpfile();\n\t\n\treturn filename.substr(0, tmpdir.size()) == tmpdir;\n}\n\nbool t_sys_settings::save_tmp_file(const string &data, const string &file_extension,\n\t\tstring &filename, string &error_msg) \n{\n\tstring fname = get_dir_tmpfile();\n\tfname += \"/XXXXXX\";\n\t\n\tchar *tmpfile = strdup(fname.c_str());\n\tMEMMAN_NEW(tmpfile);\n\tint fd = mkstemp(tmpfile);\n\t\n\tif (fd < 0) {\n\t\terror_msg = get_error_str(errno);\n\t\tMEMMAN_DELETE(tmpfile);\n\t\tfree(tmpfile);\n\t\treturn false;\n\t}\n\t\n\tclose(fd);\n\tofstream f(tmpfile);\n\tif (!f) {\n\t\terror_msg = TRANSLATE(\"Failed to create file %1\");\n\t\terror_msg = replace_first(error_msg, \"%1\", tmpfile);\n\t\tMEMMAN_DELETE(tmpfile);\n\t\tfree(tmpfile);\n\t\treturn false;\n\t}\n\t\n\tf.write(data.c_str(), data.size());\n\tif (!f.good()) {\n\t\terror_msg = TRANSLATE(\"Failed to write data to file %1\");\n\t\terror_msg = replace_first(error_msg, \"%1\", tmpfile);\n\t\tf.close();\n\t\tMEMMAN_DELETE(tmpfile);\n\t\tfree(tmpfile);\n\t\treturn false;\n\t}\n\t\n\tf.close();\n\t\n\t// Rename to name with extension\n\tfilename = apply_glob_to_filename(tmpfile, file_extension);\n\t\n\tif (rename(tmpfile, filename.c_str()) < 0) {\n\t\terror_msg = get_error_str(errno);\n\t\tMEMMAN_DELETE(tmpfile);\n\t\tfree(tmpfile);\n\t\treturn false;\n\t}\n\t\n\tMEMMAN_DELETE(tmpfile);\n\tfree(tmpfile);\n\treturn true;\n}\n\nbool t_sys_settings::save_sip_body(const t_sip_message &sip_msg,\n\t\tconst string &suggested_file_extension,\n\t\tstring &tmpname, string &save_as_name, string &error_msg)\n{\n\tbool retval = true;\n\t\n\tif (!sip_msg.body) {\n\t\terror_msg = \"Missing body\";\n\t\treturn false;\n\t}\n\t\n\t// Determine file extension and save-as name\n\t// The algorithm to get the file extension (glob expression) is:\n\t// 1) If the a file name is supplied in the Content-Disposition header, then\n\t//    take the file extension from that file name.\n\t// 2) If no extension is found, then take the suggested_file_extension\n\t// 3) If still no file extension is found, then retrieve the file extension\n\t//    from the t_media object in the Content-Type header. \n\tstring file_ext = suggested_file_extension;\n\tsave_as_name.clear();\n\t\n\tif (sip_msg.hdr_content_disp.is_populated() &&\n\t    sip_msg.hdr_content_disp.type == DISPOSITION_ATTACHMENT &&\n\t    !sip_msg.hdr_content_disp.filename.empty()) \n\t{\n\t\tstring x = get_extension_from_filename(sip_msg.hdr_content_disp.filename);\n\t\tif (!x.empty()) file_ext = string(\"*.\" + x);\n\t\t\n\t\tsave_as_name = strip_path_from_filename(sip_msg.hdr_content_disp.filename);\n\t}\n\tif (file_ext.empty()) {\n\t\tfile_ext = sip_msg.hdr_content_type.media.get_file_glob();\n\t\t\n\t\tif (file_ext.empty()) {\n\t\t\tfile_ext = \"*\";\n\t\t}\n\t}\n\t\n\t// Avoid copy of opaque data\n\tif (sip_msg.body->get_type() == BODY_OPAQUE) {\n\t\tt_sip_body_opaque *body_opaque = dynamic_cast<t_sip_body_opaque *>(sip_msg.body);\n\t\tretval = save_tmp_file(body_opaque->opaque, file_ext, tmpname, error_msg);\n\t} else {\n\t\tretval = save_tmp_file(sip_msg.body->encode(), file_ext, tmpname, error_msg);\n\t}\n\t\n\treturn retval;\n}\n\nvoid t_sys_settings::remove_all_tmp_files(void) const {\n\tDIR *tmpdir = opendir(get_dir_tmpfile().c_str());\n\t\n\tif (!tmpdir) {\n\t\tlog_file->write_report(get_error_str(errno), \"t_sys_settings::remove_all_tmp_files\");\n\t\treturn;\n\t}\n\t\n\tstruct dirent *entry = readdir(tmpdir);\n\twhile (entry) {\n\t\tif (strcmp(entry->d_name, \".\") != 0 && strcmp(entry->d_name, \"..\") != 0) {\n\t\t\tstring fname = get_dir_tmpfile();\n\t\t\tfname += PATH_SEPARATOR;\n\t\t\tfname += entry->d_name;\n\t\t\t\n\t\t\tlog_file->write_header(\"t_sys_settings::remove_all_tmp_files\");\n\t\t\tlog_file->write_raw(\"Remove tmp file \");\n\t\t\tlog_file->write_raw(fname);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tunlink(fname.c_str());\n\t\t}\n\t\t\n\t\tentry = readdir(tmpdir);\n\t}\n\t\n\tclosedir(tmpdir);\n}\n\nbool t_sys_settings::create_lock_file(bool shared_lock, string &error_msg, \n                                      bool &already_running) \n{\n\tstring lck_filename;\n\talready_running = false;\n\n        lck_filename = DIR_HOME;\n        lck_filename += \"/\";\n        lck_filename += DIR_USER;\n        lck_filename += \"/\";\n        lck_filename += LOCK_FILENAME;\n\t\n\tfd_lock_file = open(lck_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);\n\tif (fd_lock_file < 0) {\n\t\terror_msg = TRANSLATE(\"Cannot create %1 .\");\n\t\terror_msg = replace_first(error_msg, \"%1\", lck_filename);\n\t\terror_msg += \"\\n\";\n\t\terror_msg += get_error_str(errno);\n\t\treturn false;\n\t}\n\t\n\tstruct flock lock_options;\n\t\n\t// Try to acquire an exclusive lock\n\tif (!shared_lock)\n\t{\n\t\tmemset(&lock_options, 0, sizeof(struct flock));\n\t\tlock_options.l_type = F_WRLCK;\n\t\tlock_options.l_whence = SEEK_SET;\n\t\t\n\t\tif (fcntl(fd_lock_file, F_SETLK, &lock_options) < 0) {\n\t\t\talready_running = true;\n\t\t\terror_msg = TRANSLATE(\"%1 is already running.\\nLock file %2 already exists.\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", PRODUCT_NAME);\n\t\t\terror_msg = replace_first(error_msg, \"%2\", lck_filename);\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Convert the lock to a shared lock. If the user forces multiple\n\t// instances of Twinkle to run, then each will have a shared lock.\n\tmemset(&lock_options, 0, sizeof(struct flock));\n\tlock_options.l_type = F_RDLCK;\n\tlock_options.l_whence = SEEK_SET;\n\t\n\tif (fcntl(fd_lock_file, F_SETLK, &lock_options) < 0) {\n\t\terror_msg = TRANSLATE(\"Cannot lock %1 .\");\n\t\terror_msg = replace_first(error_msg, \"%1\", lck_filename);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid t_sys_settings::delete_lock_file(void) {\n\tif (fd_lock_file >= 0)\n\t{\n\t\tstruct flock lock_options;\n\t\tlock_options.l_type = F_UNLCK;\n\t\tlock_options.l_whence = SEEK_SET;\n\t\t\n\t\tfcntl(fd_lock_file, F_SETLK, &lock_options);\n\t\t\n\t\tclose(fd_lock_file);\n\t\tfd_lock_file = -1;\n\t}\n}\n\nbool t_sys_settings::read_config(string &error_msg) {\n\tstruct stat stat_buf;\n\t\n\tmtx_sys.lock();\n\t\n\t// Check if config file exists\n\tif (stat(filename.c_str(), &stat_buf) != 0) {\n\t\tmtx_sys.unlock();\n\t\treturn true;\n\t}\n\t\n\t// Open config file\n\tifstream config(filename.c_str());\n\tif (!config) {\n\t\terror_msg = TRANSLATE(\"Cannot open file for reading: %1\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\t\n\t// Read and parse config file.\n\twhile (!config.eof()) {\n\t\tstring line;\n\t\tgetline(config, line);\n\n\t\t// Check if read operation succeeded\n\t\tif (!config.good() && !config.eof()) {\n\t\t\terror_msg = TRANSLATE(\"File system error while reading file %1 .\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\t\tmtx_sys.unlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tline = trim(line);\n\n\t\t// Skip empty lines\n\t\tif (line.size() == 0) continue;\n\n\t\t// Skip comment lines\n\t\tif (line[0] == '#') continue;\n\n\t\tvector<string> l = split_on_first(line, '=');\n\t\tif (l.size() != 2) {\n\t\t\terror_msg = TRANSLATE(\"Syntax error in file %1 .\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += line;\n\t\t\tmtx_sys.unlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tstring parameter = trim(l[0]);\n\t\tstring value = trim(l[1]);\n\n\t\tif (parameter == FLD_DEV_RINGTONE) {\n\t\t\tdev_ringtone = audio_device(value);\n\t\t} else if (parameter == FLD_DEV_SPEAKER) {\n\t\t\tdev_speaker = audio_device(value);\n\t\t} else if (parameter == FLD_DEV_MIC) {\n\t\t\tdev_mic = audio_device(value);\n\t\t} else if (parameter == FLD_VALIDATE_AUDIO_DEV) {\n\t\t\tvalidate_audio_dev = yesno2bool(value);\n\t\t} else if (parameter == FLD_ALSA_PLAY_PERIOD_SIZE) {\n\t\t\talsa_play_period_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_ALSA_CAPTURE_PERIOD_SIZE) {\n\t\t\talsa_capture_period_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_OSS_FRAGMENT_SIZE) {\n\t\t\toss_fragment_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_LOG_MAX_SIZE) {\n\t\t\tlog_max_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_LOG_SHOW_SIP) {\n\t\t\tlog_show_sip = yesno2bool(value);\n\t\t} else if (parameter == FLD_LOG_SHOW_STUN) {\n\t\t\tlog_show_stun = yesno2bool(value);\n\t\t} else if (parameter == FLD_LOG_SHOW_MEMORY) {\n\t\t\tlog_show_memory = yesno2bool(value);\n\t\t} else if (parameter == FLD_LOG_SHOW_DEBUG) {\n\t\t\tlog_show_debug = yesno2bool(value);\n\t\t} else if (parameter == FLD_GUI_USE_SYSTRAY) {\n\t\t\tgui_use_systray = yesno2bool(value);\n\t\t} else if (parameter == FLD_GUI_HIDE_ON_CLOSE) {\n\t\t\tgui_hide_on_close = yesno2bool(value);\n\t\t} else if (parameter == FLD_GUI_SHOW_INCOMING_POPUP) {\n\t\t\tgui_show_incoming_popup = yesno2bool(value);\n\t\t} else if (parameter == FLD_GUI_AUTO_SHOW_INCOMING) {\n\t\t\tgui_auto_show_incoming = yesno2bool(value);\n\t\t} else if (parameter == FLD_GUI_AUTO_SHOW_TIMEOUT) {\n\t\t\tgui_auto_show_timeout = atoi(value.c_str());\n\t\t} else if (parameter == FLD_GUI_BROWSER_CMD) {\n\t\t\tgui_browser_cmd = value;\n\t\t} else if (parameter == FLD_GUI_SHOW_CALL_OSD) {\n\t\t\tgui_show_call_osd = yesno2bool(value);\n\t\t} else if (parameter == FLD_INHIBIT_IDLE_SESSION) {\n\t\t\tinhibit_idle_session = yesno2bool(value);\n\t\t} else if (parameter == FLD_AB_SHOW_SIP_ONLY) {\n\t\t\tab_show_sip_only = yesno2bool(value);\n\t\t} else if (parameter == FLD_AB_LOOKUP_NAME) {\n\t\t\tab_lookup_name = yesno2bool(value);\n\t\t} else if (parameter == FLD_AB_OVERRIDE_DISPLAY) {\n\t\t\tab_override_display = yesno2bool(value);\n\t\t} else if (parameter == FLD_AB_LOOKUP_PHOTO) {\n\t\t\tab_lookup_photo = yesno2bool(value);\n\t\t} else if (parameter == FLD_CH_MAX_SIZE) {\n\t\t\tch_max_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_CALL_WAITING) {\n\t\t\tcall_waiting = yesno2bool(value);\n\t\t} else if (parameter == FLD_HANGUP_BOTH_3WAY) {\n\t\t\thangup_both_3way = yesno2bool(value);\n\t\t} else if (parameter == FLD_START_USER_PROFILE) {\n\t\t\tif (!value.empty()) start_user_profiles.push_back(value);\n\t\t} else if (parameter == FLD_START_HIDDEN) {\n\t\t\tstart_hidden = yesno2bool(value);\n\t\t} else if (parameter == FLD_sip_udp_port) { // Deprecated parameter\n\t\t\tconfig_sip_port = atoi(value.c_str());\n\t\t} else if (parameter == FLD_sip_port) {\n\t\t\tconfig_sip_port = atoi(value.c_str());\n\t\t} else if (parameter == FLD_RTP_PORT) {\n\t\t\trtp_port = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SIP_MAX_UDP_SIZE) {\n\t\t\tsip_max_udp_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SIP_MAX_TCP_SIZE) {\n\t\t\tsip_max_tcp_size = atoi(value.c_str());\n\t\t} else if (parameter == FLD_PLAY_RINGTONE) {\n\t\t\tplay_ringtone = yesno2bool(value);\n\t\t} else if (parameter == FLD_RINGTONE_FILE) {\n\t\t\tringtone_file = value;\n\t\t} else if (parameter == FLD_PLAY_RINGBACK) {\n\t\t\tplay_ringback = yesno2bool(value);\n\t\t} else if (parameter == FLD_RINGBACK_FILE) {\n\t\t\tringback_file = value;\n\t\t} else if (parameter == FLD_LAST_USED_PROFILE) {\n\t\t\tlast_used_profile = value;\n\t\t} else if (parameter == FLD_REDIAL_URL) {\n\t\t\tredial_url.set_url(value);\n\t\t\tif (!redial_url.is_valid()) {\n\t\t\t\tredial_url.set_url(\"\");\n\t\t\t}\n\t\t} else if (parameter == FLD_REDIAL_DISPLAY) {\n\t\t\tredial_display = value;\n\t\t} else if (parameter == FLD_REDIAL_SUBJECT) {\n\t\t\tredial_subject = value;\n\t\t} else if (parameter == FLD_REDIAL_PROFILE) {\n\t\t\tredial_profile = value;\n\t\t} else if (parameter == FLD_REDIAL_HIDE_USER) {\n\t\t\tredial_hide_user = yesno2bool(value);\n\t\t} else if (parameter == FLD_DIAL_HISTORY) {\n\t\t\tdial_history.push_back(value);\n\t\t} else if (parameter == FLD_SHOW_DISPLAY) {\n\t\t\tshow_display = yesno2bool(value);\n\t\t} else if (parameter == FLD_COMPACT_LINE_STATUS) {\n\t\t\t//compact_line_status = yesno2bool(value);\n\t\t} else if (parameter == FLD_SHOW_BUDDY_LIST) {\n\t\t\tshow_buddy_list = yesno2bool(value);\n\t\t} else if (parameter == FLD_UI_SESSION_ID) {\n\t\t\tui_session_id = value;\n\t\t} else if (parameter == FLD_UI_SESSION_ACTIVE_PROFILE) {\n\t\t\tui_session_active_profiles.push_back(value);\n\t\t} else if (parameter == FLD_UI_SESSION_MAIN_GEOMETRY) {\n\t\t\tui_session_main_geometry = value;\n\t\t} else if (parameter == FLD_UI_SESSION_MAIN_HIDDEN) {\n\t\t\tui_session_main_hidden = yesno2bool(value);\n\t\t} else if (parameter == FLD_UI_SESSION_MAIN_STATE) {\n\t\t\tui_session_main_state = atoi(value.c_str());\n\t\t} else if (parameter == FLD_WARN_HIDE_USER) {\n\t\t\twarn_hide_user = yesno2bool(value);\n\t\t} else if (parameter == FLD_MIME_SHARED_DATABASE) {\n\t\t\tmime_shared_database = value;\n\t\t}\n\t\t\t\n\t\t// Unknown field names are skipped.\n\t}\n\t\t\n\tmtx_sys.unlock();\n\treturn true;\n}\n\nbool t_sys_settings::write_config(string &error_msg) {\n\tstruct stat stat_buf;\n\t\n\tmtx_sys.lock();\n\t\n\t// Make a backup of the file if we are editing an existing file, so\n\t// that can be restored when writing fails.\n\tstring f_backup = filename + '~';\n\tif (stat(filename.c_str(), &stat_buf) == 0) {\n\t\tif (rename(filename.c_str(), f_backup.c_str()) != 0) {\n\t\t\tstring err = get_error_str(errno);\n\t\t\terror_msg = TRANSLATE(\"Failed to backup %1 to %2\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\t\terror_msg = replace_first(error_msg, \"%2\", f_backup);\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += err;\n\t\t\tmtx_sys.unlock();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t// Open file\n\tofstream config(filename.c_str());\n\tif (!config) {\n\t\terror_msg = TRANSLATE(\"Cannot open file for writing: %1\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\t\n\t// Write AUDIO settings\n\tconfig << \"# AUDIO\\n\";\n\tconfig << FLD_DEV_RINGTONE << '=' << dev_ringtone.get_settings_value() << endl;\n\tconfig << FLD_DEV_SPEAKER << '=' << dev_speaker.get_settings_value() << endl;\n\tconfig << FLD_DEV_MIC << '=' << dev_mic.get_settings_value() << endl;\n\tconfig << FLD_VALIDATE_AUDIO_DEV << '=' << bool2yesno(validate_audio_dev) << endl;\n\tconfig << FLD_ALSA_PLAY_PERIOD_SIZE << '=' << alsa_play_period_size << endl;\n\tconfig << FLD_ALSA_CAPTURE_PERIOD_SIZE << '=' << alsa_capture_period_size << endl;\n\tconfig << FLD_OSS_FRAGMENT_SIZE << '=' << oss_fragment_size << endl;\n\tconfig << endl;\n\t\n\t// Write LOG settings\n\tconfig << \"# LOG\\n\";\n\tconfig << FLD_LOG_MAX_SIZE << '=' << log_max_size << endl;\n\tconfig << FLD_LOG_SHOW_SIP << '=' << bool2yesno(log_show_sip) << endl;\n\tconfig << FLD_LOG_SHOW_STUN << '=' << bool2yesno(log_show_stun) << endl;\n\tconfig << FLD_LOG_SHOW_MEMORY << '=' << bool2yesno(log_show_memory) << endl;\n\tconfig << FLD_LOG_SHOW_DEBUG << '=' << bool2yesno(log_show_debug) << endl;\n\tconfig << endl;\n\t\n\t// Write GUI settings\n\tconfig << \"# GUI\\n\";\n\tconfig << FLD_GUI_USE_SYSTRAY << '=' << bool2yesno(gui_use_systray) << endl;\n\tconfig << FLD_GUI_HIDE_ON_CLOSE << '=' << bool2yesno(gui_hide_on_close) << endl;\n\tconfig << FLD_GUI_SHOW_INCOMING_POPUP << '=' << bool2yesno(gui_show_incoming_popup) << endl;\n\tconfig << FLD_GUI_AUTO_SHOW_INCOMING << '=' << bool2yesno(gui_auto_show_incoming) << endl;\n\tconfig << FLD_GUI_AUTO_SHOW_TIMEOUT << '=' << gui_auto_show_timeout << endl;\n\tconfig << FLD_GUI_BROWSER_CMD << '=' << gui_browser_cmd << endl;\n\tconfig << FLD_GUI_SHOW_CALL_OSD << '=' << bool2yesno(gui_show_call_osd) << endl;\n\tconfig << endl;\n\n\t// Write inhibit idle session settings\n\tconfig << FLD_INHIBIT_IDLE_SESSION << '=' << bool2yesno(inhibit_idle_session) << endl;\n\tconfig << endl;\n\t\n\t// Write address book settings\n\tconfig << \"# Address book\\n\";\n\tconfig << FLD_AB_SHOW_SIP_ONLY << '=' << bool2yesno(ab_show_sip_only) << endl;\n\tconfig << FLD_AB_LOOKUP_NAME << '=' << bool2yesno(ab_lookup_name) << endl;\n\tconfig << FLD_AB_OVERRIDE_DISPLAY << '=' << bool2yesno(ab_override_display) << endl;\n\tconfig << FLD_AB_LOOKUP_PHOTO << '=' << bool2yesno(ab_lookup_photo) << endl;\n\tconfig << endl;\n\t\n\t// Write call history settings\n\tconfig << \"# Call history\\n\";\n\tconfig << FLD_CH_MAX_SIZE << '=' << ch_max_size << endl;\n\tconfig << endl;\n\t\n\t// Write service settings\n\tconfig << \"# Services\\n\";\n\tconfig << FLD_CALL_WAITING << '=' << bool2yesno(call_waiting) << endl;\n\tconfig << FLD_HANGUP_BOTH_3WAY << '=' << bool2yesno(hangup_both_3way) << endl;\n\tconfig << endl;\n\t\n\t// Write startup settings\n\tconfig << \"# Startup\\n\";\n\t\n\tfor (list<string>::iterator i = start_user_profiles.begin();\n\t     i != start_user_profiles.end(); i++)\n\t{\n\t\tconfig << FLD_START_USER_PROFILE << '=' << *i << endl;\n\t}\n\n\tconfig << FLD_START_HIDDEN << '=' << bool2yesno(start_hidden) << endl;\n\tconfig << endl;\n\t\n\t// Write network settings\n\tconfig << \"# Network\\n\";\n\tconfig << FLD_sip_port << '=' << config_sip_port << endl;\n\tconfig << FLD_RTP_PORT << '=' << rtp_port << endl;\n\tconfig << FLD_SIP_MAX_UDP_SIZE << '=' << sip_max_udp_size << endl;\n\tconfig << FLD_SIP_MAX_TCP_SIZE << '=' << sip_max_tcp_size << endl;\n\tconfig << endl;\n\t\n\t// Write ring tone settings\n\tconfig << \"# Ring tones\\n\";\n\tconfig << FLD_PLAY_RINGTONE << '=' << bool2yesno(play_ringtone) << endl;\n\tconfig << FLD_RINGTONE_FILE << '=' << ringtone_file << endl;\n\tconfig << FLD_PLAY_RINGBACK << '=' << bool2yesno(play_ringback) << endl;\n\tconfig << FLD_RINGBACK_FILE << '=' << ringback_file << endl;\n\tconfig << endl;\n\t\n\t// Write MIME settings\n\tconfig << \"# MIME settings\\n\";\n\tconfig << FLD_MIME_SHARED_DATABASE << '=' << mime_shared_database << endl;\n\tconfig << endl;\n\t\n\t// Write persistent user interface state\n\tconfig << \"# Persistent user interface state\\n\";\n\tconfig << FLD_LAST_USED_PROFILE << '=' << last_used_profile << endl;\n\tconfig << FLD_REDIAL_URL << '=' << redial_url.encode() << endl;\n\tconfig << FLD_REDIAL_DISPLAY << '=' << redial_display << endl; \n\tconfig << FLD_REDIAL_SUBJECT << '=' << redial_subject << endl;\n\tconfig << FLD_REDIAL_PROFILE << '=' << redial_profile << endl;\n\tconfig << FLD_REDIAL_HIDE_USER << '=' << bool2yesno(redial_hide_user) << endl;\n\tconfig << FLD_SHOW_DISPLAY << '=' << bool2yesno(show_display) << endl;\n\t//config << FLD_COMPACT_LINE_STATUS << '=' << bool2yesno(compact_line_status) << endl;\n\tconfig << FLD_SHOW_BUDDY_LIST << '=' << bool2yesno(show_buddy_list) << endl;\n\tconfig << FLD_WARN_HIDE_USER << '=' << bool2yesno(warn_hide_user) << endl;\n\t\n\tfor (list<string>::iterator i = dial_history.begin();\n\t     i != dial_history.end(); i++)\n\t{\n\t\tconfig << FLD_DIAL_HISTORY << '=' << *i << endl;\n\t}\n\t\n\tconfig << endl;\n\t\n\t// Write session settins\n\tconfig << \"# UI session settings\\n\";\n\tconfig << FLD_UI_SESSION_ID << '=' << ui_session_id << endl;\n\n\tfor (list<string>::iterator i = ui_session_active_profiles.begin();\n\t     i != ui_session_active_profiles.end(); i++)\n\t{\n\t\tconfig << FLD_UI_SESSION_ACTIVE_PROFILE << '=' << *i << endl;\n\t}\n\t\n\tconfig << FLD_UI_SESSION_MAIN_GEOMETRY << '=' << ui_session_main_geometry.encode() << endl;\n\tconfig << FLD_UI_SESSION_MAIN_HIDDEN << '=' << bool2yesno(ui_session_main_hidden) << endl;\n\tconfig << FLD_UI_SESSION_MAIN_STATE << '=' << ui_session_main_state << endl;\n\t\n\tconfig << endl;\n\t\n\t// Check if writing succeeded\n\tif (!config.good()) {\n\t\t// Restore backup\n\t\tconfig.close();\n\t\trename(f_backup.c_str(), filename.c_str());\n\n\t\terror_msg = TRANSLATE(\"File system error while writing file %1 .\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_sys.unlock();\n\t\treturn false;\n\t}\n\t\n\tmtx_sys.unlock();\n\treturn true;\n}\n\nlist<t_audio_device> t_sys_settings::get_oss_devices(bool playback) const {\n\tstruct stat stat_buf;\n\tlist<t_audio_device> l;\n\t\n\tfor (int i = -1; i <= 15; i ++) {\n\t\tstring dev = \"/dev/dsp\";\n\t\tif (i >= 0) dev += int2str(i);\n\t\tt_audio_device oss_dev;\n\t\toss_dev.type = t_audio_device::OSS;\n\t\t\n\t\t// Check if device exists\n\t\tif (stat(dev.c_str(), &stat_buf) != 0) continue;\n\t\t\n\t\toss_dev.device = dev;\n\t\t\n\t\t// Get sound card name\n\t\tint fd;\n\t\t\n\t\tif (playback) {\n\t\t\tfd = open(dev.c_str(), O_WRONLY | O_NONBLOCK);\n\t\t} else {\n\t\t\tfd = open(dev.c_str(), O_RDONLY | O_NONBLOCK);\n\t\t}\n\t\t\n\t\tif (fd >= 0) {\n\t\t\tstruct mixer_info soundcard_info;\n\t\t\tif (ioctl(fd, SOUND_MIXER_INFO, &soundcard_info) != -1) {\n\t\t\t\toss_dev.name = \"\";\n\t\t\t\toss_dev.name += soundcard_info.name;\n\t\t\t\toss_dev.name += \" (\";\n\t\t\t\toss_dev.name += soundcard_info.id;\n\t\t\t\toss_dev.name += \")\";\n\t\t\t}\n\t\t\t\n\t\t\tclose(fd);\n\t\t} else {\n\t\t\tif (errno == EBUSY) {\n\t\t\t\toss_dev.name = TRANSLATE(\"unknown name (device is busy)\");\n\t\t\t} else {\n\t\t\t\t// Device is not available.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Check if the device is a symbolic link\n\t\tchar buf[32];\n\t\tint len_link;\n\t\tif ((len_link = readlink(dev.c_str(), buf, 31)) != -1) {\n\t\t\tbuf[len_link] = 0;\n\t\t\toss_dev.sym_link = buf;\n\t\t}\n\t\toss_dev.type = t_audio_device::OSS;\n\t\tl.push_back(oss_dev);\n\t}\n\t\n\t// If no OSS devices can be found (this should not happen), then\n\t// just add /dev/dsp as the default device.\n\tif (l.empty()) {\n\t\tt_audio_device oss_dev;\n\t\toss_dev.device = \"/dev/dsp\";\n\t\toss_dev.type = t_audio_device::OSS;\n\t\tl.push_back(oss_dev);\n\t}\n\t\n\t// Add other device option\n\tt_audio_device other_dev;\n\tother_dev.device = DEV_OTHER;\n\tother_dev.type = t_audio_device::OSS;\n\tl.push_back(other_dev);\n\t\n\treturn l;\n}\n\n#ifdef HAVE_LIBASOUND\n// Defined in audio_device.cpp\nvoid alsa_fill_soundcards(list<t_audio_device>& l, bool playback);\n\nlist<t_audio_device> t_sys_settings::get_alsa_devices(bool playback) const {\n\tt_audio_device defaultDevice;\n\tdefaultDevice.device = \"default\";\n\tdefaultDevice.name = TRANSLATE(\"Default device\");\n\tdefaultDevice.type = t_audio_device::ALSA;\n\tlist<t_audio_device> l;\n\tl.push_back(defaultDevice);\n\t\n\talsa_fill_soundcards(l, playback);\n\t\n\t// Add other device option\n\tt_audio_device other_dev;\n\tother_dev.device = DEV_OTHER;\n\tother_dev.type = t_audio_device::ALSA;\n\tl.push_back(other_dev);\n\t\n\treturn l;\n}\n#endif\n\nlist<t_audio_device> t_sys_settings::get_audio_devices(bool playback) const {\n\tlist<t_audio_device> d, d0;\n\t\n#ifdef HAVE_LIBASOUND\n\td = get_alsa_devices(playback);\n#endif\n\td0 = get_oss_devices(playback);\n\td.insert(d.end(), d0.begin(), d0.end());\n\treturn d;\n}\n\nbool t_sys_settings::equal_audio_dev(const t_audio_device &dev1, const t_audio_device &dev2) const {\n\tif (dev1.type == t_audio_device::OSS) {\n\t\tif (dev2.type != t_audio_device::OSS) return false;\n\t\tif (dev1.device == dev2.device) return true;\n\t\t\n\t\tchar symlink1[32], symlink2[32];\n\t\tint len_link1, len_link2;\n\t\t\n\t\tlen_link1 = readlink(dev1.device.c_str(), symlink1, 31);\n\t\tlen_link2 = readlink(dev2.device.c_str(), symlink2, 31);\n\t\n\t\tif (len_link1 > 0) {\n\t\t\tsymlink1[len_link1] = 0;\n\t\t\tstring symdev1 = \"/dev/\";\n\t\t\tsymdev1 += symlink1;\n\t\t\tif (len_link2 > 0) {\n\t\t\t\tsymlink2[len_link2] = 0;\n\t\t\t\tstring symdev2 = \"/dev/\";\n\t\t\t\tsymdev2 += symlink2;\n\t\t\t\treturn symdev1 == symdev2;\n\t\t\t} else {\n\t\t\t\treturn dev2.device == symdev1;\n\t\t\t}\n\t\t} else {\n\t\t\tif (len_link2 > 0) {\n\t\t\t\tsymlink2[len_link2] = 0;\n\t\t\t\tstring symdev2 = \"/dev/\";\n\t\t\t\tsymdev2 += symlink2;\n\t\t\t\treturn dev1.device == symdev2;\n\t\t\t}\n\t\t}\n\t} else if (dev1.type == t_audio_device::ALSA) {\n\t\tif (dev2.type != t_audio_device::ALSA) return false;\n\t\treturn dev1.device == dev2.device;\n\t}\n\t\t\n\treturn false;\n}\n\n\nt_audio_device t_sys_settings::audio_device(string device) {\n\tt_audio_device d;\n\n\tif (device.empty()) device = DEV_DSP; //This is the default device\n\t\n\tif (device.substr(0, strlen(PFX_OSS)) == PFX_OSS) {\n\t\t// OSS device\n\t\td.device = device.substr(strlen(PFX_OSS));\n\t\td.type = t_audio_device::OSS;\n\t\td.name = \"\";\n\t\tchar symlink[32];\n\t\tint len_link = readlink(device.c_str(), symlink, 31);\n\t\tif(len_link > 0) {\n\t\t\td.sym_link = symlink;\n\t\t}\n\t} else if (device.substr(0, strlen(PFX_ALSA)) == PFX_ALSA) {\n\t\t// ALSA device\n\t\td.device = device.substr(strlen(PFX_ALSA));\n\t\td.type = t_audio_device::ALSA;\n\t\td.name = \"\";\n\t\td.sym_link = \"\";\n\t} else {\n\t\t// Assume it is an OSS device. Version 0.2.1 and lower\n\t\t// only supported OSS and the value only consisted of\n\t\t// the device name without \"oss:\"\n\t\td.device = device;\n\t\td.type = t_audio_device::OSS;\n\t\td.name = \"\";\n\t\tchar symlink[32];\n\t\tint len_link = readlink(device.c_str(), symlink, 31);\n\t\tif(len_link > 0) {\n\t\t\td.sym_link = symlink;\n\t\t}\n\t}\n\t\n\treturn d;\t\n}\n\nbool t_sys_settings::exec_audio_validation(bool ringtone, bool speaker, bool mic, \n\tstring &error_msg) const \n{\n\terror_msg.clear();\n\tif (!validate_audio_dev) return true;\n\t\n\tbool valid = true;\n\tbool full_duplex = speaker && mic && equal_audio_dev(dev_speaker, dev_mic);\n\t\n\tif (ringtone && !t_audio_io::validate(dev_ringtone, true, false)) {\n\t\tstring msg = TRANSLATE(\"Cannot access the ring tone device (%1).\");\n\t\terror_msg += replace_first(msg, \"%1\", dev_ringtone.get_description());\n\t\terror_msg += \"\\n\";\n\t\tvalid = false;\n\t}\n\tif (speaker && !t_audio_io::validate(dev_speaker, true, full_duplex)) {\n\t\tstring msg = TRANSLATE(\"Cannot access the speaker (%1).\");\n\t\terror_msg += replace_first(msg, \"%1\", dev_speaker.get_description());\n\t\terror_msg += \"\\n\";\n\t\tvalid = false;\n\t}\n\tif (mic && !t_audio_io::validate(dev_mic, full_duplex, true)) {\n\t\tstring msg = TRANSLATE(\"Cannot access the microphone (%1).\");\n\t\terror_msg += replace_first(msg, \"%1\", dev_mic.get_description());\n\t\terror_msg += \"\\n\";\n\t\tvalid = false;\n\t}\n\t\n\treturn valid;\n}\n\nunsigned short t_sys_settings::get_sip_port(bool force_active) {\n\tmtx_sys.lock();\n\t\n\t// The configured port becomes the active port after first\n\t// usage of the port.\n\tif (!active_sip_port || force_active) {\n\t\tif (override_sip_port > 0) {\n\t\t\t// The port provided on the command line overrides\n\t\t\t// the configured port.\n\t\t\tactive_sip_port = override_sip_port;\n\t\t} else {\n\t\t\tactive_sip_port = config_sip_port;\n\t\t}\n\t}\n\t\n\tmtx_sys.unlock();\n\treturn active_sip_port;\n}\n"
  },
  {
    "path": "src/sys_settings.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _SYS_SETTINGS_H\n#define _SYS_SETTINGS_H\n\n#include <cstdlib>\n#include <string>\n#include <list>\n#include \"parser/sip_message.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include \"twinkle_config.h\"\n\nusing namespace std;\n\n/** @name General system settings */\n//@{\n/** User directory, relative to the home directory ($HOME) */\n#define DIR_USER\t\".twinkle\"\n\n/** Home directory */\n#define DIR_HOME\t(getenv(\"HOME\"))\n\n/** Directory for storing temporary files, relative to @ref DIR_USER */\n#define DIR_TMPFILE\t\"tmp\"\n\n/** Device file for DSP */\n#define DEV_DSP\t\t\"/dev/dsp\"\n\n/** Device prefixes in settings file */\n#define PFX_OSS\t\t\"oss:\"\n#define PFX_ALSA\t\"alsa:\"\n\n/** ALSA default device */\n#define DEV_ALSA_DFLT\t\"alsa:default\"\n\n/** Device string for other device */\n#define DEV_OTHER\t\"other device\"\n\n/** File with SIP providers for the wizard */\n#define FILE_PROVIDERS\t\"providers.csv\"\n\n/** File with CLI command history */\n#define FILE_CLI_HISTORY \"twinkle.history\"\n//@}\n\n\n/** Audio device */\nclass t_audio_device {\npublic:\n\tenum t_audio_device_type {\n\t\tOSS, ALSA\n\t} type;\n\tstring\t\tdevice; \t// eg. /dev/dsp, /dev/dsp1 for OSS or hw:0,0 for ALSA\n\tstring\t\tsym_link;\t// real device if the device is a symbolic link\n\tstring\t\tname;\t\t// name of the sound card\n\n\t// Get a one-line description\n\tstring get_description(void) const;\n\t\n\t// Get string to be written in settings file\n\tstring get_settings_value(void) const;\n};\n\n/** Window geometry */\nstruct t_win_geometry {\n\tint x;\t\t/**< x-coordinate of top left corner */\n\tint y;\t\t/**< y-coordinate of top left corner */\n\tint width;\t/**< Window width */\n\tint height;\t/**< Window height */\n\t\n\t/** Constructor */\n\tt_win_geometry();\n\t\n\t/** Constructor */\n\tt_win_geometry(int x_, int y_, int width_, int height_);\n\t\n\t/**\n\t * Construct a geometry from an encoded string.\n\t * If the string cannot be parsed, all values are set to zero.\n\t * @param value [in] Encoded string \"x,y,widht,height\"\n\t */\n\tt_win_geometry(const string &value);\n\t\n\t/**\n\t * Encode geometry into a string.\n\t * @return Encoded geometry \"x,y,width,height\"\n\t */\n\tstring encode(void) const;\n};\n\n/** System settings */\nclass t_sys_settings {\nprivate:\n\t// Mutex to avoid sync concurrent access\n\tmutable t_recursive_mutex\tmtx_sys;\n\t\n\t/** File descriptor of lock file */\n\tint fd_lock_file;\n\t\n\t// Share directory for files applicable to all users\n\tstring\t\tdir_share;\n\t\n\t// Full file name for config file\n\tstring\t\tfilename;\n\t\n\t/** The SIP port that is currently used */\n\tunsigned short\tactive_sip_port;\n\t\n\t// Sound devices\n\tt_audio_device\t\tdev_ringtone;\n\tt_audio_device\t\tdev_speaker;\n\tt_audio_device\t\tdev_mic;\n\t\n\t// Indicates if audio devices should be validated before\n\t// usage.\n\tbool\t\t\tvalidate_audio_dev;\n\t\n\tint\t\t\talsa_play_period_size;\n\tint\t\t\talsa_capture_period_size;\n\tint\t\t\toss_fragment_size;\n\t\n\t// Log file settings\n\tunsigned short\tlog_max_size; // in MB\n\tbool\t\tlog_show_sip;\n\tbool\t\tlog_show_stun;\n\tbool\t\tlog_show_memory;\n\tbool\t\tlog_show_debug;\n\t\n\t/** @name GUI settings */\n\t//@{\n\tbool\t\tgui_use_systray;\n\tbool\t\tgui_hide_on_close;\n\t\n\t/** Show popup on incoming call */\n\tbool\t\tgui_show_incoming_popup;\n\t\n\t/** Show main window on incoming call after a few seconds */\n\tbool\t\tgui_auto_show_incoming;\n\tint\t\tgui_auto_show_timeout;\n\tbool\t\tgui_show_call_osd;\n\t\n\t/** Command to start an internet browser */\n\tstring\t\tgui_browser_cmd;\n\t//@}\n\t\n\t// Inhibit idle session\n\tbool\t\tinhibit_idle_session;\n\n\t// Address book settings\n\tbool\t\tab_show_sip_only;\n\tbool\t\tab_lookup_name;\n\tbool\t\tab_override_display;\n\tbool\t\tab_lookup_photo;\n\t\n\t// Call history settings\n\tint\t\tch_max_size; // #calls\n\t\n\t// Service settings\n\t// Call waiting allows an incoming call if one line is busy.\n\tbool\t\tcall_waiting;\n\t\n\t// Indicates if both lines should be hung up when ending a\n\t// 3-way conference call.\n\t// If false, then only the active line will be hung up.\n\tbool\t\thangup_both_3way;\n\t\n\t// Startup settings\n\tlist<string>\tstart_user_profiles;\n\n\tbool\t\tstart_hidden;\n\t\n\t/** The full path name of the shared mime database */\n\tstring\t\tmime_shared_database;\n\t\n\t/** @name Network settings */\n\t//@{\n\t/** Port for sending and receiving SIP messages. This is the value\n\t * written in the system settings file. This value can differ from\n\t * active_sip_port value if the user changed the system\n\t * settings while Twinkle is running.\n\t */\n\tunsigned short\tconfig_sip_port;\n\t\n\t/** SIP UDP port overridden by the command options. */\n\tunsigned short\toverride_sip_port;\n\t\n\t/** Port for RTP.\n\t * rtp_port is the base port for RTP streams. Each phone line\n\t * uses has its own RTP port number.\n\t * line x has RTP port = rtp_port + x * 2 and\n\t *           RTCP port = rtp_port + x * 2 + 1\n\t * Where x starts at 0\n\t *\n\t * NOTE: for call transfer scenario, line 2 (3rd line) is used\n\t *       which is not a line that is visible to the user. The user\n\t *       only sees 2 lines for its use. By having a dedicated port\n\t *       for line 2, the  RTP stream for a referred call uses another\n\t *       port than the RTP stream for an original call, preventing\n\t *       the RTP streams for these calls to become mixed.\n\t *\n\t * NOTE: during a call transfer, line 2 will be swapped with another\n\t *       line, so the ports swap accordingly.\n\t */\n\tunsigned short\trtp_port;\n\t\n\t/** RTP port overridden by the command options. */\n\tunsigned short\toverride_rtp_port; \n\t\n\t/** Maximum size of a SIP message received over UDP. */\n\tunsigned short\tsip_max_udp_size;\n\t\n\t/** Maximum size of a SIP message received over TCP. */\n\tunsigned long\tsip_max_tcp_size;\n\t//@}\n\t\n\t// Ring tone settings\n\tbool\t\tplay_ringtone;\n\tstring\t\tringtone_file;\n\tbool\t\tplay_ringback;\n\tstring\t\tringback_file;\n\t\n\t// Persistent storage for user interface state\n\t// The profile that was last used before Twinkle was terminated.\n\tstring\t\tlast_used_profile;\n\t\n\t// Call information for redial last call function\n\tt_url\t\tredial_url;\n\tstring\t\tredial_display;\n\tstring\t\tredial_subject;\n\tstring\t\tredial_profile; // profile used to make the call\n\tbool\t\tredial_hide_user; // Did the user request hiding?\n\t\n\t// History of latest dialed addresses\n\tlist<string>\tdial_history;\n\t\n\t/** @name GUI view settings */\n\t//@{\n\tbool\t\tshow_display;\n\tbool\t\tcompact_line_status;\n\tbool\t\tshow_buddy_list;\n\t//@}\n\t\n\t/** @name Settings to restore a previous user interface session after system shutdown */\n\t//@{\n\t/** ID of previous session */\n\tstring\t\tui_session_id;\n\t\n\t/** Active user profiles */\n\tlist<string>\tui_session_active_profiles;\n\t\n\t/** Geometry of main window */\n\tt_win_geometry\tui_session_main_geometry;\n\t\n\t/** Flag to indicate if the main window is hidden. */\n\tbool\t\tui_session_main_hidden;\n\t\n\t/** Window state of main window. */\n\tunsigned int\tui_session_main_state;\n\t//@}\n\t\n\t// One time warnings\n\tbool\t\twarn_hide_user; // Warn use that provider may not support hiding.\n\t\npublic:\n\t/** Constructor */\n\tt_sys_settings();\n\t\n\t/** @name Getters */\n\t//@{\n\tt_audio_device get_dev_ringtone(void) const;\n\tt_audio_device get_dev_speaker(void) const;\n\tt_audio_device get_dev_mic(void) const;\n\tbool get_validate_audio_dev(void) const;\n\tint get_alsa_play_period_size(void) const;\n\tint get_alsa_capture_period_size(void) const;\n\tint get_oss_fragment_size(void) const;\n\tunsigned short get_log_max_size(void) const;\n\tbool get_log_show_sip(void) const;\n\tbool get_log_show_stun(void) const;\n\tbool get_log_show_memory(void) const;\n\tbool get_log_show_debug(void) const;\n\tbool get_gui_use_systray(void) const;\n\tbool get_gui_hide_on_close(void) const;\n\tbool get_gui_show_incoming_popup(void) const;\n\tbool get_gui_auto_show_incoming(void) const;\n\tint get_gui_auto_show_timeout(void) const;\n\tstring get_gui_browser_cmd(void) const;\n\tbool get_gui_show_call_osd() const;\n\tbool get_inhibit_idle_session() const;\n\tbool get_ab_show_sip_only(void) const;\n\tbool get_ab_lookup_name(void) const;\n\tbool get_ab_override_display(void) const;\n\tbool get_ab_lookup_photo(void) const;\n\tint get_ch_max_size(void) const;\n\tbool get_call_waiting(void) const;\n\tbool get_hangup_both_3way(void) const;\n\tlist<string> get_start_user_profiles(void) const;\n\tbool get_start_hidden(void) const;\n\tunsigned short get_config_sip_port(void) const;\n\tunsigned short get_rtp_port(void) const;\n\tunsigned short get_sip_max_udp_size(void) const;\n\tunsigned long get_sip_max_tcp_size(void) const;\n\tbool get_play_ringtone(void) const;\n\tstring get_ringtone_file(void) const;\n\tbool get_play_ringback(void) const;\n\tstring get_ringback_file(void) const;\n\tstring get_last_used_profile(void) const;\n\tt_url get_redial_url(void) const;\n\tstring get_redial_display(void) const;\n\tstring get_redial_subject(void) const;\n\tstring get_redial_profile(void) const;\n\tbool get_redial_hide_user(void) const;\n\tlist<string> get_dial_history(void) const;\n\tbool get_show_display(void) const;\n\tbool get_compact_line_status(void) const;\n\tbool get_show_buddy_list(void) const;\n\tstring get_ui_session_id(void) const;\n\tlist<string> get_ui_session_active_profiles(void) const;\n\tt_win_geometry get_ui_session_main_geometry(void) const;\n\tbool get_ui_session_main_hidden(void) const;\n\tunsigned int get_ui_session_main_state(void) const;\n\tbool get_warn_hide_user(void) const;\n\tstring get_mime_shared_database(void) const;\n\t//@}\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_dev_ringtone(const t_audio_device &dev);\n\tvoid set_dev_speaker(const t_audio_device &dev);\n\tvoid set_dev_mic(const t_audio_device &dev);\n\tvoid set_validate_audio_dev(bool b);\n\tvoid set_alsa_play_period_size(int size);\n\tvoid set_alsa_capture_period_size(int size);\n\tvoid set_oss_fragment_size(int size);\n\tvoid set_log_max_size(unsigned short size);\n\tvoid set_log_show_sip(bool b);\n\tvoid set_log_show_stun(bool b);\n\tvoid set_log_show_memory(bool b);\n\tvoid set_log_show_debug(bool b);\n\tvoid set_gui_use_systray(bool b);\n\tvoid set_gui_hide_on_close(bool b);\n\tvoid set_gui_show_incoming_popup(bool b);\n\tvoid set_gui_auto_show_incoming(bool b);\n\tvoid set_gui_auto_show_timeout(int timeout);\n\tvoid set_gui_browser_cmd(const string &s);\n\tvoid set_gui_show_call_osd(bool b);\n\tvoid set_inhibit_idle_session(bool b);\n\tvoid set_ab_show_sip_only(bool b);\n\tvoid set_ab_lookup_name(bool b);\n\tvoid set_ab_override_display(bool b);\n\tvoid set_ab_lookup_photo(bool b);\n\tvoid set_ch_max_size(int size);\n\tvoid set_call_waiting(bool b);\n\tvoid set_hangup_both_3way(bool b);\n\tvoid set_start_user_profiles(const list<string> &profiles);\n\tvoid set_start_hidden(bool b);\n\tvoid set_config_sip_port(unsigned short port);\n\tvoid set_override_sip_port(unsigned short port);\n\tvoid set_rtp_port(unsigned short port);\n\tvoid set_override_rtp_port(unsigned short port);\n\tvoid set_sip_max_udp_size(unsigned short size);\n\tvoid set_sip_max_tcp_size(unsigned long size);\n\tvoid set_play_ringtone(bool b);\n\tvoid set_ringtone_file(const string &file);\n\tvoid set_play_ringback(bool b);\n\tvoid set_ringback_file(const string &file);\n\tvoid set_last_used_profile(const string &profile);\n\tvoid set_redial_url(const t_url &url);\n\tvoid set_redial_display(const string &display);\n\tvoid set_redial_subject(const string &subject);\n\tvoid set_redial_profile(const string &profile);\n\tvoid set_redial_hide_user(bool b);\n\tvoid set_dial_history(const list<string> &history);\n\tvoid set_show_display(bool b);\n\tvoid set_compact_line_status(bool b);\n\tvoid set_show_buddy_list(bool b);\n\tvoid set_ui_session_id(const string &id);\n\tvoid set_ui_session_active_profiles(const list<string> &profiles);\n\tvoid set_ui_session_main_geometry(const t_win_geometry &geometry);\n\tvoid set_ui_session_main_hidden(bool hidden);\n\tvoid set_ui_session_main_state(unsigned int state);\n\tvoid set_warn_hide_user(bool b);\n\tvoid set_mime_shared_database(const string &filename);\n\t//@}\n\t\n\t/** \n\t * Get \"about\" text.\n\t * @param html [in] Indicates if \"about\" text must be in HTML format.\n\t * @return The \"about\" text\"\n\t */\n\tstring about(bool html) const;\n\t\n\t/**\n\t * Get produce release date.\n\t * @return product release date in locale format\n\t */\n\tstring get_product_date(void) const;\n\t\n\t/** \n\t * Get a string of options that are built, e.g. ALSA, KDE\n\t * @return The string of options.\n\t */\n\tstring get_options_built(void) const;\n\n\t/** \n\t * Check if the environment of the machine satisfies all requirements.\n\t * @param error_msg [out] User readable error message when false is returned.\n\t * @return true if all requirements are met.\n\t * @return false, otherwise and error_msg contains an appropriate\n\t * error message to show the user.\n\t */\n\tbool check_environment(string &error_msg) const;\n\n\t/**\n\t * Set the share directory\n\t * @param dir [in] Absolute path of the share directory.\n\t */ \n\tvoid set_dir_share(const string &dir);\n\n\t/**\n\t * Get the share directory.\n\t * @return Absolute path of the directory with shared files.\n\t */\n\tstring get_dir_share(void) const;\n\t\n\t/**\n\t * Get the directory containing language translation files.\n\t * @return Absolute path of the language directory.\n\t */\n\tstring get_dir_lang(void) const;\n\t\n\t/**\n\t * Get the user directory.\n\t * @return Absolute path of the user directory.\n\t */\n\tstring get_dir_user(void) const;\n\t\n\t/**\n\t * Get the CLI command history file.\n\t * @return Full pathname of the history file.\n\t */\n\tstring get_history_file(void) const;\n\t\n\t/** \n\t * Get the temporary file directory.\n\t * @return The full pathname of the temporary file directory.\n\t */\n\tstring get_dir_tmpfile(void) const;\n\t\n\t/**\n\t * Check if a file is located in the temporary file directory.\n\t * @return true if the file is in the temporary file directory, false otherwise.\n\t */\n\tbool is_tmpfile(const string &filename) const;\n\t\n\t/**\n\t * Save data to a temporary file.\n\t * @param data [in] Data to save.\n\t * @param file_extension [in] Extension (glob) for file name.\n\t * @param filename [out] File name of save file, relative to the tmp directory.\n\t * @param error_msg [out] If saving failed, then this parameter contains an\n\t *        error message.\n\t * @return true if saving succeeded, false otherwise.\n\t */\n\tbool save_tmp_file(const string &data, const string &file_extension,\n\t\tstring &filename, string &error_msg);\n\t\t\n\t/**\n\t * Save the body of a SIP message to a temporary file.\n\t * @param sip_msg [in] The SIP message from which the body must be saved.\n\t * @param suggested_file_extension [in] File extension (glob) for file name to save\n\t *        if an extension cannot be determined from a filename supplied as\n\t *        in the Content-Disposition header.\n\t * @param tmpname [out] The name of the saved file.\n\t * @param save_as_name [out] Suggested file name for user for saving.\n\t * @param error_msg [out] Error message when saving failed.\n\t * @return true if saving succeeded, false otherwise.\n\t */\n\tbool save_sip_body(const t_sip_message &sip_msg,\n\t\tconst string &suggested_file_extension,\n\t\tstring &tmpname, string &save_as_name, string &error_msg);\n\t\t\n\t/** Remove all files from the temporary file directory */\n\tvoid remove_all_tmp_files(void) const;\n\n\t/** @name Lock file operations */\n\t/**\n\t * Create a lock file if it does not exist yet and take a file lock on it.\n\t * @param shared_lock [in] Indicates if the file lock must be shared or exclusive.\n\t *        A shared lock is needed when the users forces multiple Twinkle processes\n\t *        to run.\n\t * @param error_msg [out] Error message if the operation fails.\n\t * @param already_running [out] If the operation fails, this flag indicates Twinkle\n\t *        is already running.\n\t * @return True if the file is locked successfully.\n\t * @return False if the file could not be locked.\n\t */\n\tbool create_lock_file(bool shared_lock, string &error_msg, bool &already_running);\n\t\n\t/** Unlock the lock file. */\n\tvoid delete_lock_file(void);\n\t\n\t// Read and parse a config file into the t_sys_settings object.\n\t// Returns false if it fails. error_msg is an error message that can\n\t// be give to the user.\n\tbool read_config(string &error_msg);\n\n\t// Write the settings into a config file\n\tbool write_config(string &error_msg);\n\t\n\t// Get all OSS devices\n\tlist<t_audio_device> get_oss_devices(bool playback) const;\n\t\n#ifdef HAVE_LIBASOUND\n\t// Get all ALSA devices\n\tlist<t_audio_device> get_alsa_devices(bool playback) const;\n#endif\n\t\n\t// Get all audio devices\n\tlist<t_audio_device> get_audio_devices(bool playback) const;\n\t\n\t// Check if two OSS devices are equal\n\tbool equal_audio_dev(const t_audio_device &dev1, const t_audio_device &dev2) const;\n\t\n\tstatic t_audio_device audio_device(string device = \"\");\n\t\n\t// Check validate the audio devices flagged as true.\n\t// If audio validation is turned off then always true is returned.\n\tbool exec_audio_validation(bool ringtone, bool speaker, bool mic, \n\t\tstring &error_msg) const;\n\t\n\t// Get the active value of the SIP UDP port\n\t// Once the SIP UDP port is retrieved from the system settings, it\n\t// is stored as the active port. A next call to get_sip_port\n\t// returns the active port, even when the SIP UDP port in the settings\n\t// has changed.\n\t// If force_active == true, then always the SIP UDP port is returned\n\t// and made active\n\tunsigned short get_sip_port(bool force_active = false);\n};\n\nextern t_sys_settings *sys_config;\n\n#endif\n"
  },
  {
    "path": "src/threads/CMakeLists.txt",
    "content": "project(libtwinkle-threads)\n\nset(LIBTWINKLE_THREADS-SRCS\n\tthread.cpp\n\tmutex.cpp\n\tsema.cpp\n)\n\nadd_library(libtwinkle-threads OBJECT ${LIBTWINKLE_THREADS-SRCS})\n"
  },
  {
    "path": "src/threads/mutex.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <string>\n#include <iostream>\n#include \"mutex.h\"\n#include \"thread.h\"\n#include <stdexcept>\n\nusing namespace std;\n\n///////////////////////////\n// t_mutex\n///////////////////////////\n\nt_mutex::t_mutex() {\n\tpthread_mutex_init(&mutex, NULL);\n}\n\nt_mutex::t_mutex(bool recursive) {\n\tpthread_mutexattr_t attr;\n\tpthread_mutexattr_init(&attr);\n\n\n\tint ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\n\tif (ret != 0) throw string(\n\t\t\"t_mutex::t_mutex failed to create a recursive mutex.\");\n\n\tpthread_mutex_init(&mutex, &attr);\n\tpthread_mutexattr_destroy(&attr);\n}\n\nt_mutex::~t_mutex() {\n\tpthread_mutex_destroy(&mutex);\n}\n\nvoid t_mutex::lock(void) {\n\tint ret = pthread_mutex_lock(&mutex);\n\tif (ret != 0) throw string(\"t_mutex::lock failed.\");\n}\n\nint t_mutex::trylock(void) {\n\tint ret = pthread_mutex_trylock(&mutex);\n\tswitch (ret) {\n\tcase 0:\n\tcase EBUSY:\n\t\treturn ret;\n\tdefault:\n\t\tthrow string(\"t_mutex::trylock failed.\");\n\t}\n}\n\nvoid t_mutex::unlock(void) {\n\tint ret = pthread_mutex_unlock(&mutex);\n\tif (ret != 0) throw (\"t_mutex::unlock failed.\");\n}\n\n///////////////////////////\n// t_recursive_mutex\n///////////////////////////\n\nt_recursive_mutex::t_recursive_mutex() : t_mutex(true) {}\n\nt_recursive_mutex::~t_recursive_mutex() {}\n\n///////////////////////////\n// t_guard_mutex\n///////////////////////////\n\nt_mutex_guard::t_mutex_guard(t_mutex &mutex) : mutex_(mutex) {\n\tmutex_.lock();\n}\n\nt_mutex_guard::~t_mutex_guard() {\n\tmutex_.unlock();\n}\n\n\n///////////////////////////\n// t_rwmutex\n///////////////////////////\n\n// Equivalent of an invalid thread ID, to be used when initializing t_rwmutex\n// or when releasing upgrade ownership; the use of pthread_self() is only to\n// provide a dummy value of the appropriate type.\nstatic const optional_pthread_t invalid_thread_id = { false, pthread_self() };\n\nt_rwmutex::t_rwmutex() :\n\t_up_mutex_thread( invalid_thread_id )\n{\n\tint ret = pthread_rwlock_init(&_lock, nullptr);\n\tif (ret != 0) throw string(\n\t\t\"t_rwmutex::t_rwmutex failed to create a r/w mutex.\");\n}\n\nt_rwmutex::~t_rwmutex()\n{\n\tpthread_rwlock_destroy(&_lock);\n}\n\nvoid t_rwmutex::getUpgradeOwnership()\n{\n\t_up_mutex.lock();\n\t_up_mutex_thread = { true, pthread_self() };\n}\n\nvoid t_rwmutex::releaseUpgradeOwnership()\n{\n\t_up_mutex_thread = invalid_thread_id;\n\t_up_mutex.unlock();\n}\n\nbool t_rwmutex::isUpgradeOwnershipOurs() const\n{\n\t// Note that we don't need a mutex over _up_mutex_thread, being atomic\n\t// is enough for our purposes.  (We don't care about *who* owns\n\t// _up_mutex, only about whether or not *we* own it, a fact which only\n\t// our own thread can modify.)\n\toptional_pthread_t lockOwner = _up_mutex_thread;\n\treturn lockOwner.has_value && pthread_equal(lockOwner.value, pthread_self());\n}\n\nvoid t_rwmutex::_lockRead()\n{\n\tint err = pthread_rwlock_rdlock(&_lock);\n\tif (err != 0)\n\t\tthrow std::logic_error(\"Mutex lock failed\");\n}\n\nvoid t_rwmutex::_lockWrite()\n{\n\tint err = pthread_rwlock_wrlock(&_lock);\n\tif (err != 0)\n\t\tthrow std::logic_error(\"Mutex lock failed\");\n}\n\nvoid t_rwmutex::_unlock()\n{\n\tpthread_rwlock_unlock(&_lock);\n}\n\nvoid t_rwmutex::lockRead()\n{\n\tif (isUpgradeOwnershipOurs()) {\n\t\tthrow std::logic_error(\"Acquiring read lock while holding update/write lock is not supported\");\n\t}\n\n\t_lockRead();\n}\n\nvoid t_rwmutex::lockUpdate()\n{\n\tif (isUpgradeOwnershipOurs()) {\n\t\tthrow std::logic_error(\"Acquiring update lock while holding update/write lock is not supported\");\n\t}\n\n\tgetUpgradeOwnership();\n\t_lockRead();\n}\n\nvoid t_rwmutex::lockWrite()\n{\n\tif (isUpgradeOwnershipOurs()) {\n\t\tthrow std::logic_error(\"Acquiring write lock while holding update/write lock is not supported\");\n\t}\n\n\tgetUpgradeOwnership();\n\t_lockWrite();\n}\n\nvoid t_rwmutex::unlock()\n{\n\t_unlock();\n\n\tif (isUpgradeOwnershipOurs()) {\n\t\treleaseUpgradeOwnership();\n\t}\n}\n\nvoid t_rwmutex::upgradeLock()\n{\n\tif (!isUpgradeOwnershipOurs()) {\n\t\tthrow std::logic_error(\"Attempting to upgrade a lock without upgrade ownership\");\n\t}\n\n\t_unlock();\n\t_lockWrite();\n}\n\nvoid t_rwmutex::downgradeLock()\n{\n\tif (!isUpgradeOwnershipOurs()) {\n\t\tthrow std::logic_error(\"Attempting to downgrade a lock without upgrade ownership\");\n\t}\n\n\t_unlock();\n\t_lockRead();\n}\n\n///////////////////////////\n// t_rwmutex_guard\n///////////////////////////\n\nt_rwmutex_guard::t_rwmutex_guard(t_rwmutex& mutex) :\n\t_mutex(mutex),\n\t_previously_owned_upgrade(_mutex.isUpgradeOwnershipOurs())\n{\n}\n\nt_rwmutex_reader::t_rwmutex_reader(t_rwmutex& mutex) :\n\tt_rwmutex_guard(mutex)\n{\n\t// No-op if we are nested within a writer/future_writer guard\n\tif (!_previously_owned_upgrade) {\n\t\t_mutex.lockRead();\n\t}\n}\n\nt_rwmutex_reader::~t_rwmutex_reader()\n{\n\t// No-op if we are nested within a writer/future_writer guard\n\tif (!_previously_owned_upgrade) {\n\t\t_mutex.unlock();\n\t}\n}\n\nt_rwmutex_future_writer::t_rwmutex_future_writer(t_rwmutex& mutex) :\n\tt_rwmutex_guard(mutex)\n{\n\t// No-op if we are nested within a future_writer guard\n\tif (!_previously_owned_upgrade) {\n\t\t_mutex.lockUpdate();\n\t}\n}\n\nt_rwmutex_future_writer::~t_rwmutex_future_writer() {\n\t// No-op if we are nested within a future_writer guard\n\tif (!_previously_owned_upgrade) {\n\t\t_mutex.unlock();\n\t}\n}\n\nt_rwmutex_writer::t_rwmutex_writer(t_rwmutex& mutex) :\n\tt_rwmutex_guard(mutex)\n{\n\tif (_previously_owned_upgrade) {\n\t\t// Writer nested inside a future_writer: upgrade lock\n\t\t_mutex.upgradeLock();\n\t} else {\n\t\t// Stand-alone writer guard\n\t\t_mutex.lockWrite();\n\t}\n}\n\nt_rwmutex_writer::~t_rwmutex_writer()\n{\n\tif (_previously_owned_upgrade) {\n\t\t// We were nested within a future_writer guard, so return\n\t\t// the mutex to its previous state\n\t\t_mutex.downgradeLock();\n\t} else {\n\t\t_mutex.unlock();\n\t}\n}\n"
  },
  {
    "path": "src/threads/mutex.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_MUTEX\n#define _H_MUTEX\n\n#include <errno.h>\n#include <pthread.h>\n// #include <iostream>\n#include <atomic>\n\n/**\n * @file\n * Mutex operations\n */\n\nclass t_mutex {\nprotected:\n\tpthread_mutex_t\t\tmutex;\n\npublic:\n\tt_mutex();\n\n\t// Throws a string exception (error message) when failing.\n\tt_mutex(bool recursive);\n\n\tvirtual ~t_mutex();\n\n\t// These methods throw a string exception when the operation\n\t// fails.\n\tvirtual void lock(void);\n\n\t// Returns:\n\t// 0 - success\n\t// EBUSY - already locked\n\t// For other errors a string exception is thrown\n\tvirtual int trylock(void);\n\tvirtual void unlock(void);\n};\n\nclass t_recursive_mutex : public t_mutex {\npublic:\n\tt_recursive_mutex();\n\t~t_recursive_mutex();\n};\n\n\n/** \n * Guard pattern for a mutex .\n * The constructor of a guard locks a mutex and the destructor\n * unlocks it. This way a guard object can be created at entrance\n * of a function. Then at exit, the mutex is automically unlocked\n * as the guard object goes out of scope.\n */\nclass t_mutex_guard {\nprivate:\n\t/** The guarding mutex. */\n\tt_mutex\t\t&mutex_;\n\t\npublic:\n\t/**\n\t * The constructor will lock the mutex.\n\t * @param mutex [in] Mutex to lock.\n\t */\n\tt_mutex_guard(t_mutex &mutex);\n\t\n\t/**\n\t * The destructor will unlock the mutex.\n\t */\n\t~t_mutex_guard();\n};\n\n\n// Read-write-update lock\n//\n// Read-write lock with an additional \"update\" type of lock, which can later\n// be upgraded to \"write\" (and downgraded back to \"update\" again).  An update\n// lock can co-exist with other read locks, but only one update lock can be\n// held at any time, representing ownership of upgrade rights.\n//\n// See https://stackoverflow.com/a/18785300 for details and further references.\n//\n// Note that our version is rather simplistic, and does not allow downgrading\n// from update/write to read.\n\n// A cheap substitute for std::optional<pthread_t>, only available in C++14.\n// Unfortunately, POSIX.1-2004 no longer requires pthread_t to be an arithmetic\n// type, so we can't simply use 0 as an (unofficial) invalid thread ID.\nstruct optional_pthread_t {\n\tbool has_value;\n\tpthread_t value;\n};\n\nclass t_rwmutex {\nprotected:\n\t// Standard read-write lock\n\tpthread_rwlock_t _lock;\n\t// Mutex for upgrade ownership\n\tt_mutex _up_mutex;\n\t// Thread ID that currently owns the _up_mutex lock, if any\n\tstd::atomic<optional_pthread_t> _up_mutex_thread;\n\n\t// Get/release upgrade ownership\n\tvoid getUpgradeOwnership();\n\tvoid releaseUpgradeOwnership();\n\n\t// Internal methods to manipulate _lock directly\n\tvoid _lockRead();\n\tvoid _lockWrite();\n\tvoid _unlock();\npublic:\n\tt_rwmutex();\n\t~t_rwmutex();\n\n\t// Returns true if the calling thread currently owns the _up_mutex lock\n\tbool isUpgradeOwnershipOurs() const;\n\n\t// The usual methods for obtaining/releasing locks\n\tvoid lockRead();\n\tvoid lockUpdate();\n\tvoid lockWrite();\n\tvoid unlock();\n\n\t// Upgrade an update lock to a write lock, or downgrade in the\n\t// opposite direction.  Note that this does not count as an additional\n\t// lock, so only one unlock() call will be needed at the end.\n\tvoid upgradeLock();\n\tvoid downgradeLock();\n};\n\n\n// Equivalent of t_mutex_guard for t_rwmutex\n//\n// These can be nested as indicated below.  Note that nesting a weaker guard\n// will not downgrade the lock; for example, a Reader guard within a Writer\n// guard will maintain the write lock.\n\n// Base (abstract) class\nclass t_rwmutex_guard {\nprotected:\n\t// The lock itself\n\tt_rwmutex& _mutex;\n\n\t// Whether or not we had upgrade ownership beforehand, indicating that\n\t// we are nested within the scope of a writer/future_writer guard\n\tbool _previously_owned_upgrade;\n\n\t// A protected constructor to keep this class abstract\n\tt_rwmutex_guard(t_rwmutex& mutex);\n};\n\n// Reader: Can be nested within the scope of any guard\nclass t_rwmutex_reader : public t_rwmutex_guard {\npublic:\n\tt_rwmutex_reader(t_rwmutex& mutex);\n\t~t_rwmutex_reader();\n};\n\n// Future writer: Can be nested within the scope of a future_writer guard\nclass t_rwmutex_future_writer : public t_rwmutex_guard {\npublic:\n\tt_rwmutex_future_writer(t_rwmutex& mutex);\n\t~t_rwmutex_future_writer();\n};\n\n// Writer: Can be nested within the scope of a future_writer guard\nclass t_rwmutex_writer : public t_rwmutex_guard {\npublic:\n\tt_rwmutex_writer(t_rwmutex& mutex);\n\t~t_rwmutex_writer();\n};\n\n\n#endif\n"
  },
  {
    "path": "src/threads/sema.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cerrno>\n#include <cstring>\n#include <string>\n#include \"sema.h\"\n#include \"util.h\"\n\nusing namespace std;\n\nt_semaphore::t_semaphore(unsigned int value) {\n\tint ret;\n\n\tret = sem_init(&sem, 0, value);\n\tif (ret != 0) {\n\t\tstring err = get_error_str(errno);\n\t\tstring exception =\n\t\t\t\"t_semaphore::t_semaphore failed to create a semaphore.\\n\";\n\t\texception += err;\n\t\tthrow exception;\n\t}\n}\n\nt_semaphore::~t_semaphore() {\n\tsem_destroy(&sem);\n}\n\nvoid t_semaphore::up(void) {\n\tint ret;\n\n\tret = sem_post(&sem);\n\tif (ret != 0) {\n\t\tstring err = get_error_str(errno);\n\t\tstring exception = \"t_semaphore::up failed.\\n\";\n\t\texception += err;\n\t\tthrow exception;\n\t}\n}\n\nvoid t_semaphore::down(void) {\n\twhile (true) {\n\t\tint ret = sem_wait(&sem);\n\n\t\tif (ret != 0 && errno == EINTR) {\n\t\t\t// In NPTL threading sem_wait can be interrupted.\n\t\t\t// In LinuxThreads threading sem_wait is non-interruptable.\n\t\t\t// Continue with sem_wait if an interrupt is caught.\n\t\t\tcontinue;\n\t\t}\n\n\t\tbreak;\n\t}\n}\n\nbool t_semaphore::try_down(void) {\n\tint ret;\n\n\tret = sem_trywait(&sem);\n\treturn (ret == 0);\n}\n"
  },
  {
    "path": "src/threads/sema.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_SEMAPHORE\n#define _H_SEMAPHORE\n\n#include <semaphore.h>\n\nclass t_semaphore {\nprivate:\n\tsem_t\tsem;\n\npublic:\n\t// Throws a string exception (error message) when failing.\n\tt_semaphore(unsigned int value);\n\n\t~t_semaphore();\n\n\t// Throws a string exception (error message) when failing.\n\tvoid up(void);\n\n\tvoid down(void);\n\n\t// Returns true if downing the semaphore succeeded.\n\t// Returns false if the semaphore was zero already.\n\tbool try_down(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/threads/thread.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <signal.h>\n#include <cstdio>\n#include <cstdlib>\n#include <sys/types.h>\n#include <unistd.h>\n#include \"thread.h\"\n\n// Scratch variables for checking LinuxThreads vs NPTL\nstatic pid_t pid_thread;\n\nt_thread::t_thread(void *(*start_routine)(void *), void *arg) {\n\tint ret;\n\n\tret = pthread_create(&tid, NULL, start_routine, arg);\n\tif (ret != 0) throw ret;\n}\n\nvoid t_thread::join(void) {\n\tint ret = pthread_join(tid, NULL);\n\tif (ret != 0) throw ret;\n}\n\nvoid t_thread::detach(void) {\n\tint ret = pthread_detach(tid);\n\tif (ret != 0) throw ret;\n}\n\nvoid t_thread::kill(void) {\n\tint ret = pthread_kill(tid, SIGKILL);\n\tif (ret != 0) throw ret;\n}\n\nvoid t_thread::cancel(void) {\n\tint ret = pthread_cancel(tid);\n\tif (ret != 0) throw ret;\n}\n\nvoid t_thread::set_sched_fifo(int priority) {\n\tstruct sched_param sp;\n\n\tsp.sched_priority = priority;\n\tint ret = pthread_setschedparam(tid, SCHED_FIFO, &sp);\n\tif (ret != 0) throw ret;\n}\n\npthread_t t_thread::get_tid(void) const {\n\treturn tid;\n}\n\npthread_t t_thread::self(void) {\n\treturn pthread_self();\n}\n\nbool t_thread::is_equal(const t_thread &thr) const {\n\treturn pthread_equal(tid, thr.get_tid());\n}\n\nbool t_thread::is_equal(const pthread_t &_tid) const {\n\treturn pthread_equal(tid, _tid);\n}\n\nbool t_thread::is_self(void) const {\n\treturn pthread_equal(tid, pthread_self());\n}\n\nbool t_thread::is_self(const pthread_t &_tid) {\n\treturn pthread_equal(_tid, pthread_self());\n}\n\nvoid *check_threading_impl(void *arg) {\n\tpid_thread = getpid();\n\tpthread_exit(NULL);\n}\n\nbool t_thread::is_LinuxThreads(void) {\n\tt_thread *thr = new t_thread(check_threading_impl, NULL);\n\n\ttry {\n\t\tthr->join();\n\t} catch (int) {\n\t\t// Thread is already terminated.\n\t}\n\n\tdelete thr;\n\n\t// In LinuxThreads each thread has a different pid.\n\t// In NPTL all threads have the same pid.\n\treturn (getpid() != pid_thread);\n}\n\n"
  },
  {
    "path": "src/threads/thread.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _H_THREAD\n#define _H_THREAD\n\n#include <pthread.h>\n\nclass t_thread {\nprivate:\n\tpthread_t\ttid;\t\t// Thread id\n\npublic:\n\tt_thread(void *(*start_routine)(void *), void *arg);\n\n\t// These methods throw an int (return value of libpthread function)\n\t// when they fail\n\tvoid join(void);\n\tvoid detach(void);\n\tvoid kill(void);\n\tvoid cancel(void);\n\tvoid set_sched_fifo(int priority);\n\n\t// Get thread id\n\tpthread_t get_tid(void) const;\n\n\t// Get thread id of current thread\n\tstatic pthread_t self(void);\n\n\t// Check if 2 threads are equal\n\tbool is_equal(const t_thread &thr) const;\n\tbool is_equal(const pthread_t &_tid) const;\n\tbool is_self(void) const;\n\tstatic bool is_self(const pthread_t &_tid);\n\n\t// Check if LinuxThreads or NPTL is active. This check is needed\n\t// for correctly handling signals. Signal handling in LinuxThreads\n\t// is quite different from signal handling in NPTL.\n\t// This checks creates a new thread and waits on its termination,\n\t// so you better cache its result for efficient future checks.\n\tstatic bool is_LinuxThreads(void);\n};\n\n#endif\n"
  },
  {
    "path": "src/timekeeper.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <sys/time.h>\n#include <iostream>\n#include <signal.h>\n#include \"events.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"subscription.h\"\n#include \"timekeeper.h\"\n#include \"transaction_mgr.h\"\n#include \"threads/thread.h\"\n#include \"audits/memman.h\"\n\nextern t_phone\t\t*phone;\nextern t_event_queue\t*evq_trans_layer;\nextern t_event_queue\t*evq_trans_mgr;\nextern t_event_queue\t*evq_timekeeper;\nextern t_timekeeper\t*timekeeper;\nextern bool\t\tthreading_is_LinuxThreads;\n\nstring timer_type2str(t_timer_type t) {\n\tswitch(t) {\n\tcase TMR_TRANSACTION:\treturn \"TMR_TRANSACTION\";\n\tcase TMR_PHONE:\t\treturn \"TMR_PHONE\";\n\tcase TMR_LINE:\t\treturn \"TMR_LINE\";\n\tcase TMR_SUBSCRIBE:\treturn \"TMR_SUBSCRIBE\";\n\tcase TMR_PUBLISH:\treturn \"TMR_PUBLISH\";\n\tcase TMR_STUN_TRANSACTION: return \"TMR_STUN_TRANSACTION\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_timer\n///////////////////////////////////////////////////////////\n\nt_timer::t_timer(long dur) : t_id_object() {\n\tlong d = dur;\n\t\n\t// HACK: if a timer is set to zero seconds, set it to 1 ms, otherwise\n\t//       the timer will not expire.\n\tif (dur == 0) d++;\n\n\tduration = d;\n\trelative_duration = d;\n}\n\nlong t_timer::get_duration(void) const {\n\treturn duration;\n}\n\nlong t_timer::get_relative_duration(void) const {\n\treturn relative_duration;\n}\n\nvoid t_timer::set_relative_duration(long d) {\n\trelative_duration = d;\n}\n\n///////////////////////////////////////////////////////////\n// class t_tmr_transaction\n///////////////////////////////////////////////////////////\n\nt_tmr_transaction::t_tmr_transaction(long dur, t_sip_timer tmr,\n\tunsigned short tid) : t_timer(dur)\n{\n\tsip_timer = tmr;\n\ttransaction_id = tid;\n}\n\nvoid t_tmr_transaction::expired(void) {\n\t// Create a timeout event for the transaction manager\n\tevq_trans_mgr->push_timeout(this);\n}\n\nt_timer *t_tmr_transaction::copy(void) const {\n\tt_tmr_transaction *t = new t_tmr_transaction(*this);\n\tMEMMAN_NEW(t);\n\treturn t;\n}\n\nt_timer_type t_tmr_transaction::get_type(void) const {\n\treturn TMR_TRANSACTION;\n}\n\nunsigned short t_tmr_transaction::get_tid(void) const {\n\treturn transaction_id;\n}\n\nt_sip_timer t_tmr_transaction::get_sip_timer(void) const {\n\treturn sip_timer;\n}\n\nstring t_tmr_transaction::get_name(void) const {\n\tswitch(sip_timer) {\n\tcase TIMER_T1:\treturn \"TIMER_T1\";\n\tcase TIMER_T2:\treturn \"TIMER_T2\";\n\tcase TIMER_T4:\treturn \"TIMER_T4\";\n\tcase TIMER_A:\treturn \"TIMER_A\";\n\tcase TIMER_B:\treturn \"TIMER_B\";\n\tcase TIMER_C:\treturn \"TIMER_C\";\n\tcase TIMER_D:\treturn \"TIMER_D\";\n\tcase TIMER_E:\treturn \"TIMER_E\";\n\tcase TIMER_F:\treturn \"TIMER_F\";\n\tcase TIMER_G:\treturn \"TIMER_G\";\n\tcase TIMER_H:\treturn \"TIMER_H\";\n\tcase TIMER_I:\treturn \"TIMER_I\";\n\tcase TIMER_J:\treturn \"TIMER_J\";\n\tcase TIMER_K:\treturn \"TIMER_K\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_tmr_phone\n///////////////////////////////////////////////////////////\nt_tmr_phone::t_tmr_phone(long dur, t_phone_timer ptmr, t_phone *p) : t_timer(dur)\n{\n\tphone_timer = ptmr;\n\tthe_phone = p;\n}\n\nvoid t_tmr_phone::expired(void) {\n\tevq_trans_layer->push_timeout(this);\n}\n\nt_timer *t_tmr_phone::copy(void) const {\n\tt_tmr_phone *t = new t_tmr_phone(*this);\n\tMEMMAN_NEW(t);\n\treturn t;\n}\n\nt_timer_type t_tmr_phone::get_type(void) const {\n\treturn TMR_PHONE;\n}\n\nt_phone_timer t_tmr_phone::get_phone_timer(void) const {\n\treturn phone_timer;\n}\n\nt_phone *t_tmr_phone::get_phone(void) const {\n\treturn the_phone;\n}\n\nstring t_tmr_phone::get_name(void) const {\n\tswitch(phone_timer) {\n\tcase PTMR_REGISTRATION:\t\treturn \"PTMR_REGISTRATION\";\n\tcase PTMR_NAT_KEEPALIVE: \treturn \"PTMR_NAT_KEEPALIVE\";\n\tcase PTMR_TCP_PING:\t\treturn \"PTMR_TCP_PING\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_tmr_line\n///////////////////////////////////////////////////////////\nt_tmr_line::t_tmr_line(long dur, t_line_timer ltmr, t_object_id lid,\n\t\t\t\tt_object_id d) : t_timer(dur)\n{\n\tline_timer = ltmr;\n\tline_id = lid;\n\tdialog_id = d;\n}\n\nvoid t_tmr_line::expired(void) {\n\tevq_trans_layer->push_timeout(this);\n}\n\nt_timer *t_tmr_line::copy(void) const {\n\tt_tmr_line *t = new t_tmr_line(*this);\n\tMEMMAN_NEW(t);\n\treturn t;\n}\n\nt_timer_type t_tmr_line::get_type(void) const {\n\treturn TMR_LINE;\n}\n\nt_line_timer t_tmr_line::get_line_timer(void) const {\n\treturn line_timer;\n}\n\nt_object_id t_tmr_line::get_line_id(void) const {\n\treturn line_id;\n}\n\nt_object_id t_tmr_line::get_dialog_id(void) const {\n\treturn dialog_id;\n}\n\nstring t_tmr_line::get_name(void) const {\n\tswitch(line_timer) {\n\tcase LTMR_ACK_TIMEOUT:\t\treturn \"LTMR_ACK_TIMEOUT\";\n\tcase LTMR_ACK_GUARD:\t\treturn \"LTMR_ACK_GUARD\";\n\tcase LTMR_INVITE_COMP:\t\treturn \"LTMR_INVITE_COMP\";\n\tcase LTMR_NO_ANSWER:\t\treturn \"LTMR_NO_ANSWER\";\n\tcase LTMR_RE_INVITE_GUARD:\treturn \"LTMR_RE_INVITE_GUARD\";\n\tcase LTMR_100REL_TIMEOUT:\treturn \"LTMR_100REL_TIMEOUT\";\n\tcase LTMR_100REL_GUARD:\t\treturn \"LTMR_100REL_GUARD\";\n\tcase LTMR_CANCEL_GUARD:\t\treturn \"LTMR_CANCEL_GUARD\";\n\tcase LTMR_GLARE_RETRY:\t\treturn \"LTMR_GLARE_RETRY\";\n\tcase LTMR_SESSION_REFRESH:\treturn \"LTMR_SESSION_REFRESH\";\n\tcase LTMR_SESSION_EXPIRE:\treturn \"LTMR_SESSION_EXPIRE\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_tmr_subscribe\n///////////////////////////////////////////////////////////\nt_tmr_subscribe::t_tmr_subscribe(long dur, t_subscribe_timer stmr,\n\t\tt_object_id lid, t_object_id d, const string &event_type,\n\t\tconst string &event_id) : t_timer(dur)\n{\n\tsubscribe_timer = stmr;\n\tline_id = lid;\n\tdialog_id = d;\n\tsub_event_type = event_type;\n\tsub_event_id = event_id;\n}\n\nvoid t_tmr_subscribe::expired(void) {\n\tevq_trans_layer->push_timeout(this);\n}\n\nt_timer *t_tmr_subscribe::copy(void) const {\n\tt_tmr_subscribe *t = new t_tmr_subscribe(*this);\n\tMEMMAN_NEW(t);\n\treturn t;\n}\n\nt_timer_type t_tmr_subscribe::get_type(void) const {\n\treturn TMR_SUBSCRIBE;\n}\n\nt_subscribe_timer t_tmr_subscribe::get_subscribe_timer(void) const {\n\treturn subscribe_timer;\n}\n\nt_object_id t_tmr_subscribe::get_line_id(void) const {\n\treturn line_id;\n}\n\nt_object_id t_tmr_subscribe::get_dialog_id(void) const {\n\treturn dialog_id;\n}\n\nstring t_tmr_subscribe::get_sub_event_type(void) const {\n\treturn sub_event_type;\n}\n\nstring t_tmr_subscribe::get_sub_event_id(void) const {\n\treturn sub_event_id;\n}\n\nstring t_tmr_subscribe::get_name(void) const {\n\tswitch(subscribe_timer) {\n\tcase STMR_SUBSCRIPTION:\treturn \"STMR_SUBSCRIPTION\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_tmr_publish\n///////////////////////////////////////////////////////////\nt_tmr_publish::t_tmr_publish(long dur, t_publish_timer ptmr, const string &_event_type) :\n\tt_timer(dur),\n\tpublish_timer(ptmr),\n\tevent_type(_event_type)\n{}\n\nvoid t_tmr_publish::expired(void) {\n\tevq_trans_layer->push_timeout(this);\n}\n\nt_timer *t_tmr_publish::copy(void) const {\n\tt_tmr_publish *t = new t_tmr_publish(*this);\n\tMEMMAN_NEW(t);\n\treturn t;\n}\n\nt_timer_type t_tmr_publish::get_type(void) const {\n\treturn TMR_PUBLISH;\n}\n\nt_publish_timer t_tmr_publish::get_publish_timer(void) const {\n\treturn publish_timer;\n}\n\nstring t_tmr_publish::get_name(void) const {\n\tswitch (publish_timer) {\n\tcase PUBLISH_TMR_PUBLICATION: return \"PUBLISH_TMR_PUBLICATION\";\n\t}\n\t\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_tmr_stun_trans\n///////////////////////////////////////////////////////////\n\nt_tmr_stun_trans::t_tmr_stun_trans(long dur, t_stun_timer tmr,\n\tunsigned short tid) : t_timer(dur)\n{\n\tstun_timer = tmr;\n\ttransaction_id = tid;\n}\n\nvoid t_tmr_stun_trans::expired(void) {\n\t// Create a timeout event for the transaction manager\n\tevq_trans_mgr->push_timeout(this);\n}\n\nt_timer *t_tmr_stun_trans::copy(void) const {\n\tt_tmr_stun_trans *t = new t_tmr_stun_trans(*this);\n\tMEMMAN_NEW(t);\n\treturn t;\n}\n\nt_timer_type t_tmr_stun_trans::get_type(void) const {\n\treturn TMR_STUN_TRANSACTION;\n}\n\nunsigned short t_tmr_stun_trans::get_tid(void) const {\n\treturn transaction_id;\n}\n\nt_stun_timer t_tmr_stun_trans::get_stun_timer(void) const {\n\treturn stun_timer;\n}\n\nstring t_tmr_stun_trans::get_name(void) const {\n\tswitch(stun_timer) {\n\tcase STUN_TMR_REQ_TIMEOUT:\treturn \"STUN_TMR_REQ_TIMEOUT\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// class t_timekeeper\n///////////////////////////////////////////////////////////\n\nt_timekeeper::t_timekeeper() : mutex() {\n\tstopped = false;\n\ttimer_expired = false;\n}\n\nvoid t_timekeeper::start(void (*timeout_handler)(int)) {\n\tsignal(SIGALRM, timeout_handler);\n}\n\nt_timekeeper::~t_timekeeper() {\n\tstruct itimerval\titimer;\n\n\tmutex.lock();\n\n\tlog_file->write_header(\"t_timekeeper::~t_timekeeper\",\n\t\tLOG_NORMAL, LOG_INFO);\n\tlog_file->write_raw(\"Clean up timekeeper.\\n\");\n\n\t// Stop timers\n\titimer.it_interval.tv_sec = 0;\n\titimer.it_interval.tv_usec = 0;\n\titimer.it_value.tv_sec = 0;\n\titimer.it_value.tv_usec = 0;\n\tsetitimer(ITIMER_REAL, &itimer, NULL);\n\n\tfor (list<t_timer *>::iterator i = timer_list.begin();\n\t     i != timer_list.end(); i++)\n\t{\n\t\tlog_file->write_raw(\"\\nDeleting timer:\\n\");\n\t\tlog_file->write_raw(\"Id: \");\n\t\tlog_file->write_raw((*i)->get_object_id());\n\t\tlog_file->write_raw(\", Type: \");\n\t\tlog_file->write_raw(timer_type2str((*i)->get_type()));\n\t\tlog_file->write_raw(\", Timer: \");\n\t\tlog_file->write_raw((*i)->get_name());\n\t\tlog_file->write_raw(\"\\nDuration: \");\n\t\tlog_file->write_raw((*i)->get_duration());\n\t\tlog_file->write_raw(\", Relative duration: \");\n\t\tlog_file->write_raw((*i)->get_relative_duration());\n\t\tlog_file->write_endl();\n\t\tif ((*i)->get_type() == TMR_TRANSACTION) {\n\t\t\tlog_file->write_raw(\"Transaction id: \");\n\t\t\tlog_file->write_raw(\n\t\t\t\t((t_tmr_transaction *)(*i))->get_tid());\n\t\t\tlog_file->write_endl();\n\t\t}\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n\n\tif (threading_is_LinuxThreads) {\n\t\tsignal(SIGALRM, SIG_DFL);\n\t}\n\n\tlog_file->write_footer();\n\n\tmutex.unlock();\n}\n\nvoid t_timekeeper::lock(void) {\n\tmutex.lock();\n}\n\nvoid t_timekeeper::unlock(void) {\n\tmutex.unlock();\n\n\tif (timer_expired) {\n\t\ttimer_expired = false;\n\t\treport_expiry();\n\t}\n}\n\nvoid t_timekeeper::start_timer(t_timer *t) {\n\tstruct itimerval\titimer;\n\tlong\t\t\tremain_msec;\n\n\tlock();\n\n\t// The next interval option is not used\n\titimer.it_interval.tv_sec = 0;\n\titimer.it_interval.tv_usec = 0;\n\n\t// Get duration of the timer to start\n\tlong d = t->get_relative_duration();\n\n\t// If no timer is currently running then simply start the timer\n\tif (timer_list.empty()) {\n\t\ttimer_list.push_back(t);\n\t\titimer.it_value.tv_sec = d / 1000;\n\t\titimer.it_value.tv_usec = (d % 1000) * 1000;\n\t\tsetitimer(ITIMER_REAL, &itimer, NULL);\n\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// Get remaining duration of current running timer\n\tgetitimer(ITIMER_REAL, &itimer);\n\tremain_msec = itimer.it_value.tv_sec * 1000 +\n\t\t      itimer.it_value.tv_usec / 1000;\n\n\t// If the new timer is shorter than the current timer.\n\t// then the new timer should be run first.\n\tif (d < remain_msec) {\n\t\t// Change running timer to new timer\n\t\titimer.it_value.tv_sec = d / 1000;\n\t\titimer.it_value.tv_usec = (d % 1000) * 1000;\n\t\tsetitimer(ITIMER_REAL, &itimer, NULL);\n\n\t\t// Calculate the relative duration the timer\n\t\t// that was running.\n\t\tt_timer *old_timer = timer_list.front();\n\t\told_timer->set_relative_duration(remain_msec - d);\n\n\t\t// Add new timer at the front of the list\n\t\ttimer_list.push_front(t);\n\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// Calculate the relative duration for the new timer\n\tlong new_duration = d - remain_msec;\n\n\t// Insert the new timer at the right position in the list.\n\tlist<t_timer *>::iterator i;\n\tfor (i = timer_list.begin(); i != timer_list.end(); i++)\n\t{\n\t\t// skip the first timer\n\t\tif (i == timer_list.begin()) continue;\n\n\t\tlong dur = (*i)->get_relative_duration();\n\t\tif (new_duration < dur) {\n\t\t\t// Adjust relative duration existing timer\n\t\t\t(*i)->set_relative_duration(dur - new_duration);\n\n\t\t\t// Insert new timer before existing timer\n\t\t\tt->set_relative_duration(new_duration);\n\t\t\ttimer_list.insert(i, t);\n\n\t\t\tunlock();\n\t\t\treturn;\n\t\t}\n\n\t\tnew_duration -= dur;\n\t}\n\n\t// Add the new timer to the end of the list\n\tt->set_relative_duration(new_duration);\n\ttimer_list.push_back(t);\n\n\tunlock();\n}\n\nvoid t_timekeeper::stop_timer(t_object_id id) {\n\tstruct itimerval\titimer;\n\tlong\t\t\tremain_msec;\n\n\tlock();\n\n\t// The next interval option is not used\n\titimer.it_interval.tv_sec = 0;\n\titimer.it_interval.tv_usec = 0;\n\n\n\tif (timer_list.empty()) {\n\t\t// Timer already expired or stopped\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// Find timer\n\tlist<t_timer *>::iterator i = timer_list.begin();\n\twhile (i != timer_list.end()) {\n\t\tif ((*i)->get_object_id() == id) break;\n\t\ti++;\n\t}\n\n\tif (i == timer_list.end()) {\n\t\t// Timer already expired or stopped.\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// If it is the current running timer, then it must be stopped\n\tif (i == timer_list.begin()) {\n\t\tgetitimer(ITIMER_REAL, &itimer);\n\n\t\t// If remaining time is less then 100 msec then let it\n\t\t// expire to prevent race condition when timer expires\n\t\t// while stopping it now.\n\t\tremain_msec = itimer.it_value.tv_sec * 1000 +\n\t\t\t      itimer.it_value.tv_usec / 1000;\n\t\tif (remain_msec < 100) {\n\t\t\tstopped = true;\n\t\t\tunlock();\n\t\t\treturn;\n\t\t}\n\n\t\t// Stop timer\n\t\titimer.it_value.tv_sec = 0;\n\t\titimer.it_value.tv_usec = 0;\n\t\tsetitimer(ITIMER_REAL, &itimer, NULL);\n\n\t\t// Remove the timer\n\t\tMEMMAN_DELETE(timer_list.front());\n\t\tdelete timer_list.front();\n\t\ttimer_list.pop_front();\n\n\t\t// If a next timer exists then adjust its relative\n\t\t// duration and start it.\n\t\tif (!timer_list.empty()) {\n\t\t\tt_timer *next_timer = timer_list.front();\n\t\t\tlong dur = next_timer->get_relative_duration();\n\t\t\tdur += remain_msec;\n\t\t\tnext_timer->set_relative_duration(dur);\n\t\t\titimer.it_value.tv_sec = dur / 1000;\n\t\t\titimer.it_value.tv_usec = (dur % 1000) * 1000;\n\t\t\tsetitimer(ITIMER_REAL, &itimer, NULL);\n\t\t}\n\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// Timer is not the current running timer, so delete it\n\t// and adjust relative duration of the next timer.\n\tlist<t_timer *>::iterator next = i;\n\tnext++;\n\n\tif (next == timer_list.end()) {\n\t\t// There is no next timer\n\t\tMEMMAN_DELETE(timer_list.back());\n\t\tdelete timer_list.back();\n\t\ttimer_list.pop_back();\n\t\tunlock();\n\t\treturn;\n\t}\n\n\tlong dur = (*i)->get_relative_duration();\n\tlong dur_next = (*next)->get_relative_duration();\n\t(*next)->set_relative_duration(dur + dur_next);\n\tMEMMAN_DELETE(*i);\n\tdelete *i;\n\ttimer_list.erase(i);\n\n\tunlock();\n}\n\nvoid t_timekeeper::report_expiry(void) {\n\tlock();\n\t\n\tif (timer_list.empty()) {\n\t\tunlock();\n\t\treturn;\n\t}\n\t\n\tt_timer *t = timer_list.front();\n\n\t// Trigger action if timer was not stopped\n\tif (!stopped) {\n\t\tt->expired();\n\t}\n\tstopped = false;\n\n\t// Remove the timer\n\tMEMMAN_DELETE(timer_list.front());\n\tdelete timer_list.front();\n\ttimer_list.pop_front();\n\n\tif (timer_list.empty()) {\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// If the relative duration of the next timer is 0, then\n\t// it also expired. Action should be triggerd. If not, then\n\t// it should be started.\n\tt_timer *next = timer_list.front();\n\tlong dur = next->get_relative_duration();\n\twhile (dur == 0) {\n\t\tnext->expired();\n\t\tMEMMAN_DELETE(next);\n\t\tdelete next;\n\t\ttimer_list.pop_front();\n\t\tif (timer_list.empty()) break;\n\t\tnext = timer_list.front();\n\t\tdur = next->get_relative_duration();\n\t}\n\n\tif (!timer_list.empty()) {\n\t\tstruct itimerval\titimer;\n\n\t\titimer.it_interval.tv_sec = 0;\n\t\titimer.it_interval.tv_usec = 0;\n\t\titimer.it_value.tv_sec = dur / 1000;\n\t\titimer.it_value.tv_usec = (dur % 1000) * 1000;\n\t\tsetitimer(ITIMER_REAL, &itimer, NULL);\n\t}\n\n\tunlock();\n}\n\nunsigned long t_timekeeper::get_remaining_time(t_object_id timer_id) {\n\tstruct itimerval\titimer;\n\tunsigned long\t\tremain_msec = 0;\n\tunsigned long\t\tduration = 0;\n\n\tlock();\n\n\t// The next interval option is not used\n\titimer.it_interval.tv_sec = 0;\n\titimer.it_interval.tv_usec = 0;\n\n\t// Get remaining duration of current running timer\n\tgetitimer(ITIMER_REAL, &itimer);\n\tremain_msec = itimer.it_value.tv_sec * 1000 +\n\t\t      itimer.it_value.tv_usec / 1000;\n\n\t// Find the timer\n\tlist<t_timer *>::iterator i = timer_list.begin();\n\twhile (i != timer_list.end()) {\n\t\tif (i != timer_list.begin()) {\n\t\t\tremain_msec += (*i)->get_relative_duration();\n\t\t}\n\n\t\tif ((*i)->get_object_id() == timer_id) break;\n\n\t\ti++;\n\t}\n\n\t// Return duration to originator of get event\n\tif (i == timer_list.end()) {\n\t\tduration = 0;\n\t} else {\n\t\tduration = remain_msec;\n\t}\n\n\tunlock();\n\treturn duration;\n}\n\n// SIGALRM handler\nvoid timeout_handler(int signum) {\n\tsignal(SIGALRM, timeout_handler);\n\t// timekeeper.report_expiry();\n\n\t// This will signal an interrupt to the call to pop in the\n\t// main look t_timekeeper::run\n\tevq_timekeeper->interrupt();\n}\n\nvoid t_timekeeper::run(void) {\n\tt_event\t\t\t*event;\n\tt_event_start_timer\t*ev_start;\n\tt_event_stop_timer\t*ev_stop;\n\tbool\t\t\ttimeout;\n\t\n\t// The timekeeper should not try to take the phone lock as\n\t// it may lead to a deadlock. Make sure an assert is raised\n\t// if this situation ever happens.\n\tphone->add_prohibited_thread();\n\n\tif (threading_is_LinuxThreads) {\n\t\t// In LinuxThreads SIGALRM caused by the expiration of a timer\n\t\t// started with setitimer is always delivered to the thread calling\n\t\t// setitimer. So the sigwait() call from another thread does not\n\t\t// work. Use a signal handler instead.\n\t\tstart(timeout_handler);\n\t}\n\n\tbool quit = false;\n\twhile (!quit) {\n\t\tevent = evq_timekeeper->pop(timeout);\n\n\t\tif (timeout) {\n\t\t\treport_expiry();\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch(event->get_type()) {\n\t\tcase EV_START_TIMER:\n\t\t\tev_start = (t_event_start_timer *)event;\n\t\t\tstart_timer(ev_start->get_timer());\n\t\t\tbreak;\n\t\tcase EV_STOP_TIMER:\n\t\t\tev_stop = (t_event_stop_timer *)event;\n\t\t\tstop_timer(ev_stop->get_timer_id());\n\t\t\tbreak;\n\t\tcase EV_QUIT:\n\t\t\tquit = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\n\t\tMEMMAN_DELETE(event);\n\t\tdelete event;\n\t}\n}\n\nvoid *timekeeper_main(void *arg) {\n\ttimekeeper->run();\n\treturn NULL;\n}\n\nvoid *timekeeper_sigwait(void *arg) {\n\tsigset_t\tsigset;\n\tint\t\tsig;\n\n\tsigemptyset(&sigset);\n\tsigaddset(&sigset, SIGALRM);\n\n\twhile (true) {\n\t\t// When SIGCONT is received after SIGSTOP, sigwait returns\n\t\t// with EINTR ??\n\t\tif (sigwait(&sigset, &sig) == EINTR) continue;\n\t\tevq_timekeeper->interrupt();\n\t}\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/timekeeper.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TIMEKEEPER_H\n#define _TIMEKEEPER_H\n\n#include <list>\n#include \"id_object.h\"\n#include \"protocol.h\"\n#include \"transaction.h\"\n#include \"threads/mutex.h\"\n#include \"threads/sema.h\"\n\nusing namespace std;\n\n// Forward declarations\nclass t_phone;\nclass t_line;\nclass t_subscription;\n\n/** Timer type */\nenum t_timer_type {\n\tTMR_TRANSACTION,\t/**< Transaction timer */\n\tTMR_PHONE,\t\t/**< Timer associated with the phone */\n\tTMR_LINE,\t\t/**< Timer associated with a line */\n\tTMR_SUBSCRIBE,\t\t/**< Subscription timer */\n\tTMR_PUBLISH,\t\t/**< Publication timer */\n\tTMR_STUN_TRANSACTION\t/**< STUN timer */\n};\n////////////////////////////////////////////////////////////////\n// General timer.\n////////////////////////////////////////////////////////////////\n// Instances should be created from subclasses.\nclass t_timer : public t_id_object {\nprivate:\n\tlong\t\t\tduration; // milliseconds\n\tlong\t\t\trelative_duration; // milliseconds\n\npublic:\n\tt_timer(long dur);\n\tvirtual ~t_timer() {}\n\n\t// This method is invoked on expiry\n\t// Subclasses should implent the action to be taken.\n\tvirtual void expired(void) = 0;\n\n\tlong get_duration(void) const;\n\tlong get_relative_duration(void) const;\n\tvoid set_relative_duration(long d);\n\tvirtual t_timer *copy(void) const = 0;\n\tvirtual t_timer_type get_type(void) const = 0;\n\n\t// Get the name of the timer (for debugging purposes)\n\tvirtual string get_name(void) const = 0;\n};\n\n////////////////////////////////////////////////////////////////\n// Transaction timer\n////////////////////////////////////////////////////////////////\nclass t_tmr_transaction : public t_timer {\nprivate:\n\tunsigned short\ttransaction_id;\n\tt_sip_timer\tsip_timer;\n\npublic:\n\tt_tmr_transaction(long dur, t_sip_timer tmr, unsigned short tid);\n\n\tvoid expired(void);\n\tt_timer *copy(void) const;\n\tt_timer_type get_type(void) const;\n\tunsigned short get_tid(void) const;\n\tt_sip_timer get_sip_timer(void) const;\n\tstring get_name(void) const;\n};\n\n////////////////////////////////////////////////////////////////\n// Phone timer\n////////////////////////////////////////////////////////////////\nclass t_tmr_phone : public t_timer {\nprivate:\n\tt_phone\t\t*the_phone;\n\tt_phone_timer\tphone_timer;\n\npublic:\n\tt_tmr_phone(long dur, t_phone_timer ptmr, t_phone *p);\n\n\tvoid expired(void);\n\tt_timer *copy(void) const;\n\tt_timer_type get_type(void) const;\n\tt_phone_timer get_phone_timer(void) const;\n\tt_phone *get_phone(void) const;\n\tstring get_name(void) const;\n};\n\n////////////////////////////////////////////////////////////////\n// Line timer\n////////////////////////////////////////////////////////////////\nclass t_tmr_line : public t_timer {\nprivate:\n\tt_object_id\tline_id;\n\tt_line_timer\tline_timer;\n\tt_object_id\tdialog_id;\n\npublic:\n\tt_tmr_line(long dur, t_line_timer ltmr, t_object_id lid,\n\t\t\tt_object_id d);\n\n\tvoid expired(void);\n\tt_timer *copy(void) const;\n\tt_timer_type get_type(void) const;\n\tt_line_timer get_line_timer(void) const;\n\tt_object_id get_line_id(void) const;\n\tt_object_id get_dialog_id(void) const;\n\tstring get_name(void) const;\n};\n\n////////////////////////////////////////////////////////////////\n// Subscribe timer\n////////////////////////////////////////////////////////////////\nclass t_tmr_subscribe : public t_timer {\nprivate:\n\tt_subscribe_timer\tsubscribe_timer;\n\tt_object_id\t\tline_id;\n\tt_object_id\t\tdialog_id;\n\tstring\t\t\tsub_event_type;\n\tstring\t\t\tsub_event_id;\n\n\npublic:\n\tt_tmr_subscribe(long dur, t_subscribe_timer stmr, t_object_id lid, t_object_id d,\n\t\tconst string &event_type, const string &event_id);\n\n\tvoid expired(void);\n\tt_timer *copy(void) const;\n\tt_timer_type get_type(void) const;\n\tt_subscribe_timer get_subscribe_timer(void) const;\n\tt_object_id get_line_id(void) const;\n\tt_object_id get_dialog_id(void) const;\n\tstring get_sub_event_type(void) const;\n\tstring get_sub_event_id(void) const;\n\tstring get_name(void) const;\n};\n\n/** Publication timer */\nclass t_tmr_publish : public t_timer {\nprivate:\n\tt_publish_timer\t\tpublish_timer;\t/**< Type of timer */\n\tstring\t\t\tevent_type;\t/**< Event type of publication */\n\n\npublic:\n\tt_tmr_publish(long dur, t_publish_timer ptmr, const string &_event_type);\n\n\tvoid expired(void);\n\tt_timer *copy(void) const;\n\tt_timer_type get_type(void) const;\n\tt_publish_timer get_publish_timer(void) const;\n\tstring get_name(void) const;\n};\n\n////////////////////////////////////////////////////////////////\n// STUN transaction timer\n////////////////////////////////////////////////////////////////\nclass t_tmr_stun_trans : public t_timer {\nprivate:\n\tunsigned short\ttransaction_id;\n\tt_stun_timer\tstun_timer;\n\npublic:\n\tt_tmr_stun_trans(long dur, t_stun_timer tmr, unsigned short tid);\n\n\tvoid expired(void);\n\tt_timer *copy(void) const;\n\tt_timer_type get_type(void) const;\n\tunsigned short get_tid(void) const;\n\tt_stun_timer get_stun_timer(void) const;\n\tstring get_name(void) const;\n};\n\n\n////////////////////////////////////////////////////////////////\n// Timekeeper\n////////////////////////////////////////////////////////////////\n// A timekeeper keeps track of multiple timers per thread.\n// Only one single thread should call the methods of a single\n// timekeeper. Multiple threads using the same timekeeper will\n// cause havoc.\n\nclass t_timekeeper {\nprivate:\n\t// List of running timers in order of timeout. As there\n\t// is only 1 real timer running on the OS. Each timer gets\n\t// a duration relative to its predecessor in the list.\n\tlist<t_timer *>\t\ttimer_list;\n\n\t// Mutex to synchronize timekeeper actions.\n\tt_mutex\t\t\tmutex;\n\n\t// Indicate if current timer was stopped, but not removed\n\t// to prevent race conditions. Expiry of a stopped timer\n\t// will not trigger any actions.\n\tbool\t\t\tstopped;\n\n\t// Indicate if the current timer expired while the\n\t// mutex was locked.\n\tbool\t\t\ttimer_expired;\n\n\t// Every method should start with locking the timekeeper\n\t// and end with unlocking. The unlocking method will check\n\t// if a timer expired during the locked state. If so, then\n\t// the expiry will be processed.\n\tvoid lock(void);\n\tvoid unlock(void);\n\n\t// Start the timekeeper from the thread that will handle\n\t// the SIGALRM signal. Start must be called before the\n\t// timekeeper can be used.\n\tvoid start(void (*timeout_handler)(int));\n\n\t// Start a timer. The timer id is returned. This id is\n\t// needed to stop a timer. Pointer t should not be used\n\t// or deleted after starting. When the timer expires or\n\t// is stopped it will be deleted.\n\tvoid start_timer(t_timer *t);\n\n\tvoid stop_timer(t_object_id id);\n\npublic:\n\t// The timeout_handler must be a signal handler for SIGALRM\n\tt_timekeeper();\n\t~t_timekeeper();\n\n\t// Report that the current timer has expired.\n\tvoid report_expiry(void);\n\n\t// Get remaining time of a running timer.\n\t// Returns 0 if the timer is not running anymore.\n\tunsigned long get_remaining_time(t_object_id timer_id);\n\n\t// Main loop to be run in a separate thread\n\tvoid run(void);\n};\n\n// Entry function for timekeeper thread\nvoid *timekeeper_main(void *arg);\n\n// Entry function of the thread waiting for SIGALRM\n// A dedicated thread is started to catch the SIGALRM signal and take\n// the appropriate action. All threads must block the SIGALRM signal.\nvoid *timekeeper_sigwait(void *arg);\n\n#endif\n"
  },
  {
    "path": "src/transaction.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include \"log.h\"\n#include \"events.h\"\n#include \"timekeeper.h\"\n#include \"transaction.h\"\n#include \"transaction_mgr.h\"\n#include \"user.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nextern t_event_queue\t\t*evq_sender;\nextern t_event_queue\t\t*evq_trans_layer;\nextern t_transaction_mgr\t*transaction_mgr;\n\nstring trans_state2str(t_trans_state s) {\n\tswitch(s) {\n\tcase TS_NULL:\t\treturn \"TS_NULL\";\n\tcase TS_CALLING:\treturn \"TS_CALLING\";\n\tcase TS_TRYING:\t\treturn \"TS_TRYING\";\n\tcase TS_PROCEEDING:\treturn \"TS_PROCEEDING\";\n\tcase TS_COMPLETED:\treturn \"TS_COMPLETED\";\n\tcase TS_CONFIRMED:\treturn \"TS_CONFIRMED\";\n\tcase TS_TERMINATED:\treturn \"TS_TERMINATED\";\n\t}\n\n\treturn \"UNKNOWN\";\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17\n// General transaction\n///////////////////////////////////////////////////////////\n\nt_mutex t_transaction::mtx_class;\nt_tid t_transaction::next_id = 1;\n\nt_transaction::t_transaction(t_request *r, unsigned short _tuid) {\n\tmtx_class.lock();\n\tid = next_id++;\n\tif (next_id == 65535) next_id = 1;\n\tmtx_class.unlock();\n\t\n\tstate = TS_NULL;\n\trequest = (t_request *)r->copy();\n\tfinal = NULL;\n\ttuid = _tuid;\n}\n\nt_transaction::~t_transaction() {\n\tMEMMAN_DELETE(request);\n\tdelete request;\n\tif (final != NULL) {\n\t\tMEMMAN_DELETE(final);\n\t\tdelete final;\n\t}\n\n\tfor (list<t_response *>::iterator i = provisional.begin();\n\t     i != provisional.end(); i++)\n\t{\n\t\tMEMMAN_DELETE(*i);\n\t\tdelete *i;\n\t}\n}\n\nt_tid t_transaction::get_id(void) const {\n\treturn id;\n}\n\nvoid t_transaction::process_provisional(t_response *r) {\n\tprovisional.push_back((t_response *)r->copy());\n}\n\nvoid t_transaction::process_final(t_response *r) {\n\tfinal = (t_response *)r->copy();\n}\n\nvoid t_transaction::process_response(t_response *r) {\n\tif (r->is_provisional()) {\n\t\tprocess_provisional(r);\n\t} else {\n\t\tprocess_final(r);\n\t}\n}\n\nt_trans_state t_transaction::get_state(void) const {\n\treturn state;\n}\n\nvoid t_transaction::set_tuid(unsigned short _tuid) {\n\ttuid = _tuid;\n}\n\nt_method t_transaction::get_method(void) const {\n\treturn request->method;\n}\n\nstring t_transaction::get_to_tag(void) {\n\tstring\ttag;\n\n\ttag = request->hdr_to.tag;\n\tif (tag.size() > 0) return tag;\n\tif (to_tag.size() > 0) return to_tag;\n\tto_tag = random_token(TAG_LEN);\n\treturn to_tag;\n}\n\n// RCF 3261 section 8.2.6.2\nt_response *t_transaction::create_response(int code, string reason) {\n\tt_response *r;\n\n\tr = request->create_response(code, reason);\n\t\n\t// NOTE: 100 Trying does not establish a dialog\n\tif (code != R_100_TRYING) {\n\t\tr->hdr_to.set_tag(get_to_tag());\n\t}\n\n\treturn r;\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17.1\n// Client transaction\n///////////////////////////////////////////////////////////\n\nt_trans_client::t_trans_client(t_request *r, const t_ip_port &ip_port,\n\t\tunsigned short _tuid) :\n\tt_transaction(r, _tuid),\n\tdst_ip_port(ip_port)\n{\n\t// Send request\n\tevq_sender->push_network(r, dst_ip_port);\n}\n\n// RFC 3261 17.1.3, 8.2.6.2\n// Section 17.1.3 states that only the branch and CSeq method should match.\n// This can lead to the following problem however:\n//\n// 1) A response matches a BYE request, but has a wrong call id.\n// 2) As the response matches the request, the transaction finishes.\n// 3) Then the response is delivered to the TU which tries to match the\n//    response to a dialog.\n// 4) As the call id is wrong, no match is found an the response is discarded.\n// 5) Now the TU keeps waiting forever for a response on the BYE\n//\n// By taking the call id into account here, this scenario is prevented.\n// When a call id is wrong, the BYE request will be retransmitted due to\n// timeouts until the transaction times out completely and a 408 is sent\n// to the TU.\n//\n// Same problem can occur when tags do not match, so tag is take into account\n// as well. So tags are take into account as well.\nbool t_trans_client::match(t_response *r) const {\n\tt_via\t&req_top_via = request->hdr_via.via_list.front();\n\tt_via\t&resp_top_via = r->hdr_via.via_list.front();\n\n\treturn (req_top_via.branch == resp_top_via.branch &&\n\t\trequest->hdr_cseq.method == r->hdr_cseq.method &&\n\t\trequest->hdr_call_id.call_id == r->hdr_call_id.call_id &&\n\t\trequest->hdr_from.tag == r->hdr_from.tag &&\n\t\t(request->hdr_to.tag.empty() || request->hdr_to.tag == r->hdr_to.tag));\n}\n\n// An ICMP error matches a transaction when the destination IP address/port\n// of the packet that caused the ICMP error equals the destination \n// IP address/port of the transaction. Other information of the packet causing\n// the ICMP error is not available.\n// In theory when multiple transactions are open for the same destination, the\n// wrong transaction may process the ICMP error. In practice this should rarely\n// happen as the destination will be unreachable for all those transactions.\n// If it happens a transaction gets aborted.\nbool t_trans_client::match(const t_icmp_msg &icmp) const {\n\treturn (dst_ip_port.ipaddr == icmp.ipaddr && dst_ip_port.port == icmp.port);\n}\n\nbool t_trans_client::match(const string &branch, const t_method &cseq_method) const {\n\tt_via\t&req_top_via = request->hdr_via.via_list.front();\n\t\n\treturn (req_top_via.branch == branch &&\n\t        request->hdr_cseq.method == cseq_method);\n}\n\nvoid t_trans_client::process_provisional(t_response *r) {\n\t// Set the to_tag, such that an internally genrated answer (when needed) \n\t// will have the correct tag.\n\t// An INVITE transaction may receive provisional responses with\n\t// different to-tags. Only the first to-tag will be kept and an\n\t// internally generated response will match this tag.\n\tif (!r->hdr_to.tag.empty() && to_tag.empty()) {\n\t\tto_tag = r->hdr_to.tag;\n\t}\n\t\n\tt_transaction::process_provisional(r);\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17.1.1\n// Client INVITE transaction\n///////////////////////////////////////////////////////////\n\nvoid t_tc_invite::start_timer_A(void) {\n\ttimer_A = transaction_mgr->start_timer(duration_A, TIMER_A, id);\n\n\t// Double duration for a next start\n\tduration_A = 2 * duration_A;\n}\n\nvoid t_tc_invite::start_timer_B(void) {\n\ttimer_B = transaction_mgr->start_timer(DURATION_B, TIMER_B, id);\n}\n\nvoid t_tc_invite::start_timer_D(void) {\n\t// RFC 3261 17.1.1.2\n\t// For reliable transport timer D must be set to zero seconds.\n\tif (dst_ip_port.transport == \"udp\") {\n\t\ttimer_D = transaction_mgr->start_timer(DURATION_D, TIMER_D, id);\n\t} else {\n\t\ttimer_D = transaction_mgr->start_timer(0, TIMER_D, id);\n\t}\n}\n\nvoid t_tc_invite::stop_timer_A(void) {\n\tif (timer_A) {\n\t\ttransaction_mgr->stop_timer(timer_A);\n\t\ttimer_A = 0;\n\t}\n}\n\nvoid t_tc_invite::stop_timer_B(void) {\n\tif (timer_B) {\n\t\ttransaction_mgr->stop_timer(timer_B);\n\t\ttimer_B = 0;\n\t}\n}\n\nvoid t_tc_invite::stop_timer_D(void) {\n\tif (timer_D) {\n\t\ttransaction_mgr->stop_timer(timer_D);\n\t\ttimer_D = 0;\n\t}\n}\n\nt_tc_invite::t_tc_invite(t_request *r, const t_ip_port &ip_port,\n\t\tunsigned short _tuid) :\n\tt_trans_client(r, ip_port, _tuid)\n{\n\tassert(r->method == INVITE);\n\n\tack = NULL;\n\tduration_A = DURATION_A;\n\tstate = TS_CALLING;\n\n\t// RFC 3261 17.1.1.2\n\t// Start timer A for unreliable transports.\n\tif (ip_port.transport == \"udp\") start_timer_A();\n\t\n\t// RFC 3261 17.1.1.2\n\t// Start timer B for all transports\n\tstart_timer_B();\n\t\n\ttimer_D = 0;\n}\n\nt_tc_invite::~t_tc_invite() {\n\tif (ack != NULL) {\n\t\tMEMMAN_DELETE(ack);\n\t\tdelete ack;\n\t}\n\tstop_timer_A();\n\tstop_timer_B();\n\tstop_timer_D();\n}\n\nvoid t_tc_invite::process_provisional(t_response *r) {\n\tassert(r->is_provisional());\n\n\tswitch (state) {\n\tcase TS_CALLING:\n\t\tstop_timer_A();\n\t\tstop_timer_B();\n\t\t// fall through\n\tcase TS_PROCEEDING:\n\t\tt_trans_client::process_provisional(r);\n\t\tstate = TS_PROCEEDING;\n\n\t\t// Report to TU\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tbreak;\n\tdefault:\n\t\t// Discard provisional response in other states\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_invite::process_final(t_response *r) {\n\tassert(r->is_final());\n\n\tt_ip_port ip_port;\n\n\tswitch (state) {\n\tcase TS_CALLING:\n\t\tstop_timer_A();\n\t\tstop_timer_B();\n\t\t// fall through\n\tcase TS_PROCEEDING:\n\t\tt_trans_client::process_final(r);\n\n\t\tif (r->is_success()) {\n\t\t\tstate = TS_TERMINATED;\n\t\t} else {\n\t\t\t// RFC 3261 17.1.1.3\n\t\t\t// construct ACK\n\t\t\tack = new t_request(ACK);\n\t\t\tMEMMAN_NEW(ack);\n\t\t\tack->uri = request->uri;\n\t\t\tack->hdr_call_id = request->hdr_call_id;\n\t\t\tack->hdr_from = request->hdr_from;\n\t\t\tack->hdr_to = r->hdr_to;\n\t\t\tack->hdr_via.add_via(\n\t\t\t\trequest->hdr_via.via_list.front());\n\t\t\tack->hdr_cseq.set_seqnr(request->hdr_cseq.seqnr);\n\t\t\tack->hdr_cseq.set_method(ACK);\n\t\t\tack->hdr_route = request->hdr_route;\n\t\t\tack->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);\n\t\t\tSET_HDR_USER_AGENT(ack->hdr_user_agent)\n\n\t\t\t// RFC 3261 22.1\n\t\t\t// Duplicate Authorization and Proxy-Authorization\n\t\t\t// headers from INVITE if the credentials in the\n\t\t\t// INVITE are accepted.\n\t\t\tif (r->code != R_401_UNAUTHORIZED &&\n\t\t\t    r->code != R_407_PROXY_AUTH_REQUIRED)\n\t\t\t{\n\t\t\t\tack->hdr_authorization =\n\t\t\t\t\trequest->hdr_authorization;\n\t\t\t\tack->hdr_proxy_authorization =\n\t\t\t\t\trequest->hdr_proxy_authorization;\n\t\t\t}\n\t\t\t\n\t\t\t// RFC 3263 4\n\t\t\t// ACK for non-2xx SIP responses to INVITE MUST be sent t\n\t\t\t// to the same host.\n\t\t\trequest->get_current_destination(ip_port);\n\t\t\tack->set_destination(ip_port);\t\t\t\n\n\t\t\t// Send ACK\n\t\t\tevq_sender->push_network(ack, dst_ip_port);\n\n\t\t\tstart_timer_D();\n\t\t\tstate = TS_COMPLETED;\n\t\t}\n\n\t\t// Report to TU\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tbreak;\n\tcase TS_COMPLETED:\n\t\t// A failure has been received. So 2XX is not\n\t\t// expected anymore. Discard 2XX.\n\t\tif (r->is_success()) {\n\t\t\tbreak;\n\t\t}\n\n\t\t// Retransmit ACK\n\t\tevq_sender->push_network(ack, dst_ip_port);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_invite::process_icmp(const t_icmp_msg &icmp) {\n\tlog_file->write_report(\"ICMP error received.\", \"t_tc_invite::process_icmp\");\n\tprocess_failure(FAIL_TRANSPORT);\n}\n\nvoid t_tc_invite::process_failure(t_failure failure) {\n\tt_response *r;\n\n\tswitch(state) {\n\tcase TS_CALLING:\n\t\tstop_timer_A();\n\t\tstop_timer_B();\n\t\t\n\t\tswitch (failure) {\n\t\tcase FAIL_TRANSPORT:\n\t\t\t// A transport failure indicates a kind of network problem.\n\t\t\t// So the server is not available. Generate an internal\n\t\t\t// 503 Service Unavailable repsponse to notify the TU.\n\t\t\tr = create_response(R_503_SERVICE_UNAVAILABLE);\n\t\t\tbreak;\n\t\tcase FAIL_TIMEOUT:\n\t\t\tr = create_response(R_408_REQUEST_TIMEOUT);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tlog_file->write_header(\"t_tc_invite::process_failure\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unknown type of failure: \");\n\t\t\tlog_file->write_raw((int)failure);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tr = create_response(R_400_BAD_REQUEST);\n\t\t\tbreak;\n\t\t}\n\t\t\t\n\n\t\tlog_file->write_header(\"t_tc_invite::process_failure\",\n\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Transaction failed.\\n\\n\");\n\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\tlog_file->write_raw(r->encode());\n\t\tlog_file->write_footer();\n\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tMEMMAN_DELETE(r);\n\t\tdelete r;\n\t\tstate = TS_TERMINATED;\n\t\tbreak;\n\tdefault:\n\t\t// In other states a response has been received already,\n\t\t// so this failure seems to be a mismatch. Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_invite::timeout(t_sip_timer t) {\n\tt_response\t*r;\n\n\tassert (t == TIMER_A || t == TIMER_B || t == TIMER_D);\n\n\tswitch (state) {\n\tcase TS_CALLING:\n\t\tswitch (t) {\n\t\tcase TIMER_A:\n\t\t\t// Resend request\n\t\t\tevq_sender->push_network(request, dst_ip_port);\n\t\t\tstart_timer_A();\n\t\t\tbreak;\n\t\tcase TIMER_B:\n\t\t\tstop_timer_A();\n\t\t\ttimer_B = 0;\n\t\t\t// Report timer expiry to TU\n\t\t\tr = create_response(R_408_REQUEST_TIMEOUT);\n\n\t\t\tlog_file->write_header(\"t_tc_invite::timeout\",\n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\t\tlog_file->write_raw(\"Timer B expired.\\n\\n\");\n\t\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\t\tlog_file->write_raw(r->encode());\n\t\t\tlog_file->write_footer();\n\n\t\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\t\tMEMMAN_DELETE(r);\n\t\t\tdelete r;\n\t\t\tstate = TS_TERMINATED;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Ignore expiry of other timers.\n\t\t\t// Other timers should have been stopped.\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase TS_COMPLETED:\n\t\tswitch (t) {\n\t\tcase TIMER_D:\n\t\t\ttimer_D = 0;\n\t\t\tstate = TS_TERMINATED;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Ignore expiry of other timers.\n\t\t\t// Other timers should have been stopped.\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Ignore timer expiries in other states\n\t\t// Other timers should have been stopped.\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_invite::abort(void) {\n\tt_response\t*r;\n\n\tswitch (state) {\n\tcase TS_PROCEEDING:\n\t\tr = create_response(R_408_REQUEST_TIMEOUT, \"Request Aborted\");\n\n\t\tlog_file->write_header(\"t_tc_invite::abort\",\n\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Invite transaction aborted.\\n\\n\");\n\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\tlog_file->write_raw(r->encode());\n\t\tlog_file->write_footer();\n\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tMEMMAN_DELETE(r);\n\t\tdelete r;\n\t\tstate = TS_TERMINATED;\n\t\tbreak;\n\tdefault:\n\t\t// Ignore abortion in other states.\n\t\t// In other states the request can be terminated in\n\t\t// a normal way.\n\t\tbreak;\n\t}\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17.1.2\n// Client non-INVITE transaction\n///////////////////////////////////////////////////////////\n\nvoid t_tc_non_invite::start_timer_E(void) {\n\tif (state == TS_PROCEEDING) duration_E = DURATION_T2;\n\ttimer_E = transaction_mgr->start_timer(duration_E, TIMER_E, id);\n\tduration_E = 2 * duration_E;\n\tif (duration_E > DURATION_T2) duration_E = DURATION_T2;\n}\n\nvoid t_tc_non_invite::start_timer_F(void) {\n\ttimer_F = transaction_mgr->start_timer(DURATION_F, TIMER_F, id);\n}\n\nvoid t_tc_non_invite::start_timer_K(void) {\n\t// RFC 3261 17.1.2.2\n\t// For reliable transports set timer K to zero seconds.\n\tif (dst_ip_port.transport == \"udp\") {\n\t\ttimer_K = transaction_mgr->start_timer(DURATION_K, TIMER_K, id);\n\t} else {\n\t\ttimer_K = transaction_mgr->start_timer(0, TIMER_K, id);\n\t}\n}\n\nvoid t_tc_non_invite::stop_timer_E(void) {\n\tif (timer_E) {\n\t\ttransaction_mgr->stop_timer(timer_E);\n\t\ttimer_E = 0;\n\t}\n}\n\nvoid t_tc_non_invite::stop_timer_F(void) {\n\tif (timer_F) {\n\t\ttransaction_mgr->stop_timer(timer_F);\n\t\ttimer_F = 0;\n\t}\n}\n\nvoid t_tc_non_invite::stop_timer_K(void) {\n\tif (timer_K) {\n\t\ttransaction_mgr->stop_timer(timer_K);\n\t\ttimer_K = 0;\n\t}\n}\n\nt_tc_non_invite::t_tc_non_invite(t_request *r, const t_ip_port &ip_port,\n\t\tunsigned short _tuid) :\n\tt_trans_client(r, ip_port, _tuid)\n{\n\tassert(r->method != INVITE);\n\n\tstate = TS_TRYING;\n\tduration_E = DURATION_E;\n\t\n\t// RFC 3261 17.1.2.2\n\t// Start timer E for unreliable transports.\n\tif (ip_port.transport == \"udp\") start_timer_E();\n\t\n\t// RFC 3261 17.1.2.2\n\t// Start timer F for all transports.\n\tstart_timer_F();\n\t\n\ttimer_K = 0;\n}\n\nt_tc_non_invite::~t_tc_non_invite() {\n\tstop_timer_E();\n\tstop_timer_F();\n\tstop_timer_K();\n}\n\nvoid t_tc_non_invite::process_provisional(t_response *r) {\n\tassert(r->is_provisional());\n\n\tswitch (state) {\n\tcase TS_TRYING:\n\tcase TS_PROCEEDING:\n\t\tt_trans_client::process_provisional(r);\n\t\tstate = TS_PROCEEDING;\n\t\t// Report to TU\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tbreak;\n\tdefault:\n\t\t// Discard provisional response in other states\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_non_invite::process_final(t_response *r) {\n\tassert(r->is_final());\n\n\tswitch (state) {\n\tcase TS_TRYING:\n\tcase TS_PROCEEDING:\n\t\tt_trans_client::process_final(r);\n\t\tstop_timer_E();\n\t\tstop_timer_F();\n\t\t// Report to TU\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tstart_timer_K();\n\t\tstate = TS_COMPLETED;\n\t\tbreak;\n\tcase TS_COMPLETED:\n\t\t// The received response is a retransmission.\n\t\t// AS the final response is already received this\n\t\t// retransmission can be discarded.\n\t\t// fall through\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_non_invite::process_icmp(const t_icmp_msg &icmp) {\n\tlog_file->write_report(\"ICMP error received.\", \"t_tc_non_invite::process_icmp\");\n\tprocess_failure(FAIL_TRANSPORT);\n}\n\nvoid t_tc_non_invite::process_failure(t_failure failure) {\n\tt_response *r;\n\n\tswitch(state) {\n\tcase TS_TRYING:\n\t\tstop_timer_E();\n\t\tstop_timer_F();\n\t\t\n\t\tswitch (failure) {\n\t\tcase FAIL_TRANSPORT:\n\t\t\t// A transport failure indicates a kind of network problem.\n\t\t\t// So the server is not available. Generate an internal\n\t\t\t// 503 Service Unavailable repsponse to notify the TU.\n\t\t\tr = create_response(R_503_SERVICE_UNAVAILABLE);\n\t\t\tbreak;\n\t\tcase FAIL_TIMEOUT:\n\t\t\tr = create_response(R_408_REQUEST_TIMEOUT);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tlog_file->write_header(\"t_tc_non_invite::process_failure\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unknown type of failure: \");\n\t\t\tlog_file->write_raw((int)failure);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\tr = create_response(R_400_BAD_REQUEST);\n\t\t\tbreak;\n\t\t}\n\t\t\t\n\n\t\tlog_file->write_header(\"t_tc_non_invite::process_failure\",\n\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Transaction failed.\\n\\n\");\n\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\tlog_file->write_raw(r->encode());\n\t\tlog_file->write_footer();\n\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tMEMMAN_DELETE(r);\n\t\tdelete r;\n\t\tstate = TS_TERMINATED;\n\t\tbreak;\n\tdefault:\n\t\t// In other states a response has been received already,\n\t\t// so this failure seems to be a mismatch. Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_non_invite::timeout(t_sip_timer t) {\n\tt_response *r;\n\n\tassert (t == TIMER_E || t == TIMER_F || t == TIMER_K);\n\n\tswitch (state) {\n\tcase TS_TRYING:\n\tcase TS_PROCEEDING:\n\t\tswitch (t) {\n\t\tcase TIMER_E:\n\t\t\t// Resend request\n\t\t\tevq_sender->push_network(request, dst_ip_port);\n\t\t\tstart_timer_E();\n\t\t\tbreak;\n\t\tcase TIMER_F:\n\t\t\ttimer_F = 0;\n\t\t\tstop_timer_E();\n\t\t\t// Report timer expiry to TU\n\t\t\tr = create_response(R_408_REQUEST_TIMEOUT);\n\n\t\t\tlog_file->write_header(\"t_tc_non_invite::timeout\",\n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\t\tlog_file->write_raw(\"Timer F expired.\\n\\n\");\n\t\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\t\tlog_file->write_raw(r->encode());\n\t\t\tlog_file->write_footer();\n\n\t\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\t\tMEMMAN_DELETE(r);\n\t\t\tdelete r;\n\t\t\tstate = TS_TERMINATED;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Ignore expiry of other timers.\n\t\t\t// Other timers should have been stopped.\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase TS_COMPLETED:\n\t\tswitch (t) {\n\t\tcase TIMER_K:\n\t\t\ttimer_K = 0;\n\t\t\tstate = TS_TERMINATED;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Ignore expiry of other timers.\n\t\t\t// Other timers should have been stopped.\n\t\t\tbreak;\n\n\t\t}\n\tdefault:\n\t\t// Ignore timer expiries in other states\n\t\t// Other timers should have been stopped.\n\t\tbreak;\n\t}\n}\n\nvoid t_tc_non_invite::abort(void) {\n\tt_response\t*r;\n\n\tswitch (state) {\n\tcase TS_TRYING:\n\tcase TS_PROCEEDING:\n\t\tstop_timer_E();\n\t\tstop_timer_F();\n\t\tr = create_response(R_408_REQUEST_TIMEOUT, \"Request Aborted\");\n\n\t\tlog_file->write_header(\"t_tc_non_invite::abort\",\n\t\t\tLOG_NORMAL, LOG_INFO);\n\t\tlog_file->write_raw(\"Non-invite transaction aborted.\\n\\n\");\n\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\tlog_file->write_raw(r->encode());\n\t\tlog_file->write_footer();\n\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t\tMEMMAN_DELETE(r);\n\t\tdelete r;\n\t\tstate = TS_TERMINATED;\n\t\tbreak;\n\tdefault:\n\t\t// Ignore abortion in other states.\n\t\t// In other states the request can be terminated in\n\t\t// a normal way.\n\t\tbreak;\n\t}\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17.2\n// Server transaction\n///////////////////////////////////////////////////////////\n\nt_trans_server::t_trans_server(t_request *r, unsigned short _tuid) :\n\tt_transaction(r, _tuid), resp_100_trying_sent(false)\n{\n\tt_trans_server\t*t;\n\tt_tid\t\ttid_cancel = 0;\n\n\t// Report to TU\n\tif (request->method == CANCEL) {\n\t\tt = transaction_mgr->find_cancel_target(r);\n\t\tif (t) tid_cancel = t->get_id();\n\t\tevq_trans_layer->push_user_cancel(r, tuid, id, tid_cancel);\n\t} else {\n\t\tevq_trans_layer->push_user(r, tuid, id);\n\t}\n}\n\nvoid t_trans_server::process_provisional(t_response *r) {\n\tt_ip_port ip_port;\n\t\n\tif (r->code == R_100_TRYING && resp_100_trying_sent) {\n\t\t// Send 100 Trying only once\n\t\treturn;\n\t}\n\n\tt_transaction::process_provisional(r);\n\tr->get_destination(ip_port);\n\tif (ip_port.ipaddr == 0) {\n\t\t// The response cannot be sent.\n\t\tstate = TS_TERMINATED;\n\t\t// Report failure to TU\n\t\tevq_trans_layer->push_failure(FAIL_TRANSPORT, id);\n\t} else {\n\t\t// Send response\n\t\tevq_sender->push_network(r, ip_port);\n\t\t\n\t\tif (r->code == R_100_TRYING) {\n\t\t\tresp_100_trying_sent = true;\n\t\t}\n\t}\n}\n\nvoid t_trans_server::process_final(t_response *r) {\n\tt_ip_port ip_port;\n\n\tt_transaction::process_final(r);\n\tr->get_destination(ip_port);\n\n\tif (ip_port.ipaddr == 0) {\n\t\t// The response cannot be sent.\n\t\tstate = TS_TERMINATED;\n\t\t// Report failure to TU\n\t\tevq_trans_layer->push_failure(FAIL_TRANSPORT, id);\n\t} else {\n\t\t// Send response\n\t\tevq_sender->push_network(r, ip_port);\n\t}\n}\n\nvoid t_trans_server::process_retransmission(void) {\n\t// nothing to do\n}\n\n// RFC 3261 17.2.3\n// NOTE: retransmission of an incoming INVITE for which a 2XX response\n//       has been sent already is checked by the TU.\n//       see dialog::is_invite_retrans\nbool t_trans_server::match(t_request *r, bool cancel) const {\n\tt_via &orig_top_via = request->hdr_via.via_list.front();\n\tt_via &recv_top_via = r->hdr_via.via_list.front();\n\n\tif (recv_top_via.rfc3261_compliant()) {\n\t\tif (orig_top_via.branch != recv_top_via.branch) return false;\n\t\tif (orig_top_via.host != recv_top_via.host) return false;\n\t\tif (orig_top_via.port != recv_top_via.port) return false;\n\n\t\tswitch(r->method) {\n\t\tcase ACK:\n\t\t\t// return (request->hdr_cseq.method == INVITE);\n\t\t\treturn (request->method == INVITE);\n\t\t\tbreak;\n\t\tcase CANCEL:\n\t\t\tif (!cancel) {\n\t\t\t\t// return (request->hdr_cseq.method ==\n\t\t\t\t//\t \t\tr->hdr_cseq.method);\n\t\t\t\treturn (request->method == r->method);\n\t\t\t}\n\n\t\t\t// The target of CANCEL cannot be a CANCEL request\n\t\t\t// return (request->hdr_cseq.method != CANCEL);\n\t\t\treturn (request->method != CANCEL);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// return (request->hdr_cseq.method ==\n\t\t\t//\t \t\tr->hdr_cseq.method);\n\t\t\treturn (request->method == r->method);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Matching rules for backward compatibiliy with RFC 2543\n\t// TODO: verify rules for matching via headers\n\tswitch (r->method) {\n\tcase INVITE:\n\t\treturn (request->method == INVITE &&\n\t\t\trequest->uri.sip_match(r->uri) &&\n\t\t\trequest->hdr_to.tag == r->hdr_to.tag &&\n\t\t\trequest->hdr_from.tag == r->hdr_from.tag &&\n\t\t\trequest->hdr_call_id.call_id ==\n\t\t\t\t\t r->hdr_call_id.call_id &&\n\t\t\trequest->hdr_cseq.seqnr == r->hdr_cseq.seqnr &&\n\t\t\torig_top_via.host == recv_top_via.host &&\n\t\t\torig_top_via.port == recv_top_via.port);\n\t\tbreak;\n\tcase ACK:\n\t\treturn (request->method == INVITE &&\n\t\t\trequest->uri.sip_match(r->uri) &&\n\t\t\trequest->hdr_from.tag == r->hdr_from.tag &&\n\t\t\trequest->hdr_call_id.call_id ==\n\t\t\t\t\t r->hdr_call_id.call_id &&\n\t\t\trequest->hdr_cseq.seqnr == r->hdr_cseq.seqnr &&\n\t\t\torig_top_via.host == recv_top_via.host &&\n\t\t\torig_top_via.port == recv_top_via.port &&\n\t\t\tfinal != NULL &&\n\t\t\tfinal->hdr_to.tag == r->hdr_to.tag);\n\t\tbreak;\n\tcase CANCEL:\n\t\tif (cancel) {\n\t\t\treturn (request->uri.sip_match(r->uri) &&\n\t\t\t\trequest->hdr_from.tag == r->hdr_from.tag &&\n\t\t\t\trequest->hdr_call_id.call_id ==\n\t\t\t\t\t r->hdr_call_id.call_id &&\n\t\t\t\trequest->hdr_cseq.seqnr ==\n\t\t\t\t\tr->hdr_cseq.seqnr &&\n\t\t\t\trequest->hdr_cseq.method != CANCEL &&\n\t\t\t\torig_top_via.host == recv_top_via.host &&\n\t\t\t\torig_top_via.port == recv_top_via.port);\n\t\t}\n\t\t// fall through\n\tdefault:\n\t\treturn (request->uri.sip_match(r->uri) &&\n\t\t\trequest->hdr_from.tag == r->hdr_from.tag &&\n\t\t\trequest->hdr_call_id.call_id ==\n\t\t\t\t\t r->hdr_call_id.call_id &&\n\t\t\trequest->hdr_cseq == r->hdr_cseq &&\n\t\t\torig_top_via.host == recv_top_via.host &&\n\t\t\torig_top_via.port == recv_top_via.port);\n\t\tbreak;\n\t}\n\n\t// Should not get here\n\treturn false;\n}\n\nbool t_trans_server::match(t_request *r) const {\n\treturn match(r, false);\n}\n\nbool t_trans_server::match_cancel(t_request *r) const {\n\tassert(r->method == CANCEL);\n\treturn match(r, true);\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17.2.1\n// Server INVITE transaction\n///////////////////////////////////////////////////////////\n\nvoid t_ts_invite::start_timer_G(void) {\n\ttimer_G = transaction_mgr->start_timer(duration_G, TIMER_G, id);\n\tduration_G = 2 * duration_G;\n\tif (duration_G > DURATION_T2) duration_G = DURATION_T2;\n}\n\nvoid t_ts_invite::start_timer_H(void) {\n\ttimer_H = transaction_mgr->start_timer(DURATION_H, TIMER_H, id);\n}\n\nvoid t_ts_invite::start_timer_I(void) {\n\t// RFC 17.2.1\n\t// Set timer I to T4 seconds for unreliable transports and to 0 for\n\t// reliable transports.\n\tif (request->src_ip_port.transport == \"udp\") {\n\t\ttimer_I = transaction_mgr->start_timer(DURATION_I, TIMER_I, id);\n\t} else {\n\t\ttimer_I = transaction_mgr->start_timer(0, TIMER_I, id);\n\t}\n}\n\nvoid t_ts_invite::stop_timer_G(void) {\n\tif (timer_G) {\n\t\ttransaction_mgr->stop_timer(timer_G);\n\t\ttimer_G = 0;\n\t}\n}\n\nvoid t_ts_invite::stop_timer_H(void) {\n\tif (timer_H) {\n\t\ttransaction_mgr->stop_timer(timer_H);\n\t\ttimer_H = 0;\n\t}\n}\n\nvoid t_ts_invite::stop_timer_I(void) {\n\tif (timer_I) {\n\t\ttransaction_mgr->stop_timer(timer_I);\n\t\ttimer_I = 0;\n\t}\n}\n\nt_ts_invite::t_ts_invite(t_request *r, unsigned short _tuid) :\n\tt_trans_server(r, _tuid)\n{\n\tassert(r->method == INVITE);\n\n\tstate = TS_PROCEEDING;\n\tack = NULL;\n\ttimer_G = 0;\n\ttimer_H = 0;\n\ttimer_I = 0;\n\tduration_G = DURATION_G;\n}\n\nt_ts_invite::~t_ts_invite() {\n\tif (ack != NULL) {\n\t\tMEMMAN_DELETE(ack);\n\t\tdelete ack;\n\t}\n\tstop_timer_G();\n\tstop_timer_H();\n\tstop_timer_I();\n}\n\nvoid t_ts_invite::process_provisional(t_response *r) {\n\tassert(r->is_provisional());\n\n\tswitch (state) {\n\tcase TS_PROCEEDING:\n\t\tt_trans_server::process_provisional(r);\n\t\tbreak;\n\tdefault:\n\t\t// TU should not send a provisional response\n\t\t// in other states.\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_invite::process_final(t_response *r) {\n\tassert(r->is_final());\n\n\tswitch (state) {\n\tcase TS_PROCEEDING:\n\t\tt_trans_server::process_final(r);\n\t\tif (r->is_success()) {\n\t\t\tstate = TS_TERMINATED;\n\t\t} else {\n\t\t\t// RFC 3261 17.2.1\n\t\t\t// Start timer G for unreliable transports.\n\t\t\tif (request->src_ip_port.transport == \"udp\") {\n\t\t\t\tstart_timer_G();\n\t\t\t}\n\t\t\t\n\t\t\t// RFC 3261 17.2.1\n\t\t\t// Start timer H for all transports\n\t\t\tstart_timer_H();\n\t\t\t\n\t\t\tstate = TS_COMPLETED;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// No final responses are expected anymore. Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_invite::process_retransmission(void) {\n\tt_ip_port ip_port;\n\n\tswitch (state) {\n\tcase TS_PROCEEDING:\n\t\t// Retransmit the latest provisional response (if present)\n\t\tt_trans_server::process_retransmission();\n\t\tif (provisional.size() > 0) {\n\t\t\tt_response *r = provisional.back();\n\t\t\tr->get_destination(ip_port);\n\t\t\tif (ip_port.ipaddr == 0) {\n\t\t\t\t// The response cannot be sent.\n\t\t\t\tstate = TS_TERMINATED;\n\t\t\t\t// Report failure to TU\n\t\t\t\tevq_trans_layer->push_failure(\n\t\t\t\t\t\tFAIL_TRANSPORT, id);\n\t\t\t} else {\n\t\t\t\t// Send response\n\t\t\t\tevq_sender->push_network(r, ip_port);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase TS_COMPLETED:\n\t\t// Retransmit the final response\n\t\tt_trans_server::process_retransmission();\n\t\tfinal->get_destination(ip_port);\n\t\tif (ip_port.ipaddr == 0) {\n\t\t\t// The response cannot be sent.\n\t\t\tstate = TS_TERMINATED;\n\t\t\t// Report failure to TU\n\t\t\tevq_trans_layer->push_failure(FAIL_TRANSPORT, id);\n\t\t} else {\n\t\t\t// Send response\n\t\t\tevq_sender->push_network(final, ip_port);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Retransmissions should not happen in other states.\n\t\t// Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_invite::timeout(t_sip_timer t) {\n\tt_ip_port ip_port;\n\n\tassert(t == TIMER_G || t == TIMER_I || t == TIMER_H);\n\n\tswitch (state) {\n\tcase TS_COMPLETED:\n\t\tswitch (t) {\n\t\tcase TIMER_G:\n\t\t\ttimer_G = 0;\n\n\t\t\t// Retransmit the final response\n\t\t\tfinal->get_destination(ip_port);\n\t\t\tif (ip_port.ipaddr == 0) {\n\t\t\t\t// The response cannot be sent.\n\t\t\t\tstop_timer_H();\n\t\t\t\tstate = TS_TERMINATED;\n\t\t\t\t// Report failure to TU\n\t\t\t\tevq_trans_layer->push_failure(\n\t\t\t\t\t\tFAIL_TRANSPORT, id);\n\t\t\t} else {\n\t\t\t\t// Send response\n\t\t\t\tevq_sender->push_network(final, ip_port);\n\t\t\t\tstart_timer_G();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TIMER_H:\n\t\t\ttimer_H = 0;\n\t\t\tstop_timer_G();\n\t\t\tstate = TS_TERMINATED;\n\t\t\t// Report timer expiry to TU\n\t\t\tevq_trans_layer->push_failure(FAIL_TIMEOUT, id);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// No other timers should be running. Discard.\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase TS_CONFIRMED:\n\t\tswitch (t) {\n\t\tcase TIMER_I:\n\t\t\ttimer_I = 0;\n\t\t\tstate = TS_TERMINATED;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// No other timers should be running. Discard.\n\t\t\tbreak;\n\t\t}\n\tdefault:\n\t\t// In other states no timers should be running.\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_invite::acknowledge(t_request *ack_request) {\n\tassert(ack_request->method == ACK);\n\n\tswitch (state) {\n\tcase TS_COMPLETED:\n\t\tack = (t_request *)ack_request->copy();\n\t\tstop_timer_G();\n\t\tstop_timer_H();\n\t\tstart_timer_I();\n\t\tstate = TS_CONFIRMED;\n\t\t// Report TU\n\t\t// ACK should not be reported to TU for non-2xx\n\t\t// evq_trans_layer->push_user(ack_request, tuid, id);\n\t\tbreak;\n\tdefault:\n\t\t// ACK is not expected in other states. Discard;\n\t\tbreak;\n\t}\n}\n\n///////////////////////////////////////////////////////////\n// RFC 3261 17.2.2\n// Server non-INVITE transaction\n///////////////////////////////////////////////////////////\n\nvoid t_ts_non_invite::start_timer_J(void) {\n\t// RFC 3261 17.2.2\n\t// For unreliable transports set timer J to 64*T1, for reliable\n\t// transports set it to 0.\n\tif (request->src_ip_port.transport == \"udp\") {\n\t\ttimer_J = transaction_mgr->start_timer(DURATION_J, TIMER_J, id);\n\t} else {\n\t\ttimer_J = transaction_mgr->start_timer(0, TIMER_J, id);\n\t}\n}\n\nvoid t_ts_non_invite::stop_timer_J(void) {\n\tif (timer_J) {\n\t\ttransaction_mgr->stop_timer(timer_J);\n\t\ttimer_J = 0;\n\t}\n}\n\nt_ts_non_invite::t_ts_non_invite(t_request *r, unsigned short _tuid) :\n\tt_trans_server(r, _tuid)\n{\n\tassert(r->method != INVITE);\n\ttimer_J = 0;\n\tstate = TS_TRYING;\n}\n\nt_ts_non_invite::~t_ts_non_invite() {\n\tstop_timer_J();\n}\n\nvoid t_ts_non_invite::process_provisional(t_response *r) {\n\tassert(r->is_provisional());\n\n\tswitch (state) {\n\tcase TS_TRYING:\n\tcase TS_PROCEEDING:\n\t\tt_trans_server::process_provisional(r);\n\t\tstate = TS_PROCEEDING;\n\t\tbreak;\n\tdefault:\n\t\t// TU should not send a provisional response\n\t\t// in other states.\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_non_invite::process_final(t_response *r) {\n\tassert(r->is_final());\n\n\tswitch (state) {\n\tcase TS_TRYING:\n\tcase TS_PROCEEDING:\n\t\tt_trans_server::process_final(r);\n\t\tstart_timer_J();\n\t\tstate = TS_COMPLETED;\n\t\tbreak;\n\tdefault:\n\t\t// No final responses are expected anymore. Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_non_invite::process_retransmission(void) {\n\tt_ip_port\tip_port;\n\tt_response \t*r;\n\n\tswitch (state) {\n\tcase TS_PROCEEDING:\n\t\t// Retransmit the latest provisional response\n\t\tt_trans_server::process_retransmission();\n\t\tr = provisional.back();\n\t\tr->get_destination(ip_port);\n\t\tif (ip_port.ipaddr == 0) {\n\t\t\t// The response cannot be sent.\n\t\t\tstate = TS_TERMINATED;\n\t\t\t// Report failure to TU\n\t\t\tevq_trans_layer->push_failure(FAIL_TRANSPORT, id);\n\t\t} else {\n\t\t\t// Send response\n\t\t\tevq_sender->push_network(r, ip_port);\n\t\t}\n\t\tbreak;\n\tcase TS_COMPLETED:\n\t\t// Retransmit the final response\n\t\tt_trans_server::process_retransmission();\n\t\tfinal->get_destination(ip_port);\n\t\tif (ip_port.ipaddr == 0) {\n\t\t\t// The response cannot be sent.\n\t\t\tstop_timer_J();\n\t\t\tstate = TS_TERMINATED;\n\t\t\t// Report failure to TU\n\t\t\tevq_trans_layer->push_failure(FAIL_TRANSPORT, id);\n\t\t} else {\n\t\t\t// Send response\n\t\t\tevq_sender->push_network(final, ip_port);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t// Retransmissions should not happen in other states.\n\t\t// Discard.\n\t\tbreak;\n\t}\n}\n\nvoid t_ts_non_invite::timeout(t_sip_timer t) {\n\tassert (t == TIMER_J);\n\n\tswitch (state) {\n\tcase TS_COMPLETED:\n\t\tswitch (t) {\n\t\tcase TIMER_J:\n\t\t\ttimer_J = 0;\n\t\t\tstate = TS_TERMINATED;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\tdefault:\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "src/transaction.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TRANSACTION_H\n#define _TRANSACTION_H\n\n#include <string>\n#include \"protocol.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"sockets/socket.h\"\n#include \"threads/mutex.h\"\n\nusing namespace std;\n\ntypedef unsigned short\tt_tid;\n\n/////////////////////////////////////////////////////////////\n// Transaction state (see RFC 3261 17)\n/////////////////////////////////////////////////////////////\nenum t_trans_state {\n\tTS_NULL,\t// non-state used for initialization\n\tTS_CALLING,\n\tTS_TRYING,\n\tTS_PROCEEDING,\n\tTS_COMPLETED,\n\tTS_CONFIRMED,\n\tTS_TERMINATED,\n};\n\nstring trans_state2str(t_trans_state s);\n\n/////////////////////////////////////////////////////////////\n// General transaction\n/////////////////////////////////////////////////////////////\n//\n// Concurrent creation of transactions is not allowed. If this\n// is needed then updates to static members need to be\n// synchronized with a mutex.\n// All transactions are created by the transaction manager. This\n// should not be changed as transactions start timers and all timers\n// must be started from a single thread.\n\nclass t_transaction {\nprivate:\n\tstatic t_mutex\t\tmtx_class; // protect static members\n\tstatic t_tid\t\tnext_id; // next id to be issued\n\nprotected:\n\tt_tid\t\t\tid; \t// transaction id\n\tunsigned short\t\ttuid;\t// TU id\n\tt_trans_state\t\tstate;\n\tstring\t\t\tto_tag;\t// tag for to-header\n\npublic:\n\t// Request that created the transaction\n\tt_request\t\t*request;\n\n\tt_tid get_id(void) const;\n\n\t// Provisional responses in order of arrival/sending\n\tlist<t_response *>\tprovisional;\n\n\t// Final response for the transaction\n\tt_response\t\t*final;\n\n\t// The transaction will keep a copy of the request\n\tt_transaction(t_request *r, unsigned short _tuid);\n\n\t// All request and response pointers contained by the\n\t// request will be deleted.\n\tvirtual ~t_transaction();\n\n\t// Process a provisional repsonse\n\t// Transaction will keep a copy of the response\n\tvirtual void process_provisional(t_response *r);\n\n\t// Process a final response\n\t// Transaction will keep a copy of the response\n\tvirtual void process_final(t_response *r);\n\n\t// Process a response\n\tvirtual void process_response(t_response *r);\n\n\t// Process timer expiry\n\tvirtual void timeout(t_sip_timer t) = 0;\n\n\t// Get state of the transaction\n\tt_trans_state get_state(void) const;\n\n\t// Set TU ID\n\tvoid set_tuid(unsigned short _tuid);\n\n\t// Get type of request\n\tt_method get_method(void) const;\n\n\t// Get tag for to-header\n\tstring get_to_tag(void);\n\n\t// Create response according to general rules\n\tt_response *create_response(int code, string reason = \"\");\n};\n\n/////////////////////////////////////////////////////////////\n// Client transaction\n/////////////////////////////////////////////////////////////\nclass t_trans_client : public t_transaction {\nprotected:\n\t/** Destination for request. */\n\tt_ip_port\tdst_ip_port;\n\npublic:\n\t/**\n\t * Create transaction and send request to destination.\n\t * @param r [in] Request creating the transaction.\n\t * @param ip_port [in] Destination of the request.\n\t * @param _tuid [in] Transaction user id assigned to this transaction.\n\t */\n\tt_trans_client(t_request *r, const t_ip_port &ip_port,\n\t\tunsigned short _tuid);\n\n\t/**\n\t * Match a response with a transaction.\n\t * @param r [in] The response to match.\n\t * @return true if the response matches the transaction.\n\t */\n\tbool match(t_response *r) const;\n\t\n\t/**\n\t * @param icmp [in] ICMP message to match.\n\t * @return true if the ICMP error matches the transaction\n\t */\n\tbool match(const t_icmp_msg &icmp) const;\n\t\n\t/**\n\t * Match transaction with a branch and CSeq method value.\n\t * @param branch [in] Branch to match.\n\t * @param cseq_method [in] CSeq method to match.\n\t * @return true if transaction matches, otherwise false.\n\t */\n\tbool match(const string &branch, const t_method &cseq_method) const;\n\t\n\tvirtual void process_provisional(t_response *r);\n\t\n\t/** \n\t * Process ICMP errors.\n\t * @param icmp [in] ICMP message.\n\t */\n\tvirtual void process_icmp(const t_icmp_msg &icmp) = 0;\n\t\n\t/**\n\t * Process failures.\n\t * @param failure [in] Type of failure.\n\t */\n\tvirtual void process_failure(t_failure failure) = 0;\n\n\t/**\n\t * Abort a transaction.\n\t * This will send a 408 response internally to finish the transaction.\n\t */\n\tvirtual void abort(void) = 0;\n};\n\n/////////////////////////////////////////////////////////////\n// Client INVITE transaction\n/////////////////////////////////////////////////////////////\nclass t_tc_invite : public t_trans_client {\nprivate:\n\t// Timers\n\tunsigned short\ttimer_A;\n\tunsigned short\ttimer_B;\n\tunsigned short\ttimer_D;\n\n\t// Duration of next timer A in msec\n\tlong\tduration_A;\n\n\tvoid start_timer_A(void);\n\tvoid start_timer_B(void);\n\tvoid start_timer_D(void);\n\tvoid stop_timer_A(void);\n\tvoid stop_timer_B(void);\n\tvoid stop_timer_D(void);\n\n\npublic:\n\tt_request\t\t*ack;\t// ACK request\n\n\t// Create transaction and send request to destination\n\t// Start timer A and timer B\n\tt_tc_invite(t_request *r, const t_ip_port &ip_port,\n\t\tunsigned short _tuid);\n\n\tvirtual ~t_tc_invite();\n\n\t// Process a provisional repsonse\n\t// Stop timer A\n\tvoid process_provisional(t_response *r);\n\n\t// Process a final response\n\t// Stop timer B.\n\t// Start timer D (for non-2xx final).\n\tvoid process_final(t_response *r);\n\t\n\tvoid process_icmp(const t_icmp_msg &icmp);\n\t\n\tvoid process_failure(t_failure failure);\n\n\tvoid timeout(t_sip_timer t);\n\n\tvoid abort(void);\n};\n\n/////////////////////////////////////////////////////////////\n// Client non-INVITE transaction\n/////////////////////////////////////////////////////////////\nclass t_tc_non_invite : public t_trans_client {\nprivate:\n\t// Timers\n\tunsigned short\ttimer_E;\n\tunsigned short\ttimer_F;\n\tunsigned short\ttimer_K;\n\n\t// Duration of next timer E in msec\n\tlong\tduration_E;\n\n\tvoid start_timer_E(void);\n\tvoid start_timer_F(void);\n\tvoid start_timer_K(void);\n\tvoid stop_timer_E(void);\n\tvoid stop_timer_F(void);\n\tvoid stop_timer_K(void);\n\npublic:\n\t// Create transaction and send request to destination\n\t// Stop timer E and timer F\n\tt_tc_non_invite(t_request *r, const t_ip_port &ip_port,\n\t\tunsigned short _tuid);\n\n\tvirtual ~t_tc_non_invite();\n\n\t// Process a provisional repsonse\n\tvoid process_provisional(t_response *r);\n\n\t// Process final response\n\t// Stop timer E and F. Start timer K.\n\tvoid process_final(t_response *r);\n\t\n\tvoid process_icmp(const t_icmp_msg &icmp);\n\t\n\tvoid process_failure(t_failure failure);\n\n\tvoid timeout(t_sip_timer t);\n\n\tvoid abort(void);\n};\n\n/////////////////////////////////////////////////////////////\n// Server transaction\n/////////////////////////////////////////////////////////////\nclass t_trans_server : public t_transaction {\nprivate:\n\t// Match a the transaction to a request. Argument\n\t// If cancel==true then the target for a CANCEL\n\t// is matched.\n\t// If cancel==false then the request itself is matched,\n\t// eg. retransmission or ACK to INVITE matching\n\tbool match(t_request *r, bool cancel) const;\n\t\n\t// Indicates if a 100 Trying has already been sent.\n\t// A 100 Trying should only be sent once.\n\t// The reason for sending a 100 Trying is to indicate that\n\t// the request has been received but that processing will\n\t// take some time.\n\t// Based on the tasks to perform several parts of the transaction\n\t// user can decide independently to send a 100 Trying. This\n\t// flag assures that only one 100 Trying will be sent out\n\t// though.\n\tbool resp_100_trying_sent;\n\npublic:\n\tt_trans_server(t_request *r, unsigned short _tuid);\n\n\t// Process a provisional repsonse\n\t// Send provisional response\n\tvoid process_provisional(t_response *r);\n\n\t// Process a final response\n\t// Send the final response\n\tvoid process_final(t_response *r);\n\n\t// Process a received retransmission of the request\n\tvirtual void process_retransmission(void);\n\n\t// Returns true if request matches transaction\n\tbool match(t_request *r) const;\n\n\t// Returns true if the transaction is the target of CANCEL\n\tbool match_cancel(t_request *r) const;\n};\n\n/////////////////////////////////////////////////////////////\n// Server INIVITE transaction\n/////////////////////////////////////////////////////////////\nclass t_ts_invite : public t_trans_server {\nprivate:\n\t// Timers\n\tunsigned short\ttimer_G;\n\tunsigned short\ttimer_H;\n\tunsigned short\ttimer_I;\n\n\t// Duration of next timer G in msec\n\tlong\tduration_G;\n\n\tvoid start_timer_G(void);\n\tvoid start_timer_H(void);\n\tvoid start_timer_I(void);\n\tvoid stop_timer_G(void);\n\tvoid stop_timer_H(void);\n\tvoid stop_timer_I(void);\n\npublic:\n\tt_request\t\t*ack;\t// ACK request\n\n\tt_ts_invite(t_request *r, unsigned short _tuid);\n\tvirtual ~t_ts_invite();\n\n\tvoid process_provisional(t_response *r);\n\tvoid process_final(t_response *r);\n\tvoid process_retransmission(void);\n\tvoid timeout(t_sip_timer t);\n\n\t// Transaction will keep a copy of the ACK.\n\tvoid acknowledge(t_request *ack_request);\n};\n\n/////////////////////////////////////////////////////////////\n// Server non-INVITE transaction\n/////////////////////////////////////////////////////////////\nclass t_ts_non_invite : public t_trans_server {\nprivate:\n\t// Timers\n\tunsigned short\ttimer_J;\n\n\tvoid start_timer_J(void);\n\tvoid stop_timer_J(void);\n\npublic:\n\tt_ts_non_invite(t_request *r, unsigned short _tuid);\n\tvirtual ~t_ts_non_invite();\n\n\tvoid process_provisional(t_response *r);\n\tvoid process_final(t_response *r);\n\tvoid process_retransmission(void);\n\tvoid timeout(t_sip_timer t);\n};\n\n#endif\n"
  },
  {
    "path": "src/transaction_layer.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include \"events.h\"\n#include \"transaction_layer.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nextern t_event_queue\t*evq_trans_mgr;\nextern t_event_queue\t*evq_trans_layer;\nextern bool\t\tend_app;\n\nvoid t_transaction_layer::recvd_response(t_response *r, t_tuid tuid,\n\t\tt_tid tid)\n{\n\tlock();\n\n\tswitch(r->get_class()) {\n\tcase R_1XX:\n\t\trecvd_provisional(r, tuid, tid);\n\t\tbreak;\n\tcase R_2XX:\n\t\trecvd_success(r, tuid, tid);\n\t\tbreak;\n\tcase R_3XX:\n\t\trecvd_redirect(r, tuid, tid);\n\t\tbreak;\n\tcase R_4XX:\n\t\trecvd_client_error(r, tuid, tid);\n\t\tbreak;\n\tcase R_5XX:\n\t\trecvd_server_error(r, tuid, tid);\n\t\tbreak;\n\tcase R_6XX:\n\t\trecvd_global_error(r, tuid, tid);\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n\t\n\tpost_process_response(r, tuid, tid);\n\n\tunlock();\n}\n\nvoid t_transaction_layer::recvd_request(t_request *r, t_tid tid,\n\t\tt_tid tid_cancel_target)\n{\n\tbool fatal;\n\tstring reason;\n\tt_response *resp;\n\n\tlock();\n\n\t// Return a 400 response if the SIP headers are wrong\n\tif (!r->is_valid(fatal, reason)) {\n\t\tresp = r->create_response(R_400_BAD_REQUEST, reason);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tunlock();\n\t\treturn;\n\t}\n\n\t// Return a 400 response if the SIP body contained a parse error\n\tif (r->body && r->body->invalid) {\n\t\tresp = r->create_response(R_400_BAD_REQUEST, \"Invalid SIP body.\");\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tunlock();\n\t\treturn;\n\t}\n\t\n\t// If a message exceeded the maximum message size, than the body\n\t// is not parsed by the listener.\n\tif (r->hdr_content_length.is_populated() &&\n\t    r->hdr_content_length.length > 0 &&\n\t    !r->body)\n\t{\n\t\tresp = r->create_response(R_513_MESSAGE_TOO_LARGE);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tunlock();\n\t\treturn;\n\t}\n\t\n\t// RFC 3261 8.2.3\n\t// Return a 415 response if content encoding is not supported\n\tif (r->body && r->hdr_content_encoding.is_populated()) {\n\t\tfor (list<t_coding>::iterator it = r->hdr_content_encoding.coding_list.begin();\n\t\t     it != r->hdr_content_encoding.coding_list.end(); ++it)\n\t\t{\n\t\t\tif (!CONTENT_ENCODING_SUPPORTED(it->content_coding)) {\n\t\t\t\tresp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);\n\t\t\t\tSET_HDR_ACCEPT_ENCODING(resp->hdr_accept_encoding);\n\t\t\t\tsend_response(resp, 0, tid);\n\t\t\t\tMEMMAN_DELETE(resp);\n\t\t\t\tdelete resp;\n\t\t\t\tunlock();\n\t\t\t\treturn;\n\t\t\t}\t\t\n\t\t}\n\t}\n\n\t// Check if URI scheme is supported\n\tif (r->uri.get_scheme() != \"sip\") {\n\t\tresp = r->create_response(R_416_UNSUPPORTED_URI_SCHEME);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tunlock();\n\t\treturn;\n\t}\n\n\tswitch(r->method) {\n\tcase INVITE:\n\t\trecvd_invite(r, tid);\n\t\tbreak;\n\tcase ACK:\n\t\trecvd_ack(r, tid);\n\t\tbreak;\n\tcase CANCEL:\n\t\trecvd_cancel(r, tid, tid_cancel_target);\n\t\tbreak;\n\tcase BYE:\n\t\trecvd_bye(r, tid);\n\t\tbreak;\n\tcase OPTIONS:\n\t\trecvd_options(r, tid);\n\t\tbreak;\n\tcase REGISTER:\n\t\trecvd_register(r, tid);\n\t\tbreak;\n\tcase PRACK:\n\t\trecvd_prack(r, tid);\n\t\tbreak;\n\tcase SUBSCRIBE:\n\t\trecvd_subscribe(r, tid);\n\t\tbreak;\n\tcase NOTIFY:\n\t\trecvd_notify(r, tid);\n\t\tbreak;\n\tcase REFER:\n\t\trecvd_refer(r, tid);\n\t\tbreak;\n\tcase INFO:\n\t\trecvd_info(r, tid);\n\t\tbreak;\n\tcase MESSAGE:\n\t\trecvd_message(r, tid);\n\t\tbreak;\n\tdefault:\n\t\tresp = r->create_response(R_501_NOT_IMPLEMENTED);\n\t\tsend_response(resp, 0, tid);\n\t\tMEMMAN_DELETE(resp);\n\t\tdelete resp;\n\t\tbreak;\n\t}\n\t\n\tpost_process_request(r, tid, tid_cancel_target);\n\n\tunlock();\n}\n\nvoid t_transaction_layer::recvd_async_response(t_event_async_response *event) {\n\tlock();\n\t\n\tswitch (event->get_response_type()) {\n\tcase t_event_async_response::RESP_REFER_PERMISSION:\n\t\trecvd_refer_permission(event->get_bool_response());\n\t\tbreak;\n\tdefault:\n\t\t// Ignore other responses\n\t\tbreak;\n\t}\n\t\n\tunlock();\n}\n\nvoid t_transaction_layer::send_request(t_user *user_config, t_request *r, t_tuid tuid) {\n\tevq_trans_mgr->push_user(user_config, (t_sip_message *)r, tuid, 0);\n}\n\nvoid t_transaction_layer::send_request(t_user *user_config, StunMessage *r, t_tuid tuid) {\n\t// The transaction manager will determine the destination IP and port,\n\t// so they can be left to zero in the event.\n\tevq_trans_mgr->push_stun_request(user_config, r, TYPE_STUN_SIP, tuid, 0, 0, 0);\n}\n\nvoid t_transaction_layer::send_response(t_response *r, t_tuid tuid,\n\t\tt_tid tid)\n{\n\tevq_trans_mgr->push_user((t_sip_message *)r, tuid, tid);\n}\n\nvoid t_transaction_layer::run(void) {\n\tt_event\t\t\t*event;\n\tt_event_user\t\t*ev_user;\n\tt_event_timeout\t\t*ev_timeout;\n\tt_event_failure\t\t*ev_failure;\n\tt_event_stun_response\t*ev_stun_resp;\n\tt_event_async_response\t*ev_async_resp;\n\tt_event_broken_connection *ev_broken_connection;\n\tt_sip_message\t\t*msg;\n\tStunMessage\t\t*stun_msg;\n\tt_tid\t\t\ttid;\n\tt_tid\t\t\ttid_cancel;\n\tt_tuid\t\t\ttuid;\n\n\tbool quit = false;\n\twhile (!quit) {\n\t\tevent = evq_trans_layer->pop();\n\n\t\tswitch (event->get_type()) {\n\t\tcase EV_USER:\n\t\t\tev_user = (t_event_user *)event;\n\t\t\ttid = ev_user->get_tid();\n\t\t\ttuid = ev_user->get_tuid();\n\t\t\ttid_cancel = ev_user->get_tid_cancel_target();\n\t\t\tmsg = ev_user->get_msg();\n\n\t\t\tswitch(msg->get_type()) {\n\t\t\tcase MSG_REQUEST:\n\t\t\t\trecvd_request((t_request *)msg, tid,\n\t\t\t\t\t\ttid_cancel);\n\t\t\t\tbreak;\n\t\t\tcase MSG_RESPONSE:\n\t\t\t\trecvd_response((t_response *)msg, tuid, tid);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tbreak;\n\t\tcase EV_TIMEOUT:\n\t\t\tev_timeout = dynamic_cast<t_event_timeout *>(event);\n\t\t\thandle_event_timeout(ev_timeout);\n\t\t\tbreak;\n\t\tcase EV_FAILURE:\n\t\t\tev_failure = (t_event_failure *)event;\n\t\t\ttid = ev_failure->get_tid();\n\t\t\tlock();\n\t\t\tfailure(ev_failure->get_failure(), tid);\n\t\t\tunlock();\n\t\t\tbreak;\n\t\tcase EV_STUN_RESPONSE:\n\t\t\tev_stun_resp = (t_event_stun_response *)event;\n\t\t\ttid = ev_stun_resp->get_tid();\n\t\t\ttuid = ev_stun_resp->get_tuid();\n\t\t\tstun_msg = ev_stun_resp->get_msg();\n\t\t\trecvd_stun_resp(stun_msg, tuid, tid);\n\t\t\tbreak;\n\t\tcase EV_ASYNC_RESPONSE:\n\t\t\tev_async_resp = dynamic_cast<t_event_async_response *>(event);\n\t\t\trecvd_async_response(ev_async_resp);\n\t\t\tbreak;\n\t\tcase EV_BROKEN_CONNECTION:\n\t\t\tev_broken_connection = dynamic_cast<t_event_broken_connection *>(event);\n\t\t\thandle_broken_connection(ev_broken_connection);\n\t\t\tbreak;\n\t\tcase EV_QUIT:\n\t\t\tquit = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// other types of event are not expected\n\t\t\tassert(false);\n\t\t\tbreak;\n\t\t}\n\n\t\tMEMMAN_DELETE(event);\n\t\tdelete event;\n\t}\n}\n\nvoid t_transaction_layer::lock(void) const {\n\t// Prohibited threads may not lock the transaction layer\n\tassert(!is_prohibited_thread());\n\n\t// The user interface and transaction layer threads both call\n\t// functions on the transaction layer. By locking the UI mutex\n\t// first, a deadlock can never occur as the UI also takes the\n\t// UI lock first and then the transaction layer lock.\n\t// During shutdown of Twinkle the GUI has exited already and\n\t// a lock on an exited QApplication causes a segmentation fault.\n\t// Therefore the lock on the UI should not be taken during shutdown.\n\tif (!end_app) ui->lock();\n\ttl_mutex.lock();\n}\n\nvoid t_transaction_layer::unlock(void) const {\n\ttl_mutex.unlock();\n\tif (!end_app) ui->unlock();\n}\n"
  },
  {
    "path": "src/transaction_layer.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TRANSACTION_LAYER_H\n#define _TRANSACTION_LAYER_H\n\n#include \"events.h\"\n#include \"prohibit_thread.h\"\n#include \"transaction.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"stun/stun.h\"\n#include \"threads/mutex.h\"\n\ntypedef unsigned short\tt_tuid;\n\nclass t_transaction_layer : public i_prohibit_thread {\nprivate:\n\t// Mutex to guarantee that only 1 thread at a time is\n\t// accessing the transaction layer.\n\tmutable t_recursive_mutex\ttl_mutex;\n\n\tvoid recvd_response(t_response *r, t_tuid tuid, t_tid tid);\n\tvoid recvd_request(t_request *r, t_tid tid, t_tid tid_cancel_target);\n\tvoid recvd_async_response(t_event_async_response *event);\n\nprotected:\n\t// Client event handlers\n\t// After returning from this function, the response pointer\n\t// will be deleted.\n\tvirtual void recvd_provisional(t_response *r, t_tuid tuid,\n\t\t\tt_tid tid) = 0;\n\tvirtual void recvd_success(t_response *r, t_tuid tuid, t_tid tid) = 0;\n\tvirtual void recvd_redirect(t_response *r, t_tuid tuid,\n\t\t\tt_tid tid) = 0;\n\tvirtual void recvd_client_error(t_response *r, t_tuid tuid,\n\t\t\tt_tid tid) = 0;\n\tvirtual void recvd_server_error(t_response *r, t_tuid tuid,\n\t\t\tt_tid tid) = 0;\n\tvirtual void recvd_global_error(t_response *r, t_tuid tuid,\n\t\t\tt_tid tid) = 0;\n\t\t\t\n\t// General post processing for all responses\n\tvirtual void post_process_response(t_response *r, t_tuid tuid,\n\t\t\tt_tid tid) = 0;\n\n\t// Server event handlers\n\t// After returning from this function, the request pointer\n\t// will be deleted.\n\tvirtual void recvd_invite(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_ack(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_cancel(t_request *r, t_tid cancel_tid,\n\t\t\t\tt_tid target_tid) = 0;\n\tvirtual void recvd_bye(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_options(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_register(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_prack(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_subscribe(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_notify(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_refer(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_info(t_request *r, t_tid tid) = 0;\n\tvirtual void recvd_message(t_request *r, t_tid tid) = 0;\n\t\n\t// General post processing for all requests\n\tvirtual void post_process_request(t_request *r, t_tid cancel_tid,\n\t\t\t\tt_tid target_tid) = 0;\n\n\t// The transaction failed and is aborted\n\tvirtual void failure(t_failure failure, t_tid tid) = 0;\n\t\n\t// STUN event handler\n\tvirtual void recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid) = 0;\n\t\n\t// The user has granted or rejected an incoming REFER request.\n\tvirtual void recvd_refer_permission(bool permission) = 0;\n\t\n\t/**\n\t * Handle timeout event.\n\t * @param e [in] Timeout event.\n\t */\n\tvirtual void handle_event_timeout(t_event_timeout *e) = 0;\n\t\n\t/**\n\t * Handle broken connection event.\n\t * @param e [in] Broken connection event.\n\t */\n\tvirtual void handle_broken_connection(t_event_broken_connection *e) = 0;\n\npublic:\n\tvirtual ~t_transaction_layer() {};\n\n\t// Client primitives\n\tvoid send_request(t_user *user_config, t_request *r, t_tuid tuid);\n\tvoid send_request(t_user *user_config, StunMessage *r, t_tuid tuid);\n\n\t// Server primitives\n\tvoid send_response(t_response *r, t_tuid tuid, t_tid tid);\n\n\t// Main loop\n\tvoid run(void);\n\n\t// Lock and unlocking methods for dedicated access to the\n\t// transaction layer.\n\tvoid lock(void) const;\n\tvoid unlock(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/transaction_mgr.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <assert.h>\n#include <iostream>\n#include <signal.h>\n#include \"log.h\"\n#include \"transaction_mgr.h\"\n#include \"sockets/url.h\"\n#include \"util.h\"\n#include \"audits/memman.h\"\n\nextern t_event_queue\t\t*evq_trans_mgr;\nextern t_event_queue\t\t*evq_trans_layer;\nextern t_event_queue\t\t*evq_timekeeper;\nextern t_transaction_mgr\t*transaction_mgr;\n\nt_trans_client *t_transaction_mgr::find_trans_client(t_response *r) const {\n\tmap<t_tid, t_trans_client *>::const_iterator i;\n\n\tfor (i = map_trans_client.begin(); i != map_trans_client.end(); ++i)\n\t{\n\t\tif (i->second->match(r)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_trans_client *t_transaction_mgr::find_trans_client(t_tid tid) const {\n\tmap<t_tid, t_trans_client *>::const_iterator i;\n\n\ti = map_trans_client.find(tid);\n\tif (i == map_trans_client.end()) return NULL;\n\treturn i->second;\n}\n\nt_trans_client *t_transaction_mgr::find_trans_client(const string &branch, const t_method &cseq_method) const {\n\tmap<t_tid, t_trans_client *>::const_iterator i;\n\n\tfor (i = map_trans_client.begin(); i != map_trans_client.end(); ++i)\n\t{\n\t\tif (i->second->match(branch, cseq_method)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_trans_client *t_transaction_mgr::find_trans_client(const t_icmp_msg &icmp) const {\n\tmap<t_tid, t_trans_client *>::const_iterator i;\n\n\tfor (i = map_trans_client.begin(); i != map_trans_client.end(); ++i)\n\t{\n\t\tif (i->second->match(icmp)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_trans_server *t_transaction_mgr::find_trans_server(t_request *r) const {\n\tmap<t_tid, t_trans_server *>::const_iterator i;\n\n\tfor (i = map_trans_server.begin(); i != map_trans_server.end();\n\t\t\ti++)\n\t{\n\t\tif (i->second->match(r)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_trans_server *t_transaction_mgr::find_trans_server(t_tid tid) const {\n\tmap<t_tid, t_trans_server *>::const_iterator i;\n\n\ti = map_trans_server.find(tid);\n\tif (i == map_trans_server.end()) return NULL;\n\treturn i->second;\n}\n\nt_stun_transaction *t_transaction_mgr::find_stun_trans(StunMessage *r) const {\n\tmap<t_tid, t_stun_transaction *>::const_iterator i;\n\n\tfor (i = map_stun_trans.begin(); i != map_stun_trans.end(); ++i)\n\t{\n\t\tif (i->second->match(r)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_stun_transaction *t_transaction_mgr::find_stun_trans(t_tid tid) const {\n\tmap<t_tid, t_stun_transaction *>::const_iterator i;\n\n\ti = map_stun_trans.find(tid);\n\tif (i == map_stun_trans.end()) return NULL;\n\treturn i->second;\n}\n\nt_stun_transaction *t_transaction_mgr::find_stun_trans(const t_icmp_msg &icmp) const {\n\tmap<t_tid, t_stun_transaction *>::const_iterator i;\n\n\tfor (i = map_stun_trans.begin(); i != map_stun_trans.end(); ++i)\n\t{\n\t\tif (i->second->match(icmp)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_trans_server *t_transaction_mgr::find_cancel_target(t_request *r) const {\n\tmap<t_tid, t_trans_server *>::const_iterator i;\n\n\tfor (i = map_trans_server.begin(); i != map_trans_server.end(); ++i)\n\t{\n\t\tif (i->second->match_cancel(r)) return i->second;\n\t}\n\n\treturn NULL;\n}\n\nt_tc_invite *t_transaction_mgr::create_tc_invite(t_user *user_config, t_request *r,\n\t\tunsigned short tuid)\n{\n\tt_ip_port\tip_port;\n\n\tr->get_destination(ip_port, *user_config);\n\tif (ip_port.ipaddr == 0 || ip_port.port == 0) return NULL;\n\n\tt_tc_invite *t = new t_tc_invite(r, ip_port, tuid);\n\tMEMMAN_NEW(t);\n\tmap_trans_client[t->get_id()] = (t_trans_client *)t;\n\treturn t;\n}\n\nt_tc_non_invite *t_transaction_mgr::create_tc_non_invite(t_user *user_config, t_request *r,\n\t\tunsigned short tuid)\n{\n\tt_ip_port\tip_port;\n\n\tr->get_destination(ip_port, *user_config);\n\tif (ip_port.ipaddr == 0 || ip_port.port == 0) return NULL;\n\n\tt_tc_non_invite *t = new t_tc_non_invite(r, ip_port, tuid);\n\tMEMMAN_NEW(t);\n\tmap_trans_client[t->get_id()] = (t_trans_client *)t;\n\treturn t;\n}\n\nt_ts_invite *t_transaction_mgr::create_ts_invite(t_request *r) {\n\tt_ts_invite *t = new t_ts_invite(r, 0);\n\tMEMMAN_NEW(t);\n\tmap_trans_server[t->get_id()] = (t_trans_server *)t;\n\treturn t;\n}\n\nt_ts_non_invite *t_transaction_mgr::create_ts_non_invite(t_request *r) {\n\tt_ts_non_invite *t = new t_ts_non_invite(r, 0);\n\tMEMMAN_NEW(t);\n\tmap_trans_server[t->get_id()] = (t_trans_server *)t;\n\treturn t;\n}\n\nt_sip_stun_trans *t_transaction_mgr::create_sip_stun_trans(t_user *user_config, StunMessage *r,\n\t\tunsigned short tuid)\n{\n\tlist<t_ip_port> destinations = \n\t\tuser_config->get_stun_server().get_h_ip_srv(\"udp\");\n\tif (destinations.empty()) return NULL;\n\t\t\n\tt_sip_stun_trans *t = new t_sip_stun_trans(user_config, r, tuid, destinations);\n\tMEMMAN_NEW(t);\n\tmap_stun_trans[t->get_id()] = (t_stun_transaction *)t;\n\treturn t;\n}\n\nt_media_stun_trans *t_transaction_mgr::create_media_stun_trans(t_user *user_config, \n\t\tStunMessage *r, unsigned short tuid, unsigned short src_port)\n{\n\tlist<t_ip_port> destinations = \n\t\tuser_config->get_stun_server().get_h_ip_srv(\"udp\");\n\tif (destinations.empty()) return NULL;\n\t\n\tt_media_stun_trans *t = new t_media_stun_trans(user_config, r, tuid,\n\t\tdestinations, src_port);\n\tMEMMAN_NEW(t);\n\tmap_stun_trans[t->get_id()] = (t_stun_transaction *)t;\n\treturn t;\n}\n\n\nvoid t_transaction_mgr::delete_trans_client(t_trans_client *tc) {\n\tmap_trans_client.erase(tc->get_id());\n\tMEMMAN_DELETE(tc);\n\tdelete tc;\n}\n\nvoid t_transaction_mgr::delete_trans_server(t_trans_server *ts) {\n\tmap_trans_server.erase(ts->get_id());\n\tMEMMAN_DELETE(ts);\n\tdelete ts;\n}\n\nvoid t_transaction_mgr::delete_stun_trans(t_stun_transaction *st) {\n\tmap_stun_trans.erase(st->get_id());\n\tMEMMAN_DELETE(st);\n\tdelete st;\n}\n\nt_transaction_mgr::~t_transaction_mgr() {\n\tlog_file->write_header(\"t_transaction_mgr::~t_transaction_mgr\",\n\t\tLOG_NORMAL, LOG_INFO);\n\tlog_file->write_raw(\"Clean up transaction manager.\\n\");\n\n\tmap<t_tid, t_trans_client *>::iterator i;\n\tfor (i = map_trans_client.begin(); i != map_trans_client.end();\n\t     i++)\n\t{\n\t\tlog_file->write_raw(\"\\nDeleting client transaction: \\n\");\n\t\tlog_file->write_raw(\"Tid: \");\n\t\tlog_file->write_raw(i->first);\n\t\tlog_file->write_raw(\", Method: \");\n\t\tlog_file->write_raw(method2str(i->second->get_method()));\n\t\tlog_file->write_raw(\", State: \");\n\t\tlog_file->write_raw(trans_state2str(i->second->get_state()));\n\t\tlog_file->write_endl();\n\t\tMEMMAN_DELETE(i->second);\n\t\tdelete i->second;\n\t}\n\n\tmap<t_tid, t_trans_server *>::iterator j;\n\tfor (j = map_trans_server.begin(); j != map_trans_server.end();\n\t     j++)\n\t{\n\t\tlog_file->write_raw(\"\\nDeleting server transaction: \\n\");\n\t\tlog_file->write_raw(\"Tid: \");\n\t\tlog_file->write_raw(j->first);\n\t\tlog_file->write_raw(\", Method: \");\n\t\tlog_file->write_raw(method2str(j->second->get_method()));\n\t\tlog_file->write_raw(\", State: \");\n\t\tlog_file->write_raw(trans_state2str(j->second->get_state()));\n\t\tlog_file->write_endl();\n\t\tMEMMAN_DELETE(j->second);\n\t\tdelete j->second;\n\t}\n\t\n\tmap<t_tid, t_stun_transaction *>::iterator k;\n\tfor (k = map_stun_trans.begin(); k != map_stun_trans.end();\n\t     k++)\n\t{\n\t\tlog_file->write_raw(\"\\nDeleting STUN transaction: \\n\");\n\t\tlog_file->write_raw(\"Tid: \");\n\t\tlog_file->write_raw(k->first);\n\t\tlog_file->write_raw(\", State: \");\n\t\tlog_file->write_raw(trans_state2str(k->second->get_state()));\n\t\tlog_file->write_endl();\n\t\tMEMMAN_DELETE(k->second);\n\t\tdelete k->second;\n\t}\n\n\tlog_file->write_footer();\n}\n\nvoid t_transaction_mgr::handle_event_network(t_event_network *e) {\n\tt_trans_server\t*ts;\n\tt_ts_invite\t*ts_invite;\n\tt_trans_client\t*tc;\n\tt_sip_message\t*msg = e->get_msg();\n\tt_request\t*request;\n\tt_response\t*response;\n\n\tswitch(msg->get_type()) {\n\tcase MSG_REQUEST:\n\t\t// Request from network is for a server transaction\n\t\trequest = (t_request *)msg;\n\t\tts = find_trans_server(request);\n\t\tif (ts) {\n\t\t\tswitch (request->method) {\n\t\t\tcase ACK:\n\t\t\t\t// ACK for an INVITE transaction\n\t\t\t\tts_invite = (t_ts_invite *)ts;\n\t\t\t\tts_invite->acknowledge(request);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// A request that matches an existing\n\t\t\t\t// transaction is a retransmission\n\t\t\t\tts->process_retransmission();\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (ts->get_state() == TS_TERMINATED) {\n\t\t\t\tdelete_trans_server(ts);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Create a new transaction\n\t\tswitch (request->method) {\n\t\tcase INVITE:\n\t\t\tcreate_ts_invite(request);\n\t\t\tbreak;\n\t\tcase ACK:\n\t\t\t// ACK should be passed to TU\n\t\t\tevq_trans_layer->push_user(request, 0, 0);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcreate_ts_non_invite(request);\n\t\t\tbreak;\n\t\t}\n\n\t\tbreak;\n\tcase MSG_RESPONSE:\n\t\t// Response from network is for a client transaction\n\t\tresponse = (t_response *)msg;\n\t\ttc = find_trans_client(response);\n\t\tif (!tc) {\n\t\t\t// Only a 2XX for an INVITE transaction can be\n\t\t\t// received while no transaction exists anymore.\n\t\t\t// RFC 3261 17.1.1.2\n\t\t\tif (response->is_success() &&\n\t\t\t    response->hdr_cseq.method == INVITE)\n\t\t\t{\n\t\t\t\t// Report to TU\n\t\t\t\tevq_trans_layer->push_user(response, 0, 0);\n\t\t\t} else {\n\t\t\t\tlog_file->write_report(\n\t\t\t\t\t\"Response does not match any transaction. Discard.\",\n\t\t\t\t\t\"t_transaction_mgr::handle_event_network\");\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\ttc->process_response(response);\n\n\t\tif (tc->get_state() == TS_TERMINATED) {\n\t\t\tdelete_trans_client(tc);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_user(t_event_user *e) {\n\tt_trans_server\t*ts;\n\tt_sip_message\t*msg = e->get_msg();\n\tt_request\t*request;\n\tt_response\t*response;\n\n\tswitch(msg->get_type()) {\n\tcase MSG_REQUEST:\n\t\t// A user request creates a client transaction\n\t\trequest = (t_request *)msg;\n\t\tswitch (request->method) {\n\t\tcase INVITE:\n\t\t\tt_tc_invite *t1;\n\t\t\tassert(e->get_user_config());\n\t\t\tt1 = create_tc_invite(e->get_user_config(), request, e->get_tuid());\n\t\t\tif (t1 == NULL) {\n\t\t\t\t// Report 404 to TU\n\t\t\t\tresponse = request->create_response(\n\t\t\t\t\tR_404_NOT_FOUND);\n\n\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\"t_transaction_mgr::handle_event_user\",\n\t\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\t\t\tlog_file->write_raw(\"Cannot resolve destination for:\\n\");\n\t\t\t\tlog_file->write_raw(request->encode());\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\t\t\tlog_file->write_raw(response->encode());\n\t\t\t\tlog_file->write_footer();\n\n\t\t\t\tevq_trans_layer->push_user(response,\n\t\t\t\t\te->get_tuid(), 0);\n\t\t\t\tMEMMAN_DELETE(response);\n\t\t\t\tdelete response;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tt_tc_non_invite *t2;\n\t\t\tassert(e->get_user_config());\n\t\t\tt2 = create_tc_non_invite(e->get_user_config(), request, e->get_tuid());\n\t\t\tif (t2 == NULL) {\n\t\t\t\t// Report 404 to TU\n\t\t\t\tresponse = request->create_response(\n\t\t\t\t\tR_404_NOT_FOUND);\n\n\t\t\t\tlog_file->write_header(\n\t\t\t\t\t\"t_transaction_mgr::handle_event_user\",\n\t\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\t\t\tlog_file->write_raw(\"Cannot resolve destination for:\\n\");\n\t\t\t\tlog_file->write_raw(request->encode());\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_raw(\"Send internal:\\n\");\n\t\t\t\tlog_file->write_raw(response->encode());\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\t\tevq_trans_layer->push_user(response,\n\t\t\t\t\te->get_tuid(), 0);\n\t\t\t\tMEMMAN_DELETE(response);\n\t\t\t\tdelete response;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase MSG_RESPONSE:\n\t\t// A user repsonse is for a server transaction\n\t\tresponse = (t_response *)msg;\n\t\tts = find_trans_server(e->get_tid());\n\t\tif (!ts) {\n\t\t\t// This is an error. A response should match a\n\t\t\t// transaction. Ignore it.\n\t\t\tlog_file->write_report(\n\t\t\t\t\"Response from user does not match any transaction. Ignore.\",\n\t\t\t\t\"t_transaction_mgr::handle_event_user\", \n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\treturn;\n\t\t}\n\t\tts->process_response(response);\n\n\t\tif (ts->get_state() == TS_TERMINATED) {\n\t\t\tdelete_trans_server(ts);\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_timeout(t_event_timeout *e) {\n\tt_timer\t\t\t*t = e->get_timer();\n\tt_tmr_transaction\t*tmr_trans;\n\tt_tmr_stun_trans\t*tmr_stun_trans;\n\tt_tid\t\t\ttid;\n\tt_trans_client\t\t*tc;\n\tt_trans_server\t\t*ts;\n\tt_stun_transaction\t*st;\n\n\tswitch (t->get_type()) {\n\tcase TMR_TRANSACTION:\n\t\ttmr_trans = (t_tmr_transaction *)t;\n\t\ttid = tmr_trans->get_tid();\n\t\ttc = find_trans_client(tid);\n\t\tif (tc) {\n\t\t\ttc->timeout(tmr_trans->get_sip_timer());\n\n\t\t\tif (tc->get_state() == TS_TERMINATED) {\n\t\t\t\tdelete_trans_client(tc);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tts = find_trans_server(tid);\n\t\tif (ts) {\n\t\t\tts->timeout(tmr_trans->get_sip_timer());\n\n\t\t\tif (ts->get_state() == TS_TERMINATED) {\n\t\t\t\tdelete_trans_server(ts);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// The transaction is already gone. Discard timeout.\n\t\tbreak;\n\tcase TMR_STUN_TRANSACTION:\n\t\ttmr_stun_trans = (t_tmr_stun_trans *)t;\n\t\ttid = tmr_stun_trans->get_tid();\n\t\tst = find_stun_trans(tid);\n\t\tif (st) {\n\t\t\tst->timeout(tmr_stun_trans->get_stun_timer());\n\t\t\t\n\t\t\tif (st->get_state() == TS_TERMINATED) {\n\t\t\t\tdelete_stun_trans(st);\n\t\t\t}\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t// The transaction is already gone. Discard timeout.\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_abort(t_event_abort_trans *e) {\n\tt_tid\t\t\ttid;\n\tt_trans_client\t\t*tc;\n\n\t// Only a client transaction can be aborted.\n\ttid = e->get_tid();\n\ttc = find_trans_client(tid);\n\tif (tc) {\n\t\ttc->abort();\n\n\t\tif (tc->get_state() == TS_TERMINATED) {\n\t\t\tdelete_trans_client(tc);\n\t\t}\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_stun_request(t_event_stun_request *e) {\n\tStunMessage *msg = e->get_msg();\n\tunsigned short tuid = e->get_tuid();\n\tunsigned short tid = e->get_tid();\n\tt_sip_stun_trans *sst;\n\tt_media_stun_trans *mst;\n\tStunMessage *resp;\n\t\n\tswitch(e->get_stun_event_type()) {\n\tcase TYPE_STUN_SIP:\n\t\tassert(e->get_user_config());\n\t\tsst = create_sip_stun_trans(e->get_user_config(), msg, tuid);\n\t\tif (!sst) {\n\t\t\t// STUN server not found\n\t\t\tlog_file->write_header(\n\t\t\t\t\"t_transaction_mgr::handle_event_stun_request\",\n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\t\tlog_file->write_raw(\"Cannot resolve:\\n\");\n\t\t\tlog_file->write_raw(e->get_user_config()->get_stun_server().encode());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Send internal: 404 Not Found\\n\");\n\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\tresp = stunBuildError(*msg, 404, \"Not Found\");\n\t\t\tevq_trans_layer->push_stun_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t}\n\t\tbreak;\n\tcase TYPE_STUN_MEDIA:\n\t\tassert(e->get_user_config());\n\t\tmst = create_media_stun_trans(e->get_user_config(), msg, tuid, e->src_port);\n\t\tif (!mst) {\n\t\t\t// STUN server not found\n\t\t\tlog_file->write_header(\n\t\t\t\t\"t_transaction_mgr::handle_event_stun_request\",\n\t\t\t\tLOG_NORMAL, LOG_INFO);\n\t\t\tlog_file->write_raw(\"Cannot resolve:\\n\");\n\t\t\tlog_file->write_raw(e->get_user_config()->get_stun_server().encode());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(\"Send internal: 404 Not Found\\n\");\n\t\t\tlog_file->write_footer();\n\t\t\t\t\n\t\t\tresp = stunBuildError(*msg, 404, \"Not Found\");\n\t\t\tevq_trans_layer->push_stun_response(resp, tuid, tid);\n\t\t\tMEMMAN_DELETE(resp);\n\t\t\tdelete resp;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_stun_response(t_event_stun_response *e) {\n\tStunMessage *response = e->get_msg();\n\tt_stun_transaction *st = find_stun_trans(response);\n\t\n\tif (!st) {\n\t\t// This response does not match any transaction.\n\t\t// Ignore it.\n\t\treturn;\n\t}\n\t\n\tst->process_response(response);\n\t\n\tif (st->get_state() == TS_TERMINATED) {\n\t\tdelete_stun_trans(st);\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_icmp(t_event_icmp *e) {\n\t// Only a client and STUN transactions can handle ICMP errors\n\t// If both a client and STUN transaction match then send the ICMP\n\t// error to both transactions. It cannot be determined which transaction\n\t// caused the error, but as both transactions have the same destination\n\t// it is likely that both will fail.\n\t\n\tt_trans_client *tc = find_trans_client(e->get_icmp());\n\tif (tc) {\n\t\ttc->process_icmp(e->get_icmp());\n\n\t\tif (tc->get_state() == TS_TERMINATED) {\n\t\t\tdelete_trans_client(tc);\n\t\t}\n\t}\n\t\n\tt_stun_transaction *st = find_stun_trans(e->get_icmp());\n\tif (st) {\n\t\tst->process_icmp(e->get_icmp());\n\t\t\n\t\tif (st->get_state() == TS_TERMINATED) {\n\t\t\tdelete_stun_trans(st);\n\t\t}\n\t}\n}\n\nvoid t_transaction_mgr::handle_event_failure(t_event_failure *e) {\n\t// Only a client transaction can handle failure events.\t\n\tt_trans_client *tc;\n\t\n\tif (e->is_tid_populated()) {\n\t\ttc = find_trans_client(e->get_tid());\n\t} else {\n\t\ttc = find_trans_client(e->get_branch(), e->get_cseq_method());\n\t}\n\t\n\tif (tc) {\n\t\ttc->process_failure(e->get_failure());\n\t\t\n\t\tif (tc->get_state() == TS_TERMINATED) {\n\t\t\tdelete_trans_client(tc);\n\t\t}\n\t}\n}\n\nt_object_id t_transaction_mgr::start_timer(long dur, t_sip_timer tmr,\n\t\t\tunsigned short tid)\n{\n\tt_tmr_transaction *t = new t_tmr_transaction(dur, tmr, tid);\n\tMEMMAN_NEW(t);\n\tevq_timekeeper->push_start_timer(t);\n\tt_object_id timer_id = t->get_object_id();\n\tMEMMAN_DELETE(t);\n\tdelete t;\n\treturn timer_id;\n}\n\nt_object_id t_transaction_mgr::start_stun_timer(long dur, t_stun_timer tmr,\n\t\t\tunsigned short tid)\n{\n\tt_tmr_stun_trans *t = new t_tmr_stun_trans(dur, tmr, tid);\n\tMEMMAN_NEW(t);\n\tevq_timekeeper->push_start_timer(t);\n\tt_object_id timer_id = t->get_object_id();\n\tMEMMAN_DELETE(t);\n\tdelete t;\n\treturn timer_id;\n}\n\nvoid t_transaction_mgr::stop_timer(t_object_id id) {\n\tevq_timekeeper->push_stop_timer(id);\n}\n\nvoid t_transaction_mgr::run(void) {\n\tt_event\t\t\t*event;\n\tt_event_network\t\t*ev_network;\n\tt_event_user\t\t*ev_user;\n\tt_event_timeout\t\t*ev_timeout;\n\tt_event_abort_trans\t*ev_abort;\n\tt_event_stun_request\t*ev_stun_request;\n\tt_event_stun_response\t*ev_stun_response;\n\tt_event_icmp\t\t*ev_icmp;\n\tt_event_failure\t\t*ev_failure;\n\n\tbool quit = false;\n\twhile (!quit) {\n\t\tevent = evq_trans_mgr->pop();\n\n\t\tswitch (event->get_type()) {\n\t\tcase EV_NETWORK:\n\t\t\tev_network = dynamic_cast<t_event_network *>(event);\n\t\t\thandle_event_network(ev_network);\n\t\t\tbreak;\n\t\tcase EV_USER:\n\t\t\tev_user = dynamic_cast<t_event_user *>(event);\n\t\t\thandle_event_user(ev_user);\n\t\t\tbreak;\n\t\tcase EV_TIMEOUT:\n\t\t\tev_timeout = dynamic_cast<t_event_timeout *>(event);\n\t\t\thandle_event_timeout(ev_timeout);\n\t\t\tbreak;\n\t\tcase EV_ABORT_TRANS:\n\t\t\tev_abort = dynamic_cast<t_event_abort_trans *>(event);\n\t\t\thandle_event_abort(ev_abort);\n\t\t\tbreak;\n\t\tcase EV_STUN_REQUEST:\n\t\t\tev_stun_request = dynamic_cast<t_event_stun_request *>(event);\n\t\t\thandle_event_stun_request(ev_stun_request);\n\t\t\tbreak;\n\t\tcase EV_STUN_RESPONSE:\n\t\t\tev_stun_response = dynamic_cast<t_event_stun_response *>(event);\n\t\t\thandle_event_stun_response(ev_stun_response);\n\t\t\tbreak;\n\t\tcase EV_ICMP:\n\t\t\tev_icmp = dynamic_cast<t_event_icmp *>(event);\n\t\t\thandle_event_icmp(ev_icmp);\n\t\t\tbreak;\n\t\tcase EV_FAILURE:\n\t\t\tev_failure = dynamic_cast<t_event_failure *>(event);\n\t\t\thandle_event_failure(ev_failure);\n\t\t\tbreak;\n\t\tcase EV_QUIT:\n\t\t\tquit = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\tbreak;\n\t\t}\n\n\t\tMEMMAN_DELETE(event);\n\t\tdelete event;\n\t}\n}\n\n// Main function to be started in a separate thread.\nvoid *transaction_mgr_main(void *arg) {\n\ttransaction_mgr->run();\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/transaction_mgr.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TRANSACTION_MGR_H\n#define _TRANSACTION_MGR_H\n\n#include <map>\n#include \"events.h\"\n#include \"transaction.h\"\n#include \"user.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"sockets/socket.h\"\n#include \"stun/stun_transaction.h\"\n\nusing namespace std;\n\nclass t_transaction_mgr {\nprivate:\n\t// Mapping from transaction id to transaction\n\tmap<t_tid, t_trans_client *>\t\tmap_trans_client;\n\tmap<t_tid, t_trans_server *>\t\tmap_trans_server;\n\tmap<t_tid, t_stun_transaction *>\tmap_stun_trans;\n\n\t// Find existing transactions. Return NULL if not found\n\tt_trans_client *find_trans_client(t_response *r) const;\n\tt_trans_client *find_trans_client(t_tid tid) const;\n\tt_trans_client *find_trans_client(const string &branch, const t_method &cseq_method) const;\n\tt_trans_client *find_trans_client(const t_icmp_msg &icmp) const;\n\tt_trans_server *find_trans_server(t_request *r) const;\n\tt_trans_server *find_trans_server(t_tid tid) const;\n\tt_stun_transaction *find_stun_trans(StunMessage *r) const;\n\tt_stun_transaction *find_stun_trans(t_tid tid) const;\n\tt_stun_transaction *find_stun_trans(const t_icmp_msg &icmp) const;\n\n\t// Create new transactions.\n\t// Return NULL if creation failed.\n\tt_tc_invite *create_tc_invite(t_user *user_config, t_request *r, unsigned short tuid);\n\tt_tc_non_invite *create_tc_non_invite(t_user *user_config, t_request *r,\n\t\tunsigned short tuid);\n\tt_ts_invite *create_ts_invite(t_request *r);\n\tt_ts_non_invite *create_ts_non_invite(t_request *r);\n\tt_sip_stun_trans *create_sip_stun_trans(t_user *user_config, StunMessage *r, \n\t\tunsigned short tuid);\n\tt_media_stun_trans *create_media_stun_trans(t_user *user_config, StunMessage *r, \n\t\tunsigned short tuid, unsigned short src_port);\n\n\t// Delete transactions\n\tvoid delete_trans_client(t_trans_client *tc);\n\tvoid delete_trans_server(t_trans_server *ts);\n\tvoid delete_stun_trans(t_stun_transaction *st);\n\n\t// Handle events\n\tvoid handle_event_network(t_event_network *e);\n\tvoid handle_event_user(t_event_user *e);\n\tvoid handle_event_timeout(t_event_timeout *e);\n\tvoid handle_event_abort(t_event_abort_trans *e);\n\tvoid handle_event_stun_request(t_event_stun_request *e);\n\tvoid handle_event_stun_response(t_event_stun_response *e);\n\tvoid handle_event_icmp(t_event_icmp *e);\n\tvoid handle_event_failure(t_event_failure *e);\n\npublic:\n\t~t_transaction_mgr();\n\n\t// Find the target transaction for a CANCEL.\n\t// Return NULL if not found.\n\tt_trans_server *find_cancel_target(t_request *r) const;\n\n\t// Start transaction timer. Return timer id (needed for stopping)\n\tt_object_id start_timer(long dur, t_sip_timer tmr,\n\t\t\t\t\t\tunsigned short tid);\n\tt_object_id start_stun_timer(long dur, t_stun_timer tmr,\n\t\t\t\t\t\tunsigned short tid);\n\n\t// Stop timer. Pass id that is returned by start_timer\n\tvoid stop_timer(t_object_id id);\n\n\t// Main loop of the transaction manager (infinite)\n\tvoid run (void);\n};\n\n// Thread that runs the transaction manager\nvoid *transaction_mgr_main(void *arg);\n\n#endif\n"
  },
  {
    "path": "src/translator.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _TRANSLATOR_H\n#define _TRANSLATOR_H\n\n#include <string>\n\n#define TRANSLATE(s)\t\t(translator ? translator->translate(s) : s)\n#define TRANSLATE2(c, s)\t(translator ? translator->translate2(c, s) : s)\n\nusing namespace std;\n\n// This class provides an interface for languague translations.\n// The default implementation does not perform any translation.\n// The class may be subclassed to provide translation services.\n\nclass t_translator {\npublic:\n\tvirtual ~t_translator() {};\n\t\n\t// The default implementation simply returns the passed\n\t// string. A subclass should reimplement this method to\n\t// provide translation.\n\tvirtual string translate(const string &s) { return s; };\n\t\n\t// The name of the context parameter is in comments to avoid\n\t// unused argument warnings.\n\tvirtual string translate2(const string &/*context*/, const string &s) { return s; };\n};\n\nextern t_translator *translator;\n\n#endif\n"
  },
  {
    "path": "src/user.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <cassert>\n#include <cstdlib>\n#include <cstring>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <fstream>\n#include <iostream>\n#include <list>\n#include \"diamondcard.h\"\n#include \"log.h\"\n#include \"phone.h\"\n#include \"twinkle_config.h\"\n#include \"user.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"protocol.h\"\n#include \"sys_settings.h\"\n#include \"audits/memman.h\"\n#include \"sdp/sdp.h\"\n#include \"parser/parse_ctrl.h\"\n#include \"parser/request.h\"\n\nextern t_phone\t\t*phone;\n\n// Field names in the config file\n// USER fields\n#define FLD_NAME\t\t\t\"user_name\"\n#define FLD_DOMAIN\t\t\t\"user_domain\"\n#define FLD_DISPLAY\t\t\t\"user_display\"\n#define\tFLD_ORGANIZATION\t\t\"user_organization\"\n#define FLD_AUTH_REALM\t\t\t\"auth_realm\"\n#define FLD_AUTH_NAME\t\t\t\"auth_name\"\n#define FLD_AUTH_PASS\t\t\t\"auth_pass\"\n#define FLD_AUTH_AKA_OP\t\t\t\"auth_aka_op\"\n#define FLD_AUTH_AKA_AMF\t\t\"auth_aka_amf\"\n\n// SIP SERVER fields\n#define FLD_OUTBOUND_PROXY\t\t\"outbound_proxy\"\n#define FLD_ALL_REQUESTS_TO_PROXY\t\"all_requests_to_proxy\"\n#define FLD_NON_RESOLVABLE_TO_PROXY\t\"non_resolvable_to_proxy\"\n#define FLD_REGISTRAR\t\t\t\"registrar\"\n#define FLD_REGISTRATION_TIME\t\t\"registration_time\"\n#define FLD_REGISTER_AT_STARTUP\t\t\"register_at_startup\"\n#define FLD_REG_ADD_QVALUE\t\t\"reg_add_qvalue\"\n#define FLD_REG_QVALUE\t\t\t\"reg_qvalue\"\n\n// AUDIO fields\n#define FLD_CODECS\t\t\t\"codecs\"\n#define FLD_PTIME\t\t\t\"ptime\"\n#define FLD_OUT_FAR_END_CODEC_PREF\t\"out_far_end_codec_pref\"\n#define FLD_IN_FAR_END_CODEC_PREF\t\"in_far_end_codec_pref\"\n#define FLD_SPEEX_NB_PAYLOAD_TYPE\t\"speex_nb_payload_type\"\n#define FLD_SPEEX_WB_PAYLOAD_TYPE\t\"speex_wb_payload_type\"\n#define FLD_SPEEX_UWB_PAYLOAD_TYPE\t\"speex_uwb_payload_type\"\n#define FLD_SPEEX_BIT_RATE_TYPE\t\t\"speex_bit_rate_type\"\n#define FLD_SPEEX_ABR_NB\t\t\"speex_abr_nb\"\n#define FLD_SPEEX_ABR_WB\t\t\"speex_abr_wb\"\n#define FLD_SPEEX_DTX\t\t\t\"speex_dtx\"\n#define FLD_SPEEX_PENH\t\t\t\"speex_penh\"\n#define FLD_SPEEX_QUALITY\t\t\"speex_quality\"\n#define FLD_SPEEX_COMPLEXITY\t\t\"speex_complexity\"\n#define FLD_SPEEX_DSP_VAD\t\t\"speex_dsp_vad\"\n#define FLD_SPEEX_DSP_AGC\t\t\"speex_dsp_agc\"\n#define FLD_SPEEX_DSP_AGC_LEVEL\t\t\"speex_dsp_agc_level\"\n#define FLD_SPEEX_DSP_AEC\t\t\"speex_dsp_aec\"\n#define FLD_SPEEX_DSP_NRD\t\t\"speex_dsp_nrd\"\n#define FLD_ILBC_PAYLOAD_TYPE\t\t\"ilbc_payload_type\"\n#define FLD_ILBC_MODE\t\t\t\"ilbc_mode\"\n#define FLD_G726_16_PAYLOAD_TYPE\t\"g726_16_payload_type\"\n#define FLD_G726_24_PAYLOAD_TYPE\t\"g726_24_payload_type\"\n#define FLD_G726_32_PAYLOAD_TYPE\t\"g726_32_payload_type\"\n#define FLD_G726_40_PAYLOAD_TYPE\t\"g726_40_payload_type\"\n#define FLD_G726_PACKING\t\t\"g726_packing\"\n#define FLD_DTMF_TRANSPORT\t\t\"dtmf_transport\"\n#define FLD_DTMF_PAYLOAD_TYPE\t\t\"dtmf_payload_type\"\n#define FLD_DTMF_DURATION\t\t\"dtmf_duration\"\n#define FLD_DTMF_PAUSE\t\t\t\"dtmf_pause\"\n#define FLD_DTMF_VOLUME\t\t\t\"dtmf_volume\"\n\n// SIP PROTOCOL fields\n#define FLD_HOLD_VARIANT\t\t\"hold_variant\"\n#define FLD_CHECK_MAX_FORWARDS\t\t\"check_max_forwards\"\n#define FLD_ALLOW_MISSING_CONTACT_REG\t\"allow_missing_contact_reg\"\t\n#define FLD_REGISTRATION_TIME_IN_CONTACT\t\"registration_time_in_contact\"\n#define FLD_COMPACT_HEADERS\t\t\"compact_headers\"\n#define FLD_ENCODE_MULTI_VALUES_AS_LIST\t\"encode_multi_values_as_list\"\n#define FLD_USE_DOMAIN_IN_CONTACT\t\"use_domain_in_contact\"\n#define FLD_ALLOW_SDP_CHANGE\t\t\"allow_sdp_change\"\n#define FLD_ALLOW_REDIRECTION\t\t\"allow_redirection\"\n#define FLD_ASK_USER_TO_REDIRECT\t\"ask_user_to_redirect\"\n#define FLD_MAX_REDIRECTIONS\t\t\"max_redirections\"\n#define FLD_EXT_100REL\t\t\t\"ext_100rel\"\n#define FLD_EXT_REPLACES\t\t\"ext_replaces\"\n#define FLD_REFEREE_HOLD\t\t\"referee_hold\"\n#define FLD_REFERRER_HOLD\t\t\"referrer_hold\"\n#define FLD_ALLOW_REFER\t\t\t\"allow_refer\"\n#define FLD_ASK_USER_TO_REFER\t\t\"ask_user_to_refer\"\n#define FLD_AUTO_REFRESH_REFER_SUB\t\"auto_refresh_refer_sub\"\n#define FLD_ATTENDED_REFER_TO_AOR\t\"attended_refer_to_aor\"\n#define FLD_ALLOW_XFER_CONSULT_INPROG\t\"allow_xfer_consult_inprog\"\n#define FLD_SEND_P_PREFERRED_ID\t\t\"send_p_preferred_id\"\n#define FLD_SEND_P_ASSERTED_ID\t\t\"send_p_asserted_id\"\n\n// Transport/NAT fields\n#define FLD_SIP_TRANSPORT\t\t\"sip_transport\"\n#define FLD_SIP_TRANSPORT_UDP_THRESHOLD\t\"sip_transport_udp_threshold\"\n#define FLD_NAT_PUBLIC_IP\t\t\"nat_public_ip\"\n#define FLD_STUN_SERVER\t\t\t\"stun_server\"\n#define FLD_PERSISTENT_TCP\t\t\"persistent_tcp\"\n#define FLD_ENABLE_NAT_KEEPALIVE\t\"enable_nat_keepalive\"\n\n// TIMER fields\n#define FLD_TIMER_NOANSWER\t\t\"timer_noanswer\"\n#define FLD_TIMER_NAT_KEEPALIVE\t\t\"timer_nat_keepalive\"\n#define FLD_TIMER_TCP_PING\t\t\"timer_tcp_ping\"\n\n// ADDRESS FORMAT fields\n#define FLD_DISPLAY_USERONLY_PHONE\t\"display_useronly_phone\"\n#define FLD_NUMERICAL_USER_IS_PHONE\t\"numerical_user_is_phone\"\n#define FLD_REMOVE_SPECIAL_PHONE_SYM\t\"remove_special_phone_symbols\"\n#define FLD_SPECIAL_PHONE_SYMBOLS\t\"special_phone_symbols\"\n#define FLD_USE_TEL_URI_FOR_PHONE\t\"use_tel_uri_for_phone\"\n\n// Ring tone settings\n#define FLD_USER_RINGTONE_FILE\t\t\"ringtone_file\"\n#define FLD_USER_RINGBACK_FILE\t\t\"ringback_file\"\n\n// Incoming call script\n#define FLD_SCRIPT_INCOMING_CALL\t\"script_incoming_call\"\n#define FLD_SCRIPT_IN_CALL_ANSWERED\t\"script_in_call_answered\"\n#define FLD_SCRIPT_IN_CALL_FAILED\t\"script_in_call_failed\"\n#define FLD_SCRIPT_OUTGOING_CALL\t\"script_outgoing_call\"\n#define FLD_SCRIPT_OUT_CALL_ANSWERED\t\"script_out_call_answered\"\n#define FLD_SCRIPT_OUT_CALL_FAILED\t\"script_out_call_failed\"\n#define FLD_SCRIPT_LOCAL_RELEASE\t\"script_local_release\"\n#define FLD_SCRIPT_REMOTE_RELEASE\t\"script_remote_release\"\n\n// Number conversion\n#define FLD_NUMBER_CONVERSION\t\t\"number_conversion\"\n\n// Security\n#define FLD_ZRTP_ENABLED\t\t\"zrtp_enabled\"\n#define FLD_ZRTP_GOCLEAR_WARNING\t\"zrtp_goclear_warning\"\n#define FLD_ZRTP_SDP\t\t\t\"zrtp_sdp\"\n#define FLD_ZRTP_SEND_IF_SUPPORTED\t\"zrtp_send_if_supported\"\n\n// MWI\n#define FLD_MWI_SOLICITED\t\t\"mwi_solicited\"\n#define FLD_MWI_USER\t\t\t\"mwi_user\"\n#define FLD_MWI_SERVER\t\t\t\"mwi_server\"\n#define FLD_MWI_VIA_PROXY\t\t\"mwi_via_proxy\"\n#define FLD_MWI_SUBSCRIPTION_TIME\t\"mwi_subscription_time\"\n#define FLD_MWI_VM_ADDRESS\t\t\"mwi_vm_address\"\n\n// INSTANT MESSAGE\n#define FLD_IM_MAX_SESSIONS\t\t\"im_max_sessions\"\n#define FLD_IM_SEND_ISCOMPOSING\t\t\"im_send_iscomposing\"\n\n// PRESENCE\n#define FLD_PRES_SUBSCRIPTION_TIME\t\"pres_subscription_time\"\n#define FLD_PRES_PUBLICATION_TIME\t\"pres_publication_time\"\n#define FLD_PRES_PUBLISH_STARTUP\t\"pres_publish_startup\"\n\n/////////////////////////\n// class t_user\n/////////////////////////\n\n////////////////////\n// Private\n////////////////////\n\nt_ext_support t_user::str2ext_support(const string &s) const {\n\tif (s == \"disabled\") return EXT_DISABLED;\n\tif (s == \"supported\") return EXT_SUPPORTED;\n\tif (s == \"preferred\") return EXT_PREFERRED;\n\tif (s == \"required\") return EXT_REQUIRED;\n\treturn EXT_INVALID;\n}\n\nstring t_user::ext_support2str(t_ext_support e) const {\n\tswitch(e) {\n\tcase EXT_INVALID:\treturn \"invalid\";\n\tcase EXT_DISABLED:\treturn \"disabled\";\n\tcase EXT_SUPPORTED:\treturn \"supported\";\n\tcase EXT_PREFERRED:\treturn \"preferred\";\n\tcase EXT_REQUIRED:\treturn \"required\";\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn \"\";\n}\n\nt_bit_rate_type t_user::str2bit_rate_type(const string &s) const {\n\tif (s == \"cbr\") return BIT_RATE_CBR;\n\tif (s == \"vbr\") return BIT_RATE_VBR;\n\tif (s == \"abr\") return BIT_RATE_ABR;\n\treturn BIT_RATE_INVALID;\n}\n\nstring t_user::bit_rate_type2str(t_bit_rate_type b) const {\n\tswitch (b) {\n\tcase BIT_RATE_INVALID:\treturn \"invalid\";\n\tcase BIT_RATE_CBR:\treturn \"cbr\";\n\tcase BIT_RATE_VBR:\treturn \"vbr\";\n\tcase BIT_RATE_ABR:\treturn \"abr\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nt_dtmf_transport t_user::str2dtmf_transport(const string &s) const {\n\tif (s == \"inband\") return DTMF_INBAND;\n\tif (s == \"rfc2833\") return DTMF_RFC2833;\n\tif (s == \"auto\") return DTMF_AUTO;\n\tif (s == \"info\") return DTMF_INFO;\n\treturn DTMF_AUTO;\n}\n\nstring t_user::dtmf_transport2str(t_dtmf_transport d) const {\n\tswitch (d) {\n\tcase DTMF_INBAND:\treturn \"inband\";\n\tcase DTMF_RFC2833:\treturn \"rfc2833\";\n\tcase DTMF_AUTO:\t\treturn \"auto\";\n\tcase DTMF_INFO:\t\treturn \"info\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nt_g726_packing t_user::str2g726_packing(const string &s) const {\n\tif (s == \"rfc3551\") return G726_PACK_RFC3551;\n\tif (s == \"aal2\") return G726_PACK_AAL2;\n\treturn G726_PACK_AAL2;\n}\n\nstring t_user::g726_packing2str(t_g726_packing packing) const {\n\tswitch (packing) {\n\tcase G726_PACK_RFC3551:\treturn \"rfc3551\";\n\tcase G726_PACK_AAL2:\treturn \"aal2\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nt_sip_transport t_user::str2sip_transport(const string &s) const {\n\tif (s == \"udp\") return SIP_TRANS_UDP;\n\tif (s == \"tcp\") return SIP_TRANS_TCP;\n\tif (s == \"auto\") return SIP_TRANS_AUTO;\n\treturn SIP_TRANS_AUTO;\n}\n\nstring t_user::sip_transport2str(t_sip_transport transport) const {\n\tswitch (transport) {\n\tcase SIP_TRANS_UDP:\treturn \"udp\";\n\tcase SIP_TRANS_TCP:\treturn \"tcp\";\n\tcase SIP_TRANS_AUTO:\treturn \"auto\";\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn \"\";\n}\n\nstring t_user::expand_filename(const string &filename) {\n\tstring f;\n\n\tif (filename[0] == '/') {\n\t\tf = filename;\n\t} else {\n        \tf = string(DIR_HOME);\n        \tf += \"/\";\n        \tf += USER_DIR;\n        \tf += \"/\";\n        \tf += filename;\n\t}\n\n\treturn f;\n}\n\nbool t_user::parse_num_conversion(const string &value, t_number_conversion &c) {\n\tvector<string> l = split_escaped(value, ',');\n\t\n\tif (l.size() != 2) {\n\t\t// Invalid conversion rule\n\t\treturn false;\n\t}\n\t\n\ttry {\n\t\tc.re.assign(l[0]);\n\t\tc.fmt = l[1];\n    } catch (std::regex_error) {\n\t\t// Invalid regular expression\n\t\tlog_file->write_header(\"t_user::parse_num_conversion\", \n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tlog_file->write_raw(\"Bad number conversion:\\n\");\n\t\tlog_file->write_raw(l.front());\n\t\tlog_file->write_raw(\" --> \");\n\t\tlog_file->write_raw(l.back());\n\t\tlog_file->write_endl();\n\t\tlog_file->write_footer();\n\t\t\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nbool t_user::set_server_value(t_url &server, const string &scheme, const string &value) {\n\tif (value.empty()) {\n\t\tserver.set_url(\"\");\n\t\treturn false;\n\t}\n\n\tstring s = scheme + \":\" + value;\n\tserver.set_url(s);\n\n\tif (!server.is_valid() || server.get_user() != \"\")\n\t{\n\t\tstring err_msg = \"Invalid server value: \";\n\t\terr_msg += value;\n\t\tlog_file->write_report(err_msg, \"t_user::set_server_value\",\n\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\tserver.set_url(\"\");\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\n\n////////////////////\n// Public\n////////////////////\n\nt_user::t_user() {\n\t// Set defaults\n\tmemset(auth_aka_op, 0, AKA_OPLEN);\n\tmemset(auth_aka_amf, 0, AKA_AMFLEN);\n\tuse_outbound_proxy = false;\n\tall_requests_to_proxy = false;\n\tnon_resolvable_to_proxy = false;\n\tuse_registrar = false;\n\tregistration_time = 3600;\n#ifdef HAVE_SPEEX\n\tcodecs.push_back(CODEC_SPEEX_WB);\n\tcodecs.push_back(CODEC_SPEEX_NB);\n#endif\n#ifdef HAVE_ILBC\n\tcodecs.push_back(CODEC_ILBC);\n#endif\n\tcodecs.push_back(CODEC_G711_ALAW);\n\tcodecs.push_back(CODEC_G711_ULAW);\n\tcodecs.push_back(CODEC_GSM);\n#ifdef HAVE_BCG729\n\tcodecs.push_back(CODEC_G729A);\n#endif\n\tptime = 20;\n\tout_obey_far_end_codec_pref = true;\n\tin_obey_far_end_codec_pref = true;\n\thold_variant = HOLD_RFC3264;\n\tuse_nat_public_ip = false;\n\tuse_stun = false;\n\tpersistent_tcp = true;\n\tenable_nat_keepalive = false;\n\tregister_at_startup = true;\n\treg_add_qvalue = false;\n\treg_qvalue = 1.0;\n\tcheck_max_forwards = false;\n\tallow_missing_contact_reg = true;\n\tcompact_headers = false;\n\tencode_multi_values_as_list = true;\n\tregistration_time_in_contact = true;\n\tuse_domain_in_contact = false;\n\tallow_sdp_change = true;\n\tallow_redirection = true;\n\task_user_to_redirect = true;\n\tmax_redirections = 5;\n\ttimer_noanswer = 30;\n\ttimer_nat_keepalive = DUR_NAT_KEEPALIVE;\n\ttimer_tcp_ping = DUR_TCP_PING;\n\text_100rel = EXT_SUPPORTED;\n\text_replaces = true;\n\tspeex_nb_payload_type = 97;\n\tspeex_wb_payload_type = 98;\n\tspeex_uwb_payload_type = 99;\n\tspeex_bit_rate_type = BIT_RATE_CBR;\n\tspeex_abr_nb = 0;\n\tspeex_abr_wb = 0;\n\tspeex_dtx = false;\n\tspeex_penh = true;\n\tspeex_quality = 6;\n\tspeex_complexity = 3;\n\tspeex_dsp_vad = true;\n\tspeex_dsp_agc = true;\n\tspeex_dsp_aec = false;\n\tspeex_dsp_nrd = true;\n\tspeex_dsp_agc_level = 20;\n\tilbc_payload_type = 96;\n\tilbc_mode = 30;\n\tg726_16_payload_type = 102;\n\tg726_24_payload_type = 103;\n\tg726_32_payload_type = 104;\n\tg726_40_payload_type = 105;\n\tg726_packing = G726_PACK_RFC3551;\n\tdtmf_transport = DTMF_AUTO;\n\tdtmf_duration = 100;\n\tdtmf_pause = 40;\n\tdtmf_payload_type = 101;\n\tdtmf_volume = 10;\n\tdisplay_useronly_phone = true;\n\tnumerical_user_is_phone = false;\n\tremove_special_phone_symbols = true;\n\tspecial_phone_symbols = SPECIAL_PHONE_SYMBOLS;\n\tuse_tel_uri_for_phone = false;\n\treferee_hold = false;\n\treferrer_hold = true;\n\tallow_refer = true;\n\task_user_to_refer = true;\n\tauto_refresh_refer_sub = false;\n\tattended_refer_to_aor = false;\n\tallow_transfer_consultation_inprog = false;\n\tsend_p_preferred_id = false;\n\tsend_p_asserted_id = false;\n\tsip_transport = SIP_TRANS_AUTO;\n\tsip_transport_udp_threshold = 1300; // RFC 3261 18.1.1\n\tringtone_file.clear();\n\tringback_file.clear();\n\tscript_incoming_call.clear();\n\tscript_in_call_answered.clear();\n\tscript_in_call_failed.clear();\n\tscript_outgoing_call.clear();\n\tscript_out_call_answered.clear();\n\tscript_out_call_failed.clear();\n\tscript_local_release.clear();\n\tscript_remote_release.clear();\n\tnumber_conversions.clear();\n\tzrtp_enabled = false;\n\tzrtp_goclear_warning = true;\n\tzrtp_sdp = true;\n\tzrtp_send_if_supported = false;\n\tmwi_solicited = false;\n\tmwi_user.clear();\n\tmwi_via_proxy = false;\n\tmwi_subscription_time = 3600;\n\tmwi_vm_address.clear();\n\tim_max_sessions = 10;\n\tim_send_iscomposing = true;\n\tpres_subscription_time = 3600;\n\tpres_publication_time = 3600;\n\tpres_publish_startup = true;\n}\n\nt_user::t_user(const t_user &u) {\n\tu.mtx_user.lock();\n\n\tconfig_filename = u.config_filename;\n\tname = u.name;\n\tdomain = u.domain;\n\tdisplay = u.display;\t\n\torganization = u.organization;\n\tauth_realm = u.auth_realm;\n\tauth_name = u.auth_name;\n\tauth_pass = u.auth_pass;\n\tmemcpy(auth_aka_op, u.auth_aka_op, AKA_OPLEN);\n\tmemcpy(auth_aka_amf, u.auth_aka_amf, AKA_AMFLEN);\n\tuse_outbound_proxy = u.use_outbound_proxy;\n\toutbound_proxy = u.outbound_proxy;\n\tall_requests_to_proxy = u.all_requests_to_proxy;\n\tnon_resolvable_to_proxy = u.non_resolvable_to_proxy;\n\tuse_registrar = u.use_registrar;\n\treg_add_qvalue = u.reg_add_qvalue;\n\treg_qvalue = u.reg_qvalue;\n\tregistrar = u.registrar;\n\tregistration_time = u.registration_time;\n\tregister_at_startup = u.register_at_startup;\n\tcodecs = u.codecs;\n\tptime = u.ptime;\n\tout_obey_far_end_codec_pref = u.out_obey_far_end_codec_pref;\n\tin_obey_far_end_codec_pref = u.in_obey_far_end_codec_pref;\n\tspeex_nb_payload_type = u.speex_nb_payload_type;\n\tspeex_wb_payload_type = u.speex_wb_payload_type;\n\tspeex_uwb_payload_type = u.speex_uwb_payload_type;\n\tspeex_bit_rate_type = u.speex_bit_rate_type;\n\tspeex_abr_nb = u.speex_abr_nb;\n\tspeex_abr_wb = u.speex_abr_wb;\n\tspeex_dtx = u.speex_dtx;\n\tspeex_penh = u.speex_penh;\n\tspeex_quality = u.speex_quality;\n\tspeex_complexity = u.speex_complexity;\n\tspeex_dsp_vad = u.speex_dsp_vad;\n\tspeex_dsp_agc = u.speex_dsp_agc;\n\tspeex_dsp_agc_level = u.speex_dsp_agc_level;\n\tspeex_dsp_aec = u.speex_dsp_aec;\n\tspeex_dsp_nrd = u.speex_dsp_nrd;\n\tilbc_payload_type = u.ilbc_payload_type;\n\tilbc_mode = u.ilbc_mode;\n\tg726_16_payload_type = u.g726_16_payload_type;\n\tg726_24_payload_type = u.g726_24_payload_type;\n\tg726_32_payload_type = u.g726_32_payload_type;\n\tg726_40_payload_type = u.g726_40_payload_type;\n\tg726_packing = u.g726_packing;\n\tdtmf_transport = u.dtmf_transport;\n\tdtmf_payload_type = u.dtmf_payload_type;\n\tdtmf_duration = u.dtmf_duration;\n\tdtmf_pause = u.dtmf_pause;\n\tdtmf_volume = u.dtmf_volume;\n\thold_variant = u.hold_variant;\n\tcheck_max_forwards = u.check_max_forwards;\n\tallow_missing_contact_reg = u.allow_missing_contact_reg;\n\tregistration_time_in_contact = u.registration_time_in_contact;\n\tcompact_headers = u.compact_headers;\n\tencode_multi_values_as_list = u.encode_multi_values_as_list;\n\tuse_domain_in_contact = u.use_domain_in_contact;\n\tallow_sdp_change = u.allow_sdp_change;\n\tallow_redirection = u.allow_redirection;\n\task_user_to_redirect = u.ask_user_to_redirect;\n\tmax_redirections = u.max_redirections;\n\text_100rel = u.ext_100rel;\n\text_replaces = u.ext_replaces;\n\treferee_hold = u.referee_hold;\n\treferrer_hold = u.referrer_hold;\n\tallow_refer = u.allow_refer;\n\task_user_to_refer = u.ask_user_to_refer;\n\tauto_refresh_refer_sub = u.auto_refresh_refer_sub;\n\tattended_refer_to_aor = u.attended_refer_to_aor;\n\tallow_transfer_consultation_inprog = u.allow_transfer_consultation_inprog;\n\tsend_p_preferred_id = u.send_p_preferred_id;\n\tsend_p_asserted_id = u.send_p_asserted_id;\n\tsip_transport = u.sip_transport;\n\tsip_transport_udp_threshold = u.sip_transport_udp_threshold;\n\tuse_nat_public_ip = u.use_nat_public_ip;\n\tnat_public_ip = u.nat_public_ip;\n\tuse_stun = u.use_stun;\n\tstun_server = u.stun_server;\n\tpersistent_tcp = u.persistent_tcp;\n\tenable_nat_keepalive = u.enable_nat_keepalive;\n\ttimer_noanswer = u.timer_noanswer;\n\ttimer_nat_keepalive = u.timer_nat_keepalive; \n\ttimer_tcp_ping = u.timer_tcp_ping;\n\tdisplay_useronly_phone = u.display_useronly_phone;\n\tnumerical_user_is_phone = u.numerical_user_is_phone;\n\tremove_special_phone_symbols = u.remove_special_phone_symbols;\n\tspecial_phone_symbols = u.special_phone_symbols;\n\tuse_tel_uri_for_phone = u.use_tel_uri_for_phone;\n\tringtone_file = u.ringtone_file;\n\tringback_file = u.ringback_file;\n\tscript_incoming_call = u.script_incoming_call;\n\tscript_in_call_answered = u.script_in_call_answered;\n\tscript_in_call_failed = u.script_in_call_failed;\n\tscript_outgoing_call = u.script_outgoing_call;\n\tscript_out_call_answered = u.script_out_call_answered;\n\tscript_out_call_failed = u.script_out_call_failed;\n\tscript_local_release = u.script_local_release;\n\tscript_remote_release = u.script_remote_release;\n\tnumber_conversions = u.number_conversions;\n\tzrtp_enabled = u.zrtp_enabled;\n\tzrtp_goclear_warning = u.zrtp_goclear_warning;\n\tzrtp_sdp = u.zrtp_sdp;\n\tzrtp_send_if_supported = u.zrtp_send_if_supported;\n\tmwi_solicited = u.mwi_solicited;\n\tmwi_user = u.mwi_user;\n\tmwi_server = u.mwi_server;\n\tmwi_via_proxy = u.mwi_via_proxy;\n\tmwi_subscription_time = u.mwi_subscription_time;\n\tmwi_vm_address = u.mwi_vm_address;\n\tim_max_sessions = u.im_max_sessions;\n\tim_send_iscomposing = u.im_send_iscomposing;\n\tpres_subscription_time = u.pres_subscription_time;\n\tpres_publication_time = u.pres_publication_time;\n\tpres_publish_startup = u.pres_publish_startup;\n\t\n\tu.mtx_user.unlock();\n}\n\nt_user *t_user::copy(void) const {\n\tt_user *u = new t_user(*this);\n\tMEMMAN_NEW(u);\n\treturn u;\n}\n\nstring t_user::get_name(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = name;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_domain(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = domain;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_display(bool anonymous) const {\n\tif (anonymous) return ANONYMOUS_DISPLAY;\n\n\tstring result;\n\tmtx_user.lock();\n\tresult = display;\n\tmtx_user.unlock();\n\treturn result;\n}\n\t\nstring t_user::get_organization(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = organization;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_auth_realm(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = auth_realm;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_auth_name(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = auth_name;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_auth_pass(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = auth_pass;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nvoid t_user::get_auth_aka_op(uint8 *aka_op) const {\n\tt_mutex_guard guard(mtx_user);\n\tmemcpy(aka_op, auth_aka_op, AKA_OPLEN);\n}\n\t\nvoid t_user::get_auth_aka_amf(uint8 *aka_amf) const {\n\tt_mutex_guard guard(mtx_user);\n\tmemcpy(aka_amf, auth_aka_amf, AKA_AMFLEN);\n}\n\nbool t_user::get_use_outbound_proxy(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = use_outbound_proxy;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_url t_user::get_outbound_proxy(void) const {\n\tt_url result;\n\tmtx_user.lock();\n\tresult = outbound_proxy;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_all_requests_to_proxy(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = all_requests_to_proxy;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_non_resolvable_to_proxy(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = non_resolvable_to_proxy;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_use_registrar(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = use_registrar;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_url t_user::get_registrar(void) const {\n\tt_url result;\n\tmtx_user.lock();\n\tresult = registrar;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned long t_user::get_registration_time(void) const {\n\tunsigned long result;\n\tmtx_user.lock();\n\tresult = registration_time;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_register_at_startup(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = register_at_startup;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_reg_add_qvalue(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = reg_add_qvalue;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nfloat t_user::get_reg_qvalue(void) const {\n\tfloat result;\n\tmtx_user.lock();\n\tresult = reg_qvalue;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nlist<t_audio_codec> t_user::get_codecs(void) const {\n\tlist<t_audio_codec> result;\n\tmtx_user.lock();\n\tresult = codecs;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_ptime(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = ptime;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_out_obey_far_end_codec_pref(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = out_obey_far_end_codec_pref;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_in_obey_far_end_codec_pref(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = in_obey_far_end_codec_pref;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_speex_nb_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = speex_nb_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_speex_wb_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = speex_wb_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_speex_uwb_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = speex_uwb_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_bit_rate_type t_user::get_speex_bit_rate_type(void) const {\n\tt_bit_rate_type result;\n\tmtx_user.lock();\n\tresult = speex_bit_rate_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nint t_user::get_speex_abr_nb(void) const {\n\tint result;\n\tmtx_user.lock();\n\tresult = speex_abr_nb;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nint t_user::get_speex_abr_wb(void) const {\n\tint result;\n\tmtx_user.lock();\n\tresult = speex_abr_wb;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_speex_dtx(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = speex_dtx;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_speex_penh(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = speex_penh;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_speex_quality(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = speex_quality;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_speex_complexity(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = speex_complexity;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_speex_dsp_vad(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = speex_dsp_vad;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_speex_dsp_agc(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = speex_dsp_agc;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_speex_dsp_agc_level(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = speex_dsp_agc_level;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_speex_dsp_aec(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = speex_dsp_aec;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_speex_dsp_nrd(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = speex_dsp_nrd;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_ilbc_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = ilbc_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_ilbc_mode(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = ilbc_mode;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_g726_16_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = g726_16_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_g726_24_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = g726_24_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_g726_32_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = g726_32_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_g726_40_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = g726_40_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_g726_packing t_user::get_g726_packing(void) const {\n\tt_g726_packing result;\n\tmtx_user.lock();\n\tresult = g726_packing;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_dtmf_transport t_user::get_dtmf_transport(void) const {\n\tt_dtmf_transport result;\n\tmtx_user.lock();\n\tresult = dtmf_transport;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_dtmf_payload_type(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = dtmf_payload_type;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_dtmf_duration(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = dtmf_duration;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_dtmf_pause(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = dtmf_pause;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_dtmf_volume(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = dtmf_volume;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_hold_variant t_user::get_hold_variant(void) const {\n\tt_hold_variant result;\n\tmtx_user.lock();\n\tresult = hold_variant;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_check_max_forwards(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = check_max_forwards;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_allow_missing_contact_reg(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = allow_missing_contact_reg;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_registration_time_in_contact(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = registration_time_in_contact;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_compact_headers(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = compact_headers;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_encode_multi_values_as_list(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = encode_multi_values_as_list;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_use_domain_in_contact(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = use_domain_in_contact;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_allow_sdp_change(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = allow_sdp_change;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_allow_redirection(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = allow_redirection;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_ask_user_to_redirect(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = ask_user_to_redirect;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_max_redirections(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = max_redirections;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_ext_support t_user::get_ext_100rel(void) const {\n\tt_ext_support result;\n\tmtx_user.lock();\n\tresult = ext_100rel;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_ext_replaces(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = ext_replaces;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_referee_hold(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn referee_hold;\n}\n\nbool t_user::get_referrer_hold(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn referrer_hold;\n}\n\nbool t_user::get_allow_refer(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn allow_refer;\n}\n\nbool t_user::get_ask_user_to_refer(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn ask_user_to_refer;\n}\n\nbool t_user::get_auto_refresh_refer_sub(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn auto_refresh_refer_sub;\n}\n\nbool t_user::get_attended_refer_to_aor(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn attended_refer_to_aor;\n}\n\nbool t_user::get_allow_transfer_consultation_inprog(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn allow_transfer_consultation_inprog;\n}\n\nbool t_user::get_send_p_preferred_id(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = send_p_preferred_id;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_send_p_asserted_id(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = send_p_asserted_id;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_sip_transport t_user::get_sip_transport(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn sip_transport;\n}\n\nunsigned short t_user::get_sip_transport_udp_threshold(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn sip_transport_udp_threshold;\n}\n\nbool t_user::get_use_nat_public_ip(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = use_nat_public_ip;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_nat_public_ip(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = nat_public_ip;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_use_stun(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = use_stun;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_url t_user::get_stun_server(void) const {\n\tt_url result;\n\tmtx_user.lock();\n\tresult = stun_server;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_persistent_tcp(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn persistent_tcp;\n}\n\nbool t_user::get_enable_nat_keepalive(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn enable_nat_keepalive;\n}\n\nunsigned short t_user::get_timer_noanswer(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = timer_noanswer;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_timer_nat_keepalive(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = timer_nat_keepalive;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_timer_tcp_ping(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn timer_tcp_ping;\n}\n \nbool t_user::get_display_useronly_phone(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = display_useronly_phone;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_numerical_user_is_phone(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = numerical_user_is_phone;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_remove_special_phone_symbols(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = remove_special_phone_symbols;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_special_phone_symbols(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = special_phone_symbols;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_use_tel_uri_for_phone(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn use_tel_uri_for_phone;\n}\n\nstring t_user::get_ringtone_file(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = ringtone_file;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_ringback_file(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = ringback_file;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_incoming_call(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_incoming_call;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_in_call_answered(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_in_call_answered;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_in_call_failed(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_in_call_failed;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_outgoing_call(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_outgoing_call;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_out_call_answered(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_out_call_answered;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_out_call_failed(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_out_call_failed;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_local_release(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_local_release;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_script_remote_release(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = script_remote_release;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nlist<t_number_conversion> t_user::get_number_conversions(void) const {\n\tlist<t_number_conversion> result;\n\tmtx_user.lock();\n\tresult = number_conversions;\n\tmtx_user.unlock();\n\treturn result;\t\n}\n\nbool t_user::get_zrtp_enabled(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = zrtp_enabled;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_zrtp_goclear_warning(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = zrtp_goclear_warning;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_zrtp_sdp(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = zrtp_sdp;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_zrtp_send_if_supported(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = zrtp_send_if_supported;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_mwi_solicited(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = mwi_solicited;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_mwi_user(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = mwi_user;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nt_url t_user::get_mwi_server(void) const {\n\tt_url result;\n\tmtx_user.lock();\n\tresult = mwi_server;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_mwi_via_proxy(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = mwi_via_proxy;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned long t_user::get_mwi_subscription_time(void) const {\n\tunsigned long result;\n\tmtx_user.lock();\n\tresult = mwi_subscription_time;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nstring t_user::get_mwi_vm_address(void) const {\n\tstring result;\n\tmtx_user.lock();\n\tresult = mwi_vm_address;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned short t_user::get_im_max_sessions(void) const {\n\tunsigned short result;\n\tmtx_user.lock();\n\tresult = im_max_sessions;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_im_send_iscomposing(void) const {\n\tt_mutex_guard guard(mtx_user);\n\treturn im_send_iscomposing;\n}\n\nunsigned long t_user::get_pres_subscription_time(void) const {\n\tunsigned long result;\n\tmtx_user.lock();\n\tresult = pres_subscription_time;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nunsigned long t_user::get_pres_publication_time(void) const {\n\tunsigned long result;\n\tmtx_user.lock();\n\tresult = pres_publication_time;\n\tmtx_user.unlock();\n\treturn result;\n}\n\nbool t_user::get_pres_publish_startup(void) const {\n\tbool result;\n\tmtx_user.lock();\n\tresult = pres_publish_startup;\n\tmtx_user.unlock();\n\treturn result;\n}\n\n\t\nvoid t_user::set_name(const string &_name) {\n\tmtx_user.lock();\n\tname = _name;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_domain(const string &_domain) {\n\tmtx_user.lock();\n\tdomain = _domain;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_display(const string &_display) {\t\n\tmtx_user.lock();\n\tdisplay = _display;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_organization(const string &_organization) {\n\tmtx_user.lock();\n\torganization = _organization;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_auth_realm(const string &realm) {\n\tmtx_user.lock();\n\tauth_realm = realm;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_auth_name(const string &name) {\n\tmtx_user.lock();\n\tauth_name = name;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_auth_pass(const string &pass) {\n\tmtx_user.lock();\n\tauth_pass = pass;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_auth_aka_op(const uint8 *aka_op) {\n\tt_mutex_guard guard(mtx_user);\n\tmemcpy(auth_aka_op, aka_op, AKA_OPLEN);\n}\n\nvoid t_user::set_auth_aka_amf(const uint8 *aka_amf) {\n\tt_mutex_guard guard(mtx_user);\n\tmemcpy(auth_aka_amf, aka_amf, AKA_AMFLEN);\n}\n\nvoid t_user::set_use_outbound_proxy(bool b) {\n\tmtx_user.lock();\n\tuse_outbound_proxy = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_outbound_proxy(const t_url &url) {\n\tmtx_user.lock();\n\toutbound_proxy = url;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_all_requests_to_proxy(bool b) {\n\tmtx_user.lock();\n\tall_requests_to_proxy = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_non_resolvable_to_proxy(bool b) {\n\tmtx_user.lock();\n\tnon_resolvable_to_proxy = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_use_registrar(bool b) {\n\tmtx_user.lock();\n\tuse_registrar = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_registrar(const t_url &url) {\n\tmtx_user.lock();\n\tregistrar = url;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_registration_time(const unsigned long time) {\n\tmtx_user.lock();\n\tregistration_time = time;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_register_at_startup(bool b) {\n\tmtx_user.lock();\n\tregister_at_startup = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_reg_add_qvalue(bool b) {\n\tmtx_user.lock();\n\treg_add_qvalue = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_reg_qvalue(float q) {\n\tmtx_user.lock();\n\treg_qvalue = q;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_codecs(const list<t_audio_codec> &_codecs) {\n\tmtx_user.lock();\n\tcodecs = _codecs;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ptime(unsigned short _ptime) {\n\tmtx_user.lock();\n\tptime = _ptime;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_out_obey_far_end_codec_pref(bool b) {\n\tmtx_user.lock();\n\tout_obey_far_end_codec_pref = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_in_obey_far_end_codec_pref(bool b) {\n\tmtx_user.lock();\n\tin_obey_far_end_codec_pref = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_nb_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tspeex_nb_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_wb_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tspeex_wb_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_uwb_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tspeex_uwb_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_bit_rate_type(t_bit_rate_type bit_rate_type) {\n\tmtx_user.lock();\n\tspeex_bit_rate_type = bit_rate_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_abr_nb(int abr) {\n\tmtx_user.lock();\n\tspeex_abr_nb = abr;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_abr_wb(int abr) {\n\tmtx_user.lock();\n\tspeex_abr_wb = abr;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_dtx(bool b) {\n\tmtx_user.lock();\n\tspeex_dtx = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_penh(bool b) {\n\tmtx_user.lock();\n\tspeex_penh = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_quality(unsigned short quality) {\n\tmtx_user.lock();\n\tspeex_quality = quality;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_complexity(unsigned short complexity) {\n\tmtx_user.lock();\n\tspeex_complexity = complexity;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_dsp_vad(bool b) {\n\tmtx_user.lock();\n\tspeex_dsp_vad = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_dsp_agc(bool b) {\n\tmtx_user.lock();\n\tspeex_dsp_agc = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_dsp_agc_level(unsigned short level) {\n\tmtx_user.lock();\n\tspeex_dsp_agc_level = level;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_dsp_aec(bool b) {\n\tmtx_user.lock();\n\tspeex_dsp_aec = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_speex_dsp_nrd(bool b) {\n\tmtx_user.lock();\n\tspeex_dsp_nrd = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ilbc_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tilbc_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ilbc_mode(unsigned short mode) {\n\tmtx_user.lock();\n\tilbc_mode = mode;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_g726_16_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tg726_16_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_g726_24_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tg726_24_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_g726_32_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tg726_32_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_g726_40_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tg726_40_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_g726_packing(t_g726_packing packing) {\n\tmtx_user.lock();\n\tg726_packing = packing;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_dtmf_transport(t_dtmf_transport _dtmf_transport) {\n\tmtx_user.lock();\n\tdtmf_transport = _dtmf_transport;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_dtmf_payload_type(unsigned short payload_type) {\n\tmtx_user.lock();\n\tdtmf_payload_type = payload_type;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_dtmf_duration(unsigned short duration) {\n\tmtx_user.lock();\n\tdtmf_duration = duration;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_dtmf_pause(unsigned short pause) {\n\tmtx_user.lock();\n\tdtmf_pause = pause;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_dtmf_volume(unsigned short volume) {\n\tmtx_user.lock();\n\tdtmf_volume = volume;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_hold_variant(t_hold_variant _hold_variant) {\n\tmtx_user.lock();\n\thold_variant = _hold_variant;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_check_max_forwards(bool b) {\n\tmtx_user.lock();\n\tcheck_max_forwards = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_allow_missing_contact_reg(bool b) {\n\tmtx_user.lock();\n\tallow_missing_contact_reg = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_registration_time_in_contact(bool b) {\n\tmtx_user.lock();\n\tregistration_time_in_contact = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_compact_headers(bool b) {\n\tmtx_user.lock();\n\tcompact_headers = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_encode_multi_values_as_list(bool b) {\n\tmtx_user.lock();\n\tencode_multi_values_as_list = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_use_domain_in_contact(bool b) {\n\tmtx_user.lock();\n\tuse_domain_in_contact = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_allow_sdp_change(bool b) {\n\tmtx_user.lock();\n\tallow_sdp_change = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_allow_redirection(bool b) {\n\tmtx_user.lock();\n\tallow_redirection = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ask_user_to_redirect(bool b) {\n\tmtx_user.lock();\n\task_user_to_redirect = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_max_redirections(unsigned short _max_redirections) {\n\tmtx_user.lock();\n\tmax_redirections = _max_redirections;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ext_100rel(t_ext_support ext_support) {\n\tmtx_user.lock();\n\text_100rel = ext_support;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ext_replaces(bool b) {\n\tmtx_user.lock();\n\text_replaces = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_referee_hold(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\treferee_hold = b;\n}\n\nvoid t_user::set_referrer_hold(bool b) {\n\tmtx_user.lock();\n\treferrer_hold = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_allow_refer(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tallow_refer = b;\n}\n\nvoid t_user::set_ask_user_to_refer(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\task_user_to_refer = b;\n}\n\nvoid t_user::set_auto_refresh_refer_sub(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tauto_refresh_refer_sub = b;\n}\n\nvoid t_user::set_attended_refer_to_aor(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tattended_refer_to_aor = b;\n}\n\nvoid t_user::set_allow_transfer_consultation_inprog(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tallow_transfer_consultation_inprog = b;\n}\n\nvoid t_user::set_send_p_preferred_id(bool b) {\n\tmtx_user.lock();\n\tsend_p_preferred_id = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_send_p_asserted_id(bool b) {\n\tmtx_user.lock();\n\tsend_p_asserted_id = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_sip_transport(t_sip_transport transport) {\n\tt_mutex_guard guard(mtx_user);\n\tsip_transport = transport;\n}\n\nvoid t_user::set_sip_transport_udp_threshold(unsigned short threshold) {\n\tt_mutex_guard guard(mtx_user);\n\tsip_transport_udp_threshold = threshold;\n}\n\nvoid t_user::set_use_nat_public_ip(bool b) {\n\tmtx_user.lock();\n\tuse_nat_public_ip = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_nat_public_ip(const string &public_ip) {\n\tmtx_user.lock();\n\tnat_public_ip = public_ip;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_use_stun(bool b) {\n\tmtx_user.lock();\n\tuse_stun = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_stun_server(const t_url &url) {\n\tmtx_user.lock();\n\tstun_server = url;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_persistent_tcp(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tpersistent_tcp = b;\n}\n\nvoid t_user::set_enable_nat_keepalive(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tenable_nat_keepalive = b;\n}\n\nvoid t_user::set_timer_noanswer(unsigned short timer) {\n\tmtx_user.lock();\n\ttimer_noanswer = timer;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_timer_nat_keepalive(unsigned short timer) { \n\tmtx_user.lock();\n\ttimer_nat_keepalive = timer;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_timer_tcp_ping(unsigned short timer) {\n\tt_mutex_guard guard(mtx_user);\n\ttimer_tcp_ping = timer;\n}\n\nvoid t_user::set_display_useronly_phone(bool b) {\n\tmtx_user.lock();\n\tdisplay_useronly_phone = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_numerical_user_is_phone(bool b) {\n\tmtx_user.lock();\n\tnumerical_user_is_phone = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_remove_special_phone_symbols(bool b) {\n\tmtx_user.lock();\n\tremove_special_phone_symbols = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_special_phone_symbols(const string &symbols) {\n\tmtx_user.lock();\n\tspecial_phone_symbols = symbols;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_use_tel_uri_for_phone(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tuse_tel_uri_for_phone = b;\n}\n\nvoid t_user::set_ringtone_file(const string &file) {\n\tmtx_user.lock();\n\tringtone_file = file;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_ringback_file(const string &file) {\n\tmtx_user.lock();\n\tringback_file = file;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_incoming_call(const string &script) {\n\tmtx_user.lock();\n\tscript_incoming_call = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_in_call_answered(const string &script) {\n\tmtx_user.lock();\n\tscript_in_call_answered = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_in_call_failed(const string &script) {\n\tmtx_user.lock();\n\tscript_in_call_failed = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_outgoing_call(const string &script) {\n\tmtx_user.lock();\n\tscript_outgoing_call = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_out_call_answered(const string &script) {\n\tmtx_user.lock();\n\tscript_out_call_answered = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_out_call_failed(const string &script) {\n\tmtx_user.lock();\n\tscript_out_call_failed = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_local_release(const string &script) {\n\tmtx_user.lock();\n\tscript_local_release = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_script_remote_release(const string &script) {\n\tmtx_user.lock();\n\tscript_remote_release = script;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_number_conversions(const list<t_number_conversion> &l) {\n\tmtx_user.lock();\n\tnumber_conversions = l;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_zrtp_enabled(bool b) {\n\tmtx_user.lock();\n\tzrtp_enabled = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_zrtp_goclear_warning(bool b) {\n\tmtx_user.lock();\n\tzrtp_goclear_warning = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_zrtp_sdp(bool b) {\n\tmtx_user.lock();\n\tzrtp_sdp = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_zrtp_send_if_supported(bool b) {\n\tmtx_user.lock();\n\tzrtp_send_if_supported = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_mwi_solicited(bool b) {\n\tmtx_user.lock();\n\tmwi_solicited = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_mwi_user(const string &user) {\n\tmtx_user.lock();\n\tmwi_user = user;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_mwi_server(const t_url &url) {\n\tmtx_user.lock();\n\tmwi_server = url;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_mwi_via_proxy(bool b) {\n\tmtx_user.lock();\n\tmwi_via_proxy = b;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_mwi_subscription_time(unsigned long t) {\n\tmtx_user.lock();\n\tmwi_subscription_time = t;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_mwi_vm_address(const string &address) {\n\tmtx_user.lock();\n\tmwi_vm_address = address;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_im_max_sessions(unsigned short max_sessions) {\n\tmtx_user.lock();\n\tim_max_sessions = max_sessions;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_im_send_iscomposing(bool b) {\n\tt_mutex_guard guard(mtx_user);\n\tim_send_iscomposing = b;\n}\n\nvoid t_user::set_pres_subscription_time(unsigned long t) {\n\tmtx_user.lock();\n\tpres_subscription_time = t;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_pres_publication_time(unsigned long t) {\n\tmtx_user.lock();\n\tpres_publication_time = t;\n\tmtx_user.unlock();\n}\n\nvoid t_user::set_pres_publish_startup(bool b) {\n\tmtx_user.lock();\n\tpres_publish_startup = b;\n\tmtx_user.unlock();\n}\n\nbool t_user::read_config(const string &filename, string &error_msg) {\n\tstring f;\n\tstring msg;\n\t\n\tmtx_user.lock();\n\t\n\tif (filename.size() == 0) {\n\t\terror_msg = \"Cannot read user profile: missing file name.\";\n\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmtx_user.unlock();\n\t\treturn false;\n\t}\n\n\tconfig_filename = filename;\n\tf = expand_filename(filename);\n\n\tifstream config(f.c_str());\n\tif (!config) {\n\t\terror_msg = \"Cannot open file for reading: \";\n\t\terror_msg += f;\n\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmtx_user.unlock();\n\t\treturn false;\n\t}\n\n\tlog_file->write_header(\"t_user::read_config\");\n\tlog_file->write_raw(\"Reading config: \");\n\tlog_file->write_raw(filename);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\n\twhile (!config.eof()) {\n\t\tstring line;\n\t\tgetline(config, line);\n\n\t\t// Check if read operation succeeded\n\t\tif (!config.good() && !config.eof()) {\n\t\t\terror_msg = \"File system error while reading file \";\n\t\t\terror_msg += f;\n\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tmtx_user.unlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tline = trim(line);\n\n\t\t// Skip empty lines\n\t\tif (line.size() == 0) continue;\n\n\t\t// Skip comment lines\n\t\tif (line[0] == '#') continue;\n\n\t\tvector<string> l = split_on_first(line, '=');\n\t\tif (l.size() != 2) {\n\t\t\terror_msg = \"Syntax error in file \";\n\t\t\terror_msg += f;\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += line;\n\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tmtx_user.unlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tstring parameter = trim(l[0]);\n\t\tstring value = trim(l[1]);\n\t\t\n\t\tif (parameter == FLD_NAME) {\n\t\t\tname = value;\n\t\t} else if (parameter == FLD_DOMAIN) {\n\t\t\tdomain = value;\n\t\t} else if (parameter == FLD_DISPLAY) {\n\t\t\tdisplay = value;\n\t\t} else if (parameter == FLD_ORGANIZATION) {\n\t\t\torganization = value;\n\t\t} else if (parameter == FLD_REGISTRATION_TIME) {\n\t\t\tregistration_time = atol(value.c_str());\n\t\t} else if (parameter == FLD_REGISTRATION_TIME_IN_CONTACT) {\n\t\t\tregistration_time_in_contact = yesno2bool(value);\n\t\t} else if (parameter == FLD_REGISTRAR) {\n\t\t\tuse_registrar = set_server_value(registrar, USER_SCHEME, value); \n\t\t} else if (parameter == FLD_REGISTER_AT_STARTUP) {\n\t\t\tregister_at_startup = yesno2bool(value);\n\t\t} else if (parameter == FLD_REG_ADD_QVALUE) {\n\t\t\treg_add_qvalue = yesno2bool(value);\n\t\t} else if (parameter == FLD_REG_QVALUE) {\n\t\t\treg_qvalue = atof(value.c_str());\n\t\t} else if (parameter == FLD_OUTBOUND_PROXY) {\n\t\t\tuse_outbound_proxy = set_server_value(outbound_proxy,\n\t\t\t\t\tUSER_SCHEME, value);\n\t\t} else if (parameter == FLD_ALL_REQUESTS_TO_PROXY) {\n\t\t\tall_requests_to_proxy = yesno2bool(value);\n\t\t} else if (parameter == FLD_NON_RESOLVABLE_TO_PROXY) {\n\t\t\tnon_resolvable_to_proxy = yesno2bool(value);\n\t\t} else if (parameter == FLD_AUTH_REALM) {\n\t\t\tauth_realm = value;\n\t\t} else if (parameter == FLD_AUTH_NAME) {\n\t\t\tauth_name = value;\n\t\t} else if (parameter == FLD_AUTH_PASS) {\n\t\t\tauth_pass = value;\n\t\t} else if (parameter == FLD_AUTH_AKA_OP) {\n\t\t\thex2binary(value, auth_aka_op);\n\t\t} else if (parameter == FLD_AUTH_AKA_AMF) {\n\t\t\thex2binary(value, auth_aka_amf);\n\t\t} else if (parameter == FLD_CODECS) {\n\t\t\tvector<string> l = split(value, ',');\n\t\t\tif (l.size() > 0) codecs.clear();\n\t\t\tfor (vector<string>::iterator i = l.begin();\n\t\t\t     i != l.end(); i++)\n\t\t\t{\n\t\t\t\tstring codec = trim(*i);\n\t\t\t\tif (codec == \"g711a\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G711_ALAW);\n\t\t\t\t} else if (codec == \"g711u\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G711_ULAW);\n\t\t\t\t} else if (codec == \"gsm\") {\n\t\t\t\t\tcodecs.push_back(CODEC_GSM);\n#ifdef HAVE_SPEEX\n\t\t\t\t} else if (codec == \"speex-nb\") {\n\t\t\t\t\tcodecs.push_back(CODEC_SPEEX_NB);\n\t\t\t\t} else if (codec == \"speex-wb\") {\n\t\t\t\t\tcodecs.push_back(CODEC_SPEEX_WB);\n\t\t\t\t} else if (codec == \"speex-uwb\") {\n\t\t\t\t\tcodecs.push_back(CODEC_SPEEX_UWB);\n#endif\n#ifdef HAVE_ILBC\n\t\t\t\t} else if (codec == \"ilbc\") {\n\t\t\t\t\tcodecs.push_back(CODEC_ILBC);\n#endif\n\t\t\t\t} else if (codec == \"g722\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G722);\n\t\t\t\t} else if (codec == \"g726-16\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G726_16);\n\t\t\t\t} else if (codec == \"g726-24\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G726_24);\n\t\t\t\t} else if (codec == \"g726-32\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G726_32);\n\t\t\t\t} else if (codec == \"g726-40\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G726_40);\n#ifdef HAVE_BCG729\n\t\t\t\t} else if (codec == \"g729a\") {\n\t\t\t\t\tcodecs.push_back(CODEC_G729A);\n#endif\n\t\t\t\t} else {\n\t\t\t\t\tmsg = \"Syntax error in file \";\n\t\t\t\t\tmsg += f;\n\t\t\t\t\tmsg += \"\\n\";\n\t\t\t\t\tmsg += \"Invalid codec: \";\n\t\t\t\t\tmsg += value;\n\t\t\t\t\tlog_file->write_report(msg,\n\t\t\t\t\t\t\"t_user::read_config\",\n\t\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (parameter == FLD_PTIME) {\n\t\t\tptime = atoi(value.c_str());\n\t\t} else if (parameter == FLD_OUT_FAR_END_CODEC_PREF) {\n\t\t\tout_obey_far_end_codec_pref = yesno2bool(value);\n\t\t} else if (parameter == FLD_IN_FAR_END_CODEC_PREF) {\n\t\t\tin_obey_far_end_codec_pref = yesno2bool(value);\n\t\t} else if (parameter == FLD_HOLD_VARIANT) {\n\t\t\tif (value == \"rfc2543\") {\n\t\t\t\thold_variant = HOLD_RFC2543;\n\t\t\t} else if (value == \"rfc3264\") {\n\t\t\t\thold_variant = HOLD_RFC3264;\n\t\t\t} else {\n\t\t\t\terror_msg = \"Syntax error in file \";\n\t\t\t\terror_msg += f;\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += \"Invalid hold variant: \";\n\t\t\t\terror_msg += value;\n\t\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tmtx_user.unlock();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (parameter == FLD_CHECK_MAX_FORWARDS) {\n\t\t\tcheck_max_forwards = yesno2bool(value);\n\t\t} else if (parameter == FLD_ALLOW_MISSING_CONTACT_REG) {\n\t\t\tallow_missing_contact_reg = yesno2bool(value);\n\t\t} else if (parameter == FLD_USE_DOMAIN_IN_CONTACT) {\n\t\t\tuse_domain_in_contact = yesno2bool(value);\n\t\t} else if (parameter == FLD_ALLOW_SDP_CHANGE) {\n\t\t\tallow_sdp_change = yesno2bool(value);\n\t\t} else if (parameter == FLD_ALLOW_REDIRECTION) {\n\t\t\tallow_redirection = yesno2bool(value);\n\t\t} else if (parameter == FLD_ASK_USER_TO_REDIRECT) {\n\t\t\task_user_to_redirect = yesno2bool(value);\n\t\t} else if (parameter == FLD_MAX_REDIRECTIONS) {\n\t\t\tmax_redirections = atoi(value.c_str());\n\t\t} else if (parameter == FLD_REFEREE_HOLD) {\n\t\t\treferee_hold = yesno2bool(value);\n\t\t} else if (parameter == FLD_REFERRER_HOLD) {\n\t\t\treferrer_hold = yesno2bool(value);\n\t\t} else if (parameter == FLD_ALLOW_REFER) {\n\t\t\tallow_refer = yesno2bool(value);\n\t\t} else if (parameter == FLD_ASK_USER_TO_REFER) {\n\t\t\task_user_to_refer = yesno2bool(value);\n\t\t} else if (parameter == FLD_AUTO_REFRESH_REFER_SUB) {\n\t\t\tauto_refresh_refer_sub = yesno2bool(value);\n\t\t} else if (parameter == FLD_ATTENDED_REFER_TO_AOR) {\n\t\t\tattended_refer_to_aor = yesno2bool(value);\n\t\t} else if (parameter == FLD_ALLOW_XFER_CONSULT_INPROG) {\n\t\t\tallow_transfer_consultation_inprog = yesno2bool(value);\n\t\t} else if (parameter == FLD_SEND_P_PREFERRED_ID) {\n\t\t\tsend_p_preferred_id = yesno2bool(value);\n\t\t} else if (parameter == FLD_SEND_P_ASSERTED_ID) {\n\t\t\tsend_p_asserted_id = yesno2bool(value);\n\t\t} else if (parameter == FLD_SIP_TRANSPORT) {\n\t\t\tsip_transport = str2sip_transport(value);\n\t\t} else if (parameter == FLD_SIP_TRANSPORT_UDP_THRESHOLD) {\n\t\t\tsip_transport_udp_threshold = atoi(value.c_str());\n\t\t} else if (parameter == FLD_NAT_PUBLIC_IP) {\n\t\t\tif (value.size() == 0) continue;\n\t\t\tuse_nat_public_ip = true;\n\t\t\tnat_public_ip = value;\n\t\t} else if (parameter == FLD_STUN_SERVER) {\n\t\t\tuse_stun = set_server_value(stun_server, \"stun\", value);\n\t\t} else if (parameter == FLD_PERSISTENT_TCP) {\n\t\t\tpersistent_tcp = yesno2bool(value);\n\t\t} else if (parameter == FLD_ENABLE_NAT_KEEPALIVE) {\n\t\t\tenable_nat_keepalive = yesno2bool(value);\n\t\t} else if (parameter == FLD_TIMER_NOANSWER) {\n\t\t\ttimer_noanswer = atoi(value.c_str());\n\t\t} else if (parameter == FLD_TIMER_NAT_KEEPALIVE) {\n\t\t\ttimer_nat_keepalive = atoi(value.c_str());\n\t\t} else if (parameter == FLD_TIMER_TCP_PING) {\n\t\t\ttimer_tcp_ping = atoi(value.c_str());\n\t\t} else if (parameter == FLD_EXT_100REL) {\n\t\t\text_100rel = str2ext_support(value);\n\t\t\tif (ext_100rel == EXT_INVALID) {\n\t\t\t\terror_msg = \"Syntax error in file \";\n\t\t\t\terror_msg += f;\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += \"Invalid value for ext_100rel: \";\n\t\t\t\terror_msg += value;\n\t\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tmtx_user.unlock();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (parameter == FLD_EXT_REPLACES) {\n\t\t\text_replaces = yesno2bool(value);\n\t\t} else if (parameter == FLD_COMPACT_HEADERS) {\n\t\t\tcompact_headers = yesno2bool(value);\n\t\t} else if (parameter == FLD_ENCODE_MULTI_VALUES_AS_LIST) {\n\t\t\tencode_multi_values_as_list = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_NB_PAYLOAD_TYPE) {\n\t\t\tspeex_nb_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SPEEX_WB_PAYLOAD_TYPE) {\n\t\t\tspeex_wb_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SPEEX_UWB_PAYLOAD_TYPE) {\n\t\t\tspeex_uwb_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SPEEX_BIT_RATE_TYPE) {\n\t\t\tspeex_bit_rate_type = str2bit_rate_type(value);\n\t\t\tif (speex_bit_rate_type == BIT_RATE_INVALID) {\n\t\t\t\terror_msg = \"Syntax error in file \";\n\t\t\t\terror_msg += f;\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += \"Invalid value for speex bit rate type: \";\n\t\t\t\terror_msg += value;\n\t\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tmtx_user.unlock();\n\t\t\t\treturn false;\t\t\n\t\t\t}\n\t\t} else if (parameter == FLD_SPEEX_ABR_NB) {\n\t\t\tspeex_abr_nb = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SPEEX_ABR_WB) {\n\t\t\tspeex_abr_wb = atoi(value.c_str());\n\t\t} else if (parameter == FLD_SPEEX_DTX) {\n\t\t\tspeex_dtx = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_PENH) {\n\t\t\tspeex_penh = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_QUALITY) {\n\t\t\tspeex_quality = atoi(value.c_str());\n\t\t\tif (speex_quality > 10) {\n\t\t\t\terror_msg = \"Syntax error in file \";\n\t\t\t\terror_msg += f;\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += \"Invalid value for speex quality: \";\n\t\t\t\terror_msg += value;\n\t\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tmtx_user.unlock();\n\t\t\t\treturn false;\t\n\t\t\t}\n\t\t} else if (parameter == FLD_SPEEX_COMPLEXITY) {\n\t\t\tspeex_complexity = atoi(value.c_str());\n\t\t\tif (speex_complexity < 1 || speex_complexity > 10) {\n\t\t\t\terror_msg = \"Syntax error in file \";\n\t\t\t\terror_msg += f;\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += \"Invalid value for speex complexity: \";\n\t\t\t\terror_msg += value;\n\t\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tmtx_user.unlock();\n\t\t\t\treturn false;\t\n\t\t\t}\n\t\t} else if (parameter == FLD_SPEEX_DSP_VAD) {\n\t\t\tspeex_dsp_vad = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_DSP_AGC) {\n\t\t\tspeex_dsp_agc = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_DSP_AEC) {\n\t\t\tspeex_dsp_aec = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_DSP_NRD) {\n\t\t\tspeex_dsp_nrd = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPEEX_DSP_AGC_LEVEL) {\n\t\t\tspeex_dsp_agc_level = atoi(value.c_str());\n\t\t\tif (speex_dsp_agc_level < 1 || speex_dsp_agc_level > 100) {\n\t\t\t\terror_msg = \"Syntax error in file \";\n\t\t\t\terror_msg += f;\n\t\t\t\terror_msg += \"\\n\";\n\t\t\t\terror_msg += \"Invalid value for automatic gain control level: \";\n\t\t\t\terror_msg += value;\n\t\t\t\tlog_file->write_report(error_msg, \"t_user::read_config\",\n\t\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\t\tmtx_user.unlock();\n\t\t\t\treturn false;\t\n\t\t\t}\n\t\t} else if (parameter == FLD_ILBC_PAYLOAD_TYPE) {\n\t\t\tilbc_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_ILBC_MODE) {\n\t\t\tilbc_mode = atoi(value.c_str());\n\t\t} else if (parameter == FLD_G726_16_PAYLOAD_TYPE) {\n\t\t\tg726_16_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_G726_24_PAYLOAD_TYPE) {\n\t\t\tg726_24_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_G726_32_PAYLOAD_TYPE) {\n\t\t\tg726_32_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_G726_40_PAYLOAD_TYPE) {\n\t\t\tg726_40_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_G726_PACKING) {\n\t\t\tg726_packing = str2g726_packing(value);\n\t\t} else if (parameter == FLD_DTMF_TRANSPORT) {\n\t\t\tdtmf_transport = str2dtmf_transport(value);\t\n\t\t} else if (parameter == FLD_DTMF_PAYLOAD_TYPE) {\n\t\t\tdtmf_payload_type = atoi(value.c_str());\n\t\t} else if (parameter == FLD_DTMF_DURATION) {\n\t\t\tdtmf_duration = atoi(value.c_str());\n\t\t} else if (parameter == FLD_DTMF_PAUSE) {\n\t\t\tdtmf_pause = atoi(value.c_str());\n\t\t} else if (parameter == FLD_DTMF_VOLUME) {\n\t\t\tdtmf_volume = atoi(value.c_str());\n\t\t} else if (parameter == FLD_DISPLAY_USERONLY_PHONE) {\n\t\t\tdisplay_useronly_phone = yesno2bool(value);\n\t\t} else if (parameter == FLD_NUMERICAL_USER_IS_PHONE) {\n\t\t\tnumerical_user_is_phone = yesno2bool(value);\n\t\t} else if (parameter == FLD_REMOVE_SPECIAL_PHONE_SYM) {\n\t\t\tremove_special_phone_symbols = yesno2bool(value);\n\t\t} else if (parameter == FLD_SPECIAL_PHONE_SYMBOLS) {\n\t\t\tspecial_phone_symbols = value;\n\t\t} else if (parameter == FLD_USE_TEL_URI_FOR_PHONE) {\n\t\t\tuse_tel_uri_for_phone = yesno2bool(value);\n\t\t} else if (parameter == FLD_USER_RINGTONE_FILE) {\n\t\t\tringtone_file = value;\n\t\t} else if (parameter == FLD_USER_RINGBACK_FILE) {\n\t\t\tringback_file = value;\n\t\t} else if (parameter == FLD_SCRIPT_INCOMING_CALL) {\n\t\t\tscript_incoming_call = value;\n\t\t} else if (parameter == FLD_SCRIPT_IN_CALL_ANSWERED) {\n\t\t\tscript_in_call_answered = value;\n\t\t} else if (parameter == FLD_SCRIPT_IN_CALL_FAILED) {\n\t\t\tscript_in_call_failed = value;\n\t\t} else if (parameter == FLD_SCRIPT_OUTGOING_CALL) {\n\t\t\tscript_outgoing_call = value;\n\t\t} else if (parameter == FLD_SCRIPT_OUT_CALL_ANSWERED) {\n\t\t\tscript_out_call_answered = value;\n\t\t} else if (parameter == FLD_SCRIPT_OUT_CALL_FAILED) {\n\t\t\tscript_out_call_failed = value;\n\t\t} else if (parameter == FLD_SCRIPT_LOCAL_RELEASE) {\n\t\t\tscript_local_release = value;\n\t\t} else if (parameter == FLD_SCRIPT_REMOTE_RELEASE) {\n\t\t\tscript_remote_release = value;\n\t\t} else if (parameter == FLD_NUMBER_CONVERSION) {\n\t\t\tt_number_conversion c;\n\t\t\tif (parse_num_conversion(value, c)) {\n\t\t\t\tnumber_conversions.push_back(c);\n\t\t\t}\n\t\t} else if (parameter == FLD_ZRTP_ENABLED) {\n\t\t\tzrtp_enabled = yesno2bool(value);\n\t\t} else if (parameter == FLD_ZRTP_GOCLEAR_WARNING) {\n\t\t\tzrtp_goclear_warning = yesno2bool(value);\n\t\t} else if (parameter == FLD_ZRTP_SDP) {\n\t\t\tzrtp_sdp = yesno2bool(value);\n\t\t} else if (parameter == FLD_ZRTP_SEND_IF_SUPPORTED) {\n\t\t\tzrtp_send_if_supported = yesno2bool(value);\n\t\t} else if (parameter == FLD_MWI_SOLICITED) {\n\t\t\tmwi_solicited = yesno2bool(value);\n\t\t} else if (parameter == FLD_MWI_USER) {\n\t\t\tmwi_user = value;\n\t\t} else if (parameter == FLD_MWI_SERVER) {\n\t\t\t(void)set_server_value(mwi_server, USER_SCHEME, value);\n\t\t} else if (parameter == FLD_MWI_VIA_PROXY) {\n\t\t\tmwi_via_proxy = yesno2bool(value);\n\t\t} else if (parameter == FLD_MWI_SUBSCRIPTION_TIME) {\n\t\t\tmwi_subscription_time = atol(value.c_str());\n\t\t} else if (parameter == FLD_MWI_VM_ADDRESS) {\n\t\t\tmwi_vm_address = value;\n\t\t} else if (parameter == FLD_IM_MAX_SESSIONS) {\n\t\t\tim_max_sessions = atoi(value.c_str());\n\t\t} else if (parameter == FLD_IM_SEND_ISCOMPOSING) {\n\t\t\tim_send_iscomposing = yesno2bool(value);\n\t\t} else if (parameter == FLD_PRES_SUBSCRIPTION_TIME) {\n\t\t\tpres_subscription_time = atol(value.c_str());\n\t\t} else if (parameter == FLD_PRES_PUBLICATION_TIME) {\n\t\t\tpres_publication_time = atol(value.c_str());\n\t\t} else if (parameter == FLD_PRES_PUBLISH_STARTUP) {\n\t\t\tpres_publish_startup = yesno2bool(value);\n\t\t} else {\n\t\t\t// Ignore unknown parameters. Only report in log file.\n\t\t\tlog_file->write_header(\"t_user::read_config\",\n\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Unknown parameter in user profile: \");\n\t\t\tlog_file->write_raw(parameter);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t}\n\t}\n\n\t// Set parser options\n\tt_parser::check_max_forwards = check_max_forwards;\n\tt_parser::compact_headers = compact_headers;\n\tt_parser::multi_values_as_list = encode_multi_values_as_list;\n\n\tmtx_user.unlock();\n\treturn true;\n}\n\nbool t_user::write_config(const string &filename, string &error_msg) {\n\tstruct stat stat_buf;\n\tstring f;\n\t\n\tmtx_user.lock();\n\n\tif (filename.size() == 0) {\n\t\terror_msg = \"Cannot write user profile: missing file name.\";\n\t\tlog_file->write_report(error_msg, \"t_user::write_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmtx_user.unlock();\n\t\treturn false;\n\t}\n\n\tconfig_filename = filename;\n\tf = expand_filename(filename);\n\n\t// Make a backup of the file if we are editing an existing file, so\n\t// that can be restored when writing fails.\n\tstring f_backup = f + '~';\n\tif (stat(f.c_str(), &stat_buf) == 0) {\n\t\tif (rename(f.c_str(), f_backup.c_str()) != 0) {\n\t\t\tstring err = get_error_str(errno);\n\t\t\terror_msg = \"Failed to backup \";\n\t\t\terror_msg += f;\n\t\t\terror_msg += \" to \";\n\t\t\terror_msg += f_backup;\n\t\t\terror_msg += \"\\n\";\n\t\t\terror_msg += err;\n\t\t\tlog_file->write_report(error_msg, \"t_user::write_config\",\n\t\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\t\tmtx_user.unlock();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tofstream config(f.c_str());\n\tif (!config) {\n\t\terror_msg = \"Cannot open file for writing: \";\n\t\terror_msg += f;\n\t\tlog_file->write_report(error_msg, \"t_user::write_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmtx_user.unlock();\n\t\treturn false;\n\t}\n\n\tlog_file->write_header(\"t_user::write_config\");\n\tlog_file->write_raw(\"Writing config: \");\n\tlog_file->write_raw(filename);\n\tlog_file->write_endl();\n\tlog_file->write_footer();\n\n\t// Write USER settings\n\tconfig << \"# USER\\n\";\n\tconfig << FLD_NAME << '=' << name << endl;\n\tconfig << FLD_DOMAIN << '=' << domain << endl;\n\tconfig << FLD_DISPLAY << '=' << display << endl;\n\tconfig << FLD_ORGANIZATION << '=' << organization << endl;\n\tconfig << FLD_AUTH_REALM << '=' << auth_realm << endl;\n\tconfig << FLD_AUTH_NAME << '=' << auth_name << endl;\n\tconfig << FLD_AUTH_PASS << '=' << auth_pass << endl;\n\tconfig << FLD_AUTH_AKA_OP << '=' << binary2hex(auth_aka_op, AKA_OPLEN) << endl;\n\tconfig << FLD_AUTH_AKA_AMF << '=' << binary2hex(auth_aka_amf, AKA_AMFLEN) << endl;\n\tconfig << endl;\n\n\t// Write SIP SERVER settings\n\tconfig << \"# SIP SERVER\\n\";\n\tif (use_outbound_proxy) {\n\t\tconfig << FLD_OUTBOUND_PROXY << '=';\n\t\tconfig << outbound_proxy.encode_noscheme() << endl;\n\t\tconfig << FLD_ALL_REQUESTS_TO_PROXY << '=';\n\t\tconfig << bool2yesno(all_requests_to_proxy) << endl;\n\t\tconfig << FLD_NON_RESOLVABLE_TO_PROXY << '=';\n\t\tconfig << bool2yesno(non_resolvable_to_proxy) << endl;\n\t} else {\n\t\tconfig << FLD_OUTBOUND_PROXY << '=' << endl;\n\t\tconfig << FLD_ALL_REQUESTS_TO_PROXY << \"=no\" << endl;\n\t}\n\tif (use_registrar) {\n\t\tconfig << FLD_REGISTRAR << '=' << registrar.encode_noscheme();\n\t\tconfig << endl;\n\t} else {\n\t\tconfig << FLD_REGISTRAR << '=' << endl;\n\t}\n\tconfig << FLD_REGISTER_AT_STARTUP << '=';\n\tconfig << bool2yesno(register_at_startup) << endl;\n\tconfig << FLD_REGISTRATION_TIME << '=' << registration_time << endl;\n\tconfig << FLD_REG_ADD_QVALUE << '=' << bool2yesno(reg_add_qvalue) << endl;\n\tconfig << FLD_REG_QVALUE << '=' << reg_qvalue << endl;\n\tconfig << endl;\n\n\t// Write AUDIO settings\n\tconfig << \"# RTP AUDIO\\n\";\n\tconfig << FLD_CODECS << '=';\n\tfor (list<t_audio_codec>::iterator i = codecs.begin();\n\t     i != codecs.end(); i++)\n\t{\n\t\tif (i != codecs.begin()) config << ',';\n\t\tswitch(*i) {\n\t\tcase CODEC_G711_ALAW:\n\t\t\tconfig << \"g711a\";\n\t\t\tbreak;\n\t\tcase CODEC_G711_ULAW:\n\t\t\tconfig << \"g711u\";\n\t\t\tbreak;\n\t\tcase CODEC_GSM:\n\t\t\tconfig << \"gsm\";\n\t\t\tbreak;\n\t\tcase CODEC_SPEEX_NB:\n\t\t\tconfig << \"speex-nb\";\n\t\t\tbreak;\n\t\tcase CODEC_SPEEX_WB:\n\t\t\tconfig << \"speex-wb\";\n\t\t\tbreak;\n\t\tcase CODEC_SPEEX_UWB:\n\t\t\tconfig << \"speex-uwb\";\n\t\t\tbreak;\n\t\tcase CODEC_ILBC:\n\t\t\tconfig << \"ilbc\";\n\t\t\tbreak;\n\t\tcase CODEC_G722:\n\t\t\tconfig << \"g722\";\n\t\t\tbreak;\n\t\tcase CODEC_G726_16:\n\t\t\tconfig << \"g726-16\";\n\t\t\tbreak;\n\t\tcase CODEC_G726_24:\n\t\t\tconfig << \"g726-24\";\n\t\t\tbreak;\n\t\tcase CODEC_G726_32:\n\t\t\tconfig << \"g726-32\";\n\t\t\tbreak;\n\t\tcase CODEC_G726_40:\n\t\t\tconfig << \"g726-40\";\n\t\t\tbreak;\n\t\tcase CODEC_G729A:\n\t\t\tconfig << \"g729a\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t}\n\tconfig << endl;\n\tconfig << FLD_PTIME << '=' << ptime << endl;\n\tconfig << FLD_OUT_FAR_END_CODEC_PREF << '=' << bool2yesno(out_obey_far_end_codec_pref) << endl;\n\tconfig << FLD_IN_FAR_END_CODEC_PREF << '=' << bool2yesno(in_obey_far_end_codec_pref) << endl;\n\tconfig << FLD_SPEEX_NB_PAYLOAD_TYPE << '=' << speex_nb_payload_type << endl;\n\tconfig << FLD_SPEEX_WB_PAYLOAD_TYPE << '=' << speex_wb_payload_type << endl;\n\tconfig << FLD_SPEEX_UWB_PAYLOAD_TYPE << '=' << speex_uwb_payload_type << endl;\n\tconfig << FLD_SPEEX_BIT_RATE_TYPE << '=';\n\t// config << FLD_SPEEX_ABR_NB << '=' << speex_abr_nb << endl;\n\t// config << FLD_SPEEX_ABR_WB << '=' << speex_abr_wb << endl;\n\tconfig << bit_rate_type2str(speex_bit_rate_type) << endl;\n\tconfig << FLD_SPEEX_DTX << '=' << bool2yesno(speex_dtx) << endl;\n\tconfig << FLD_SPEEX_PENH << '=' << bool2yesno(speex_penh) << endl;\n\tconfig << FLD_SPEEX_QUALITY << '=' << speex_quality << endl;\n\tconfig << FLD_SPEEX_COMPLEXITY << '=' << speex_complexity << endl;\n\tconfig << FLD_SPEEX_DSP_VAD << '=' << bool2yesno(speex_dsp_vad) << endl;\n\tconfig << FLD_SPEEX_DSP_AGC << '=' << bool2yesno(speex_dsp_agc) << endl;\n\tconfig << FLD_SPEEX_DSP_AEC << '=' << bool2yesno(speex_dsp_aec) << endl;\n\tconfig << FLD_SPEEX_DSP_NRD << '=' << bool2yesno(speex_dsp_nrd) << endl;\n\tconfig << FLD_SPEEX_DSP_AGC_LEVEL << '=' << speex_dsp_agc_level << endl;\n\tconfig << FLD_ILBC_PAYLOAD_TYPE << '=' << ilbc_payload_type << endl;\n\tconfig << FLD_ILBC_MODE << '=' << ilbc_mode << endl;\n\tconfig << FLD_G726_16_PAYLOAD_TYPE << '=' << g726_16_payload_type << endl;\n\tconfig << FLD_G726_24_PAYLOAD_TYPE << '=' << g726_24_payload_type << endl;\n\tconfig << FLD_G726_32_PAYLOAD_TYPE << '=' << g726_32_payload_type << endl;\n\tconfig << FLD_G726_40_PAYLOAD_TYPE << '=' << g726_40_payload_type << endl;\n\tconfig << FLD_G726_PACKING << '=' << g726_packing2str(g726_packing) << endl;\n\tconfig << FLD_DTMF_TRANSPORT << '=' << dtmf_transport2str(dtmf_transport) << endl;\n\tconfig << FLD_DTMF_PAYLOAD_TYPE << '=' << dtmf_payload_type << endl;\n\tconfig << FLD_DTMF_DURATION << '=' << dtmf_duration << endl;\n\tconfig << FLD_DTMF_PAUSE << '=' << dtmf_pause << endl;\n\tconfig << FLD_DTMF_VOLUME << '=' << dtmf_volume << endl;\n\tconfig << endl;\n\n\t// Write SIP PROTOCOL settings\n\tconfig << \"# SIP PROTOCOL\\n\";\n\tconfig << FLD_HOLD_VARIANT << '=';\n\tswitch(hold_variant) {\n\tcase HOLD_RFC2543:\n\t\tconfig << \"rfc2543\";\n\t\tbreak;\n\tcase HOLD_RFC3264:\n\t\tconfig << \"rfc3264\";\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\tconfig << endl;\n\tconfig << FLD_CHECK_MAX_FORWARDS << '=';\n\tconfig << bool2yesno(check_max_forwards) << endl;\n\tconfig << FLD_ALLOW_MISSING_CONTACT_REG << '=';\n\tconfig << bool2yesno(allow_missing_contact_reg) << endl;\n\tconfig << FLD_REGISTRATION_TIME_IN_CONTACT << '=';\n\tconfig << bool2yesno(registration_time_in_contact) << endl;\n\tconfig << FLD_COMPACT_HEADERS << '=' << bool2yesno(compact_headers) << endl;\n\tconfig << FLD_ENCODE_MULTI_VALUES_AS_LIST << '=';\n\tconfig << bool2yesno(encode_multi_values_as_list) << endl;\n\tconfig << FLD_USE_DOMAIN_IN_CONTACT << '=';\n\tconfig << bool2yesno(use_domain_in_contact) << endl;\n\tconfig << FLD_ALLOW_SDP_CHANGE << '=' << bool2yesno(allow_sdp_change) << endl;\n\tconfig << FLD_ALLOW_REDIRECTION << '=' << bool2yesno(allow_redirection);\n\tconfig << endl;\n\tconfig << FLD_ASK_USER_TO_REDIRECT << '=';\n\tconfig << bool2yesno(ask_user_to_redirect) << endl;\n\tconfig << FLD_MAX_REDIRECTIONS << '=' << max_redirections << endl;\n\tconfig << FLD_EXT_100REL << '=' << ext_support2str(ext_100rel) << endl;\n\tconfig << FLD_EXT_REPLACES << '=' << bool2yesno(ext_replaces) << endl;\n\tconfig << FLD_REFEREE_HOLD << '=' << bool2yesno(referee_hold) << endl;\n\tconfig << FLD_REFERRER_HOLD << '=' << bool2yesno(referrer_hold) << endl;\n\tconfig << FLD_ALLOW_REFER << '=' << bool2yesno(allow_refer) << endl;\n\tconfig << FLD_ASK_USER_TO_REFER << '=';\n\tconfig << bool2yesno(ask_user_to_refer) << endl;\n\tconfig << FLD_AUTO_REFRESH_REFER_SUB << '=';\n\tconfig << bool2yesno(auto_refresh_refer_sub) << endl;\n\tconfig << FLD_ATTENDED_REFER_TO_AOR << '=';\n\tconfig << bool2yesno(attended_refer_to_aor) << endl;\n\tconfig << FLD_ALLOW_XFER_CONSULT_INPROG << '=';\n\tconfig << bool2yesno(allow_transfer_consultation_inprog) << endl;\n\tconfig << FLD_SEND_P_PREFERRED_ID << '=';\n\tconfig << bool2yesno(send_p_preferred_id) << endl;\n\tconfig << FLD_SEND_P_ASSERTED_ID << '=';\n\tconfig << bool2yesno(send_p_asserted_id) << endl;\n\tconfig << endl;\n\n\t// Write Transport/NAT settings\n\tconfig << \"# Transport/NAT\\n\";\n\tconfig << FLD_SIP_TRANSPORT << '=' << sip_transport2str(sip_transport) << endl;\n\tconfig << FLD_SIP_TRANSPORT_UDP_THRESHOLD << '=' << sip_transport_udp_threshold << endl;\n\tif (use_nat_public_ip) {\n\t\tconfig << FLD_NAT_PUBLIC_IP << '=' << nat_public_ip << endl;\n\t} else {\n\t\tconfig << FLD_NAT_PUBLIC_IP << '=' << endl;\n\t}\n\tif (use_stun) {\n\t\tconfig << FLD_STUN_SERVER << '=' << \n\t\t\tstun_server.encode_noscheme() << endl;\n\t} else {\n\t\tconfig << FLD_STUN_SERVER << '=' << endl;\n\t}\n\tconfig << FLD_PERSISTENT_TCP << '=' << bool2yesno(persistent_tcp) << endl;\n\tconfig << FLD_ENABLE_NAT_KEEPALIVE << '=' << bool2yesno(enable_nat_keepalive) << endl;\n\tconfig << endl;\n\n\t// Write TIMER settings\n\tconfig << \"# TIMERS\\n\";\n\tconfig << FLD_TIMER_NOANSWER << '=' << timer_noanswer << endl;\n\tconfig << FLD_TIMER_NAT_KEEPALIVE << '=' << timer_nat_keepalive << endl;\n\tconfig << FLD_TIMER_TCP_PING << '=' << timer_tcp_ping << endl;\n\tconfig << endl;\n\n\t// Write ADDRESS FORMAT settings\n\tconfig << \"# ADDRESS FORMAT\\n\";\n\tconfig << FLD_DISPLAY_USERONLY_PHONE << '=';\n\tconfig << bool2yesno(display_useronly_phone) << endl;\n\tconfig << FLD_NUMERICAL_USER_IS_PHONE << '=';\n\tconfig << bool2yesno(numerical_user_is_phone) << endl;\n\tconfig << FLD_REMOVE_SPECIAL_PHONE_SYM << '=';\n\tconfig << bool2yesno(remove_special_phone_symbols) << endl;\n\tconfig << FLD_SPECIAL_PHONE_SYMBOLS << '=' << special_phone_symbols << endl;\n\tconfig << FLD_USE_TEL_URI_FOR_PHONE << '=' << bool2yesno(use_tel_uri_for_phone) << endl;\n\tconfig << endl;\n\t\n\t// Write RING TONE settings\n\tconfig << \"# RING TONES\\n\";\n\tconfig << FLD_USER_RINGTONE_FILE << '=' << ringtone_file << endl;\n\tconfig << FLD_USER_RINGBACK_FILE << '=' << ringback_file << endl;\n\tconfig << endl;\n\t\n\t// Write script settings\n\tconfig << \"# SCRIPTS\\n\";\n\tconfig << FLD_SCRIPT_INCOMING_CALL << '=' << script_incoming_call << endl;\n\tconfig << FLD_SCRIPT_IN_CALL_ANSWERED << '=' << script_in_call_answered << endl;\n\tconfig << FLD_SCRIPT_IN_CALL_FAILED << '=' << script_in_call_failed << endl;\n\tconfig << FLD_SCRIPT_OUTGOING_CALL << '=' << script_outgoing_call << endl;\n\tconfig << FLD_SCRIPT_OUT_CALL_ANSWERED << '=' << script_out_call_answered << endl;\n\tconfig << FLD_SCRIPT_OUT_CALL_FAILED << '=' << script_out_call_failed << endl;\n\tconfig << FLD_SCRIPT_LOCAL_RELEASE << '=' << script_local_release << endl;\n\tconfig << FLD_SCRIPT_REMOTE_RELEASE << '=' << script_remote_release << endl;\n\tconfig << endl;\n\t\n\t// Write number conversion rules\n\tconfig << \"# NUMBER CONVERSION\\n\";\n\n\tfor (list<t_number_conversion>::iterator i = number_conversions.begin();\n\t     i != number_conversions.end(); i++)\n\t{\n\t\tconfig << FLD_NUMBER_CONVERSION << '=';\n        config << escape(i->re, ',');\n\t\tconfig << ',';\n\t\tconfig << escape(i->fmt, ',');\n\t\tconfig << endl;\n\t}\n\tconfig << endl;\n\t\n\t// Write security settings\n\tconfig << \"# SECURITY\\n\";\n\tconfig << FLD_ZRTP_ENABLED << '=' << bool2yesno(zrtp_enabled) << endl;\n\tconfig << FLD_ZRTP_GOCLEAR_WARNING << '=' << bool2yesno(zrtp_goclear_warning) << endl;\n\tconfig << FLD_ZRTP_SDP << '=' << bool2yesno(zrtp_sdp) << endl;\n\tconfig << FLD_ZRTP_SEND_IF_SUPPORTED << '=' << bool2yesno(zrtp_send_if_supported) << endl;\n\tconfig << endl;\n\t\n\t// Write MWI settings\n\tconfig << \"# MWI\\n\";\n\tconfig << FLD_MWI_SOLICITED << '=' << bool2yesno(mwi_solicited) << endl;\n\tconfig << FLD_MWI_USER << '=' << mwi_user << endl;\n\tif (mwi_server.is_valid()) {\n\t\tconfig << FLD_MWI_SERVER << '=' << mwi_server.encode_noscheme() << endl;\n\t} else {\n\t\tconfig << FLD_MWI_SERVER << '=' << endl;\n\t}\n\tconfig << FLD_MWI_VIA_PROXY << '=' << bool2yesno(mwi_via_proxy) << endl;\n\tconfig << FLD_MWI_SUBSCRIPTION_TIME << '=' << mwi_subscription_time << endl;\n\tconfig << FLD_MWI_VM_ADDRESS << '=' << mwi_vm_address << endl;\n\tconfig << endl;\n\t\n\tconfig << \"# INSTANT MESSAGE\\n\";\n\tconfig << FLD_IM_MAX_SESSIONS << '=' << im_max_sessions << endl;\n\tconfig << FLD_IM_SEND_ISCOMPOSING << '=' << bool2yesno(im_send_iscomposing) << endl;\n\tconfig << endl;\n\t\n\t// Write presence settings\n\tconfig << \"# PRESENCE\\n\";\n\tconfig << FLD_PRES_SUBSCRIPTION_TIME << '=' << pres_subscription_time << endl;\n\tconfig << FLD_PRES_PUBLICATION_TIME << '=' << pres_publication_time << endl;\n\tconfig << FLD_PRES_PUBLISH_STARTUP << '=' << bool2yesno(pres_publish_startup) << endl;\n\n\t// Check if writing succeeded\n\tif (!config.good()) {\n\t\t// Restore backup\n\t\tconfig.close();\n\t\trename(f_backup.c_str(), f.c_str());\n\n\t\terror_msg = \"File system error while writing file \";\n\t\terror_msg += f;\n\t\tlog_file->write_report(error_msg, \"t_user::write_config\",\n\t\t\tLOG_NORMAL, LOG_CRITICAL);\n\t\tmtx_user.unlock();\n\t\treturn false;\n\t}\n\n\t// Set parser options\n\tt_parser::check_max_forwards = check_max_forwards;\n\tt_parser::compact_headers = compact_headers;\n\tt_parser::multi_values_as_list = encode_multi_values_as_list;\n\n\tmtx_user.unlock();\n\treturn true;\n}\n\nstring t_user::get_filename(void) const {\n\tstring result;\n\t\n\tmtx_user.lock();\n\tresult = config_filename;\n\tmtx_user.unlock();\n\t\n\treturn result;\n}\n\nbool t_user::set_config(string filename) {\n\tt_mutex_guard guard(mtx_user);\n\t\n\tstruct stat stat_buf;\n\n\tconfig_filename = filename;\n\tstring fullpath = expand_filename(filename);\n\t\n\treturn (stat(fullpath.c_str(), &stat_buf) != 0);\n}\n\nstring t_user::get_profile_name(void) const {\n\tstring result;\n\t\n\tmtx_user.lock();\n\t\n\tstring::size_type pos_ext = config_filename.find(USER_FILE_EXT);\n\n\tif (pos_ext == string::npos) {\n\t\tresult = config_filename;\n\t} else {\n\t\tresult = config_filename.substr(0, pos_ext);\n\t}\n\t\n\tmtx_user.unlock();\n\t\n\treturn result;\n}\n\nstring t_user::get_contact_name(void) const {\n\tmtx_user.lock();\n\t\n\tstring s = name;\n\t\n\t// Some broken proxies expect the contact name to be the same\n\t// as the SIP user name.\n\tif (!use_domain_in_contact) {\n\t\tmtx_user.unlock();\n\t\treturn s;\n\t}\n\t\n\t// Create a unique contact name from the user name and domain:\n\t// \n\t//   username_domain, where all dots in domain are replace\n\t//\n\t// This way it is possible to activate 2 profiles that have the\n\t// same username, but different domains, e.g.\n\t//\n\t//   michel@domainA\n\t//   michel@domainB\n\n\ts += '_';\n\t\n\t// Cut of port and/or uri-parameters if present in domain\n\tstring::size_type i = domain.find_first_of(\":;\");\n\tif (i != string::npos) {\n\t\t// Some broken SIP proxies think that their own address appears\n\t\t// in the contact header when they see the domain in the user part.\n\t\t// By replacing the dots with underscores Twinkle interoperates\n\t\t// with those proxies (yuck).\n\t\ts += replace_char(domain.substr(0, i), '.', '_');\n\t} else {\n\t\ts += replace_char(domain, '.', '_');\n\t}\n\n\tmtx_user.unlock();\n\treturn s;\n}\n\nstring t_user::get_display_uri(void) const {\n\tmtx_user.lock();\n\tstring s;\n\t\n\ts = display;\n\tif (!s.empty()) s += ' ';\n\ts += '<';\n\ts += USER_SCHEME;\n\ts += ':';\n\ts += name;\n\ts += '@';\n\ts += domain;\n\ts += '>';\n\t\n\tmtx_user.unlock();\n\treturn s;\n}\n\nbool t_user::check_required_ext(t_request *r, list<string> &unsupported) const {\n\tbool all_supported = true;\n\t\n\tmtx_user.lock();\n\n\tunsupported.clear();\n\tif (!r->hdr_require.is_populated()) {\n\t\tmtx_user.unlock();\n\t\treturn true;\n\t}\n\t\n\tfor (list<string>::iterator i = r->hdr_require.features.begin();\n\t     i != r->hdr_require.features.end(); i++)\n\t{\n\t\tif (*i == EXT_100REL) {\n\t\t\tif (ext_100rel != EXT_DISABLED) continue;\n\t\t} else if (*i == EXT_REPLACES) {\n\t\t\tif (ext_replaces) continue;\n\t\t} else if (*i == EXT_NOREFERSUB) {\n\t\t\tcontinue;\n\t\t} else if (*i == EXT_TIMER) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Extension is not supported\n\t\tunsupported.push_back(*i);\n\t\tall_supported = false;\n\t}\n\n\tmtx_user.unlock();\n\treturn all_supported;\n}\n\nstring t_user::create_user_contact(bool anonymous, const string &auto_ip) {\n\tstring s;\n\t\n\tmtx_user.lock();\n\n\ts = USER_SCHEME;\n\ts += ':';\n\t\n\tif (!anonymous) {\n\t\ts += t_url::escape_user_value(get_contact_name());\n\t\ts += '@';\n\t}\n\t\n\ts += USER_HOST(this, auto_ip);\n\n\tif (PUBLIC_SIP_PORT(this) != get_default_port(USER_SCHEME)) {\n\t\ts += ':';\n\t\ts += int2str(PUBLIC_SIP_PORT(this));\n\t}\n\t\n\tif (phone->use_stun(this)) {\n\t\t// The port discovered via STUN can only be used for UDP.\n\t\ts += \";transport=udp\";\n\t} else {\n\t\t// Add transport parameter if a single transport is provisioned only.\n\t\tswitch (sip_transport) {\n\t\tcase SIP_TRANS_UDP:\n\t\t\ts += \";transport=udp\";\n\t\t\tbreak;\n\t\tcase SIP_TRANS_TCP:\n\t\t\ts += \";transport=tcp\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!anonymous && \n\t    numerical_user_is_phone && looks_like_phone(name, special_phone_symbols))\n\t{\n\t\t// RFC 3261 19.1.1\n\t\t// If the URI contains a telephone number it SHOULD contain\n\t\t// the user=phone parameter.\n\t\ts += \";user=phone\";\n\t}\n\n\tmtx_user.unlock();\n\treturn s;\n}\n\nstring t_user::create_user_uri(bool anonymous) {\n\tif (anonymous) return ANONYMOUS_URI;\n\t\n\tstring s;\n\tmtx_user.lock();\n\n\ts = USER_SCHEME;\n\ts += ':';\n\ts += t_url::escape_user_value(name);\n\ts += '@';\n\ts += domain;\n\n\tif (numerical_user_is_phone && looks_like_phone(name, special_phone_symbols))\n\t{\n\t\t// RFC 3261 19.1.1\n\t\t// If the URI contains a telephone number it SHOULD contain\n\t\t// the user=phone parameter.\n\t\ts += \";user=phone\";\n\t}\n\n\tmtx_user.unlock();\n\treturn s;\n}\n\nstring t_user::convert_number(const string &number, const list<t_number_conversion> &l) const {\n\tfor (list<t_number_conversion>::const_iterator i = l.begin();\n\t     i != l.end(); i++)\n\t{\n        std::smatch m;\n\t\t\n\t\ttry {\n            if (std::regex_match(number, m, std::regex(i->re))) {\n\t\t\t\tstring result;\n\n\t\t\t\tm.format(std::back_inserter(result), i->fmt);\n\t\t\t\n\t\t\t\tlog_file->write_header(\"t_user::convert_number\", \n\t\t\t\t\tLOG_NORMAL, LOG_DEBUG);\n\t\t\t\tlog_file->write_raw(\"Apply conversion: \");\n\t\t\t\tlog_file->write_raw(i->str());\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_raw(number);\n\t\t\t\tlog_file->write_raw(\" converted to \");\n\t\t\t\tlog_file->write_raw(result);\n\t\t\t\tlog_file->write_endl();\n\t\t\t\tlog_file->write_footer();\n\t\t\t\t\t\n\t\t\t\treturn result;\n\t\t\t}\n\t\t} catch (std::runtime_error) {\n\t\t\tlog_file->write_header(\"t_user::convert_number\", \n\t\t\t\t\tLOG_NORMAL, LOG_WARNING);\n\t\t\tlog_file->write_raw(\"Number conversion rule too complex:\\n\");\n\t\t\tlog_file->write_raw(\"Number: \");\n\t\t\tlog_file->write_raw(number);\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_raw(i->str());\n\t\t\tlog_file->write_endl();\n\t\t\tlog_file->write_footer();\n\t\t\t\n\t\t\treturn number;\n\t\t}\n\t}\n\t\n\t// No match found\n\treturn number;\n}\n\nstring t_user::convert_number(const string &number) const {\n\treturn convert_number(number, number_conversions);\n}\n\nt_url t_user::get_mwi_uri(void) const {\n\tt_url u(mwi_server);\n\tu.set_user(mwi_user);\n\t\n\treturn u;\n}\n\nbool t_user::is_diamondcard_account(void) const {\n\t// A profile is a Diamondcard account if the end configured domain\n\t// is equal to the DIAMONDCARD_DOMAIN\n\tsize_t domain_len = strlen(DIAMONDCARD_DOMAIN);\n\tif (domain.size() < domain_len) return false;\n\t\n\tsize_t pos = domain.size() - domain_len;\n\treturn (domain.substr(pos) == DIAMONDCARD_DOMAIN);\n}\n"
  },
  {
    "path": "src/user.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n// NOTE:\n// When adding attributes to t_user, make sure to add them to the\n// copy constructor too!\n\n#ifndef _H_USER\n#define _H_USER\n\n#include <string>\n#include <list>\n#include \"protocol.h\"\n#include \"sys_settings.h\"\n#include \"audio/audio_codecs.h\"\n#include \"sockets/url.h\"\n#include \"threads/mutex.h\"\n#include <regex>\n\n// Forward declaration\nclass t_request;\n\n// Default config file name\n#define USER_CONFIG_FILE\t\"twinkle.cfg\"\n#define USER_FILE_EXT\t\t\".cfg\"\n#define USER_DIR\t\tDIR_USER\n\n#define USER_SCHEME\t\t\"sip\"\n\n#define PUBLIC_SIP_PORT(u)\tphone->get_public_port_sip(u)\n#define USER_HOST(u,local_ip)\tphone->get_ip_sip(u,(local_ip))\n#define LOCAL_IP\t\tuser_host\n#define LOCAL_HOSTNAME\t\tlocal_hostname\n\n#define SPECIAL_PHONE_SYMBOLS\t\"-()/.\"\n\nusing namespace std;\n\nenum t_hold_variant {\n\tHOLD_RFC2543,\t// set IP = 0.0.0.0 in c-line\n\tHOLD_RFC3264\t// use direction attribute to put call on-hold\n};\n\n/** SIP transport mode */\nenum t_sip_transport {\n\tSIP_TRANS_UDP,\t/**< SIP over UDP */\n\tSIP_TRANS_TCP,\t/**< SIP over TCP */\n\tSIP_TRANS_AUTO\t/**< UDP for small messages, TCP for large messages */\n};\n\nenum t_ext_support {\n\tEXT_INVALID,\n\tEXT_DISABLED,\n\tEXT_SUPPORTED,\n\tEXT_PREFERRED,\n\tEXT_REQUIRED\n};\n\nenum t_bit_rate_type {\n\tBIT_RATE_INVALID,\n\tBIT_RATE_CBR,\t// Constant\n\tBIT_RATE_VBR,\t// Variable\n\tBIT_RATE_ABR\t// Average\n};\n\nenum t_dtmf_transport {\n\tDTMF_INBAND,\n\tDTMF_RFC2833,\n\tDTMF_AUTO,\n\tDTMF_INFO\n};\n\nenum t_g726_packing {\n\tG726_PACK_RFC3551,\n\tG726_PACK_AAL2\n};\n\nstruct t_number_conversion {\n    string re;\n    string fmt;\n\t\n    string str(void) const { return re + \" --> \" + fmt; }\n};\n\n\nclass t_user {\nprivate:\n\tstring\t\t\tconfig_filename;\n\t\n\t// Mutex for exclusive access to the user profile\n\tmutable t_recursive_mutex\tmtx_user;\n\n\t/** @name USER */\n\t//@{\n\t// SIP user\n\t/** User name (public user identity). */\n\tstring\t\t\tname;\n\t\n\t/** Domain of the user. */\n\tstring\t\t\tdomain;\n\t\n\t/** Display name. */\n\tstring\t\t\tdisplay;\n\n\t/**\n\t * The organization will be put in an initial INVITE and in a\n\t * 200 OK on an INVITE.\n\t */\n\tstring\t\t\torganization;\n\n\t// SIP authentication\n\t/** Authentication realm. An empty realm matches with all realms. */\n\tstring\t\t\tauth_realm;\n\t\n\t/** Authentication name (private user identity). */\n\tstring\t\t\tauth_name;\n\t\n\t/** Authentication password (aka_k for akav1-md5 authentication) */\n\tstring\t\t\tauth_pass;\n\t\n\t/** Operator variant key for akav1-md5 authentication. */\n\tuint8\t\t\tauth_aka_op[AKA_OPLEN];\n\t\n\t/** Authentication management field for akav1-md5 authentication. */\n\tuint8\t\t\tauth_aka_amf[AKA_AMFLEN];\n\t//@}\n\n\n\t// SIP SERVER\n\n\t// Send all non-REGISTER requests to the outbound proxy\n\tbool\t\t\tuse_outbound_proxy;\n\tt_url\t\t\toutbound_proxy;\n\n\t// By default only out-of-dialog requests (including the ones the\n\t// establish a dialog) are sent to the outbound proxy.\n\t// In-dialog requests go to the address established via the contact\n\t// header.\n\t// By setting this parameter to true, in-dialog requests go to the\n\t// outbound proxy as well.\n\tbool\t\t\tall_requests_to_proxy;\n\n\t// Only send the request to the proxy, if the destination cannot\n\t// be resolved to an IP address, adhearing to the previous setting\n\t// though. I.e. use_outbound_proxy must be true. And an in-dialog\n\t// request will only be sent to the proxy if all_requests_to_proxy\n\t// is true.\n\tbool\t\t\tnon_resolvable_to_proxy;\n\n\t// Send REGISTER to registrar\n\tbool\t\t\tuse_registrar;\n\tt_url\t\t\tregistrar;\n\t\n\t// Registration time requested by the client. If set to zero, then\n\t// no specific time is requested. The registrar will set a time.\n\tunsigned long\t\tregistration_time;\n\n\t// Automatically register at startup of the client.\n\tbool\t\t\tregister_at_startup;\n\n\t// q-value for registration\n\tbool\t\t\treg_add_qvalue;\n\tfloat\t\t\treg_qvalue;\n\n\n\t// AUDIO\n\n\tlist<t_audio_codec>\tcodecs; // in order of preference\n\tunsigned short\t\tptime; // ptime (ms) for G.711/G.726\n\t\n\t// For outgoing calls, obey the preference from the far-end (SDP answer),\n\t// i.e. pick the first codec from the SDP answer that we support.\n\tbool\t\t\tout_obey_far_end_codec_pref;\n\t\n\t// For incoming calls, obey the preference from the far-end (SDP offer),\n\t// i.e. pick the first codec from the SDP offer that we support.\n\tbool\t\t\tin_obey_far_end_codec_pref;\n\t\n\t// RTP dynamic payload types for speex\n\tunsigned short\t\tspeex_nb_payload_type;\n\tunsigned short\t\tspeex_wb_payload_type;\n\tunsigned short\t\tspeex_uwb_payload_type;\n\t\n\t// Speex preprocessing options\n\tbool\t\t\tspeex_dsp_vad;       // voice activity reduction\n\tbool\t\t\tspeex_dsp_agc;       // automatic gain control\n\tbool\t\t\tspeex_dsp_aec;       // acoustic echo cancellation\n\tbool\t\t\tspeex_dsp_nrd;       // noise reduction\n\tunsigned short\t\tspeex_dsp_agc_level; // gain level of AGC (1-100[%])\n\n\t// Speex coding options\n\tt_bit_rate_type\t\tspeex_bit_rate_type;\n\tint\t\t\tspeex_abr_nb;\n\tint\t\t\tspeex_abr_wb;\n\tbool\t\t\tspeex_dtx;\n\tbool\t\t\tspeex_penh;\n\tunsigned short\t\tspeex_complexity;\n\tunsigned short\t\tspeex_quality; // quality measure (worst 0-10 best)\n\t\n\t// RTP dynamic payload types for iLBC\n\tunsigned short\t\tilbc_payload_type;\n\t\n\t// iLBC options\n\tunsigned short\t\tilbc_mode; // 20 or 30 ms frame size\n\t\n\t// RTP dynamic payload types for G.726\n\tunsigned short\t\tg726_16_payload_type;\n\tunsigned short\t\tg726_24_payload_type;\n\tunsigned short\t\tg726_32_payload_type;\n\tunsigned short\t\tg726_40_payload_type;\n\t\n\t// Bit packing order for G,726\n\tt_g726_packing\t\tg726_packing;\n\t\n\t// Transport mode for DTMF\n\tt_dtmf_transport\tdtmf_transport;\n\n\t// RTP dynamic payload type for out-of-band DTMF.\n\tunsigned short\t\tdtmf_payload_type;\n\n\t// DTMF duration and pause between 2 tones. During the pause the last\n\t// DTMF event will be repeated so the far end can detect the end of\n\t// the event in case of packet loss.\n\tunsigned short\t\tdtmf_duration; // ms\n\tunsigned short\t\tdtmf_pause; // ms\n\t\n\t// Volume of the tone in -dBm\n\tunsigned short\t\tdtmf_volume;\n\n\n\t/** @name SIP PROTOCOL */\n\t//@{\n\t// SIP protocol options\n\t// hold variants: rfc2543, rfc3264\n\t// rfc2543 - set IP address to 0.0.0.0\n\t// rfc3264 - use direction attribute (sendrecv, sendonly, ...)\n\tt_hold_variant\t\thold_variant;\n\n\t// Indicate if the mandatory Max-Forwards header should be present.\n\t// If true and the header is missing, then the request will fail.\n\tbool\t\t\tcheck_max_forwards;\n\t\n\t// RFC 3261 10.3 states that a registrar must include a contact\n\t// header in a 200 OK on a REGISTER. This contact should match the\n\t// contact that a UA puts in the REGISTER. Unfortunately many\n\t// registrars do not include the contact header or put a wrong\n\t// IP address in the host-part due to NAT.\n\t// This settings allows for a missing/non-matching contact header.\n\t// In that case Twinkle assumes that it is registered for the\n\t// requested interval.\n\tbool\t\t\tallow_missing_contact_reg;\n\n\t// Indicate the place of the requested registration time in a REGISTER.\n\t// true - expires parameter in contact header\n\t// false - Expires header\n\tbool\t\t\tregistration_time_in_contact;\n\n\t// Indicate if compact header names should be used in outgoing messages.\n\tbool\t\t\tcompact_headers;\n\t\n\t// Indicate if headers containing multiple values should be encoded\n\t// as a comma separated list or as multiple headers.\n\tbool\t\t\tencode_multi_values_as_list;\n\t\n\t// Indicate if a unique contact name should be created by using\n\t// the domain name: username_domain\n\t// If false then the SIP user name is used as contact name\n\tbool\t\t\tuse_domain_in_contact;\n\t\n\t// Allow SDP to change in different INVITE responses. \n\t// According to RFC 3261 13.2.1, if SDP is received in a 1XX response,\n\t// then SDP received in subsequent responses should be ignored.\n\t// Some SIP proxies do send different SDP in 1XX and 200 though.\n\t// E.g. first SDP is to play ring tone, second SDP is to create\n\t// an end-to-end media path.\n\tbool\t\t\tallow_sdp_change;\n\n\t// Redirections\n\t// Allow redirection of a request when a 3XX is received.\n\tbool\t\t\tallow_redirection;\n\n\t// Ask user for permission to redirect a request when a 3XX is received.\n\tbool\t\t\task_user_to_redirect;\n\n\t// Maximum number of locations to be tried when a request is redirected.\n\tunsigned short\t\tmax_redirections;\n\n\t// SIP extensions\n\t// 100rel extension (PRACK, RFC 3262)\n\t// Possible values:\n\t// - disabled\t100rel extension is disabled\n\t// - supported\t100rel is supported (it is added in the supported header of\n\t//               an outgoing INVITE). A far-end can now require a PRACK on a\n\t//               1xx response.\n\t// - required\t100rel is required (it is put in the require header of an\n\t//               outgoing INVITE).\n\t//\t\tIf an incoming INVITE indicates that it supports 100rel, then\n\t//\t\tTwinkle will require a PRACK when sending a 1xx response.\n\t// - preferred\tSimilar to required, but if a call fails because the far-end\n\t//\t\tindicates it does not support 100rel (420 response) then the\n\t//\t\tcall will be re-attempted without the 100rel requirement.\n\tt_ext_support\t\text_100rel;\n\t\n\t// Replaces (RFC 3891)\n\tbool\t\t\text_replaces;\n\t//@}\n\n\t/** @name REFER options */\n\t//@{\n\t/** Hold the current call when an incoming REFER is accepted. */\n\tbool\t\t\treferee_hold;\n\n\t/** Hold the current call before sending a REFER. */\n\tbool\t\t\treferrer_hold;\n\n\t/** Allow an incoming refer */\n\tbool\t\t\tallow_refer;\n\n\t/** Ask user for permission when a REFER is received. */\n\tbool\t\t\task_user_to_refer;\n\n\t/** Referrer automatically refreshes subscription before expiry. */\n\tbool\t\t\tauto_refresh_refer_sub;\n\t\n\t/**\n\t * An attended transfer should use the contact-URI of the transfer target.\n\t * This contact-URI is not always globally routable however. As an\n\t * alternative the AoR (address of record) can be used. Disadvantage is\n\t * that the AoR may route to multiple phones in case of forking, whereas\n\t * the contact-URI routes to a particular phone.\n\t */\n\tbool\t\t\tattended_refer_to_aor;\n\t\n\t/** \n\t * Allow to transfer a call while the consultation call is still\n\t * in progress.\n\t */\n\tbool\t\t\tallow_transfer_consultation_inprog;\n\t//@}\n\t\n\t/** @name Privacy options */\n\t//@{\n\t// Send P-Preferred-Identity header in initial INVITE when hiding\n\t// user identity.\n\tbool\t\t\tsend_p_preferred_id;\n\t//@}\n\t\n\t/** @name Privacy options */\n\t//@{\n\t// Send P-Assrted-Identity header in initial INVITE when hiding\n\t// user identity.\n\tbool\t\t\tsend_p_asserted_id;\n\t//@}\n\t\n\t/** @name Transport */\n\t//@{\n\t/** SIP transport protocol */\n\tt_sip_transport\t\tsip_transport;\n\t\n\t/** \n\t * Threshold to decide which transport to use in auto transport mode. \n\t * A message with a size up to this threshold is sent via UDP. Larger messages\n\t * are sent via TCP.\n\t */\n\tunsigned short\t\tsip_transport_udp_threshold;\n\t//@}\n\n\t/** @name NAT */\n\t//@{\n\t/**\n\t * NAT traversal\n\t * You can set nat_public_ip to your public IP or FQDN if you are behind\n\t * a NAT. This will then be used inside the SIP messages instead of your\n\t * private IP. On your NAT you have to create static bindings for port 5060\n\t * and ports 8000 - 8005 to the same ports on your private IP address.\n\t */\n\tbool\t\t\tuse_nat_public_ip;\n\t\n\t/** The public IP address of the NAT device. */\n\tstring\t\t\tnat_public_ip;\n\t\n\t/** NAT traversal via STUN. */\n\tbool\t\t\tuse_stun;\n\t\n\t/** URL of the STUN server. */\n\tt_url\t\t\tstun_server;\n\t\n\t/** User persistent TCP connections. */\n\tbool\t\t\tpersistent_tcp;\n\t\n\t/** Enable sending of NAT keepalive packets for UDP. */\n\tbool\t\t\tenable_nat_keepalive;\n\t//@}\n\n\t/** @name TIMERS */\n\t//@{\n\t/** \n\t * Noanswer timer is started when an initial INVITE is received. If\n\t * the user does not respond within the timer, then the call will be\n\t * released with a 480 Temporarily Unavailable response.\n\t */\n\tunsigned short\t\ttimer_noanswer; // seconds\n\t\n\t/** Duration of NAT keepalive timer (s) */\n\tunsigned short\t\ttimer_nat_keepalive;\n\t\n\t/** Duration of TCP ping timer (s) */\n\tunsigned short\t\ttimer_tcp_ping;\n\t//@}\n\n\t/** @name ADDRESS FORMAT */\n\t//@{\n\t/**\n\t * Telephone numbers\n\t * Display only the user-part of a URI if it is a telephone number\n\t * I.e. the user=phone parameter is present, or the user indicated\n\t * that the format of the user-part is a telephone number.\n\t * If the URI is a tel-URI then display the telephone number.\n\t */\n\tbool\t\t\tdisplay_useronly_phone;\n\n\t/**\n\t * Consider user-parts that consist of 0-9,+,-,*,# as a telephone\n\t * number. I.e. in outgoing messages the user=phone parameter will\n\t * be added to the URI. For incoming messages the URI will be considered\n\t * to be a telephone number regardless of the presence of the\n\t * user=phone parameter.\n\t */\n\tbool\t\t\tnumerical_user_is_phone;\n\t\n\t/** Remove special symbols from numerical dial strings */\n\tbool\t\t\tremove_special_phone_symbols;\n\t\n\t/** Special symbols that must be removed from telephone numbers */\n\tstring\t\t\tspecial_phone_symbols;\n\t\n\t/**\n\t * If the user enters a telephone number as address, then complete it\n\t * to a tel-URI instead of a sip-URI.\n\t */\n\tbool\t\t\tuse_tel_uri_for_phone;\n\t\n\t/** Number conversion */\n\tlist<t_number_conversion>\tnumber_conversions;\n\t//@}\n\t\n\t// RING TONES\n\tstring\t\tringtone_file;\n\tstring\t\tringback_file;\n\t\n\t// SCRIPTS\n\t// Script to be called on incoming call\n\tstring\t\tscript_incoming_call;\n\tstring\t\tscript_in_call_answered;\n\tstring\t\tscript_in_call_failed;\n\tstring\t\tscript_outgoing_call;\n\tstring\t\tscript_out_call_answered;\n\tstring\t\tscript_out_call_failed;\n\tstring\t\tscript_local_release;\n\tstring\t\tscript_remote_release;\n\t\n\t// SECURITY\n\t// zrtp setting\n\tbool\t\tzrtp_enabled;\n\t\n\t// Popup warning when far-end sends goclear command\n\tbool\t\tzrtp_goclear_warning;\n\t\n\t// Send a=zrtp in SDP\n\tbool\t\tzrtp_sdp;\n\t\n\t// Only negotiate zrtp if far-end signalled support for zrtp\n\tbool\t\tzrtp_send_if_supported;\n\t\n\t// MWI\n\t// Indicate if MWI is solicited or unsolicited.\n\t// RFC 3842 specifies that MWI must be solicited (SUBSCRIBE).\n\t// Asterisk however only supported non-standard unsolicited MWI.\n\tbool\t\tmwi_solicited;\n\t\n\t// User name for subscribing to the mailbox\n\tstring\t\tmwi_user;\n\t\n\t// The mailbox server to which the SUBSCRIBE must be sent\n\tt_url\t\tmwi_server;\n\t\n\t// Send the SUBSCRIBE via the proxy to the mailbox server\n\tbool\t\tmwi_via_proxy;\n\t\n\t// Requested MWI subscription duration\n\tunsigned long\tmwi_subscription_time;\n\t\n\t// The voice mail address to call to access messages\n\tstring\t\tmwi_vm_address;\n\t\n\t/** @name INSTANT MESSAGE */\n\t//@{\n\t/** Maximum number of simultaneous IM sessions. */\n\tunsigned short\tim_max_sessions;\n\t\n\t/** Flag to indicate that IM is-composing indications (RFC 3994) should be sent. */\n\tbool\t\tim_send_iscomposing;\n\t//@}\n\t\n\t/** @name PRESENCE */\n\t//@{\n\t/** Requested presence subscription duration in seconds. */\n\tunsigned long\tpres_subscription_time;\n\t\n\t/** Requested presence publication duration in seconds. */\n\tunsigned long\tpres_publication_time;\n\t\n\t/** Publish online presence state at startup */\n\tbool\t\tpres_publish_startup;\n\t//@}\n\n\tt_ext_support str2ext_support(const string &s) const;\n\tstring ext_support2str(t_ext_support e) const;\n\tt_bit_rate_type str2bit_rate_type(const string &s) const;\n\tstring bit_rate_type2str(t_bit_rate_type b) const;\n\tt_dtmf_transport str2dtmf_transport(const string &s) const;\n\tstring dtmf_transport2str(t_dtmf_transport d) const;\n\tt_g726_packing str2g726_packing(const string &s) const;\n\tstring g726_packing2str(t_g726_packing packing) const;\n\tt_sip_transport str2sip_transport(const string &s) const;\n\tstring sip_transport2str(t_sip_transport transport) const;\n\t\n\t// Parse a number conversion rule\n\t// If the rule can be parsed, then c contains the conversion rule and\n\t// true is returned. Otherwise false is returned.\n\tbool parse_num_conversion(const string &value, t_number_conversion &c);\n\t\n\t// Set a server URL.\n\t// Returns false, if the passed value is not a valid URL.\n\tbool set_server_value(t_url &server, const string &scheme, const string &value);\n\t\npublic:\n\tt_user();\n\tt_user(const t_user &u);\n\t\n\tt_user *copy(void) const;\n\t\n\t/** @name Getters */\n\t//@{\n\tstring get_name(void) const;\n\tstring get_domain(void) const;\n\tstring get_display(bool anonymous) const;\t\n\tstring get_organization(void) const;\n\tstring get_auth_realm(void) const;\n\tstring get_auth_name(void) const;\n\tstring get_auth_pass(void) const;\n\tvoid get_auth_aka_op(uint8 *aka_op) const;\n\tvoid get_auth_aka_amf(uint8 *aka_amf) const;\n\tbool get_use_outbound_proxy(void) const;\n\tt_url get_outbound_proxy(void) const;\n\tbool get_all_requests_to_proxy(void) const;\n\tbool get_non_resolvable_to_proxy(void) const;\n\tbool get_use_registrar(void) const;\n\tt_url get_registrar(void) const;\n\tunsigned long get_registration_time(void) const;\n\tbool get_register_at_startup(void) const;\n\tbool get_reg_add_qvalue(void) const;\n\tfloat get_reg_qvalue(void) const;\n\tlist<t_audio_codec> get_codecs(void) const;\n\tunsigned short get_ptime(void) const;\n\tbool get_out_obey_far_end_codec_pref(void) const;\n\tbool get_in_obey_far_end_codec_pref(void) const;\n\tunsigned short get_speex_nb_payload_type(void) const;\n\tunsigned short get_speex_wb_payload_type(void) const;\n\tunsigned short get_speex_uwb_payload_type(void) const;\n\tt_bit_rate_type get_speex_bit_rate_type(void) const;\n\tint get_speex_abr_nb(void) const;\n\tint get_speex_abr_wb(void) const;\n\tbool get_speex_dtx(void) const;\n\tbool get_speex_penh(void) const;\n\tunsigned short get_speex_quality(void) const;\n\tunsigned short get_speex_complexity(void) const;\n\tbool get_speex_dsp_vad(void) const;\n\tbool get_speex_dsp_agc(void) const;\n\tbool get_speex_dsp_aec(void) const;\n\tbool get_speex_dsp_nrd(void) const;\n\tunsigned short get_speex_dsp_agc_level(void) const;\n\tunsigned short get_ilbc_payload_type(void) const;\n\tunsigned short get_ilbc_mode(void) const;\n\tunsigned short get_g726_16_payload_type(void) const;\n\tunsigned short get_g726_24_payload_type(void) const;\n\tunsigned short get_g726_32_payload_type(void) const;\n\tunsigned short get_g726_40_payload_type(void) const;\n\tt_g726_packing get_g726_packing(void) const;\n\tt_dtmf_transport get_dtmf_transport(void) const;\n\tunsigned short get_dtmf_payload_type(void) const;\n\tunsigned short get_dtmf_duration(void) const;\n\tunsigned short get_dtmf_pause(void) const;\n\tunsigned short get_dtmf_volume(void) const;\n\tt_hold_variant get_hold_variant(void) const;\n\tbool get_check_max_forwards(void) const;\n\tbool get_allow_missing_contact_reg(void) const;\n\tbool get_registration_time_in_contact(void) const;\n\tbool get_compact_headers(void) const;\n\tbool get_encode_multi_values_as_list(void) const;\n\tbool get_use_domain_in_contact(void) const;\n\tbool get_allow_sdp_change(void) const;\n\tbool get_allow_redirection(void) const;\n\tbool get_ask_user_to_redirect(void) const;\n\tunsigned short get_max_redirections(void) const;\n\tt_ext_support get_ext_100rel(void) const;\n\tbool get_ext_replaces(void) const;\n\tbool get_referee_hold(void) const;\n\tbool get_referrer_hold(void) const;\n\tbool get_allow_refer(void) const;\n\tbool get_ask_user_to_refer(void) const;\n\tbool get_auto_refresh_refer_sub(void) const;\n\tbool get_attended_refer_to_aor(void) const;\n\tbool get_allow_transfer_consultation_inprog(void) const;\n\tbool get_send_p_preferred_id(void) const;\n\tbool get_send_p_asserted_id(void) const;\n\tt_sip_transport get_sip_transport(void) const;\n\tunsigned short get_sip_transport_udp_threshold(void) const;\n\tbool get_use_nat_public_ip(void) const;\n\tstring get_nat_public_ip(void) const;\n\tbool get_use_stun(void) const;\n\tt_url get_stun_server(void) const;\n\tbool get_persistent_tcp(void) const;\n\tbool get_enable_nat_keepalive(void) const;\n\tunsigned short get_timer_noanswer(void) const;\n\tunsigned short get_timer_nat_keepalive(void) const; \n\tunsigned short get_timer_tcp_ping(void) const;\n\tbool get_display_useronly_phone(void) const;\n\tbool get_numerical_user_is_phone(void) const;\n\tbool get_remove_special_phone_symbols(void) const;\n\tstring get_special_phone_symbols(void) const;\n\tbool get_use_tel_uri_for_phone(void) const;\n\tstring get_ringtone_file(void) const;\n\tstring get_ringback_file(void) const;\n\tstring get_script_incoming_call(void) const;\n\tstring get_script_in_call_answered(void) const;\n\tstring get_script_in_call_failed(void) const;\n\tstring get_script_outgoing_call(void) const;\n\tstring get_script_out_call_answered(void) const;\n\tstring get_script_out_call_failed(void) const;\n\tstring get_script_local_release(void) const;\n\tstring get_script_remote_release(void) const;\n\tlist<t_number_conversion> get_number_conversions(void) const;\n\tbool get_zrtp_enabled(void) const;\n\tbool get_zrtp_goclear_warning(void) const;\n\tbool get_zrtp_sdp(void) const;\n\tbool get_zrtp_send_if_supported(void) const;\n\tbool get_mwi_solicited(void) const;\n\tstring get_mwi_user(void) const;\n\tt_url get_mwi_server(void) const;\n\tbool get_mwi_via_proxy(void) const;\n\tunsigned long get_mwi_subscription_time(void) const;\n\tstring get_mwi_vm_address(void) const;\n\tunsigned short get_im_max_sessions(void) const;\n\tbool get_im_send_iscomposing(void) const;\n\tunsigned long get_pres_subscription_time(void) const;\n\tunsigned long get_pres_publication_time(void) const;\n\tbool get_pres_publish_startup(void) const;\n\t//@}\n\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_name(const string &_name);\n\tvoid set_domain(const string &_domain);\n\tvoid set_display(const string &_display);\t\n\tvoid set_organization(const string &_organization);\n\tvoid set_auth_realm(const string &realm);\n\tvoid set_auth_name(const string &name);\n\tvoid set_auth_pass(const string &pass);\n\tvoid set_auth_aka_op(const uint8 *aka_op);\n\tvoid set_auth_aka_amf(const uint8 *aka_amf);\n\tvoid set_use_outbound_proxy(bool b);\n\tvoid set_outbound_proxy(const t_url &url);\n\tvoid set_all_requests_to_proxy(bool b);\n\tvoid set_non_resolvable_to_proxy(bool b);\n\tvoid set_use_registrar(bool b);\n\tvoid set_registrar(const t_url &url);\n\tvoid set_registration_time(const unsigned long time);\n\tvoid set_register_at_startup(bool b);\n\tvoid set_reg_add_qvalue(bool b);\n\tvoid set_reg_qvalue(float q);\n\tvoid set_codecs(const list<t_audio_codec> &_codecs);\n\tvoid set_ptime(unsigned short _ptime);\n\tvoid set_out_obey_far_end_codec_pref(bool b);\n\tvoid set_in_obey_far_end_codec_pref(bool b);\n\tvoid set_speex_nb_payload_type(unsigned short payload_type);\n\tvoid set_speex_wb_payload_type(unsigned short payload_type);\n\tvoid set_speex_uwb_payload_type(unsigned short payload_type);\n\tvoid set_speex_bit_rate_type(t_bit_rate_type bit_rate_type);\n\tvoid set_speex_abr_nb(int abr);\n\tvoid set_speex_abr_wb(int abr);\n\tvoid set_speex_dtx(bool b);\n\tvoid set_speex_penh(bool b);\n\tvoid set_speex_quality(unsigned short quality);\n\tvoid set_speex_complexity(unsigned short complexity);\n\tvoid set_speex_dsp_vad(bool b);\n\tvoid set_speex_dsp_agc(bool b);\n\tvoid set_speex_dsp_aec(bool b);\n\tvoid set_speex_dsp_nrd(bool b);\n\tvoid set_speex_dsp_agc_level(unsigned short level);\n\tvoid set_ilbc_payload_type(unsigned short payload_type);\n\tvoid set_g726_16_payload_type(unsigned short payload_type);\n\tvoid set_g726_24_payload_type(unsigned short payload_type);\n\tvoid set_g726_32_payload_type(unsigned short payload_type);\n\tvoid set_g726_40_payload_type(unsigned short payload_type);\n\tvoid set_g726_packing(t_g726_packing packing);\n\tvoid set_ilbc_mode(unsigned short mode);\n\tvoid set_dtmf_transport(t_dtmf_transport _dtmf_transport);\n\tvoid set_dtmf_payload_type(unsigned short payload_type);\n\tvoid set_dtmf_duration(unsigned short duration);\n\tvoid set_dtmf_pause(unsigned short pause);\n\tvoid set_dtmf_volume(unsigned short volume);\n\tvoid set_hold_variant(t_hold_variant _hold_variant);\n\tvoid set_check_max_forwards(bool b);\n\tvoid set_allow_missing_contact_reg(bool b);\n\tvoid set_registration_time_in_contact(bool b);\n\tvoid set_compact_headers(bool b);\n\tvoid set_encode_multi_values_as_list(bool b);\n\tvoid set_use_domain_in_contact(bool b);\n\tvoid set_allow_sdp_change(bool b);\n\tvoid set_allow_redirection(bool b);\n\tvoid set_ask_user_to_redirect(bool b);\n\tvoid set_max_redirections(unsigned short _max_redirections);\n\tvoid set_ext_100rel(t_ext_support ext_support);\n\tvoid set_ext_replaces(bool b);\n\tvoid set_referee_hold(bool b);\n\tvoid set_referrer_hold(bool b);\n\tvoid set_allow_refer(bool b);\n\tvoid set_ask_user_to_refer(bool b);\n\tvoid set_auto_refresh_refer_sub(bool b);\n\tvoid set_attended_refer_to_aor(bool b);\n\tvoid set_allow_transfer_consultation_inprog(bool b);\n\tvoid set_send_p_preferred_id(bool b);\n\tvoid set_send_p_asserted_id(bool b);\n\tvoid set_sip_transport(t_sip_transport transport);\n\tvoid set_sip_transport_udp_threshold(unsigned short threshold);\n\tvoid set_use_nat_public_ip(bool b);\n\tvoid set_nat_public_ip(const string &public_ip);\n\tvoid set_use_stun(bool b);\n\tvoid set_stun_server(const t_url &url);\n\tvoid set_persistent_tcp(bool b);\n\tvoid set_enable_nat_keepalive(bool b);\n\tvoid set_timer_noanswer(unsigned short timer);\n\tvoid set_timer_nat_keepalive(unsigned short timer); \n\tvoid set_timer_tcp_ping(unsigned short timer);\n\tvoid set_display_useronly_phone(bool b);\n\tvoid set_numerical_user_is_phone(bool b);\n\tvoid set_remove_special_phone_symbols(bool b);\n\tvoid set_special_phone_symbols(const string &symbols);\n\tvoid set_use_tel_uri_for_phone(bool b);\n\tvoid set_ringtone_file(const string &file);\n\tvoid set_ringback_file(const string &file);\n\tvoid set_script_incoming_call(const string &script);\n\tvoid set_script_in_call_answered(const string &script);\n\tvoid set_script_in_call_failed(const string &script);\n\tvoid set_script_outgoing_call(const string &script);\n\tvoid set_script_out_call_answered(const string &script);\n\tvoid set_script_out_call_failed(const string &script);\n\tvoid set_script_local_release(const string &script);\n\tvoid set_script_remote_release(const string &script);\n\tvoid set_number_conversions(const list<t_number_conversion> &l);\n\tvoid set_zrtp_enabled(bool b);\n\tvoid set_zrtp_goclear_warning(bool b);\n\tvoid set_zrtp_sdp(bool b);\n\tvoid set_zrtp_send_if_supported(bool b);\n\tvoid set_mwi_solicited(bool b);\n\tvoid set_mwi_user(const string &user);\n\tvoid set_mwi_server(const t_url &url);\n\tvoid set_mwi_via_proxy(bool b);\n\tvoid set_mwi_subscription_time(unsigned long t);\n\tvoid set_mwi_vm_address(const string &address);\n\tvoid set_im_max_sessions(unsigned short max_sessions);\n\tvoid set_im_send_iscomposing(bool b);\n\tvoid set_pres_subscription_time(unsigned long t);\n\tvoid set_pres_publication_time(unsigned long t);\n\tvoid set_pres_publish_startup(bool b);\n\t//@}\n\n\t// Read and parse a config file into the user object.\n\t// Returns false if it fails. error_msg is an error message that can\n\t// be given to the user.\n\tbool read_config(const string &filename, string &error_msg);\n\n\t/**\n\t * Write the settings into a config file.\n\t * @param filename [in] Name of the file to write.\n\t * @param error_msg [out] Human readable error message when writing fails.\n\t * @return Returns true of writing succeeded, otherwise false.\n\t */\n\tbool write_config(const string &filename, string &error_msg);\n\t\n\t/** Get the file name for this user profile */\n\tstring get_filename(void) const;\n\n\t/** \n\t * Set a config file name.\n\t * @return True if file name did not yet exist.\n\t * @return False if file name already exists.\n\t */\n\tbool set_config(string _filename);\n\n\t// Get the name of the profile (filename without extension)\n\tstring get_profile_name(void) const;\n\t\n\t// Expand file name to a fully qualified file name\n\tstring expand_filename(const string &filename);\n\t\n\t// The contact name is created from the name and domain values.\n\t// Just the name value is not unique when multiple user profiles are\n\t// activated.\n\tstring get_contact_name(void) const;\n\t\n\t// Returns \"display <sip:name@domain>\"\n\tstring get_display_uri(void) const;\n\t\n\t// Check if all required extensions are supported\n\tbool check_required_ext(t_request *r, list<string> &unsupported) const;\n\t\n\t/**\n\t * Create contact URI.\n\t * @param anonymous [in] Indicates if an anonymous contact should be created.\n\t * @param auto_ip [in] Automatically determined local IP address that should be\n\t *                     used if not IP address has been determined through other means.\n\t * @return String representation of the contact URI.\n\t */\n\tstring create_user_contact(bool anonymous, const string &auto_ip);\n\t\n\t// Create user uri\n\tstring create_user_uri(bool anonymous);\n\t\n\t// Convert a number by applying the number conversions.\n\tstring convert_number(const string &number, const list<t_number_conversion> &l) const;\n\tstring convert_number(const string &number) const;\n\t\n\t// Get URI for sending a SUBSCRIBE for MWI\n\tt_url get_mwi_uri(void) const;\n\t\n\t/** Is this a user profile for a Diamondcard account? */\n\tbool is_diamondcard_account(void) const;\n};\n\n#endif\n"
  },
  {
    "path": "src/userintf.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include <cstdlib>\n#include <errno.h>\n#include <fcntl.h>\n#include <readline/readline.h>\n#include <readline/history.h>\n#include <signal.h>\n#include <stdio.h>\n#include <string>\n#include <sys/select.h>\n#include <unistd.h>\n#include \"address_book.h\"\n#include \"events.h\"\n#include \"line.h\"\n#include \"log.h\"\n#include \"sys_settings.h\"\n#include \"translator.h\"\n#include \"userintf.h\"\n#include \"util.h\"\n#include \"user.h\"\n#include \"audio/rtp_telephone_event.h\"\n#include \"parser/parse_ctrl.h\"\n#include \"sockets/interfaces.h\"\n#include \"audits/memman.h\"\n#include \"utils/file_utils.h\"\n#include \"utils/mime_database.h\"\n\n#define CLI_PROMPT              \"Twinkle> \"\n#define CLI_MAX_HISTORY_LENGTH\t1000\n\nextern string user_host;\nextern t_event_queue *evq_trans_layer;\n\nusing namespace utils;\n\n\n\n////////////////////////\n// GNU Readline helpers\n////////////////////////\n\nchar ** tw_completion (const char *text, int start, int end);\nchar * tw_command_generator (const char *text, int state);\n\nchar ** tw_completion (const char *text, int start, int end)\n{\n\tchar **matches;\n\tmatches = (char **)NULL;\n\tif (start == 0)\n\t\tmatches = rl_completion_matches (text, tw_command_generator);\n\treturn (matches);\n}\n\n\n\nchar * tw_command_generator (const char *text, int state)\n{\n\tstatic int len;\n\tstatic list<string>::const_iterator i;\n\n\tif (!state){\n\t\tlen = strlen(text);\n\t\ti = ui->get_all_commands().begin();\n\t}\n\n\tfor (; i != ui->get_all_commands().end(); i++){\n\t\tconst char * s = i->c_str();\n\t\t//cout << s << endl;\n\t\tif ( s && strncmp(s, text, len) == 0  ){\n\t\t\ti++;\n\t\t\treturn strdup(s);\n\t\t}\n\t}\n\n\t/* If no names matched, then return NULL. */\n\treturn ((char *)NULL);\n}\n\n// Ugly hack to allow invoking methods on our object from within a C-style\n// callback function.  This relies on the object being a singleton.\nstatic t_userintf *cb_user_intf;\n// Callback method (a.k.a. \"line handler\") that will be invoked by Readline\n// once a complete line has been read.\nstatic void tw_readline_cb(char *line)\n{\n\tif (!line) {\n\t\t// EOF\n\t\tcout << endl;\n\t\t// Calling this from the line handler prevents one extra\n\t\t// prompt from being displayed.  (The duplicate call later on\n\t\t// will not be an issue.)\n\t\trl_callback_handler_remove();\n\n\t\tcb_user_intf->cmd_quit();\n\t} else {\n\t\tif (*line) {\n\t\t\tadd_history(line);\n\t\t\tcb_user_intf->exec_command(line);\n\t\t}\n\t\tfree(line);\n\t}\n}\n\n// SIGWINCH handler to help us relay that information to Readline\nstatic int sigwinch_received;\nstatic void sigwinch_handler(int signum)\n{\n\tsigwinch_received = 1;\n\tsignal(SIGWINCH, sigwinch_handler);\n}\n\n/////////////////////////////\n// Private\n/////////////////////////////\n\nstring t_userintf::expand_destination(t_user *user_config, const string &dst, const string &scheme) {\n\tassert(user_config);\n\t\n\tstring s = dst;\n\n\t// Apply number conversion rules if applicable\n\t// Add domain if it is missing from a sip-uri\n\tif (s.find('@') == string::npos) {\n\t\tbool is_tel_uri = (s.substr(0, 4) == \"tel:\");\n\t\t\n\t\t// Strip tel-scheme\n\t\tif (is_tel_uri) s = s.substr(4);\n\t\n\t\t// Remove white space\n\t\ts = remove_white_space(s);\n\t\n\t\t// Remove special phone symbols\n\t\tif (user_config->get_remove_special_phone_symbols() &&\n\t\t    looks_like_phone(s, user_config->get_special_phone_symbols())) \n\t\t{\n\t\t\ts = remove_symbols(s, user_config->get_special_phone_symbols());\n\t\t}\n\t\t\n\t\t// Convert number according to the number conversion rules\n\t\ts = user_config->convert_number(s);\n\t\t\t\n\t\tif (is_tel_uri) {\n\t\t\t// Add tel-scheme again.\n\t\t\ts = \"tel:\" + s;\n\t\t} else if (s.substr(0, 4) != \"sip:\" &&\n\t\t           (user_config->get_use_tel_uri_for_phone() || scheme == \"tel\") &&\n\t\t           user_config->get_numerical_user_is_phone() &&\n\t\t           looks_like_phone(s, user_config->get_special_phone_symbols()))\n\t\t{\n\t\t\t// Add tel-scheme if a telephone number must be expanded\n\t\t        // to a tel-uri according to user profile settings.\n\t\t\ts = \"tel:\" + s;\n\t\t} else {\n\t\t\t// Add domain\n\t\t\ts += '@';\n\t\t\ts += user_config->get_domain();\n\t\t}\n\t}\n\t\n\t// Add sip-scheme if a scheme is missing\n\tif (s.substr(0, 4) != \"sip:\" && s.substr(0, 4) != \"tel:\") {\n\t\ts = \"sip:\" + s;\n\t}\n\n\t// RFC 3261 19.1.1\n\t// Add user=phone for telehpone numbers in a SIP-URI\n\t// If the SIP-URI contains a telephone number it SHOULD contain\n\t// the user=phone parameter.\n\tif (user_config->get_numerical_user_is_phone() && s.substr(0, 4) == \"sip:\") {\n\t\tt_url u(s);\n\t\tif (u.get_user_param().empty() && \n\t\t    u.user_looks_like_phone(user_config->get_special_phone_symbols())) {\n\t\t\ts += \";user=phone\";\n\t\t}\n\t}\n\n\treturn s;\n}\n\nvoid t_userintf::expand_destination(t_user *user_config, \n\t\tconst string &dst, string &display, string &dst_url) \n{\n\tdisplay.clear();\n\tdst_url.clear();\n\t\t\n\tif (dst.empty()) {\n\t\treturn;\n\t}\n\t\n\t// If there is a display name then the url part is between angle\n\t// brackets.\n\tif (dst[dst.size() - 1] != '>') {\n\t\tdst_url = expand_destination(user_config, dst);\n\t\treturn;\n\t}\n\t\n\t// Find start of url\n\tstring::size_type i = dst.rfind('<');\n\tif (i == string::npos) {\n\t\t// It seems the string is invalid.\n\t\treturn;\n\t}\n\t\n\tdst_url = expand_destination(user_config, dst.substr(i + 1, dst.size() - i - 2));\n\t\n\tif (i > 0) {\n\t\tdisplay = unquote(trim(dst.substr(0, i)));\n\t}\n}\n\nvoid t_userintf::expand_destination(t_user *user_config, \n\t\tconst string &dst, t_display_url &display_url) \n{\n\tstring url_str;\n\t\n\texpand_destination(user_config, dst, display_url.display, url_str);\n\tdisplay_url.url.set_url(url_str);\n}\n\nvoid t_userintf::expand_destination(t_user *user_config,\n\t\tconst string &dst, t_display_url &display_url, string &subject,\n\t\tstring &dst_no_headers)\n{\n\tstring headers;\n\tdst_no_headers = dst;\n\tt_url u(dst);\t\n\t\n\t// Split headers from URI\n\tif (u.is_valid()) {\n\t\t// destination is a valid URI. Strip off the headers if any\n\t\theaders = u.get_headers();\n\t\t\n\t\t// Cut off headers\n\t\t// Note that a separator (?) will be in front of the \n\t\t// headers string\n\t\tif (!headers.empty()) {\n\t\t\tstring::size_type i = dst.find(headers);\n\t\t\tif (i != string::npos) {\n\t\t\t\tdst_no_headers = dst.substr(0, i - 1);\n\t\t\t}\n\t\t}\n\t\t\n\t\texpand_destination(user_config, dst_no_headers, display_url);\n\t} else {\n\t\t// destination may be a short URI.\n\t\t// Split at a '?' to find any headers.\n\t\t// NOTE: this is not fool proof. A user name may contain a '?'\n\t\tvector<string> l = split_on_first(dst, '?');\n\t\tdst_no_headers = l[0];\n\t\texpand_destination(user_config, dst_no_headers, display_url);\n\t\tif (display_url.is_valid() && l.size() == 2) {\n\t\t\theaders = l[1];\n\t\t}\n\t}\n\t\n\t// Parse headers to find subject header\n\tsubject.clear();\n\tif (!headers.empty()) {\n\t\ttry {\n\t\t\tlist<string> parse_errors;\n\t\t\tt_sip_message *m = t_parser::parse_headers(headers, parse_errors);\n\t\t\tif (m->hdr_subject.is_populated()) {\n\t\t\t\tsubject = m->hdr_subject.subject;\n\t\t\t}\n\t\t\tMEMMAN_DELETE(m);\n\t\t\tdelete m;\n\t\t} catch (int) {\n\t\t\t// ignore invalid headers\n\t\t}\n\t}\n}\n\nbool t_userintf::parse_args(const list<string> command_list,\n                            list<t_command_arg> &al)\n{\n\tt_command_arg\targ;\n\tbool parsed_flag = false;\n\n\tal.clear();\n\targ.flag = 0;\n\targ.value = \"\";\n\n\tfor (list<string>::const_iterator i = command_list.begin();\n\t     i != command_list.end(); i++)\n\t{\n\t\tif (i == command_list.begin()) continue;\n\n\t\tconst string &s = *i;\n\t\tif (s[0] == '-') {\n\t\t\tif (s.size() == 1) return false;\n\t\t\tif (parsed_flag) al.push_back(arg);\n\n\t\t\targ.flag = s[1];\n\n\t\t\tif (s.size() > 2) {\n\t\t\t\targ.value = unquote(s.substr(2));\n\t\t\t\tal.push_back(arg);\n\t\t\t\targ.flag = 0;\n\t\t\t\targ.value = \"\";\n\t\t\t\tparsed_flag = false;\n\t\t\t} else {\n\t\t\t\targ.value = \"\";\n\t\t\t\tparsed_flag = true;\n\t\t\t}\n\t\t} else {\n\t\t\tif (parsed_flag) {\n\t\t\t\targ.value = unquote(s);\n\t\t\t} else {\n\t\t\t\targ.flag = 0;\n\t\t\t\targ.value = unquote(s);\n\t\t\t}\n\n\t\t\tal.push_back(arg);\n\t\t\tparsed_flag = false;\n\t\t\targ.flag = 0;\n\t\t\targ.value = \"\";\n\t\t}\n\t}\n\n\t// Last parsed argument was a flag only\n\tif (parsed_flag) al.push_back(arg);\n\n\treturn true;\n}\n\nbool t_userintf::exec_invite(const list<string> command_list, bool immediate) {\n\tlist<t_command_arg> al;\n\tstring display;\n\tstring subject;\n\tstring destination;\n\tbool hide_user = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help call\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'd':\n\t\t\tdisplay = i->value;\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tsubject = i->value;\n\t\t\tbreak;\n\t\tcase 'h':\n\t\t\thide_user = true;\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tdestination = i->value;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help call\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn do_invite(destination, display, subject, immediate, hide_user);\n}\n\nbool t_userintf::do_invite(const string &destination, const string &display,\n\t\tconst string &subject, bool immediate, bool anonymous)\n{\n\tt_url dest_url(expand_destination(active_user, destination));\n\t\n\tif (!dest_url.is_valid()) {\n\t\texec_command(\"help call\");\n\t\treturn false;\n\t}\n\n\tt_url vm_url(expand_destination(active_user, active_user->get_mwi_vm_address()));\n\tif (dest_url != vm_url) {\n\t\t// Keep call information for redial\n\t\tlast_called_url = dest_url;\n\t\tlast_called_display = display;\n\t\tlast_called_subject = subject;\n\t\tlast_called_profile = active_user->get_profile_name();\n\t\tlast_called_hide_user = anonymous;\n\t}\n\n\tphone->pub_invite(active_user, dest_url, display, subject, anonymous);\n\treturn true;\n}\n\nbool t_userintf::exec_redial(const list<string> command_list) {\n\tif (can_redial()) {\n\t\tdo_redial();\n\t\treturn true;\n\t}\n\t\n\treturn false;\n}\n\nvoid t_userintf::do_redial(void) {\n\tt_user *user_config = phone->ref_user_profile(last_called_profile);\n\tphone->pub_invite(user_config, last_called_url, last_called_display,\n\t\tlast_called_subject, last_called_hide_user);\n}\n\nbool t_userintf::exec_answer(const list<string> command_list) {\n\tdo_answer();\n\treturn true;\n}\n\nvoid t_userintf::do_answer(void) {\n\tcb_stop_call_notification(phone->get_active_line());\n\tphone->pub_answer();\n}\n\nbool t_userintf::exec_answerbye(const list<string> command_list) {\n\tdo_answerbye();\n\treturn true;\n}\n\nvoid t_userintf::do_answerbye(void) {\n\tunsigned short line = phone->get_active_line();\n\t\n\tswitch (phone->get_line_substate(line)) {\n\tcase LSSUB_INCOMING_PROGRESS:\n\t\tdo_answer();\n\t\tbreak;\n\tcase LSSUB_OUTGOING_PROGRESS:\n\tcase LSSUB_ESTABLISHED:\n\t\tdo_bye();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nbool t_userintf::exec_reject(const list<string> command_list) {\n\tdo_reject();\n\treturn true;\n}\n\nvoid t_userintf::do_reject(void) {\n\tcb_stop_call_notification(phone->get_active_line());\n\tphone->pub_reject();\n\tcout << endl;\n\tcout << \"Line \" << phone->get_active_line() + 1 << \": call rejected.\\n\";\n\tcout << endl;\n}\n\nbool t_userintf::exec_redirect(const list<string> command_list, bool immediate) {\n\tlist<t_command_arg> al;\n\tlist<string> dest_list;\n\tint num_redirections = 0;\n\tbool show_status = false;\n\tbool action_present = false;\n\tbool enable = true;\n\tbool type_present = false;\n\tt_cf_type cf_type = CF_ALWAYS;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help redirect\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 's':\n\t\t\tshow_status = true;\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\tif (i->value == \"always\") {\n\t\t\t\tcf_type = CF_ALWAYS;\n\t\t\t} else if (i->value == \"busy\") {\n\t\t\t\tcf_type = CF_BUSY;\n\t\t\t} else if (i->value == \"noanswer\") {\n\t\t\t\tcf_type = CF_NOANSWER;\n\t\t\t} else {\n\t\t\t\texec_command(\"help redirect\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttype_present = true;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tif (i->value == \"on\") {\n\t\t\t\tenable = true;\n\t\t\t} else if (i->value == \"off\") {\n\t\t\t\tenable = false;\n\t\t\t} else {\n\t\t\t\texec_command(\"help redirect\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\taction_present = true;\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tdest_list.push_back(i->value);\n\t\t\tnum_redirections++;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help redirect\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (type_present && enable && (num_redirections == 0 || num_redirections > 5)) {\n\t\texec_command(\"help redirect\");\n\t\treturn false;\n\t}\n\t\n\tif (!type_present && action_present && enable) { \n\t\texec_command(\"help redirect\");\n\t\treturn false;\n\t}\n\t\n\tif (!type_present && !action_present && \n\t    (num_redirections == 0 || num_redirections > 5)) \n\t{\n\t\texec_command(\"help redirect\");\n\t\treturn false;\n\t}\n\t\t\n\tdo_redirect(show_status, type_present, cf_type, action_present, enable,\n\t\t\tnum_redirections, dest_list, immediate);\n\treturn true;\n}\n\nvoid t_userintf::do_redirect(bool show_status, bool type_present, t_cf_type cf_type, \n\t\tbool action_present, bool enable, int num_redirections,\n\t\tconst list<string> &dest_strlist, bool immediate)\n{\n\tlist<t_display_url> dest_list;\n\tfor (list<string>::const_iterator i = dest_strlist.begin();\n\t     i != dest_strlist.end(); i++)\n\t{\n\t\tt_display_url du;\n\t\tdu.url = expand_destination(active_user, *i);\n\t\tdu.display.clear();\n\t\tif (!du.is_valid()) return;\n\t\tdest_list.push_back(du);\n\t}\n\n\tif (show_status) {\n\t\tlist<t_display_url> cf_dest; // call forwarding destinations\n\n\t\tcout << endl;\n\n\t\tcout << \"Redirect always: \";\n\t\tif (phone->ref_service(active_user)->get_cf_active(CF_ALWAYS, cf_dest)) {\n\t\t\tfor (list<t_display_url>::iterator i = cf_dest.begin();\n\t\t\t     i != cf_dest.end(); i++)\n\t\t\t{\n\t\t\t\tif (i != cf_dest.begin()) cout << \", \";\n\t\t\t\tcout << i->encode();\n\t\t\t}\n\t\t} else {\n\t\t\tcout << \"not active\";\n\t\t}\n\t\tcout << endl;\n\n\t\tcout << \"Redirect busy: \";\n\t\tif (phone->ref_service(active_user)->get_cf_active(CF_BUSY, cf_dest)) {\n\t\t\tfor (list<t_display_url>::iterator i = cf_dest.begin();\n\t\t\t     i != cf_dest.end(); i++)\n\t\t\t{\n\t\t\t\tif (i != cf_dest.begin()) cout << \", \";\n\t\t\t\tcout << i->encode();\n\t\t\t}\n\t\t} else {\n\t\t\tcout << \"not active\";\n\t\t}\n\t\tcout << endl;\n\n\t\tcout << \"Redirect noanswer: \";\n\t\tif (phone->ref_service(active_user)->get_cf_active(CF_NOANSWER, cf_dest)) {\n\t\t\tfor (list<t_display_url>::iterator i = cf_dest.begin();\n\t\t\t     i != cf_dest.end(); i++)\n\t\t\t{\n\t\t\t\tif (i != cf_dest.begin()) cout << \", \";\n\t\t\t\tcout << i->encode();\n\t\t\t}\n\t\t} else {\n\t\t\tcout << \"not active\";\n\t\t}\n\t\tcout << endl;\n\n\t\tcout << endl;\n\t\treturn;\n\t}\n\n\t// Enable/disable permanent redirections\n\tif (type_present) {\n\t\tif (enable) {\n\t\t\tphone->ref_service(active_user)->enable_cf(cf_type, dest_list);\n\t\t\tcout << \"Redirection enabled.\\n\\n\";\n\t\t} else {\n\t\t\tphone->ref_service(active_user)->disable_cf(cf_type);\n\t\t\tcout << \"Redirection disabled.\\n\\n\";\n\t\t}\n\t\t\n\t\treturn;\n\t} else {\n\t\tif (action_present) {\n\t\t\tif (!enable) {\n\t\t\t\tphone->ref_service(active_user)->disable_cf(CF_ALWAYS);\n\t\t\t\tphone->ref_service(active_user)->disable_cf(CF_BUSY);\n\t\t\t\tphone->ref_service(active_user)->disable_cf(CF_NOANSWER);\n\t\t\t\tcout << \"All redirections disabled.\\n\\n\";\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Redirect current call\n\tcb_stop_call_notification(phone->get_active_line());\n\tphone->pub_redirect(dest_list, 302);\n\tcout << endl;\n\tcout << \"Line \" << phone->get_active_line() + 1 << \": call redirected.\\n\";\n\tcout << endl;\n}\n\nbool t_userintf::exec_dnd(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tbool show_status = false;\n\tbool toggle = true;\n\tbool enable = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help dnd\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 's':\n\t\t\tshow_status = true;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tif (i->value == \"on\") {\n\t\t\t\tenable = true;\n\t\t\t} else if (i->value == \"off\") {\n\t\t\t\tenable = false;\n\t\t\t} else {\n\t\t\t\texec_command(\"help dnd\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttoggle = false;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help dnd\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tdo_dnd(show_status, toggle, enable);\n\treturn true;\n}\n\nvoid t_userintf::do_dnd(bool show_status, bool toggle, bool enable) {\n\tif (show_status) {\n\t\tcout << endl;\n\t\tcout << \"Do not disturb: \";\n\t\tif (phone->ref_service(active_user)->is_dnd_active()) {\n\t\t\tcout << \"active\";\n\t\t} else {\n\t\t\tcout << \"not active\";\n\t\t}\n\t\tcout << endl;\n\t\treturn;\n\t}\n\t\n\tif (toggle) {\n\t\tenable = !phone->ref_service(active_user)->is_dnd_active();\n\t}\n\n\tif (enable) {\n\t\tphone->ref_service(active_user)->enable_dnd();\n\t\tcout << \"Do not disturb enabled.\\n\\n\";\n\t\treturn;\n\t} else {\n\t\tphone->ref_service(active_user)->disable_dnd();\n\t\tcout << \"Do not disturb disabled.\\n\\n\";\n\t\treturn;\n\t}\n}\n\nbool t_userintf::exec_auto_answer(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tbool show_status = false;\n\tbool toggle = true;\n\tbool enable = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help auto_answer\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 's':\n\t\t\tshow_status = true;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tif (i->value == \"on\") {\n\t\t\t\tenable = true;\n\t\t\t} else if (i->value == \"off\") {\n\t\t\t\tenable = false;\n\t\t\t} else {\n\t\t\t\texec_command(\"help auto_answer\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttoggle = false;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help auto_answer\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tdo_auto_answer(show_status, toggle, enable);\n\treturn true;\n}\n\nvoid t_userintf::do_auto_answer(bool show_status, bool toggle, bool enable) {\n\tif (show_status) {\n\t\tcout << endl;\n\t\tcout << \"Auto answer: \";\n\t\tif (phone->ref_service(active_user)->is_auto_answer_active()) {\n\t\t\tcout << \"active\";\n\t\t} else {\n\t\t\tcout << \"not active\";\n\t\t}\n\t\tcout << endl;\n\t\treturn;\n\t}\n\t\n\tif (toggle) {\n\t\tenable = !phone->ref_service(active_user)->is_auto_answer_active();\n\t}\n\n\tif (enable) {\n\t\tphone->ref_service(active_user)->enable_auto_answer(true);\n\t\tcout << \"Auto answer enabled.\\n\\n\";\n\t\treturn;\n\t} else {\n\t\tphone->ref_service(active_user)->enable_auto_answer(false);\n\t\tcout << \"Auto answer disabled.\\n\\n\";\n\t\treturn;\n\t}\n}\n\nbool t_userintf::exec_bye(const list<string> command_list) {\n\tdo_bye();\n\treturn true;\n}\n\nvoid t_userintf::do_bye(void) {\n\tphone->pub_end_call();\n}\n\nbool t_userintf::exec_hold(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tbool toggle = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help hold\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 't':\n\t\t\ttoggle = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help hold\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tdo_hold(toggle);\n\treturn true;\n}\n\nvoid t_userintf::do_hold(bool toggle) {\n\tif (toggle && phone->is_line_on_hold(phone->get_active_line()))\n\t\tphone->pub_retrieve();\n\telse\n\t\tphone->pub_hold();\n}\n\nbool t_userintf::exec_retrieve(const list<string> command_list) {\n\tdo_retrieve();\n\treturn true;\n}\n\nvoid t_userintf::do_retrieve(void) {\n\tphone->pub_retrieve();\n}\n\nbool t_userintf::exec_refer(const list<string> command_list, bool immediate) {\n\tlist<t_command_arg> al;\n\tstring destination;\n\tbool dest_set = false;\n\tt_transfer_type transfer_type = TRANSFER_BASIC;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help transfer\");\n\t\treturn false;\n\t}\n\t\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'c':\n\t\t\tif (transfer_type != TRANSFER_BASIC) {\n\t\t\t\texec_command(\"help transfer\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttransfer_type = TRANSFER_CONSULT;\n\t\t\tif (!i->value.empty()) {\n\t\t\t\tdestination = i->value;\n\t\t\t\tdest_set = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l':\n\t\t\tif (transfer_type != TRANSFER_BASIC) {\n\t\t\t\texec_command(\"help transfer\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttransfer_type = TRANSFER_OTHER_LINE;\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tdestination = i->value;\n\t\t\tdest_set = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help transfer\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!dest_set && transfer_type == TRANSFER_BASIC) {\n\t\texec_command(\"help transfer\");\n\t\treturn false;\n\t}\n\n\treturn do_refer(destination, transfer_type, immediate);\n}\n\nbool t_userintf::do_refer(const string &destination, t_transfer_type transfer_type, \n\t\tbool immediate) \n{\n\tt_url dest_url;\n\t\n\tif (transfer_type == TRANSFER_BASIC || \n\t    (transfer_type == TRANSFER_CONSULT && !destination.empty())) \n\t{\n\t\tdest_url.set_url(expand_destination(active_user, destination));\n\t\n\t\tif (!dest_url.is_valid()) {\n\t\t\texec_command(\"help transfer\");\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tunsigned short active_line;\n\tunsigned short other_line;\n\tunsigned short line_to_be_transferred;\n\t\n\tswitch (transfer_type) {\n\tcase TRANSFER_BASIC:\n\t\tphone->pub_refer(dest_url, \"\");\n\t\tbreak;\n\tcase TRANSFER_CONSULT:\n\t\tif (destination.empty()) {\n\t\t\tactive_line = phone->get_active_line();\n\t\t\tif (!phone->is_line_transfer_consult(active_line,\n\t\t\t\t\tline_to_be_transferred)) \n\t\t\t{\n\t\t\t\t// There is no call to transfer\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tphone->pub_refer(line_to_be_transferred, active_line);\n\t\t} else {\n\t\t\tphone->pub_setup_consultation_call(dest_url, \"\");\n\t\t}\n\t\tbreak;\n\tcase TRANSFER_OTHER_LINE:\n\t\tactive_line = phone->get_active_line();\n\t\tother_line = (active_line == 0 ? 1 : 0);\n\t\tphone->pub_refer(active_line, other_line);\n\t\tbreak;\n\t}\n\t\n\treturn true;\n}\n\n\nbool t_userintf::exec_conference(const list<string> command_list) {\n\tdo_conference();\n\treturn true;\n}\n\nvoid t_userintf::do_conference(void) {\n\tif (phone->join_3way(0, 1)) {\n\t\tcout << endl;\n\t\tcout << \"Started 3-way conference.\\n\";\n\t\tcout << endl;\n\t} else {\n\t\tcout << endl;\n\t\tcout << \"Failed to start 3-way conference.\\n\";\n\t\tcout << endl;\n\t}\n}\n\nbool t_userintf::exec_mute(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tbool show_status = false;\n\tbool toggle = true;\n\tbool enable = true;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help mute\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 's':\n\t\t\tshow_status = true;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\tif (i->value == \"on\") {\n\t\t\t\tenable = true;\n\t\t\t} else if (i->value == \"off\") {\n\t\t\t\tenable = false;\n\t\t\t} else {\n\t\t\t\texec_command(\"help mute\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttoggle = false;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help mute\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tdo_mute(show_status, toggle, enable);\n\treturn true;\n}\n\nvoid t_userintf::do_mute(bool show_status, bool toggle, bool enable) {\n\tif (show_status) {\n\t\tcout << endl;\n\t\tcout << \"Line is \";\n\t\tif (phone->is_line_muted(phone->get_active_line())) {\n\t\t\tcout << \"muted.\";\n\t\t} else {\n\t\t\tcout << \"not muted.\";\n\t\t}\n\t\tcout << endl;\n\t\treturn;\n\t}\n\n\tif (toggle) enable = !phone->is_line_muted(phone->get_active_line());\n\tif (enable) {\n\t\tphone->mute(enable);\n\t\tcout << \"Line muted.\\n\\n\";\n\t\treturn;\n\t} else {\n\t\tphone->mute(enable);\n\t\tcout << \"Line unmuted.\\n\\n\";\n\t\treturn;\n\t}\n}\n\nbool t_userintf::exec_dtmf(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tstring digits;\n\tbool raw_mode = false;\n\n\tif (phone->get_line_state(phone->get_active_line()) == LS_IDLE) {\n\t\treturn false;\n\t}\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help dtmf\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'r':\n\t\t\traw_mode = true;\n\t\t\tif (!i->value.empty()) digits = i->value;\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tdigits = i->value;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help dtmf\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!raw_mode) {\n\t\tdigits = str2dtmf(digits);\n\t}\n\t\n\tif (digits == \"\") {\n\t\texec_command(\"help dtmf\");\n\t\treturn false;\n\t}\n\n\tdo_dtmf(digits);\n\treturn true;\n}\n\nvoid t_userintf::do_dtmf(const string &digits) {\n\tconst t_call_info call_info = phone->get_call_info(phone->get_active_line());\n\tthrottle_dtmf_not_supported = false;\n\t\n\tif (!call_info.dtmf_supported) return;\n\t\n\tfor (string::const_iterator i = digits.begin(); i != digits.end(); i++) {\n\t\tif (is_valid_dtmf_sym(*i)) {\n\t\t\tphone->pub_send_dtmf(*i, call_info.dtmf_inband, call_info.dtmf_info);\n\t\t}\n\t}\n}\n\nbool t_userintf::exec_register(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tbool reg_all_profiles = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help register\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'a':\n\t\t\treg_all_profiles = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help register\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tdo_register(reg_all_profiles);\n\treturn true;\n}\n\nvoid t_userintf::do_register(bool reg_all_profiles) {\n\tif (reg_all_profiles) {\n\t\tlist<t_user *> user_list = phone->ref_users();\n\t\t\n\t\tfor (list<t_user *>::iterator i = user_list.begin();\n\t\t     i != user_list.end(); i++)\n\t\t{\n\t\t\tphone->pub_registration(*i, REG_REGISTER, \n\t\t\t\t\tDUR_REGISTRATION(*i));\n\t\t}\t\n\t} else {\n\t\tphone->pub_registration(active_user, REG_REGISTER, \n\t\t\t\tDUR_REGISTRATION(active_user));\n\t}\n}\n\nbool t_userintf::exec_deregister(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tbool dereg_all_devices = false;\n\tbool dereg_all_profiles = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help deregister\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'a':\n\t\t\tdereg_all_profiles = true;\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tdereg_all_devices = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help deregister\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tdo_deregister(dereg_all_profiles, dereg_all_devices);\n\treturn true;\n}\n\nvoid t_userintf::do_deregister(bool dereg_all_profiles, bool dereg_all_devices) {\n\tt_register_type dereg_type = REG_DEREGISTER;\n\t\n\tif (dereg_all_devices) {\n\t\tdereg_type = REG_DEREGISTER_ALL;\n\t}\n\t\n\tif (dereg_all_profiles) {\n\t\tlist<t_user *> user_list = phone->ref_users();\n\t\t\n\t\tfor (list<t_user *>::iterator i = user_list.begin();\n\t\t     i != user_list.end(); i++)\n\t\t{\n\t\t\tphone->pub_registration(*i, dereg_type);\n\t\t}\n\t} else {\n\t\tphone->pub_registration(active_user, dereg_type);\n\t}\n}\n\nbool t_userintf::exec_fetch_registrations(const list<string> command_list) {\n\tdo_fetch_registrations();\n\treturn true;\n}\n\nvoid t_userintf::do_fetch_registrations(void) {\n\tphone->pub_registration(active_user, REG_QUERY);\n}\n\nbool t_userintf::exec_options(const list<string> command_list, bool immediate) {\n\tlist<t_command_arg> al;\n\tstring destination;\n\tbool dest_set = false;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help options\");\n\t\treturn false;\n\t}\n\t\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 0:\n\t\t\tdestination = i->value;\n\t\t\tdest_set = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help options\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!dest_set) {\n\t\tif (phone->get_line_state(phone->get_active_line()) == LS_IDLE) {\n\t\t\texec_command(\"help options\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn do_options(dest_set, destination, immediate);\n}\n\nbool t_userintf::do_options(bool dest_set, const string &destination, bool immediate) {\n\tif (!dest_set) {\n\t\tphone->pub_options();\n\t} else {\n\t\tt_url dest_url;\n\t\tdest_url.set_url(expand_destination(active_user, destination));\n\t\t\n\t\tif (!dest_url.is_valid()) {\n\t\t\texec_command(\"help options\");\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tphone->pub_options(active_user, dest_url);\n\t}\n\t\n\treturn true;\n}\n\nbool t_userintf::exec_line(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tint line = 0;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help line\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'o':\n\t\t\tline = -1;\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tline = atoi(i->value.c_str());\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help line\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (line < -1 || line > 2) {\n\t\texec_command(\"help line\");\n\t\treturn false;\n\t}\n\n\tdo_line(line);\n\treturn true;\n}\n\nvoid t_userintf::do_line(int line) {\n\tif (line == 0) {\n\t\tcout << endl;\n\t\tcout << \"Active line is: \" << phone->get_active_line()+1 << endl;\n\t\tcout << endl;\n\t\treturn;\n\t}\n\n\tint current = phone->get_active_line();\n\n\tif (line == -1) {\n\t\tint other = 1 - current;\n\t\tline = other + 1;\n\t}\n\n\tif (line == current + 1) {\n\t\tcout << endl;\n\t\tcout << \"Line \" << current + 1 << \" is already active.\\n\";\n\t\tcout << endl;\n\t\treturn;\n\t}\n\n\tphone->pub_activate_line(line - 1);\n\tif (phone->get_active_line() == current) {\n\t\tcout << endl;\n\t\tcout << \"Current call cannot be put on-hold.\\n\";\n\t\tcout << \"Cannot switch to another line now.\\n\";\n\t\tcout << endl;\n\t} else {\n\t\tcout << endl;\n\t\tcout << \"Line \" << phone->get_active_line()+1 << \" is now active.\\n\";\n\t\tcout << endl;\n\t}\n}\n\nbool t_userintf::exec_user(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tstring profile_name;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help user\");\n\t\treturn false;\n\t}\n\t\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 0:\n\t\t\tprofile_name = i->value;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help user\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tdo_user(profile_name);\n\treturn true;\n}\n\nvoid t_userintf::do_user(const string &profile_name) {\n\tlist<t_user *> user_list = phone->ref_users();\n\tif (profile_name.empty()) {\n\t\t// Show all users\n\t\tcout << endl;\n\t\tfor (list<t_user *>::iterator i = user_list.begin();\n\t\t     i != user_list.end(); i++)\n\t\t{\n\t\t\tif (*i == active_user) {\n\t\t\t\tcout << \"* \";\n\t\t\t} else {\n\t\t\t\tcout << \"  \";\n\t\t\t}\n\t\t\t\n\t\t\tcout << (*i)->get_profile_name();\n\t\t\tcout << \"\\n    \";\n\t\t\tcout << (*i)->get_display(false);\n\t\t\tcout << \" <sip:\" << (*i)->get_name();\n\t\t\tcout << \"@\" << (*i)->get_domain() << \">\\n\";\n\t\t}\n\t\tcout << endl;\n\t\treturn;\n\t}\n\n\tfor (list<t_user *>::iterator i = user_list.begin();\n\t     i != user_list.end(); i++)\n\t{\n\t\tif ((*i)->get_profile_name() == profile_name) {\n\t\t\tactive_user = (*i);\n\t\t\tcout << endl;\n\t\t\tcout << profile_name;\n\t\t\tcout << \" activated.\\n\";\n\t\t\tcout << endl;\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}\n\t\n\tcout << endl;\n\tcout << \"Unknown user profile: \";\n\tcout << profile_name;\n\tcout << endl << endl;\n}\n\nbool t_userintf::exec_zrtp(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tt_zrtp_cmd zrtp_cmd = ZRTP_ENCRYPT;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help zrtp\");\n\t\treturn false;\n\t}\n\t\n\tif (al.size() != 1) {\n\t\texec_command(\"help zrtp\");\n\t\treturn false;\n\t}\n\t\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 0:\n\t\t\tif (i->value == \"encrypt\") {\n\t\t\t\tzrtp_cmd = ZRTP_ENCRYPT;\n\t\t\t} else if (i->value == \"go-clear\") {\n\t\t\t\tzrtp_cmd = ZRTP_GO_CLEAR;\n\t\t\t} else if (i->value == \"confirm-sas\") {\n\t\t\t\tzrtp_cmd = ZRTP_CONFIRM_SAS;\n\t\t\t} else if (i->value == \"reset-sas\") {\n\t\t\t\tzrtp_cmd = ZRTP_RESET_SAS;\n\t\t\t} else {\n\t\t\t\texec_command(\"help zrtp\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help zrtp\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tdo_zrtp(zrtp_cmd);\n\treturn true;\n}\n\nvoid t_userintf::do_zrtp(t_zrtp_cmd zrtp_cmd) {\n\tswitch (zrtp_cmd) {\n\tcase ZRTP_ENCRYPT:\n\t\tphone->pub_enable_zrtp();\n\t\tbreak;\n\tcase ZRTP_GO_CLEAR:\n\t\tphone->pub_zrtp_request_go_clear();\n\t\tbreak;\n\tcase ZRTP_CONFIRM_SAS:\n\t\tphone->pub_confirm_zrtp_sas();\n\t\tbreak;\n\tcase ZRTP_RESET_SAS:\n\t\tphone->pub_reset_zrtp_sas_confirmation();\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\nbool t_userintf::exec_message(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tstring display;\n\tstring subject;\n\tstring filename;\n\tstring destination;\n\tstring text;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help message\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 's':\n\t\t\tsubject = i->value;\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tfilename = i->value;\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tdisplay = i->value;\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tif (destination.empty()) {\n\t\t\t\tdestination = i->value;\n\t\t\t} else {\n\t\t\t\ttext = i->value;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help message\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tif (destination.empty() || (text.empty() && filename.empty())) {\n\t\texec_command(\"help message\");\n\t\treturn false;\n\t}\n\t\n\tim::t_msg msg(text, im::MSG_DIR_OUT, im::TXT_PLAIN);\n\tmsg.subject = subject;\n\t\n\tif (!filename.empty()) {\n\t\tt_media media(\"application/octet-stream\");\n\t\tstring mime_type = mime_database->get_mimetype(filename);\n\t\t\n\t\tif (!mime_type.empty()) {\n\t\t\tmedia = t_media(mime_type);\n\t\t}\n\t\t\n\t\tmsg.set_attachment(filename, media, strip_path_from_filename(filename));\n\t}\n\n\treturn do_message(destination, display, msg);\n}\n\nbool t_userintf::do_message(const string &destination, const string &display,\n\t\tconst im::t_msg &msg)\n{\n\tt_url dest_url(expand_destination(active_user, destination));\n\t\n\tif (!dest_url.is_valid()) {\n\t\texec_command(\"help message\");\n\t\treturn false;\n\t}\n\t\n\t(void)phone->pub_send_message(active_user, dest_url, display, msg);\n\treturn true;\n}\n\nbool t_userintf::exec_presence(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\tt_presence_state::t_basic_state basic_state = t_presence_state::ST_BASIC_OPEN;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help presence\");\n\t\treturn false;\n\t}\n\n\tfor (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {\n\t\tswitch (i->flag) {\n\t\tcase 'b':\n\t\t\tif (i->value == \"online\") {\n\t\t\t\tbasic_state = t_presence_state::ST_BASIC_OPEN;\n\t\t\t} else if (i->value == \"offline\") {\n\t\t\t\tbasic_state = t_presence_state::ST_BASIC_CLOSED;\n\t\t\t} else {\n\t\t\t\texec_command(\"help presence\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texec_command(\"help presence\");\n\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tdo_presence(basic_state);\n\treturn true;\n}\n\nvoid t_userintf::do_presence(t_presence_state::t_basic_state basic_state)\n{\n\tphone->pub_publish_presence(active_user, basic_state);\n}\n\nbool t_userintf::exec_quit(const list<string> command_list) {\n\tdo_quit();\n\treturn true;\n}\n\nvoid t_userintf::do_quit(void) {\n\tend_interface = true;\n\t// Signal the main thread that it should interrupt Readline\n\tif (break_readline_loop_pipe[1] != -1) {\n\t\twrite(break_readline_loop_pipe[1], \"X\", 1);\n\t}\n}\n\nbool t_userintf::exec_help(const list<string> command_list) {\n\tlist<t_command_arg> al;\n\n\tif (!parse_args(command_list, al)) {\n\t\texec_command(\"help help\");\n\t\treturn false;\n\t}\n\n\tif (al.size() > 1) {\n\t\texec_command(\"help help\");\n\t\treturn false;\n\t}\n\t\n\tdo_help(al);\n\treturn true;\n}\n\t\nvoid t_userintf::do_help(const list<t_command_arg> &al) {\n\tif (al.size() == 0) {\n\t\tcout << endl;\n\t\tcout << \"call\t\tCall someone\\n\";\n\t\tcout << \"answer\t\tAnswer an incoming call\\n\";\n\t\tcout << \"answerbye\tAnswer an incoming call or end a call\\n\";\n\t\tcout << \"reject\t\tReject an incoming call\\n\";\n\t\tcout << \"redirect\tRedirect an incoming call\\n\";\n\t\tcout << \"transfer\tTransfer a standing call\\n\";\n\t\tcout << \"bye\t\tEnd a call\\n\";\n\t\tcout << \"hold\t\tPut a call on-hold\\n\";\n\t\tcout << \"retrieve\tRetrieve a held call\\n\";\n\t\tcout << \"conference\tJoin 2 calls in a 3-way conference\\n\";\n\t\tcout << \"mute\t\tMute a line\\n\";\n\t\tcout << \"dtmf\t\tSend DTMF\\n\";\n\t\tcout << \"redial\t\tRepeat last call\\n\";\n\t\tcout << \"register\tRegister your phone at a registrar\\n\";\n\t\tcout << \"deregister\tDe-register your phone at a registrar\\n\";\n\t\tcout << \"fetch_reg\tFetch registrations from registrar\\n\";\n\t\tcout << \"options\\t\\tGet capabilities of another SIP endpoint\\n\";\n\t\tcout << \"line\t\tToggle between phone lines\\n\";\n\t\tcout << \"dnd\t\tDo not disturb\\n\";\n\t\tcout << \"auto_answer\tAuto answer\\n\";\n\t\tcout << \"user\t\tShow users / set active user\\n\";\n#ifdef HAVE_ZRTP\n\t\tcout << \"zrtp\t\tZRTP command for voice encryption\\n\";\n#endif\n\t\tcout << \"message\\t\\tSend an instant message\\n\";\n\t\tcout << \"presence\tPublish your presence state\\n\";\n\t\tcout << \"quit\t\tQuit\\n\";\n\t\tcout << \"help\t\tGet help on a command\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tbool ambiguous;\n\tstring c = complete_command(tolower(al.front().value), ambiguous);\n\n\tif (c == \"call\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tcall [-s subject] [-d display] [-h] dst\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tCall someone.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-s subject\tAdd a subject header to the INVITE\\n\";\n\t\tcout << \"\\t-d display\tAdd display name to To-header\\n\";\n\t\tcout << \"\\t-h\t\tHide your identity\\n\";\n\t\tcout << \"\\tdst\t\tSIP uri of party to invite\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"answer\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tanswer\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tAnswer an incoming call.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\t\n\tif (c == \"answerbye\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tanswerbye\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tWith this command you can answer an incoming call or\\n\";\n\t\tcout << \"\\tend an established call.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"reject\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\treject\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tReject an incoming call. A 486 Busy Here response\\n\";\n\t\tcout << \"\\twill be sent.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"redirect\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tredirect [-s] [-t type] [-a on|off] [dst ... dst]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tRedirect an incoming call. A 302 Moved Temporarily\\n\";\n\t\tcout << \"\\tresponse will be sent.\\n\";\n\t\tcout << \"\\tYou can redirect the current incoming call by specifying\\n\";\n\t\tcout << \"\\tone or more destinations without any other arguments.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-s\t\tShow which redirections are active.\\n\";\n\t\tcout << \"\\t-t type\\t\tType for permanent redirection of calls.\\n\";\n\t\tcout << \"\\t\t\tValues: always, busy, noanswer.\\n\";\n\t\tcout << \"\\t-a on|off\tEnable/disable permanent redirection.\\n\";\n\t\tcout << \"\\t\t\tThe default action is 'on'.\\n\";\n\t\tcout << \"\\t\t\tYou can disable all redirections with the\\n\";\n\t\tcout << \"\\t\t\t'off' action and no type.\\n\";\n\t\tcout << \"\\tdst\t\tSIP uri where the call should be redirected.\\n\";\n\t\tcout << \"\\t\t\tYou can specify up to 5 destinations.\\n\";\n\t\tcout << \"\\t\t\tThe destinations will be tried in sequence.\\n\";\n\t\tcout << \"Examples:\\n\";\n\t\tcout << \"\\tRedirect current incoming call to michel@twinklephone.com\\n\";\n\t\tcout << \"\\tredirect michel@twinklephone.com\\n\";\n\t\tcout << endl;\n\t\tcout << \"\\tRedirect busy calls permanently to michel@twinklephone.com\\n\";\n\t\tcout << \"\\tredirect -t busy michel@twinklephone.com\\n\";\n\t\tcout << endl;\n\t\tcout << \"\\tDisable redirection of busy calls.\\n\";\n\t\tcout << \"\\tredirect -t busy -a off\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"transfer\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\ttransfer [-c] [-l] [dst]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tTransfer a standing call to another destination.\\n\";\n\t\tcout << \"\\tFor a transfer with consultation, first use the -c flag with a\\n\";\n\t\tcout << \"\\tdestination. This sets up the consultation call. When the\\n\";\n\t\tcout << \"\\tconsulted party agrees, give the command with the -c flag once\\n\";\n\t\tcout << \"\\tmore, but now without a destination. This transfers the call.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-c\tConsult destination before transferring call.\\n\";\n\t\tcout << \"\\t-l\tTransfer call to party on other line.\\n\";\n\t\tcout << \"\\tdst\tSIP uri of transfer destination\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"bye\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tbye\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tEnd a call.\\n\";\n\t\tcout << \"\\tFor a stable call a BYE will be sent.\\n\";\n\t\tcout << \"\\tIf the invited party did not yet sent a final answer,\\n\";\n\t\tcout << \"\\tthen a CANCEL will be sent.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"hold\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\thold [-t]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tPut the current call on the active line on-hold.\\n\";\n\t\tcout << \"\\tIf the -t flag is passed and the call is currently held,\\n\";\n\t\tcout << \"\\tit will be retrieved instead.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-t\t\tToggle the on-hold status of a call.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"retrieve\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tretrieve\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tRetrieve a held call on the active line.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"conference\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tconference\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tJoin 2 calls in a 3-way conference. Before you give this\\n\";\n\t\tcout << \"\\tcommand you must have a call on each line.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"mute\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tmute [-s] [-a on|off]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tMute/unmute the active line.\\n\";\n\t\tcout << \"\\tYou can hear the other side of the line, but they cannot\\n\";\n\t\tcout << \"\\thear you.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-s\t\tShow if line is muted.\\n\";\n\t\tcout << \"\\t-a on|off\tMute/unmute.\\n\";\n\t\tcout << \"Notes:\\n\";\n\t\tcout << \"\\tWithout any arguments you can toggle the status.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"dtmf\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tdtmf digits\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tSend the digits as out-of-band DTMF telephone events \";\n\t\tcout << \"(RFC 2833).\\n\";\n\t\tcout << \"\\tThis command can only be given when a call is \";\n\t\tcout << \"established.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-r\tRaw mode: do not convert letters to digits.\\n\";\n\t\tcout << \"\\tdigits\t0-9 | A-D | * | #\\n\";\n\t\tcout << \"Example:\\n\";\n\t\tcout << \"\\tdtmf 1234#\\n\";\n\t\tcout << \"\\tdmtf movies\\n\";\n\t\tcout << \"Notes:\\n\";\n\t\tcout << \"\\tThe overdecadic digits A-D can only be sent in raw mode.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\t\n\tif (c == \"redial\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tredial\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tRepeat last call.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"register\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tregister\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tRegister your phone at a registrar.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-a\tRegister all enabled user profiles.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"deregister\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tderegister [-a]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tDe-register your phone at a registrar.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-a\tDe-register all enabled user profiles.\\n\";\n\t\tcout << \"\\t-d\tDe-register all devices.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"fetch_reg\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tfetch_reg\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tFetch current registrations from registrar.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"options\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\toptions [dst]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tGet capabilities of another SIP endpoint.\\n\";\n\t\tcout << \"\\tIf no destination is passed as an argument, then\\n\";\n\t\tcout << \"\\tthe capabilities of the far-end in the current call\\n\";\n\t\tcout << \"\\ton the active line are requested.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\tdst\t\tSIP uri of end-point\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"line\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tline [-o] [lineno]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tIf no argument is passed then the current active \";\n\t\tcout << \"line is shown.\\n\";\n\t\tcout << \"\\tIf the -o flag is passed, switch to the other (inactive) line.\\n\";\n\t\tcout << \"\\tOtherwise switch to another line. If the current active\\n\";\n\t\tcout << \"\\thas a call, then this call will be put on-hold.\\n\";\n\t\tcout << \"\\tIf the new active line has a held call, then this call\\n\";\n\t\tcout << \"\\twill be retrieved.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-o\t\tSwitch to the current inactive line.\\n\";\n\t\tcout << \"\\tlineno\t\tSwitch to another line (values = \";\n\t\tcout << \"1,2)\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"dnd\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tdnd [-s] [-a on|off]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tEnable/disable the do not disturb service.\\n\";\n\t\tcout << \"\\tIf dnd is enabled then a 480 Temporarily Unavailable \";\n\t\tcout << \"response is given\\n\";\n\t\tcout << \"\\ton incoming calls.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-s\t\tShow if dnd is active.\\n\";\n\t\tcout << \"\\t-a on|off\tEnable/disable dnd.\\n\";\n\t\tcout << \"Notes:\\n\";\n\t\tcout << \"\\tWithout any arguments you can toggle the status.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\t\n\tif (c == \"auto_answer\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tauto_answer [-s] [-a on|off]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tEnable/disable the auto answer service.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-s\t\tShow if auto answer is active.\\n\";\n\t\tcout << \"\\t-a on|off\tEnable/disable auto answer.\\n\";\n\t\tcout << \"Notes:\\n\";\n\t\tcout << \"\\tWithout any arguments you can toggle the status.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\t\n\tif (c == \"user\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tuser [profile name]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tMake a user profile the active profile.\\n\";\n\t\tcout << \"\\tCommands like 'invite' are executed for the active profile.\\n\";\n\t\tcout << \"\\tWithout an argument this command lists all users. The active\\n\";\n\t\tcout << \"\\tuser will be marked with '*'.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\tprofile name\tThe user profile to activate.\\n\";\n\t\tcout << endl;\n\t\t\n\t\treturn;\n\t}\n\t\n#ifdef HAVE_ZRTP\n\tif (c == \"zrtp\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tzrtp <zrtp-command>\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tExecute a ZRTP command.\\n\";\n\t\tcout << \"ZRTP commands:\\n\";\n\t\tcout << \"\\tencrypt      Start ZRTP negotiation for encryption.\\n\";\n\t\tcout << \"\\tgo-clear     Send ZRTP go-clear request.\\n\";\n\t\tcout << \"\\tconfirm-sas  Confirm the SAS value.\\n\";\n\t\tcout << \"\\treset-sas    Reset SAS confirmation.\\n\";\n\t\tcout << endl;\n\t\t\n\t\treturn;\n\t}\n#endif\n\t\n\tif (c == \"message\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tmessage [-s subject] [-f file name] [-d display] dst [text]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tSend an instant message.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-s subject\tSubject of the message.\\n\";\n\t\tcout << \"\\t-f file name\tFile name of the file to send.\\n\";\n\t\tcout << \"\\t-d display\tAdd display name to To-header\\n\";\n\t\tcout << \"\\tdst\t\tSIP uri of party to message\\n\";\n\t\tcout << \"\\ttext\t\tMessage text to send. Surround with double quotes\\n\";\n\t\tcout << \"\\t\\t\\twhen your text contains whitespace.\\n\";\n\t\tcout << \"\\t\\t\\tWhen you send a file, then the text is ignored.\\n\";\n\t\tcout << endl;\n\t\t\n\t\treturn;\n\t}\n\t\n\tif (c == \"presence\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tpresence -b [online|offline]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tPublish your presence state to a presence agent\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\t-b\t\tA basic presence state: online or offline\\n\";\n\t\tcout << endl;\n\t\t\n\t\treturn;\n\t}\n\n\tif (c == \"quit\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\tquit\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tQuit.\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tif (c == \"help\") {\n\t\tcout << endl;\n\t\tcout << \"Usage:\\n\";\n\t\tcout << \"\\thelp [command]\\n\";\n\t\tcout << \"Description:\\n\";\n\t\tcout << \"\\tShow help on a command.\\n\";\n\t\tcout << \"Arguments:\\n\";\n\t\tcout << \"\\tcommand\t\tCommand you want help with\\n\";\n\t\tcout << endl;\n\n\t\treturn;\n\t}\n\n\tcout << endl;\n\tcout << \"\\nUnknown command\\n\\n\";\n\tcout << endl;\n}\n\n\n/////////////////////////////\n// Public\n/////////////////////////////\n\nt_userintf::t_userintf(t_phone *_phone) {\n\tphone = _phone;\n\tend_interface = false;\n\ttone_gen = NULL;\n\tactive_user = NULL;\n\tuse_stdout = true;\n\tthrottle_dtmf_not_supported = false;\n\tthr_process_events = NULL;\n\n\tall_commands.push_back(\"invite\");\n\tall_commands.push_back(\"call\");\n\tall_commands.push_back(\"answer\");\n\tall_commands.push_back(\"answerbye\");\n\tall_commands.push_back(\"reject\");\n\tall_commands.push_back(\"redirect\");\n\tall_commands.push_back(\"bye\");\n\tall_commands.push_back(\"hold\");\n\tall_commands.push_back(\"retrieve\");\n\tall_commands.push_back(\"refer\");\n\tall_commands.push_back(\"transfer\");\n\tall_commands.push_back(\"conference\");\n\tall_commands.push_back(\"mute\");\n\tall_commands.push_back(\"dtmf\");\n\tall_commands.push_back(\"redial\");\n\tall_commands.push_back(\"register\");\n\tall_commands.push_back(\"deregister\");\n\tall_commands.push_back(\"fetch_reg\");\n\tall_commands.push_back(\"options\");\n\tall_commands.push_back(\"line\");\n\tall_commands.push_back(\"dnd\");\n\tall_commands.push_back(\"auto_answer\");\n\tall_commands.push_back(\"user\");\n#ifdef HAVE_ZRTP\n\tall_commands.push_back(\"zrtp\");\n#endif\n\tall_commands.push_back(\"message\");\n\tall_commands.push_back(\"presence\");\n\tall_commands.push_back(\"quit\");\n\tall_commands.push_back(\"exit\");\n\tall_commands.push_back(\"q\");\n\tall_commands.push_back(\"x\");\n\tall_commands.push_back(\"help\");\n\tall_commands.push_back(\"h\");\n\tall_commands.push_back(\"?\");\n}\n\nt_userintf::~t_userintf() {\n\tif (tone_gen) {\n\t\tMEMMAN_DELETE(tone_gen);\n\t\tdelete tone_gen;\n\t}\n\t\n\tif (thr_process_events) {\n\t\tevq_ui_events.push_quit();\n\t\tthr_process_events->join();\n\t\tlog_file->write_report(\"thr_process_events stopped.\", \n\t\t\t\"t_userintf::~t_userintf\", LOG_NORMAL, LOG_DEBUG);\n\t\tMEMMAN_DELETE(thr_process_events);\n\t\tdelete thr_process_events;\n\t}\n}\n\nstring t_userintf::complete_command(const string &c, bool &ambiguous) {\n\tambiguous = false;\n\tstring full_command;\n\n\tfor (list<string>::const_iterator i = all_commands.begin();\n\t     i != all_commands.end(); i++)\n\t{\n\n\t\t// If there is an exact match, then this is the command.\n\t\t// This allows a one command to be a prefix of another command.\n\t\tif (c == *i) {\n\t\t\tambiguous = false;\n\t\t\treturn c;\n\t\t}\n\n\t\tif (c.size() < i->size() && c == i->substr(0, c.size())) {\n\t\t\tif (full_command != \"\") {\n\t\t\t\tambiguous = true;\n\t\t\t\t// Do not return here, as there might still be\n\t\t\t\t// an exact match.\n\t\t\t}\n\n\t\t\tfull_command = *i;\n\t\t}\n\t}\n\n\tif (ambiguous) return \"\";\n\treturn full_command;\n}\n\nbool t_userintf::exec_command(const string &command_line, bool immediate) {\n\tvector<string> v = split_ws(command_line, true);\n\tif (v.size() == 0) return false;\n\n\tbool ambiguous;\n\tstring command = complete_command(tolower(v[0]), ambiguous);\n\n\tif (ambiguous) {\n\t\tif (use_stdout) {\n\t\t\tcout << endl;\n\t\t\tcout << \"Ambiguous command\\n\";\n\t\t\tcout << endl;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\t\n\tlist<string> l(v.begin(), v.end());\n\n\tif (command == \"invite\") return exec_invite(l, immediate);\n\tif (command == \"call\") return exec_invite(l, immediate);\n\tif (command == \"answer\") return exec_answer(l);\n\tif (command == \"answerbye\") return exec_answerbye(l);\n\tif (command == \"reject\") return exec_reject(l);\n\tif (command == \"redirect\") return exec_redirect(l, immediate);\n\tif (command == \"bye\") return exec_bye(l);\n\tif (command == \"hold\") return exec_hold(l);\n\tif (command == \"retrieve\") return exec_retrieve(l);\n\tif (command == \"refer\") return exec_refer(l, immediate);\n\tif (command == \"transfer\") return exec_refer(l, immediate);\n\tif (command == \"conference\") return exec_conference(l);\n\tif (command == \"mute\") return exec_mute(l);\n\tif (command == \"dtmf\") return exec_dtmf(l);\n\tif (command == \"redial\") return exec_redial(l);\n\tif (command == \"register\") return exec_register(l);\n\tif (command == \"deregister\") return exec_deregister(l);\n\tif (command == \"fetch_reg\") return exec_fetch_registrations(l);\n\tif (command == \"options\") return exec_options(l, immediate);\n\tif (command == \"line\") return exec_line(l);\n\tif (command == \"dnd\") return exec_dnd(l);\n\tif (command == \"auto_answer\") return exec_auto_answer(l);\n\tif (command == \"user\") return exec_user(l);\n#ifdef HAVE_ZRTP\n\tif (command == \"zrtp\") return exec_zrtp(l);\n#endif\n\tif (command == \"message\") return exec_message(l);\n\tif (command == \"presence\") return exec_presence(l);\n\tif (command == \"quit\") return exec_quit(l);\n\tif (command == \"exit\") return exec_quit(l);\n\tif (command == \"x\") return exec_quit(l);\n\tif (command == \"q\") return exec_quit(l);\n\tif (command == \"help\") return exec_help(l);\n\tif (command == \"h\") return exec_help(l);\n\tif (command == \"?\") return exec_help(l);\n\n\tif (use_stdout) {\n\t\tcout << endl;\n\t\tcout << \"Unknown command\\n\";\n\t\tcout << endl;\n\t}\n\t\n\treturn false;\n}\n\nstring t_userintf::format_sip_address(t_user *user_config, const string &display,\n\t                              const t_url &uri) const\n{\n\tstring s;\n\t\n\tif (uri.encode() == ANONYMOUS_URI) {\n\t\treturn TRANSLATE(\"Anonymous\");\n\t}\n\n\ts = display;\n\tif (display != \"\") s += \" <\";\n\t\n\tstring number;\n\tif (uri.get_scheme() == \"tel\") {\n\t\tnumber = uri.get_host();\n\t} else {\n\t\tnumber = uri.get_user();\n\t}\n\n\tif (user_config->get_display_useronly_phone() &&\n\t    uri.is_phone(user_config->get_numerical_user_is_phone(),\n\t    \t\t\tuser_config->get_special_phone_symbols()))\n\t{\n\t\t// Display telephone number only\n\t\ts += user_config->convert_number(number);\n\t} else {\n\t\t// Display full URI\n\t\t// Convert the username according to the number conversion\n\t\t// rules.\n\t\tt_url u(uri);\n\t\tstring username = user_config->convert_number(number);\n\t\tif (username != number) {\n\t\t\tif (uri.get_scheme() == \"tel\") {\n\t\t\t\tu.set_host(username);\n\t\t\t} else {\n\t\t\t\tu.set_user(username);\n\t\t\t}\n\t\t}\n\t\ts += u.encode_no_params_hdrs(false);\n\t}\n\n\tif (display != \"\") s += \">\";\n\n\treturn s;\n}\n\nlist<string> t_userintf::format_warnings(const t_hdr_warning &hdr_warning) const {\n\tstring s;\n\tlist<string> l;\n\n\tfor (list<t_warning>::const_iterator i = hdr_warning.warnings.begin();\n\t     i != hdr_warning.warnings.end(); i++)\n\t{\n\t\ts = TRANSLATE(\"Warning:\");\n\t\ts += \" \";\n\t\ts += int2str(i->code);\n\t\ts += ' ';\n\t\ts += i->text;\n\t\ts += \" (\";\n\t\ts += i->host;\n\t\tif (i->port > 0) s += int2str(i->port, \":%d\");\n\t\ts += ')';\n\t\tl.push_back(s);\n\t}\n\n\treturn l;\n}\n\nstring t_userintf::format_codec(t_audio_codec codec) const {\n\tswitch (codec) {\n\tcase CODEC_NULL: \treturn \"null\";\n\tcase CODEC_UNSUPPORTED:\treturn \"???\";\n\tcase CODEC_G711_ALAW:\treturn \"g711a\";\n\tcase CODEC_G711_ULAW:\treturn \"g711u\";\n\tcase CODEC_GSM:\t\treturn \"gsm\";\n\tcase CODEC_SPEEX_NB:\treturn \"spx-nb\";\n\tcase CODEC_SPEEX_WB:\treturn \"spx-wb\";\n\tcase CODEC_SPEEX_UWB:\treturn \"spx-uwb\";\n\tcase CODEC_ILBC:\treturn \"ilbc\";\n\tcase CODEC_G722:\treturn \"g722\";\n\tcase CODEC_G726_16:\treturn \"g726-16\";\n\tcase CODEC_G726_24:\treturn \"g726-24\";\n\tcase CODEC_G726_32:\treturn \"g726-32\";\n\tcase CODEC_G726_40:\treturn \"g726-40\";\n\tcase CODEC_G729A: return \"g729a\";\n\tdefault:\t\treturn \"???\";\n\t}\n}\n\nvoid t_userintf::run(void) {\n\t// Start asynchronous event processor\n\tthr_process_events = new t_thread(process_events_main, NULL);\n\tMEMMAN_NEW(thr_process_events);\n\t\n\tlist<t_user *> user_list = phone->ref_users();\n\tactive_user = user_list.front();\n\n\tcout << PRODUCT_NAME << \" \" << PRODUCT_VERSION << \", \" << PRODUCT_DATE;\n\tcout << endl;\n\tcout << \"Copyright (C) 2005-2015  \" << PRODUCT_AUTHOR << endl;\n\tcout << endl;\n\t\n\tcout << \"Users:\";\n\texec_command(\"user\");\n\t\n\tcout << \"Local IP:       \" << user_host << endl;\n\tcout << endl;\n\t\n\trestore_state();\n\n\t// Initialize phone functions\n\tphone->init();\n\n\t// Set up the self-pipe used to interrupt Readline\n\tif (pipe(break_readline_loop_pipe) == 0) {\n\t\t// Mark both file descriptors as close-on-exec for good measure\n\t\tfor (int i = 0; i < 2; i++) {\n\t\t\tint flags = fcntl(break_readline_loop_pipe[i], F_GETFD);\n\t\t\tif (flags != -1) {\n\t\t\t\tflags |= FD_CLOEXEC;\n\t\t\t\tfcntl(break_readline_loop_pipe[i], F_SETFD, flags);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Not fatal -- we just won't be able to interrupt Readline\n\t\tstring msg(\"pipe() failed: \");\n\t\tmsg += get_error_str(errno);\n\t\tui->cb_show_msg(msg, MSG_WARNING);\n\n\t\t// Mark both file descriptors as invalid\n\t\tbreak_readline_loop_pipe[0] = -1;\n\t\tbreak_readline_loop_pipe[1] = -1;\n\t}\n\n\t//Initialize GNU readline functions\n\trl_attempted_completion_function = tw_completion;\n\tusing_history();\n\tread_history(sys_config->get_history_file().c_str());\n\tstifle_history(CLI_MAX_HISTORY_LENGTH);\n\n\t// Additional stuff for using the Readline callback interface\n\tcb_user_intf = this;\n\tsignal(SIGWINCH, sigwinch_handler);\n\trl_callback_handler_install(CLI_PROMPT, tw_readline_cb);\n\n\twhile (!end_interface) {\n\t\t// File descriptors we are watching (stdin + self-pipe)\n\t\tfd_set fds;\n\t\tFD_ZERO(&fds);\n\t\tFD_SET(fileno(rl_instream), &fds);\n\t\tif (break_readline_loop_pipe[0] != -1) {\n\t\t\tFD_SET(break_readline_loop_pipe[0], &fds);\n\t\t}\n\n\t\tint ret = select(FD_SETSIZE, &fds, NULL, NULL, NULL);\n\t\tif ((ret == -1) && (errno != EINTR)) {\n\t\t\tstring msg(\"select() failed: \");\n\t\t\tmsg += get_error_str(errno);\n\t\t\tui->cb_show_msg(msg, MSG_CRITICAL);\n\t\t\tbreak;\n\t\t}\n\t\t// Relay any SIGWINCH to Readline\n\t\tif (sigwinch_received) {\n\t\t\trl_resize_terminal();\n\t\t\tsigwinch_received = 0;\n\t\t}\n\t\tif (ret == -1) {\n\t\t\t// errno == EINTR\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (FD_ISSET(fileno(rl_instream), &fds)) {\n\t\t\trl_callback_read_char();\n\t\t}\n\t}\n\n\trl_callback_handler_remove();\n\tsignal(SIGWINCH, SIG_DFL);\n\t\n\t// Terminate phone functions\n\twrite_history(sys_config->get_history_file().c_str());\n\tphone->terminate();\n\t\n\tsave_state();\n\tcout << endl;\n}\n\nvoid t_userintf::run_on_event_queue(std::function<void()> fn) {\n\tevq_ui_events.push_fncall(fn);\n}\n\nvoid t_userintf::process_events(void) {\n\tt_event\t\t*event;\n\tt_event_ui\t*ui_event;\n\t\n\tbool quit = false;\n\twhile (!quit) {\n\t\tevent = evq_ui_events.pop();\n\t\tswitch (event->get_type()) {\n\t\tcase EV_UI:\n\t\t\tui_event = dynamic_cast<t_event_ui *>(event);\n\t\t\tassert(ui_event);\n\t\t\tui_event->exec(this);\n\t\t\tbreak;\n\t\tcase EV_QUIT:\n\t\t\tquit = true;\n\t\t\tbreak;\n\t\tcase EV_FN_CALL:\n\t\t\tstatic_cast<t_event_fncall*>(event)->invoke();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\tbreak;\n\t\t}\n\t\t\t\n\t\tMEMMAN_DELETE(event);\n\t\tdelete event;\n\t}\n}\n\nvoid t_userintf::save_state(void) {\n\tstring err_msg;\n\t\n\tsys_config->set_redial_url(last_called_url);\n\tsys_config->set_redial_display(last_called_display);\n\tsys_config->set_redial_subject(last_called_subject);\n\tsys_config->set_redial_profile(last_called_profile);\n\tsys_config->set_redial_hide_user(last_called_hide_user);\n\t\n\tsys_config->write_config(err_msg);\n}\n\nvoid t_userintf::restore_state(void) {\n\tlast_called_url = sys_config->get_redial_url();\n\tlast_called_display = sys_config->get_redial_display();\n\tlast_called_subject = sys_config->get_redial_subject();\n\tlast_called_profile = sys_config->get_redial_profile();\n\tlast_called_hide_user = sys_config->get_redial_hide_user();\n}\n\nvoid t_userintf::lock(void) {\n\tassert(!is_prohibited_thread());\n\t// TODO: lock for CLI\n}\n\nvoid t_userintf::unlock(void) {\n\t// TODO: lock for CLI\n}\n\nstring t_userintf::select_network_intf(void) {\n\tstring ip;\n\tlist<t_interface> *l = get_interfaces();\n\t// As memman has no hooks in the socket routines, report it here.\n\tMEMMAN_NEW(l);\n\tif (l->size() == 0) {\n\t\t// cout << \"Cannot find a network interface\\n\";\n\t\tcout << \"Cannot find a network interface. Twinkle will use\\n\"\n\t\t\t\"127.0.0.1 as the local IP address. When you connect to\\n\"\n\t\t\t\"the network you have to restart Twinkle to use the correct\\n\"\n\t\t\t\"IP address.\\n\";\n\t\tMEMMAN_DELETE(l);\n\t\tdelete l;\n\t\treturn \"127.0.0.1\";\n\t}\n\n\tif (l->size() == 1) {\n\t\tip = l->front().get_ip_addr();\n\t} else {\n\t\tsize_t num = 1;\n\t\tcout << \"Multiple network interfaces found.\\n\";\n\t\tfor (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {\n\t\t\tcout << num << \") \" << i->name << \": \";\n\t\t\tcout << i->get_ip_addr() << endl;\n\t\t\tnum++;\n\t\t}\n\n\t\tcout << endl;\n\n\t\tsize_t selection = 0;\n\t\twhile (selection < 1 || selection > l->size()) {\n\t\t\tcout << \"Which interface do you want to use (enter number): \";\n\t\t\tstring choice;\n\t\t\tgetline(cin, choice);\n\t\t\tselection = atoi(choice.c_str());\n\t\t}\n\n\t\tnum = 1;\n\t\tfor (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {\n\t\t\tif (num == selection) {\n\t\t\t\tip = i->get_ip_addr();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnum++;\n\t\t}\n\n\t}\n\n\tMEMMAN_DELETE(l);\n\tdelete l;\n\treturn ip;\n}\n\nbool t_userintf::select_user_config(list<string> &config_files) {\n\t// In CLI mode, simply select the default config file\n\tconfig_files.clear();\n\tconfig_files.push_back(USER_CONFIG_FILE);\n\treturn true;\n}\n\nvoid t_userintf::cb_incoming_call(t_user *user_config, int line, const t_request *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"incoming call\\n\";\n\tcout << \"From:\\t\\t\";\n\t\n\tstring from_party = format_sip_address(user_config, \n\t\tr->hdr_from.get_display_presentation(), r->hdr_from.uri);\n\tcout << from_party << endl;\n\n\tif (r->hdr_organization.is_populated()) {\n\t\tcout << \"Organization:\\t\" << r->hdr_organization.name << endl;\n\t}\n\n\tcout << \"To:\\t\\t\";\n\tcout << format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri) << endl;\n\n\tif (r->hdr_referred_by.is_populated()) {\n\t\tcout << \"Referred-by:\\t\";\n\t\tcout << format_sip_address(user_config, r->hdr_referred_by.display,\n\t\t\tr->hdr_referred_by.uri);\n\t\tcout << endl;\n\t}\n\n\tif (r->hdr_subject.is_populated()) {\n\t\tcout << \"Subject:\\t\" << r->hdr_subject.subject << endl;\n\t}\n\n\tcout << endl;\n\tcout.flush();\n\n\tcb_notify_call(line, from_party);\n}\n\nvoid t_userintf::cb_call_cancelled(int line, const std::string &reason) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"far end cancelled call.\\n\";\n\tif (!reason.empty()) {\n\t\tcout << reason << endl;\n\t}\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_far_end_hung_up(int line, const std::string &reason) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"far end ended call.\\n\";\n\tif (!reason.empty()) {\n\t\tcout << reason << endl;\n\t}\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_answer_timeout(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"answer timeout.\\n\";\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_sdp_answer_not_supported(int line, const string &reason) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"SDP answer from far end not supported.\\n\";\n\tcout << reason << endl;\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_sdp_answer_missing(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"SDP answer from far end missing.\\n\";\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_unsupported_content_type(int line, const t_sip_message *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"Unsupported content type in answer from far end.\\n\";\n\tcout << r->hdr_content_type.media.type << \"/\";\n\tcout << r->hdr_content_type.media.subtype << endl;\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_ack_timeout(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"no ACK received, call will be terminated.\\n\";\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_100rel_timeout(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"no PRACK received, call will be terminated.\\n\";\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_session_expired(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": \";\n\tcout << \"session has expired, call will be terminated.\\n\";\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_prack_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": PRACK failed.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n\n\tcb_stop_call_notification(line);\n}\n\nvoid t_userintf::cb_provisional_resp_invite(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": received \";\n\tcout << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_cancel_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": cancel failed.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_call_answered(t_user *user_config, int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": far end answered call.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\n\tcout << \"To: \";\n\tcout << format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri) << endl;\n\n\tif (r->hdr_organization.is_populated()) {\n\t\tcout << \"Organization: \" << r->hdr_organization.name << endl;\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_call_failed(t_user *user_config, int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call failed.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\n\t// Warnings\n\tif (r->hdr_warning.is_populated()) {\n\t\tlist<string> l = format_warnings(r->hdr_warning);\n\t\tfor (list<string>::iterator i = l.begin(); i != l.end(); i++) {\n\t\t\tcout << *i << endl;\n\t\t}\n\t}\n\n\t// Redirection response\n\tif (r->get_class() == R_3XX && r->hdr_contact.is_populated()) {\n\t\tlist<t_contact_param> l = r->hdr_contact.contact_list;\n\t\tl.sort();\n\t\tcout << \"You can try the following contacts:\\n\";\n\t\tfor (list<t_contact_param>::iterator i = l.begin();\n\t\t     i != l.end(); i++)\n\t\t{\n\t\t\tcout << format_sip_address(user_config, i->display, i->uri) << endl;\n\t\t}\n\t}\n\n\t// Unsupported extensions\n\tif (r->code == R_420_BAD_EXTENSION) {\n\t\tcout << r->hdr_unsupported.encode();\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_stun_failed_call_ended(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call failed.\\n\";\n\tcout << endl;\n\tcout.flush();\t\t\n}\n\nvoid t_userintf::cb_call_ended(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call ended.\\n\";\n\tcout.flush();\n}\n\nvoid t_userintf::cb_call_established(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call established.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_options_response(const t_response *r) {\n\tcout << endl;\n\tcout << \"OPTIONS response received: \";\n\tcout << r->code << ' ' << r->reason << endl;\n\n\tcout << \"Capabilities of \" << r->hdr_to.uri.encode() << endl;\n\n\tcout << \"Accepted body types\\n\";\n\tif (r->hdr_accept.is_populated()) {\n\t\tcout << \"\\t\" << r->hdr_accept.encode();\n\t} else {\n\t\tcout << \"\\tUnknown\\n\";\n\t}\n\n\tcout << \"Accepted encodings\\n\";\n\tif (r->hdr_accept_encoding.is_populated()) {\n\t\tcout << \"\\t\" << r->hdr_accept_encoding.encode();\n\t} else {\n\t\tcout << \"\\tUnknown\\n\";\n\t}\n\n\tcout << \"Accepted languages\\n\";\n\tif (r->hdr_accept_language.is_populated()) {\n\t\tcout << \"\\t\" << r->hdr_accept_language.encode();\n\t} else {\n\t\tcout << \"\\tUnknown\\n\";\n\t}\n\n\tcout << \"Allowed requests\\n\";\n\tif (r->hdr_allow.is_populated()) {\n\t\tcout << \"\\t\" << r->hdr_allow.encode();\n\t} else {\n\t\tcout << \"\\tUnknown\\n\";\n\t}\n\n\tcout << \"Supported extensions\\n\";\n\tif (r->hdr_supported.is_populated()) {\n\t\tif (r->hdr_supported.features.empty()) {\n\t\t\tcout << \"\\tNone\\n\";\n\t\t} else {\n\t\t\tcout << \"\\t\" << r->hdr_supported.encode();\n\t\t}\n\t} else {\n\t\tcout << \"\\tUnknown\\n\";\n\t}\n\n\tcout << \"End point type\\n\";\n\tbool endpoint_known = false;\n\tif (r->hdr_server.is_populated()) {\n\t\tcout << \"\\t\" << r->hdr_server.encode();\n\t\tendpoint_known = true;\n\t}\n\n\tif (r->hdr_user_agent.is_populated()) {\n\t\t// Some end-point put a User-Agent header in the response\n\t\t// instead of a Server header.\n\t\tcout << \"\\t\" << r->hdr_user_agent.encode();\n\t\tendpoint_known = true;\n\t}\n\n\tif (!endpoint_known) {\n\t\tcout << \"\\tUnknown\\n\";\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_reinvite_success(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": re-INVITE successful.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_reinvite_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": re-INVITE failed.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_retrieve_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\t// The status code from the response has already been reported\n\t// by cb_reinvite_failed.\n\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": retrieve failed.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_invalid_reg_resp(t_user *user_config, \n\t\tconst t_response *r, const string &reason) \n{\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", registration failed: \" << r->code << ' ' << r->reason << endl;\n\tcout << reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_register_success(t_user *user_config, \n\tconst t_response *r, unsigned long expires, bool first_success)\n{\n\t// Only report success if this is the first success in a sequence\n\tif (!first_success) return;\n\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \": registration succeeded (expires = \" << expires << \" seconds)\\n\";\n\n\t// Date at registrar\n\tif (r->hdr_date.is_populated()) {\n\t\tcout << \"Registrar \";\n\t\tcout << r->hdr_date.encode() << endl;\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_register_failed(t_user *user_config, \n\t\tconst t_response *r, bool first_failure) \n{\n\t// Only report the first failure in a sequence of failures\n\tif (!first_failure) return;\n\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", registration failed: \" << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_register_stun_failed(t_user *user_config, bool first_failure) {\n\t// Only report the first failure in a sequence of failures\n\tif (!first_failure) return;\n\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", registration failed: STUN failure\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_deregister_success(t_user *user_config, const t_response *r) {\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", de-registration succeeded: \" << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_deregister_failed(t_user *user_config,  const t_response *r) {\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", de-registration failed: \" << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\nvoid t_userintf::cb_fetch_reg_failed(t_user *user_config, const t_response *r) {\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", fetch registrations failed: \" << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_fetch_reg_result(t_user *user_config, const t_response *r) {\n\tcout << endl;\n\n\tcout << user_config->get_profile_name();\n\tconst list<t_contact_param> &l = r->hdr_contact.contact_list;\n\tif (l.size() == 0) {\n\t\tcout << \": you are not registered\\n\";\n\t} else {\n\t\tcout << \": you have the following registrations\\n\";\n\t\tfor (list<t_contact_param>::const_iterator i = l.begin();\n\t\t     i != l.end(); i++)\n\t\t{\n\t\t\tcout << i->encode() << endl;\n\t\t}\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_register_inprog(t_user *user_config, t_register_type register_type) {\n\tswitch (register_type) {\n\tcase REG_REGISTER:\n\t\t// Do not report a register refreshment\n\t\tif (phone->get_is_registered(user_config)) return;\n\n\t\t// Do not report an automatic register re-attempt\n\t\tif (phone->get_last_reg_failed(user_config)) return;\n\n\t\tcout << endl;\n\t\tcout << user_config->get_profile_name();\n\t\tcout << \": registering phone...\\n\";\n\t\tbreak;\n\tcase REG_DEREGISTER:\n\t\tcout << endl;\n\t\tcout << user_config->get_profile_name();\n\t\tcout << \": deregistering phone...\\n\";\n\t\tbreak;\n\tcase REG_DEREGISTER_ALL:\n\t\tcout << endl;\n\t\tcout << user_config->get_profile_name();\n\t\tcout << \": deregistering all phones...\";\n\t\tbreak;\n\tcase REG_QUERY:\n\t\tcout << endl;\n\t\tcout << user_config->get_profile_name();\n\t\tcout << \": fetching registrations...\";\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_redirecting_request(t_user *user_config, \n\t\tint line, const t_contact_param &contact) \n{\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": redirecting request to:\\n\";\n\n\tcout << format_sip_address(user_config, contact.display, contact.uri) << endl;\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_redirecting_request(t_user *user_config, const t_contact_param &contact) {\n\tcout << endl;\n\tcout << \"Redirecting request to: \";\n\n\tcout << format_sip_address(user_config, contact.display, contact.uri) << endl;\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_play_ringtone(int line) {\n\tif (!sys_config->get_play_ringtone()) return;\n\n\tif (tone_gen) {\n\t\ttone_gen->stop();\n\t\tMEMMAN_DELETE(tone_gen);\n\t\tdelete tone_gen;\n\t}\n\t\n\t// Determine ring tone\n\tstring ringtone_file = phone->get_ringtone(line);\n\n\ttone_gen = new t_tone_gen(ringtone_file, sys_config->get_dev_ringtone());\n\tMEMMAN_NEW(tone_gen);\n\t\n\t// If ring tone does not exist, then fall back to system default.\n\tif (!tone_gen->is_valid() && ringtone_file != FILE_RINGTONE) {\n\t\tMEMMAN_DELETE(tone_gen);\n\t\tdelete tone_gen;\n\t\ttone_gen = new t_tone_gen(FILE_RINGTONE, sys_config->get_dev_ringtone());\n\t\tMEMMAN_NEW(tone_gen);\n\t}\n\t\n\t// Play ring tone\n\ttone_gen->start_play_thread(true, INTERVAL_RINGTONE);\n}\n\nvoid t_userintf::cb_play_ringback(t_user *user_config) {\n\tif (!sys_config->get_play_ringback()) return;\n\t\n\tif (tone_gen) {\n\t\ttone_gen->stop();\n\t\tMEMMAN_DELETE(tone_gen);\n\t\tdelete tone_gen;\n\t}\n\t\n\t// Determine ring back tone\n\tstring ringback_file;\n\tif (!user_config->get_ringback_file().empty()) {\n\t\tringback_file = user_config->get_ringback_file();\n\t} else if (!sys_config->get_ringback_file().empty()) {\n\t\tringback_file = sys_config->get_ringback_file();\n\t} else {\n\t\t// System default\n\t\tringback_file = FILE_RINGBACK;\n\t}\n\n\ttone_gen = new t_tone_gen(ringback_file, sys_config->get_dev_speaker());\n\tMEMMAN_NEW(tone_gen);\n\t\n\t// If ring back tone does not exist, then fall back to system default.\n\tif (!tone_gen->is_valid() && ringback_file != FILE_RINGBACK) {\n\t\tMEMMAN_DELETE(tone_gen);\n\t\tdelete tone_gen;\n\t\ttone_gen = new t_tone_gen(FILE_RINGBACK, sys_config->get_dev_speaker());\n\t\tMEMMAN_NEW(tone_gen);\n\t}\n\t\n\t// Play ring back tone\n\ttone_gen->start_play_thread(true, INTERVAL_RINGBACK);\n}\n\nvoid t_userintf::cb_stop_tone(int line) {\n\t// Only stop the tone if the current line is the active line\n\tif (line != phone->get_active_line()) return;\n\n\tif (!tone_gen) return;\n\ttone_gen->stop();\n\tMEMMAN_DELETE(tone_gen);\n\tdelete tone_gen;\n\ttone_gen = NULL;\n}\n\nvoid t_userintf::cb_notify_call(int line, string from_party) {\n\t// Play ringtone if the call is received on the active line\n\tif (line == phone->get_active_line() &&\n\t    !phone->is_line_auto_answered(line))\n\t{\n\t\tcb_play_ringtone(line);\n\t}\n}\n\nvoid t_userintf::cb_stop_call_notification(int line) {\n\tcb_stop_tone(line);\n}\n\nvoid t_userintf::cb_dtmf_detected(int line, t_dtmf_ev dtmf_event) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": DTMF detected: \";\n\n\tif (is_valid_dtmf_ev(dtmf_event)) {\n\t\tcout << dtmf_ev2char(dtmf_event) << endl;\n\t} else {\n\t\tcout << \"invalid DTMF telephone event (\" << (int)dtmf_event << endl;\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_async_dtmf_detected(int line, t_dtmf_ev dtmf_event) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_DTMF_DETECTED);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_line(line);\n\tevent->set_dtmf_event(dtmf_event);\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_send_dtmf(int line, t_dtmf_ev dtmf_event) {\n\t// No feed back in CLI\n}\n\nvoid t_userintf::cb_async_send_dtmf(int line, t_dtmf_ev dtmf_event) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_SEND_DTMF);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_line(line);\n\tevent->set_dtmf_event(dtmf_event);\t\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_dtmf_not_supported(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tif (throttle_dtmf_not_supported) return;\n\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": far end does not support DTMF events.\\n\";\n\tcout << endl;\n\tcout.flush();\n\n\t// Throttle subsequent call backs\n\tthrottle_dtmf_not_supported = true;\n}\n\nvoid t_userintf::cb_dtmf_supported(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": far end supports DTMF telephone event.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_line_state_changed(void) {\n\t// Nothing to do for CLI\n}\n\nvoid t_userintf::cb_async_line_state_changed(void) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_LINE_STATE_CHANGED);\n\tMEMMAN_NEW(event);\n\t\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_send_codec_changed(int line, t_audio_codec codec) {\n\t// No feedback in CLI\n}\n\nvoid t_userintf::cb_recv_codec_changed(int line, t_audio_codec codec) {\n\t// No feedback in CLI\n}\n\nvoid t_userintf::cb_async_recv_codec_changed(int line, t_audio_codec codec) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_RECV_CODEC_CHANGED);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_line(line);\n\tevent->set_codec(codec);\t\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_notify_recvd(int line, const t_request *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 <<  \": received notification.\\n\";\n\tcout << \"Event:    \" << r->hdr_event.event_type << endl;\n\tcout << \"State:    \" << r->hdr_subscription_state.substate << endl;\n\n\tif (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {\n\t\tcout << \"Reason:   \" << r->hdr_subscription_state.reason << endl;\n\t}\n\n\tt_response *sipfrag = (t_response *)((t_sip_body_sipfrag *)r->body)->sipfrag;\n\tcout << \"Progress: \" << sipfrag->code << ' ' << sipfrag->reason << endl;\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_refer_failed(int line, const t_response *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": refer request failed.\\n\";\n\tcout << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_refer_result_success(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call successfully referred.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_refer_result_failed(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call refer failed.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_refer_result_inprog(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call refer in progress.\\n\";\n\tcout << \"No further notifications will be received.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_call_referred(t_user *user_config, int line, t_request *r) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": transferring call to \";\n\tcout << format_sip_address(user_config, r->hdr_refer_to.display,\n\t\tr->hdr_refer_to.uri);\n\tcout << endl;\n\n\tif (r->hdr_referred_by.is_populated()) {\n\t\tcout << \"Transfer requested by \";\n\t\tcout << format_sip_address(user_config, r->hdr_referred_by.display,\n\t\t\tr->hdr_referred_by.uri);\n\t\tcout << endl;\n\t}\n\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_retrieve_referrer(t_user *user_config, int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tconst t_call_info call_info = phone->get_call_info(line);\n\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": call transfer failed.\\n\";\n\tcout << \"Retrieving call: \\n\";\n\tcout << \"From:    \";\n\tcout << format_sip_address(user_config, call_info.from_display, call_info.from_uri);\n\tcout << endl;\n\tif (!call_info.from_organization.empty()) {\n\t\tcout << \"         \" << call_info.from_organization;\n\t\tcout << endl;\n\t}\n\tcout << \"To:      \";\n\tcout << format_sip_address(user_config, call_info.to_display, call_info.to_uri);\n\tcout << endl;\n\tif (!call_info.to_organization.empty()) {\n\t\tcout << \"         \" << call_info.to_organization;\n\t\tcout << endl;\n\t}\n\tcout << \"Subject: \";\n\tcout << call_info.subject;\n\tcout << endl << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_consultation_call_setup(t_user *user_config, int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tconst t_call_info call_info = phone->get_call_info(line);\n\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": setup consultation call.\\n\";\n\tcout << \"From:    \";\n\tcout << format_sip_address(user_config, call_info.from_display, call_info.from_uri);\n\tcout << endl;\n\tif (!call_info.from_organization.empty()) {\n\t\tcout << \"         \" << call_info.from_organization;\n\t\tcout << endl;\n\t}\n\tcout << \"To:      \";\n\tcout << format_sip_address(user_config, call_info.to_display, call_info.to_uri);\n\tcout << endl;\n\tif (!call_info.to_organization.empty()) {\n\t\tcout << \"         \" << call_info.to_organization;\n\t\tcout << endl;\n\t}\n\tcout << \"Subject: \";\n\tcout << call_info.subject;\n\tcout << endl << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_stun_failed(t_user *user_config, int err_code, const string &err_reason) {\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", STUN request failed: \";\n\tcout << err_code << \" \" << err_reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_stun_failed(t_user *user_config) {\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", STUN request failed.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\n\nbool t_userintf::cb_ask_user_to_redirect_invite(t_user *user_config, \n\t\tconst t_url &destination, const string &display)\n{\n\t// Cannot ask user for permission in CLI, so deny redirection.\n\treturn false;\n}\n\nbool t_userintf::cb_ask_user_to_redirect_request(t_user *user_config, \n\t\tconst t_url &destination, const string &display, t_method method)\n{\n\t// Cannot ask user for permission in CLI, so deny redirection.\n\treturn false;\n}\n\nbool t_userintf::cb_ask_credentials(t_user *user_config, \n\t\tconst string &realm, string &username, string &password)\n{\n\t// Cannot ask user for username/password in CLI\n\treturn false;\n}\n\nvoid t_userintf::cb_ask_user_to_refer(t_user *user_config, \n\t\t\tconst t_url &refer_to_uri,\n\t\t\tconst string &refer_to_display,\n\t\t\tconst t_url &referred_by_uri,\n\t\t\tconst string &referred_by_display)\n{\n\t// Cannot ask user for permission in CLI, so deny REFER\n\tsend_refer_permission(false);\n}\n\nvoid t_userintf::send_refer_permission(bool permission) {\n\tevq_trans_layer->push_refer_permission_response(permission);\n}\n\nvoid t_userintf::cb_show_msg(const string &msg, t_msg_priority prio) {\n\tcout << endl;\n\n\tswitch (prio) {\n\tcase MSG_NO_PRIO:\n\t\tbreak;\n\tcase MSG_INFO:\n\t\tcout << \"Info: \";\n\t\tbreak;\n\tcase MSG_WARNING:\n\t\tcout << \"Warning: \";\n\t\tbreak;\n\tcase MSG_CRITICAL:\n\t\tcout << \"Critical: \";\n\t\tbreak;\n\tdefault:\n\t\tcout << \"???: \";\n\t}\n\n\tcout << msg << endl;\n\n\tcout << endl;\n\tcout.flush();\n}\n\nbool t_userintf::cb_ask_msg(const string &msg, t_msg_priority prio) {\n\t// Cannot ask questions in CLI mode.\n\t// Print message and return false\n\tcb_show_msg(msg, prio);\n\treturn false;\n}\n\nvoid t_userintf::cb_display_msg(const string &msg, t_msg_priority prio) {\n\t// In CLI mode this is the same as cb_show_msg\n\tcb_show_msg(msg, prio);\n}\n\nvoid t_userintf::cb_async_display_msg(const string &msg, t_msg_priority prio) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_DISPLAY_MSG);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_display_msg(msg, prio);\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_log_updated(bool log_zapped) {\n\t// In CLI mode there is no log viewer.\n}\n\nvoid t_userintf::cb_call_history_updated(void) {\n\t// In CLI mode there is no call history viewer.\n}\n\nvoid t_userintf::cb_missed_call(int num_missed_calls) {\n\t// In CLI mode there is no missed call indication.\n}\n\nvoid t_userintf::cb_nat_discovery_progress_start(int num_steps) {\n\tcout << endl;\n\tcout << \"Firewall/NAT discovery in progress.\\n\";\n\tcout << \"Please wait.\\n\";\n\tcout << endl;\n}\n\nvoid t_userintf::cb_nat_discovery_finished(void) {\n\t// Nothing to do in CLI mode.\n}\n\nvoid t_userintf::cb_nat_discovery_progress_step(int step) {\n\t// Nothing to do in CLI mode.\n}\n\nbool t_userintf::cb_nat_discovery_cancelled(void) {\n\t// User cannot cancel NAT discovery in CLI mode.\n\treturn false;\n}\n\nvoid t_userintf::cb_line_encrypted(int line, bool encrypted, const string &cipher_mode) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tif (encrypted) {\n\t\tcout << \"Line \" << line + 1 << \": audio encryption enabled (\";\n\t\tcout << cipher_mode << \").\\n\";\n\t} else {\n\t\tcout << \"Line \" << line + 1 << \": audio encryption disabled.\\n\";\n\t}\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_async_line_encrypted(int line, bool encrypted, const string &cipher_mode) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_LINE_ENCRYPTED);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_line(line);\n\tevent->set_encrypted(encrypted);\n\tevent->set_cipher_mode(cipher_mode);\t\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_show_zrtp_sas(int line, const string &sas) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": ZRTP SAS = \" << sas << endl;\n\tcout << \"Confirm the SAS if it is correct.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_async_show_zrtp_sas(int line, const string &sas) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_SHOW_ZRTP_SAS);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_line(line);\n\tevent->set_zrtp_sas(sas);\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_zrtp_confirm_go_clear(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": remote user disabled encryption.\\n\";\n\tcout << endl;\n\tcout.flush();\n\t\n\tphone->pub_zrtp_go_clear_ok(line);\n}\n\nvoid t_userintf::cb_async_zrtp_confirm_go_clear(int line) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_ZRTP_CONFIRM_GO_CLEAR);\n\tMEMMAN_NEW(event);\n\t\n\tevent->set_line(line);\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cb_zrtp_sas_confirmed(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": SAS confirmed.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_zrtp_sas_confirmation_reset(int line) {\n\tif (line >= NUM_USER_LINES) return;\n\t\n\tcout << endl;\n\tcout << \"Line \" << line + 1 << \": SAS confirmation reset.\\n\";\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_update_mwi(void) {\n\t// Nothing to do in CLI mode.\n}\n\nvoid t_userintf::cb_mwi_subscribe_failed(t_user *user_config, t_response *r, bool first_failure) {\n\t// Only report the first failure in a sequence of failures\n\tif (!first_failure) return;\n\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", MWI subscription failed: \" << r->code << ' ' << r->reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nvoid t_userintf::cb_mwi_terminated(t_user *user_config, const string &reason) {\n\tcout << endl;\n\tcout << user_config->get_profile_name();\n\tcout << \", MWI subscription terminated: \" << reason << endl;\n\tcout << endl;\n\tcout.flush();\n}\n\nbool t_userintf::cb_message_request(t_user *user_config, t_request *r) {\n\tcout << endl;\n\tcout << \"Received message\\n\";\n\tcout << \"From:\\t\\t\";\n\t\n\tstring from_party = format_sip_address(user_config, \n\t\tr->hdr_from.get_display_presentation(), r->hdr_from.uri);\n\tcout << from_party << endl;\n\n\tif (r->hdr_organization.is_populated()) {\n\t\tcout << \"Organization:\\t\" << r->hdr_organization.name << endl;\n\t}\n\n\tcout << \"To:\\t\\t\";\n\tcout << format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri) << endl;\n\n\tif (r->hdr_subject.is_populated()) {\n\t\tcout << \"Subject:\\t\" << r->hdr_subject.subject << endl;\n\t}\n\t\n\tcout << endl;\n\tif (r->body && r->body->get_type() == BODY_PLAIN_TEXT)\n\t{\n\t\tt_sip_body_plain_text *sb = dynamic_cast<t_sip_body_plain_text *>(r->body);\n\t\tcout << sb->text << endl;\n\t} else if (r->body && r->body->get_type() == BODY_HTML_TEXT) {\n\t\tt_sip_body_html_text *sb = dynamic_cast<t_sip_body_html_text *>(r->body);\n\t\tcout << sb->text << endl;\n\t} else {\n\t\tcout << \"Unsupported content type.\\n\";\n\t}\n\n\tcout << endl;\n\tcout.flush();\n\t\n\t// There are no session in CLI mode, so all messages are accepted.\n\treturn true;\n}\n\nvoid t_userintf::cb_message_response(t_user *user_config, t_response *r, t_request *req) {\n\tif (r->is_success()) return;\n\t\n\tcout << endl;\n\tcout << \"Failed to send MESSAGE.\\n\";\n\tcout << r->code << \" \" << r->reason << endl;\n\t\n\tcout << endl;\n\tcout.flush();\t\n}\n\nvoid t_userintf::cb_im_iscomposing_request(t_user *user_config, t_request *r,\n\t\t\tim::t_composing_state state, time_t refresh)\n{\n\t// Nothing to do in CLI mode\n\treturn;\n}\n\nvoid t_userintf::cb_im_iscomposing_not_supported(t_user *user_config, t_response *r) {\n\t// Nothing to do in CLI mode\n\treturn;\n}\n\nbool t_userintf::get_last_call_info(t_url &url, string &display,\n\t\t\tstring &subject, t_user **user_config, bool &hide_user) const\n{\n\tif (!last_called_url.is_valid()) return false;\n\t\n\turl = last_called_url;\n\tdisplay = last_called_display;\n\tsubject = last_called_subject;\n\t*user_config = phone->ref_user_profile(last_called_profile);\n\thide_user = last_called_hide_user;\n\t\n\treturn *user_config != NULL;\n}\n\nbool t_userintf::can_redial(void) const {\n\treturn last_called_url.is_valid() && \n\t       phone->ref_user_profile(last_called_profile) != NULL;\n}\n\nvoid t_userintf::cmd_call(const string &destination, bool immediate) {\n\tstring s = \"invite \";\n\ts += destination;\n\texec_command(s);\n}\n\nvoid t_userintf::cmd_quit(void) {\n\texec_command(\"quit\");\n}\n\nvoid t_userintf::cmd_quit_async(void) {\n\tt_event_ui *event = new t_event_ui(TYPE_UI_CB_QUIT);\n\tMEMMAN_NEW(event);\n\tevq_ui_events.push(event);\n}\n\nvoid t_userintf::cmd_cli(const string &command, bool immediate) {\n\texec_command(command, immediate);\n}\n\nvoid t_userintf::cmd_show(void) {\n\t// Do nothing in CLI mode.\n}\n\nvoid t_userintf::cmd_hide(void) {\n\t// Do nothing in CLI mode.\n}\n\nstring t_userintf::get_name_from_abook(t_user *user_config, const t_url &u) {\n\treturn ab_local->find_name(user_config, u);\n}\n\nvoid *process_events_main(void *arg) {\n\tui->process_events();\n\treturn NULL;\n}\n\nconst list<string>& t_userintf::get_all_commands(void)\n{\n\treturn all_commands;\n}\n"
  },
  {
    "path": "src/userintf.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _USERINTF_H\n#define _USERINTF_H\n\n#include <list>\n#include <string>\n\n#include \"events.h\"\n#include \"phone.h\"\n#include \"protocol.h\"\n#include \"parser/request.h\"\n#include \"parser/response.h\"\n#include \"audio/tone_gen.h\"\n#include \"threads/thread.h\"\n#include \"im/msg_session.h\"\n#include \"presence/presence_state.h\"\n\n#include \"twinkle_config.h\"\n\n#define PRODUCT_DATE VERSION_DATE\n#define PRODUCT_AUTHOR\t\"Michel de Boer and contributors\"\n\n// Tone definitions\n// The intervals indicate the length of silence between repetitions\n// of the wav file. Duration is in ms\n#define FILE_RINGTONE           \"ringtone.wav\"\n#define INTERVAL_RINGTONE       3000\n#define FILE_RINGBACK           \"ringback.wav\"\n#define INTERVAL_RINGBACK       3000\n\nusing namespace std;\n\nstruct t_command_arg {\n        char    flag;\n        string  value;\n};\n\nclass t_userintf : public i_prohibit_thread {\nprotected:\n\tenum t_zrtp_cmd {\n\t\tZRTP_ENCRYPT,\n\t\tZRTP_GO_CLEAR,\n\t\tZRTP_CONFIRM_SAS,\n\t\tZRTP_RESET_SAS\n\t};\n\t\nprivate:\n        bool            end_interface; // indicates if interface loop should quit\n        int             break_readline_loop_pipe[2]; // pipe used to interrupt Readline\n        list<string>    all_commands;  // list of all commands\n        t_tone_gen      *tone_gen;     // tone generator for ringing\n        \n        // The user for which out-of-dialog requests are executed.\n        t_user\t\t*active_user;\n\n        // The user can type a prefix of the command only. This method\n        // completes a prefix to a full command.\n        // If no command is found then the empty string is returned.\n        // If the prefix is ambiguous, then argument ambiguous is set to true\n        // and the empty string is returned.\n        string complete_command(const string &c, bool &ambiguous);\n\n        // Parse command arguments. The list must contain the command as first\n        // element followed by the arguments.\n        bool parse_args(const list<string> command_list, list<t_command_arg> &al);\n\n        // The command_list must contain the command itself as first\n        // argument. Subsequent elements are the arguments.\n        bool exec_invite(const list<string> command_list, bool immediate = false);\n        bool exec_redial(const list<string> command_list);\n        bool exec_answer(const list<string> command_list);\n        bool exec_answerbye(const list<string> command_list);\n        bool exec_reject(const list<string> command_list);\n\tbool exec_redirect(const list<string> command_list, bool immediate = false);\n\tbool exec_dnd(const list<string> command_list);\n\tbool exec_auto_answer(const list<string> command_list);\n        bool exec_bye(const list<string> command_list);\n        bool exec_hold(const list<string> command_list);\n        bool exec_retrieve(const list<string> command_list);\n\tbool exec_refer(const list<string> command_list, bool immediate = false);\n\tbool exec_conference(const list<string> command_list);\n\tbool exec_mute(const list<string> command_list);\n\tbool exec_dtmf(const list<string> command_list);\n        bool exec_register(const list<string> command_list);\n        bool exec_deregister(const list<string> command_list);\n        bool exec_fetch_registrations(const list<string> command_list);\n        bool exec_options(const list<string> command_list, bool immediate = false);\n        bool exec_line(const list<string> command_list);\n        bool exec_user(const list<string> command_list);\n        bool exec_zrtp(const list<string> command_list);\n        bool exec_message(const list<string> command_list);\n        bool exec_presence(const list<string> command_list);\n        bool exec_quit(const list<string> command_list);\n        bool exec_help(const list<string> command_list);\n\nprotected:\n        t_phone         *phone;\n        \n        // Asynchronous event queue\n        t_event_queue\tevq_ui_events;\n        t_thread\t*thr_process_events;\n        \n        // Indicates if commands should print output to stdout\n        bool\t\tuse_stdout;\n\n\t// Throttle dtmtf not supported messages\n\tbool\t\tthrottle_dtmf_not_supported;\n\n\t// Last call information\n\tt_url\t\tlast_called_url;\n\tstring\t\tlast_called_display;\n\tstring\t\tlast_called_subject;\n\tstring\t\tlast_called_profile; // profile used to make the call\n\tbool\t\tlast_called_hide_user;\n\t\n\t// The do_* methods perform the commands parsed by the exec_* methods.\n\tvirtual bool do_invite(const string &destination, const string &display, \n\t\t\tconst string &subject, bool immediate, bool anonymous);\n\tvirtual void do_redial(void);\n\tvirtual void do_answer(void);\n\tvirtual void do_answerbye(void);\n\tvirtual void do_reject(void);\n\tvirtual void do_redirect(bool show_status, bool type_present, t_cf_type cf_type, \n\t\tbool action_present, bool enable, int num_redirections,\n\t\tconst list<string> &dest_strlist, bool immediate);\n\tvirtual void do_dnd(bool show_status, bool toggle, bool enable);\n\tvirtual void do_auto_answer(bool show_status, bool toggle, bool enable);\n\tvirtual void do_bye(void);\n\tvirtual void do_hold(bool toggle);\n\tvirtual void do_retrieve(void);\n\tvirtual bool do_refer(const string &destination, t_transfer_type transfer_type, \n\t\tbool immediate);\n\tvirtual void do_conference(void);\n\tvirtual void do_mute(bool show_status, bool toggle, bool enable);\n\tvirtual void do_dtmf(const string &digits);\n\tvirtual void do_register(bool reg_all_profiles);\n\tvirtual void do_deregister(bool dereg_all_profiles, bool dereg_all_devices);\n\tvirtual void do_fetch_registrations(void);\n\tvirtual bool do_options(bool dest_set, const string &destination, bool immediate);\n\tvirtual void do_line(int line);\n\tvirtual void do_user(const string &profile_name);\n\tvirtual void do_zrtp(t_zrtp_cmd zrtp_cmd);\n\tvirtual bool do_message(const string &destination, const string &display,\n\t\tconst im::t_msg &msg);\n\tvirtual void do_presence(t_presence_state::t_basic_state basic_state);\n\tvirtual void do_quit(void);\n\tvirtual void do_help(const list<t_command_arg> &al);\n\npublic:\n        t_userintf(t_phone *_phone);\n        virtual ~t_userintf();\n\n\t/**\n         * Expand a SIP destination to a full SIP/TEL uri, i.e. add sip/tel scheme\n         * and domain if these are missing.\n         * @param user_config [in] User profile of the user for which the expansion is done.\n         * @param dst [in] The address string to expand.\n         * @param scheme [in] Scheme to expand to (sip/tel/\"\"). If scheme is empty then\n         *        the expansion is done according to preferences from the user profile.\n         * @return The expanded address.\n         */\n        string expand_destination(t_user *user_config, const string &dst, const string &scheme = \"\");\n        \n        // Expand a SIP destination into a display and a full SIP uri\n        void expand_destination(t_user *user_config, \n        \tconst string &dst, string &display, string &dst_url);\n        void expand_destination(t_user *user_config, \n        \tconst string &dst, t_display_url &display_url);\n        \t\n        // Expand a SIP destination as above, but split off any headers if any.\n        // If the subject header is present, then its value will be returned in\n        // subject.\n        // The dst_no_headers parameter will contain the dst string with the headers\n        // cut off.\n\tvoid expand_destination(t_user *user_config,\n\t\tconst string &dst, t_display_url &display_url, string &subject,\n\t\tstring &dst_no_headers);\n\n\t// Format a SIP address for user display\n\tvirtual string format_sip_address(t_user *user_config, const string &display,\n\t                                  const t_url &uri) const;\n\n\t// Format a warning for user display\n\tvirtual list<string> format_warnings(const t_hdr_warning &hdr_warning) const;\n\n\t// Format a codec for user display\n\tvirtual string format_codec(t_audio_codec codec) const;\n\n\t// The immediate flag is by the cmd_cli method (see below)\n        bool exec_command(const string &command_line, bool immediate = false);\n        \n        // Run the user interface\n        virtual void run(void);\n        \n        // This method executes asynchronous uier interface events\n        virtual void process_events(void);\n        \n        // Save user interface state to system settings\n        virtual void save_state(void);\n        \n        // Restore user interface state from system settings\n        virtual void restore_state(void);\n\n\t// Lock the user interface to synchornize output\n\tvirtual void lock(void);\n\tvirtual void unlock(void);\n\n        // Select a network interface. Returns string representation of IP address.\n        virtual string select_network_intf(void);\n\n\t// Select a user configuration file. Returns false if selection failed.\n\tvirtual bool select_user_config(list<string> &config_files);\n\n        // Call back functions\n        virtual void cb_incoming_call(t_user *user_config, int line, const t_request *r);\n        virtual void cb_call_cancelled(int line, const std::string &reason);\n        virtual void cb_far_end_hung_up(int line, const std::string &reason);\n        virtual void cb_answer_timeout(int line);\n        virtual void cb_sdp_answer_not_supported(int line, const string &reason);\n        virtual void cb_sdp_answer_missing(int line);\n\tvirtual void cb_unsupported_content_type(int line, const t_sip_message *r);\n        virtual void cb_ack_timeout(int line);\n\tvirtual void cb_100rel_timeout(int line);\n\tvirtual void cb_session_expired(int line);\n\tvirtual void cb_prack_failed(int line, const t_response *r);\n        virtual void cb_provisional_resp_invite(int line, const t_response *r);\n        virtual void cb_cancel_failed(int line, const t_response *r);\n        virtual void cb_call_answered(t_user *user_config, int line, const t_response *r);\n        virtual void cb_call_failed(t_user *user_config, int line, const t_response *r);\n        virtual void cb_stun_failed_call_ended(int line);\n        virtual void cb_call_ended(int line);\n        virtual void cb_call_established(int line);\n        virtual void cb_options_response(const t_response *r);\n        virtual void cb_reinvite_success(int line, const t_response *r);\n        virtual void cb_reinvite_failed(int line, const t_response *r);\n\tvirtual void cb_retrieve_failed(int line, const t_response *r);\n        virtual void cb_invalid_reg_resp(t_user *user_config, \n        \t\tconst t_response *r, const string &reason);\n        virtual void cb_register_success(t_user *user_config, \n        \t\tconst t_response *r, unsigned long expires, bool first_success);\n        virtual void cb_register_failed(t_user *user_config, \n        \t\tconst t_response *r, bool first_failure);\n        virtual void cb_register_stun_failed(t_user *user_config, bool first_failure);\n        virtual void cb_deregister_success(t_user *user_config, const t_response *r);\n        virtual void cb_deregister_failed(t_user *user_config, const t_response *r);\n        virtual void cb_fetch_reg_failed(t_user *user_config, const t_response *r);\n        virtual void cb_fetch_reg_result(t_user *user_config, const t_response *r);\n\tvirtual void cb_register_inprog(t_user *user_config, t_register_type register_type);\n\tvirtual void cb_redirecting_request(t_user *user_config, \n\t\t\tint line, const t_contact_param &contact);\n\tvirtual void cb_redirecting_request(t_user *user_config, \n\t\t\tconst t_contact_param &contact);\n        virtual void cb_play_ringtone(int line);\n\tvirtual void cb_play_ringback(t_user *user_config);\n        virtual void cb_stop_tone(int line);\n        virtual void cb_notify_call(int line, string from_party);\n        virtual void cb_stop_call_notification(int line);\n\tvirtual void cb_dtmf_detected(int line, t_dtmf_ev dtmf_event);\n\tvirtual void cb_async_dtmf_detected(int line, t_dtmf_ev dtmf_event);\n\tvirtual void cb_send_dtmf(int line, t_dtmf_ev dtmf_event);\n\tvirtual void cb_async_send_dtmf(int line, t_dtmf_ev dtmf_event);\n\tvirtual void cb_dtmf_not_supported(int line);\n\tvirtual void cb_dtmf_supported(int line);\n\tvirtual void cb_line_state_changed(void);\n\tvirtual void cb_async_line_state_changed(void);\n\tvirtual void cb_send_codec_changed(int line, t_audio_codec codec);\n\tvirtual void cb_recv_codec_changed(int line, t_audio_codec codec);\n\tvirtual void cb_async_recv_codec_changed(int line, t_audio_codec codec);\n\tvirtual void cb_notify_recvd(int line, const t_request *r);\n\tvirtual void cb_refer_failed(int line, const t_response *r);\n\tvirtual void cb_refer_result_success(int line);\n\tvirtual void cb_refer_result_failed(int line);\n\tvirtual void cb_refer_result_inprog(int line);\n\n\t// A call is being referred by the far end. r must be the REFER request.\n\tvirtual void cb_call_referred(t_user *user_config, int line, t_request *r);\n\n\t// The reference failed. Call to referrer is retrieved.\n\tvirtual void cb_retrieve_referrer(t_user *user_config, int line);\n\t\n\t// A consulation call for a call transfer is being setup.\n\tvirtual void cb_consultation_call_setup(t_user *user_config, int line);\n\t\n\t// STUN errors\n\tvirtual void cb_stun_failed(t_user *user_config, int err_code, const string &err_reason);\n\tvirtual void cb_stun_failed(t_user *user_config);\n\n\t// Interactive call back functions\n\tvirtual bool cb_ask_user_to_redirect_invite(t_user *user_config, \n\t\t\tconst t_url &destination, const string &display);\n\tvirtual bool cb_ask_user_to_redirect_request(t_user *user_config, \n\t\t\tconst t_url &destination, const string &display, t_method method);\n\tvirtual bool cb_ask_credentials(t_user *user_config, \n\t\t\tconst string &realm, string &username, string &password);\n\t\t\t\n\t// Ask questions asynchronously.\n\tvirtual void cb_ask_user_to_refer(t_user *user_config, \n\t\t\tconst t_url &refer_to_uri,\n\t\t\tconst string &refer_to_display,\n\t\t\tconst t_url &referred_by_uri,\n\t\t\tconst string &referred_by_display);\n\t\t\t\n\t// Send the answer for refer permission to the transaction layer.\n\tvoid send_refer_permission(bool permission);\n\n\t// Show an error message to the user. Depending on the interface mode\n\t// the user has to acknowledge the error before processing continues.\n\tvirtual void cb_show_msg(const string &msg, t_msg_priority prio = MSG_INFO);\n\t\n\t// Ask a yes/no question to the user.\n\t// Returns true for yes and false for no.\n\tvirtual bool cb_ask_msg(const string &msg, t_msg_priority prio = MSG_INFO);\n\n\t/** \n\t * Display an error/information message.\n\t * @param msg [in] Message to display.\n\t * @param prio [in] Priority associated with the message.\n\t */\n\tvirtual void cb_display_msg(const string &msg,\n\t\t\tt_msg_priority prio = MSG_INFO);\n\t\t\t\n\t/**\n\t * Display an error/information message in an asynchronous way.\n\t * @param msg [in] Message to display.\n\t * @param prio [in] Priority associated with the message.\n\t */\n\tvirtual void cb_async_display_msg(const string &msg, \n\t\t\tt_msg_priority prio = MSG_INFO);\n\t\t\t\n\t// Log file has been updated\n\tvirtual void cb_log_updated(bool log_zapped = false);\n\t\n\t// Call history has been updated\n\tvirtual void cb_call_history_updated(void);\n\tvirtual void cb_missed_call(int num_missed_calls);\n\t\n\t// Show firewall/NAT discovery progress\n\tvirtual void cb_nat_discovery_progress_start(int num_steps);\n\tvirtual void cb_nat_discovery_progress_step(int step);\n\tvirtual void cb_nat_discovery_finished(void);\n\tvirtual bool cb_nat_discovery_cancelled(void);\n\t\n\t// ZRTP\n\tvirtual void cb_line_encrypted(int line, bool encrypted, const string &cipher_mode = \"\");\n\tvirtual void cb_async_line_encrypted(int line, bool encrypted, const string &cipher_mode = \"\");\n\tvirtual void cb_show_zrtp_sas(int line, const string &sas);\n\tvirtual void cb_async_show_zrtp_sas(int line, const string &sas);\n\tvirtual void cb_zrtp_confirm_go_clear(int line);\n\tvirtual void cb_async_zrtp_confirm_go_clear(int line);\n\tvirtual void cb_zrtp_sas_confirmed(int line);\n\tvirtual void cb_zrtp_sas_confirmation_reset(int line);\n\t\n\t// MWI\n\tvirtual void cb_update_mwi(void);\n\tvirtual void cb_mwi_subscribe_failed(t_user *user_config, t_response *r, bool first_failure);\n\tvirtual void cb_mwi_terminated(t_user *user_config, const string &reason);\n\t\n\t/** @name Instant messaging */\n\t//@{\n\t/**\n\t * Incoming MESSAGE request callback.\n\t * @param user_config [in] User profile of the user receiving this MESSAGE request.\n\t * @param r [in] The MESSAGE request.\n\t * @return True if the message is accepted.\n\t * @return False if the message is rejected, i.e. maximum number of sessions reached.\n\t */\n\tvirtual bool cb_message_request(t_user *user_config, t_request *r);\n\t\n\t/**\n\t * Incoming MESSAGE response callback.\n\t * @param user_config [in] User profile of the user receiving this MESSAGE response.\n\t * @param r [in] The MESSAGE response.\n\t * @param req [in] The MESSAGE request for which the response is received.\n\t */\n\tvirtual void cb_message_response(t_user *user_config, t_response *r, t_request *req);\n\t\n\t/**\n\t * Incoming MESSAGE request with composing indication callback.\n\t * @param user_config [in] User profile of the user receiving this MESSAGE response.\n\t * @param r [in] The MESSAGE request containing the composing indication.\n\t * @param state [in] The message composing state.\n\t * @param refresh [in] The refresh interval in seconds when state is active.\n\t */\n\tvirtual void cb_im_iscomposing_request(t_user *user_config, t_request *r,\n\t\t\tim::t_composing_state state, time_t refresh);\n\t\t\n\t/** \n\t * Indication that the far-end does not support message composing indications.\n\t * @param user_config [in] User profile of the user receiving this MESSAGE response.\n\t * @param r [in] The MESSAGE response on the composing indication.\n\t */\n\tvirtual void cb_im_iscomposing_not_supported(t_user *user_config, t_response *r);\n\t//@}\n\n\t// Get last call information\n\t// Returns true if last call information is valid\n\t// Returns false is there is no valid last call information\n\tvirtual bool get_last_call_info(t_url &url, string &display,\n\t\t\t\tstring &subject, t_user **user_config,\n\t\t\t\tbool &hide_user) const;\n\tvirtual bool can_redial(void) const;\n\t\n\t// Execute external commands\n\t// Some comments require confirmation from the user via the user\n\t// interface, e.g. in GUI mode, a call dialog may popup for cmd_call.\n\t// The 'immediate' flag indicates that no user confirmation is required.\n\t// The command should be executed immediately.\n\tvirtual void cmd_call(const string &destination, bool immediate);\n\tvirtual void cmd_quit(void);\n\tvoid cmd_quit_async(void);\n\tvirtual void cmd_cli(const string &command, bool immeidate);\n\t\n\t/** Execute the SHOW command. */\n\tvirtual void cmd_show(void);\n\t\n\t/** Execute the HIDE command. */\n\tvirtual void cmd_hide(void);\n\t\n\t// Lookup a URL in the address book\n\tvirtual string get_name_from_abook(t_user *user_config, const t_url &u);\n\n\t// Get all command names\n\tconst list<string>& get_all_commands(void);\n\n\t// Asynchronously run a function on this class' event queue\n\tvoid run_on_event_queue(std::function<void()> fn);\n\n};\n\nvoid *process_events_main(void *arg);\n\nextern t_userintf *ui;\n\n#endif\n"
  },
  {
    "path": "src/util.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include <iostream>\n#include <iomanip>\n#include <sstream>\n#include <locale>\n#include <cassert>\n#include <cctype>\n#include <cstdlib>\n#include <cstdio>\n#include <cstring>\n#include <sys/time.h>\n\n#include \"util.h\"\n\n#include \"twinkle_config.h\"\n\nstring month_abbrv[] = {\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \n\t\t\t\"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"};\n\t\t\t\nstring month_full[] = {\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\",\n\t\t       \"August\", \"September\", \"October\", \"November\", \"December\"};\n\t\t\t\nstring day_abbrv[] = {\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"};\n\nstring random_token(int length) {\n\tstring s;\n\n\tfor (int i = 0; i < length; i++) {\n\t\ts += char(rand() % 26 + 97);\n\t}\n\n\treturn s;\n}\n\nstring random_hexstr(int length) {\n\tstring s;\n\tint x;\n\n\tfor (int i = 0; i < length; i++) {\n\t\tx = rand() % 16;\n\t\tif (x <= 9)\n\t\t\ts += '0' + x;\n\t\telse\n\t\t\ts += 'a' + x - 10;\n\t}\n\n\treturn s;\n}\n\nstring float2str(float f, int precision) {\n\tostringstream s;\n\t\n\t// Force the locale to POSIX, such that a dot is used for\n\t// the decimal point.\n\ts.imbue(locale(\"POSIX\"));\n\ts.setf(ios::fixed,ios::floatfield);\n\ts.precision(precision);\n\ts << f;\n\t\n\treturn s.str();\n}\n\nstring int2str(int i, const char *format) {\n\tchar buf[32];\n\n\tsnprintf(buf, 32, format, i);\n\treturn string(buf);\n}\n\nstring int2str(int i) {\n\treturn int2str(i, \"%d\");\n}\n\nstring ulong2str(unsigned long i, const char *format) {\n\tchar buf[32];\n\n\tsnprintf(buf, 32, format, i);\n\treturn string(buf);\n}\n\nstring ulong2str(unsigned long i) {\n\treturn ulong2str(i, \"%u\");\n}\n\nstring ptr2str(void *p) {\n\tchar buf[32];\n\t\n\tsnprintf(buf, 32, \"%p\", p);\n\treturn string(buf);\n}\n\nstring bool2str(bool b) {\n\treturn (b ? \"true\" : \"false\");\n}\n\nstring time2str(time_t t, const char *format) {\n\tstruct tm tm;\n\tchar buf[64];\n\t\n\tlocaltime_r(&t, &tm);\n\tstrftime(buf, 64, format, &tm);\n\treturn string(buf);\n}\n\nstring current_time2str(const char *format) {\n\tstruct timeval t;\n\t\n\tgettimeofday(&t, NULL);\n\treturn time2str(t.tv_sec, format);\n}\n\nstring weekday2str(int wkday) {\n\tif (wkday >= 0 && wkday <= 6) return day_abbrv[wkday];\n\treturn \"XXX\";\n}\n\nstring month2str(int month) {\n\tif (month >= 0 && month <= 11) return month_abbrv[month];\n\treturn \"XXX\";\n}\n\nint str2month_full(const string &month) {\n\tfor (int i = 0; i < 12; i++) {\n\t\tif (cmp_nocase(month_full[i], month) == 0) {\n\t\t\treturn i;\n\t\t}\n\t}\n\t\n\treturn 0;\n}\n\nstring duration2str(unsigned long seconds) {\n\tstring result;\n\tlong remainder, h, m, s;\n\t\n\th = seconds / 3600;\n\tremainder = seconds % 3600;\n\tm = remainder / 60;\n\ts = remainder % 60;\n\n\tif (h > 0) {\n\t\tresult = ulong2str(h);\n\t\tresult += \"h \";\n\t}\n\t\n\tif (!result.empty() || m > 0) {\n\t\tresult += ulong2str(m);\n\t\tresult += \"m \";\n\t}\n\t\n\tresult += ulong2str(s);\n\tresult += \"s\";\n\n\treturn result;\n}\n\nstring timer2str(unsigned long seconds) {\n\tstring result;\n\tunsigned long remainder, h, m, s;\n\t\n\th = seconds / 3600;\n\tremainder = seconds % 3600;\n\tm = remainder / 60;\n\ts = remainder % 60;\n\t\n\tchar buf[16];\n\tsnprintf(buf, 16, \"%01lu:%02lu:%02lu\", (h % 1000), m, s);\n\treturn string(buf);\n}\n\nstatic uint8 hexdig2value(char hexdig) {\n\tuint8 val = 0;\n\t\n\tif (hexdig >= '0' && hexdig <= '9')\n\t\tval = hexdig - '0';\n\telse if (hexdig >= 'a' && hexdig <= 'f')\n\t\tval = hexdig - 'a' + 10;\n\telse if (hexdig >= 'A' && hexdig <= 'F')\n\t\tval = hexdig - 'A' + 10;\n\t\t\n\treturn val;\n}\n\nstatic char value2hexdig(uint8 val) {\n\tchar c = '0';\n\t\n\tif (val <= 9) {\n\t\tc = '0' + val;\n\t} else if (val <= 15) {\n\t\tc = 'a' + val - 10;\n\t}\n\t\n\treturn c;\n}\n\nunsigned long hex2int(const string &h) {\n\tunsigned long u = 0;\n\n\tint power = 1;\n\tfor (string::const_reverse_iterator i = h.rbegin(); i != h.rend(); ++i) {\n\t\tu += hexdig2value(*i) * power;\n\t\tpower = power * 16;\n\t}\n\n\treturn u;\n}\n\nvoid hex2binary(const string &h, uint8 *buf) {\n\tuint8 *p = buf;\n\t\n\tbool hi_nibble = true;\n\tfor (string::const_iterator i = h.begin() ; i != h.end(); ++i) {\n\t\tif (hi_nibble) {\n\t\t\t*p = hexdig2value(*i) << 4;\n\t\t} else {\n\t\t\t*(p++) |= hexdig2value(*i);\n\t\t}\n\t\t\n\t\thi_nibble = !hi_nibble;\n\t}\n}\n\nstring binary2hex(uint8 *buf, unsigned long len) {\n\tstring s;\n\t\n\tfor (uint8 *p = buf; p < buf + len; ++p) {\n\t\ts += value2hexdig((*p >> 4) & 0xf);\n\t\ts += value2hexdig(*p & 0xf);\n\t}\n\t\n\treturn s;\n}\n\nstring tolower(const string &s) {\n\tstring result;\n\n\tfor (string::const_iterator i = s.begin(); i != s.end(); ++i) {\n\t\tresult += tolower(*i);\n\t}\n\n\treturn result;\n}\n\nstring toupper(const string &s) {\n\tstring result;\n\n\tfor (string::const_iterator i = s.begin(); i != s.end(); ++i) {\n\t\tresult += toupper(*i);\n\t}\n\n\treturn result;\n}\n\nstring rtrim(const string &s) {\n\tstring::size_type i;\n\n\ti = s.find_last_not_of(' ');\n\tif (i == string::npos) return \"\";\n\tif (i == s.size()-1) return s;\n\treturn s.substr(0, i+1);\n}\n\nstring ltrim(const string &s) {\n\tstring::size_type i;\n\n\ti = s.find_first_not_of(' ');\n\tif (i == string::npos) return \"\";\n\tif (i == 0) return s;\n\treturn s.substr(i, s.size()-i+1);\n}\n\nstring trim(const string &s) {\n\treturn ltrim(rtrim(s));\n}\n\nstring padleft(const string &s, char c, unsigned long len) {\n\tstring result(c, len);\n\tresult += s;\n\treturn result.substr(result.size() - len);\n}\n\nint cmp_nocase(const string &s1, const string &s2) {\n\tstring::const_iterator i1 = s1.begin();\n\tstring::const_iterator i2 = s2.begin();\n\n\twhile (i1 != s1.end() && i2 != s2.end()) {\n\t\tif (toupper(*i1) != toupper(*i2)) {\n\t\t\treturn (toupper(*i1) < toupper(*i2)) ? -1 : 1;\n\t\t}\n\t\t++i1;\n\t\t++i2;\n\t}\n\n\tif (s1.size() == s2.size()) return 0;\n\tif (s1.size() < s2.size()) return -1;\n\treturn 1;\n}\n\nbool must_quote(const string &s) {\n\tstring special(\"()<>@,;:\\\\\\\"/[]?={} \\t\");\n\n\tif (s.size() == 0) return true;\n\treturn (s.find_first_of(special) != string::npos);\n}\n\nstring escape(const string &s, char c) {\n\tstring result;\n\n\tfor (string::size_type i = 0; i < s.size(); i++) {\n\t\tif (s[i] == '\\\\' || s[i] == c) {\n\t\t\tresult += '\\\\';\n\t\t}\n\t\t\n\t\tresult += s[i];\n\t}\n\t\n\treturn result;\n}\n\nstring unescape(const string &s) {\n\tstring result;\n\n\tfor (string::size_type i = 0; i < s.size(); i++) {\n\t\tif (s[i] == '\\\\' && i < s.size() - 1) {\n\t\t\ti++;\n\t\t}\n\t\t\n\t\tresult += s[i];\n\t}\n\t\n\treturn result;\n}\n\nstring escape_hex(const string &s, const string &unreserved) {\n\tstring result;\n\t\n\tfor (string::size_type i = 0; i < s.size(); i++) {\n\t\tif (unreserved.find(s[i], 0) != string::npos) {\n\t\t\t// Unreserved symbol\n\t\t\tresult += s[i];\n\t\t} else {\n\t\t\t// Reserved symbol\n\t\t\tresult += int2str((int)s[i], \"%%%02x\");\n\t\t}\n\t}\n\t\n\treturn result;\n}\nstring unescape_hex(const string &s) {\n\tstring result;\n\t\n\tfor (string::size_type i = 0; i < s.size(); i++) {\n\t\tif (s[i] == '%' && i < s.size() - 2 &&\n\t\t    isxdigit(s[i+1]) && isxdigit(s[i+2])) \n\t\t{\n\t\t\t// Escaped hex-value\n\t\t\tstring hexval = s.substr(i+1, 2);\n\t\t\tresult += static_cast<char>(hex2int(hexval));\n\t\t\ti += 2;\n\t\t} else {\n\t\t\tresult += s[i];\n\t\t}\n\t}\n\t\n\treturn result;\n}\n\nstring replace_char(const string &s, char from, char to) {\n\tstring result = s;\n\n\tfor (string::size_type i = 0; i < result.size(); i++) {\n        \tif (result[i] == from) result[i] = to;\n   \t}\n   \t\n   \treturn result;\n}\n\nstring replace_first(const string &s, const string &from, const string &to) {\n\tstring result = s;\n\t\n\tstring::size_type i = result.find(from, 0);\n\tif (i != string::npos) {\n\t\tresult.replace(i, from.size(), to);\n\t}\n\t\n\treturn result;\n}\n\nvector<string> split(const string &s, char c) {\n\tstring::size_type i;\n\tstring::size_type j = 0;\n\tvector<string> l;\n\n\twhile (true) {\n\t\ti = s.find(c, j);\n\t\tif (i == string::npos) {\n\t\t\tl.push_back(s.substr(j));\n\t\t\treturn l;\n\t\t}\n\n\t\tif (i == j)\n\t\t\tl.push_back(\"\");\n\t\telse\n\t\t\tl.push_back(s.substr(j, i-j));\n\n\t\tj = i+1;\n\n\t\tif (j == s.size()) {\n\t\t\tl.push_back(\"\");\n\t\t\treturn l;\n\t\t}\n\t}\n}\n\nvector<string> split(const string &s, const string& separator) {\n\tstring::size_type i;\n\tstring::size_type j = 0;\n\tvector<string> l;\n\n\twhile (true) {\n\t\ti = s.find(separator, j);\n\t\tif (i == string::npos) {\n\t\t\tl.push_back(s.substr(j));\n\t\t\treturn l;\n\t\t}\n\n\t\tif (i == j)\n\t\t\tl.push_back(\"\");\n\t\telse\n\t\t\tl.push_back(s.substr(j, i-j));\n\n\t\tj = i + separator.size();\n\n\t\tif (j == s.size()) {\n\t\t\tl.push_back(\"\");\n\t\t\treturn l;\n\t\t}\n\t}\n}\n\nvector<string> split_linebreak(const string &s) {\n\tif (s.find(\"\\r\\n\") != string::npos) {\n\t\treturn split(s, \"\\r\\n\");\n\t} else if (s.find(\"\\r\") != string::npos) {\n\t\treturn split(s, \"\\r\");\n\t}\n\t\n\treturn split(s, \"\\n\");\n}\n\nvector<string> split_on_first(const string &s, char c) {\n\tvector<string> l;\n\tstring::size_type i = s.find(c);\n\tif (i == string::npos) {\n\t\tl.push_back(s);\n\t} else {\n\t\tif (i == 0) {\n\t\t\tl.push_back(\"\");\n\t\t} else {\n\t\t\tl.push_back(s.substr(0, i));\n\t\t}\n\t\t\n\t\tif (i == s.size() - 1) {\n\t\t\tl.push_back(\"\");\n\t\t} else {\n\t\t\tl.push_back(s.substr(i + 1));\n\t\t}\n\t}\n\t\n\treturn l;\n}\n\nvector<string> split_on_last(const string &s, char c) {\n\tvector<string> l;\n\tstring::size_type i = s.find_last_of(c);\n\tif (i == string::npos) {\n\t\tl.push_back(s);\n\t} else {\n\t\tif (i == 0) {\n\t\t\tl.push_back(\"\");\n\t\t} else {\n\t\t\tl.push_back(s.substr(0, i));\n\t\t}\n\t\t\n\t\tif (i == s.size() - 1) {\n\t\t\tl.push_back(\"\");\n\t\t} else {\n\t\t\tl.push_back(s.substr(i + 1));\n\t\t}\n\t}\n\t\n\treturn l;\n}\n\nvector<string> split_escaped(const string &s, char c) {\n\tvector<string> l;\n\t\n\tstring::size_type start_pos = 0;\n\tfor (string::size_type i = 0; i < s.size(); i++) {\n\t\tif (s[i] == '\\\\') {\n\t\t\t// Skip escaped character\n\t\t\tif (i < s.size()) i++;\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tif (s[i] == c) {\n\t\t\tl.push_back(unescape(s.substr(start_pos, i - start_pos)));\n\t\t\tstart_pos = i + 1;\n\t\t}\n\t}\n\t\n\tif (start_pos < s.size()) {\n\t\tl.push_back(unescape(s.substr(start_pos, s.size() - start_pos)));\n\t} else if (start_pos == s.size()) {\n\t\tl.push_back(\"\");\n\t}\n\t\n\treturn l;\n}\n\nvector<string> split_ws(const string &s, bool quote_sensitive) {\n        vector<string> l;\n        bool in_quotes = false;\n\n        string::size_type start_pos = 0;\n        for (string::size_type i = 0; i < s.size(); i++ ) {\n                if (quote_sensitive && s[i] == '\"') {\n                        in_quotes = !in_quotes;\n                        continue;\n                }\n\n                if (in_quotes) continue;\n\n                if (s[i] == ' ' || s[i] == '\\t') {\n                        // Skip consecutive white space\n                        if (start_pos != i) {\n                                l.push_back(s.substr(start_pos, i - start_pos));\n                        }\n                        start_pos = i + 1;\n                }\n        }\n\n        if (start_pos < s.size()) {\n                l.push_back(s.substr(start_pos, s.size() - start_pos));\n        }\n\n        return l;\n}\n\nstring join_strings(const vector<string> &v, const string &separator) {\n\tstring text;\n\tfor (vector<string>::const_iterator it = v.begin(); it != v.end(); ++it)\n\t{\n\t\tif (it != v.begin()) {\n\t\t\ttext += separator;\n\t\t}\n\t\ttext += *it;\n\t}\n\t\n\treturn text;\n}\n\nstring unquote(const string &s) {\n        if (s.size() <= 1) return s;\n\n        if (s[0] == '\"' && s[s.size() - 1] == '\"')\n                return s.substr(1, s.size() - 2);\n\n        return s;\n}\n\nbool is_number(const string &s) {\n\tif (s.empty()) return false;\n\t\n        for (string::size_type i = 0; i < s.size(); i++ ) {\n\t\tif (!isdigit(s[i])) return false;\n\t}\n\n\treturn true;\n}\n\nbool is_ipaddr(const string &s) {\n\tvector<string> l = split(s, '.');\n\tif (l.size() != 4) return false;\n\t\n\tfor (vector<string>::iterator i = l.begin(); i != l.end(); ++i) {\n\t\tif (!is_number(*i) || atoi(i->c_str()) > 255) return false;\n\t}\n\t\n\treturn true;\n}\n\nbool yesno2bool(const string &yesno) {\n\treturn (yesno == \"yes\" ? true : false);\n}\nstring bool2yesno(bool b) {\n\treturn (b ? \"yes\" : \"no\");\n}\n\nstring str2dtmf(const string &s) {\n\tstring result;\n\tstring to_convert = tolower(s);\n\t\n\tfor (string::size_type i = 0; i < to_convert.size(); i++) {\n\t\tswitch (to_convert[i]) {\n\t\tcase '1':\n\t\t\tresult += '1';\n\t\t\tbreak;\n\t\tcase '2':\n\t\tcase 'a':\n\t\tcase 'b':\n\t\tcase 'c':\n\t\t\tresult += '2';\n\t\t\tbreak;\n\t\tcase '3':\n\t\tcase 'd':\n\t\tcase 'e':\n\t\tcase 'f':\n\t\t\tresult += '3';\n\t\t\tbreak;\n\t\tcase '4':\n\t\tcase 'g':\n\t\tcase 'h':\n\t\tcase 'i':\n\t\t\tresult += '4';\n\t\t\tbreak;\n\t\tcase '5':\n\t\tcase 'j':\n\t\tcase 'k':\n\t\tcase 'l':\n\t\t\tresult += '5';\n\t\t\tbreak;\n\t\tcase '6':\n\t\tcase 'm':\n\t\tcase 'n':\n\t\tcase 'o':\n\t\t\tresult += '6';\n\t\t\tbreak;\n\t\tcase '7':\n\t\tcase 'p':\n\t\tcase 'q':\n\t\tcase 'r':\n\t\tcase 's':\n\t\t\tresult += '7';\n\t\t\tbreak;\n\t\tcase '8':\n\t\tcase 't':\n\t\tcase 'u':\n\t\tcase 'v':\n\t\t\tresult += '8';\n\t\t\tbreak;\n\t\tcase '9':\n\t\tcase 'w':\n\t\tcase 'x':\n\t\tcase 'y':\n\t\tcase 'z':\n\t\t\tresult += '9';\n\t\t\tbreak;\n\t\tcase '0':\n\t\tcase ' ':\n\t\t\tresult += '0';\n\t\t\tbreak;\n\t\tcase '#':\n\t\tcase '*':\n\t\t\tresult += to_convert[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\treturn result;\n}\n\nbool looks_like_phone(const string &s, const string &special_symbols) {\n\tstring phone_symbols= special_symbols + \"0123456789*#+ \\t\";\n\tstring t;\n\t\n\tfor (string::const_iterator i = s.begin(); i != s.end(); ++i) {\n\t\tif (phone_symbols.find(*i) == string::npos) return false;\n\t}\n\n\treturn true;\n}\n\nstring remove_symbols(const string &s, const string &special_symbols) {\n\tstring result;\n\t\n\tfor (string::const_iterator i = s.begin(); i != s.end(); ++i) {\n\t\tif (special_symbols.find(*i) == string::npos) {\n\t\t\tresult += *i;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nstring remove_white_space(const string &s) {\n\tstring result;\n\t\n\tfor (string::const_iterator i = s.begin(); i != s.end(); ++i) {\n\t\tif (*i != ' ' && *i != '\\t') {\n\t\t\tresult += *i;\n\t\t}\n\t}\n\t\n\treturn result;\n}\n\nstring dotted_truncate(const string &s, string::size_type len) {\n\tif (len >= s.size()) return s;\n\t\n\treturn s.substr(0, len) + \"...\";\n}\n\nstring to_printable(const string &s) {\n\tstring result;\n\t\n\tfor (string::const_iterator i = s.begin(); i != s.end(); ++i) {\n\t\tif (isprint(*i) || *i == '\\n' || *i == '\\r') {\n\t\t\tresult += *i;\n\t\t} else {\n\t\t\tresult += '.';\n\t\t}\n\t}\n\t\n\treturn result;\n}\n\nstring get_error_str(int errnum) {\n#ifdef HAVE_STRERROR_R\n\tchar buf[81];\n\tmemset(buf, 0, sizeof(buf));\n#ifdef STRERROR_R_CHAR_P\n\tstring errmsg(strerror_r(errnum, buf, sizeof(buf)-1));\n#else\n\tstring errmsg;\n\tif (strerror_r(errnum, buf, sizeof(buf)-1) == 0) {\n\t\terrmsg = buf;\n\t} else {\n\t\terrmsg = \"unknown error: \";\n\t\terrmsg += int2str(errnum);\n\t}\n#endif\n#else\n\tstring errmsg(\"strerror_r is not available: \");\n\terrmsg += int2str(errnum);\n#endif\n\treturn errmsg;\n}\n"
  },
  {
    "path": "src/util.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#ifndef _UTIL_H\n#define _UTIL_H\n\n/**\n * @file\n * Utility functions\n */\n\n#include <vector>\n#include <string>\n#include <commoncpp/config.h>\n\nusing namespace std;\n\nstring random_token(int length);\nstring random_hexstr(int length);\n\n/**\n * Convert a float to a string.\n * @param f [in] Float to convert.\n * @param precision [in] Number of digits after the decimal point in output.\n * @return String representation of the float.\n */\nstring float2str(float f, int precision);\n\n// Convert an int to a string. format is a printf format\nstring int2str(int i, const char *format);\nstring int2str(int i);\n\n// Convert an unsigned long to a string. format is a printf format\nstring ulong2str(unsigned long i, const char *format);\nstring ulong2str(unsigned long i);\n\n// Convert a pointer to a string (hexadecimal)\nstring ptr2str(void *p);\n\n// Convert a bool to a string: \"false\", \"true\"\nstring bool2str(bool b);\n\n// Convert time/date to string\n// The format parameter is a strftime() format string\nstring time2str(time_t t, const char *format);\nstring current_time2str(const char *format);\n\nstring weekday2str(int wkday);\nstring month2str(int month);\n\n// Convert a full month name to an int (0-11)\nint str2month_full(const string &month);\n\n// Convert a duration in seconds to a string with hours, minutes seconds.\n// The hours and minutes are only present if there is at least 1 hour/minute.\n// E.g. 65s -> \"1m 5s\"\n//      3601s -> \"1h 0m 1s\"\nstring duration2str(unsigned long seconds);\n\n// Convert a timer in seconds to a string (h:mm:ss)\nstring timer2str(unsigned long seconds);\n\n/** \n * Convert a hex string to an integer.\n * @param h [in] A hex string.\n * @return The integer.\n */\nunsigned long hex2int(const string &h);\n\n/**\n * Convert a hex string to a binary blob representing the hex value.\n * @param h [in] A hex string.\n * @param buf [in] A pointer to a buffer to store the binary blob.\n * @pre The buffer must be large enough to contain the binary blob.\n * @post buf contains the binary representation of the hex string.\n */\nvoid hex2binary(const string &h, uint8 *buf);\n\n/**\n * Convert a binary blob to a hexadecimal string.\n * @param buf [in] Pointer to the binary blob.\n * @param len [in] Length of the blob.\n * @return The hexadecimal string.\n */\nstring binary2hex(uint8 *buf, unsigned long len);\n\n// Convert a string to lower case\nstring tolower(const string &s);\n\n// Convert a string to upper case\nstring toupper(const string &s);\n\n// Trim a string\nstring rtrim(const string &s);\nstring ltrim(const string &s);\nstring trim(const string &s);\n\n/**\n * Pad a string on the left side till a certain length.\n * @param s [in] The string to pad.\n * @param c [in] The pad character.\n * @param len [in] The length to which the string must be padded.\n * @return The padded string.\n */\nstring padleft(const string &s, char c, unsigned long len);\n\n// Compare 2 strings case insensive, return\n// -1 --> s1 < s2\n// 0  --> s1 == s2\n// 1  --> s1 > s2\nint cmp_nocase(const string &s1, const string &s2);\n\n// Return true if a string must be quoted in text encoding\nbool must_quote(const string &s);\n\n// Escape character c in string by prepending it with a backslash.\n// Backslashed are automatically escaped as well\nstring escape(const string &s, char c);\n\n// Unescape a string\nstring unescape(const string &s);\n\n// Escape reserved chars in s by there hex-notation (%HEX)\n// All chars that are not in unreserved are considered as reserved.\nstring escape_hex(const string &s, const string &unreserved);\n\n// Unescape the hex-values in a string\nstring unescape_hex(const string &s);\n\n// Replace all occurrences of 'from' char 'to' char in s\nstring replace_char(const string &s, char from, char to);\n\n// Replace first occurrence of 'from'-string to 'to'-string in s\nstring replace_first(const string &s, const string &from, const string &to);\n\n/**\n *  Split a string into elements using a single character as separator.\n * @param s [in] The string to split.\n * @param c [in] The character separator.\n * @return Vector containing the split parts.\n */\nvector<string> split(const string &s, char c);\n\n/** \n * Split a string into elements using a string separator.\n * @param s [in] The string to split.\n * @param separator [in] The string separator.\n * @return Vector containing the split parts.\n */\nvector<string> split(const string &s, const string &separator);\n\n/**\n * Split a string into elements using line breaks as seperator\n * If the string contains a CRLF, then CRLF is used as line break.\n * Otherwise if the string contains a CR, then CR is used as line break.\n * Otherwise LF is used as line break.\n * @param s [in] The string to split.\n * @return Vector containing the split parts.\n */\nvector<string> split_linebreak(const string &s);\n\n/**\n * Split a string in two on the first occurrence of a separator.\n * @param s [in] The string to split.\n * @param c [in] The separator.\n * @return Vector containing the split parts.\n */\nvector<string> split_on_first(const string &s, char c);\n\n/**\n * Split a string in two on the last occurrence of a separator.\n * @param s [in] The string to split.\n * @param c [in] The separator.\n * @return Vector containing the split parts.\n */\nvector<string> split_on_last(const string &s, char c);\n\n// Split an escaped string into elements using c as a separator\n// Escaped means: \\c will not be seen as a seperator and backslash is\n//                escaped itself (\\\\)\nvector<string> split_escaped(const string &s, char c);\n\n// Split a string into elements using spaces as separator\n// If quote_sensitive = true, then spaces within quoted strings will\n// not be used to split the string.\nvector<string> split_ws(const string &s, bool quote_sensitive = false);\n\n/**\n * Join a vector of strings into one string.\n * @param v Vector of strings.\n * @param separator String to be inserted between the strings to join.\n * @return A string containing the concatenarion of all strings in v.\n *         The invidual strings are separated by separator.\n */\nstring join_strings(const vector<string> &v, const string &separator);\n\n// Remove surrounding quotes of a string if present.\nstring unquote(const string &s);\n\n// Check if a string is a number\nbool is_number(const string &s);\n\n// Check if a string is an IP address\nbool is_ipaddr(const string &s);\n\n// Conversion between yes/no values and bool\nbool yesno2bool(const string &yesno);\nstring bool2yesno(bool b);\n\n// Convert a text string to DTMF digits\n// Characters that cannot be converted will be removed\nstring str2dtmf(const string &s);\n\n// Return true if string s looks like a phone number\n// A string looks like a phone number if it consists of digits,\n// *, #, special symbols and white space\nbool looks_like_phone(const string &s, const string &special_symbols);\n\n/**\n * Remove all special symbols from a string.\n * @param s [in] The string to convert.\n * @param special_symbols [in] The special symbols to remove.\n * @return The string without the special symbols.\n */\nstring remove_symbols(const string &s, const string &special_symbols);\n\n/**\n * Remove spaces and tabs from a string.\n * @param s [in] The string to convert.\n * @return The string without spaces and tabs.\n */\nstring remove_white_space(const string &s);\n\n/**\n * Truncate a string. If the string was longer than the truncated\n * result, then \"...\" will be appended.\n * @param s [in] The string to truncate.\n * @param len [in] The length in bytes to truncate to.\n * @return The truncated string.\n */\nstring dotted_truncate(const string &s, string::size_type len);\n\n/**\n * Convert a string to a printable representation, i.e. change\n * all non-printable chars into dots.\n * @param s [in] The string to convert.\n * @return The converted string.\n */\nstring to_printable(const string &s);\n\n/**\n * Get the error message describing an error number.\n * @param errnum [in] The error number.\n * @return The error message.\n */\nstring get_error_str(int errnum);\n\n#endif\n"
  },
  {
    "path": "src/utils/CMakeLists.txt",
    "content": "project(libtwinkle-utils)\n\nset(LIBTWINKLE_UTILS-SRCS\n\tfile_utils.cpp\n\tmime_database.cpp\n)\n\nadd_library(libtwinkle-utils OBJECT ${LIBTWINKLE_UTILS-SRCS})\n"
  },
  {
    "path": "src/utils/file_utils.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"file_utils.h\"\n#include \"util.h\"\n\n#include <fstream>\n#include <string>\n#include <vector>\n#include <cerrno>\n#include <cstdlib>\n#include <unistd.h>\n\nusing namespace std;\nusing namespace utils;\n\nbool utils::filecopy(const string &from, const string &to) {\n\tifstream from_file(from.c_str());\n\tif (!from_file) {\n\t\treturn false;\n\t}\n\t\n\tofstream to_file(to.c_str());\n\tif (!to_file) {\n\t\treturn false;\n\t}\n\t\n\tto_file << from_file.rdbuf();\n\t\n\tif (!to_file.good() || !from_file.good()) {\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n\nstring utils::strip_path_from_filename(const string &filename) {\n\tvector<string> v = split_on_last(filename, PATH_SEPARATOR);\n\treturn v.back();\n}\n\nstring utils::get_path_from_filename(const string &filename) {\n\tvector<string> v = split_on_last(filename, PATH_SEPARATOR);\n\t\n\tif (v.size() == 1) {\n\t\t// There is no path.\n\t\treturn \"\";\n\t}\n\t\n\treturn v.front();\n}\n\nstring utils::get_extension_from_filename(const string &filename) {\n\tvector<string> v = split_on_last(filename, '.');\n\t\n\tif (v.size() == 1) {\n\t\t// There is no file extension.\n\t\treturn \"\";\n\t} else {\n\t\treturn v.back();\n\t}\n}\n\nstring utils::apply_glob_to_filename(const string &filename, const string &glob) {\n\tstring name = strip_path_from_filename(filename);\n\tstring path = get_path_from_filename(filename);\n\tstring new_name = glob;\n\t\n\tstring::size_type idx = new_name.find('*');\n\t\n\tif (idx == string::npos) \n\t{\n\t\t// The glob expression does not contain a wild card to replace.\n\t\treturn filename;\n\t}\n\t\n\tnew_name.replace(new_name.begin() + idx, new_name.begin() + idx + 1, name);\n\t\n\tstring new_filename = path;\n\tif (!new_filename.empty()) {\n\t\tnew_filename += PATH_SEPARATOR;\n\t}\n\tnew_filename += new_name;\n\t\n\treturn new_filename;\n}\n\nstring get_working_dir(void) {\n\tsize_t buf_size = 1024;\n\tchar *buf = (char*)malloc(buf_size);\n\tchar *dir = NULL;\n\t\n\twhile (true) {\n\t\tif ((dir = getcwd(buf, buf_size)) != NULL) break;\n\t\tif (errno != ERANGE) break;\n\t\t\n\t\t// The buffer is too small.\n\t\t// Avoid eternal looping.\n\t\tif (buf_size > 8192) break;\n\t\t\n\t\t// Increase the buffer size\n\t\tfree(buf);\n\t\tbuf_size *= 2;\n\t\tbuf = (char*)malloc(buf_size);\n\t}\n\t\n\tstring result;\n\tif (dir) result = dir;\n\t\n\tfree(buf);\n\t\n\treturn result;\n}\n"
  },
  {
    "path": "src/utils/file_utils.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * File utilities\n */\n\n#ifndef _FILE_UTILS_H\n#define _FILE_UTILS_H\n\n#include <string>\n\nusing namespace std;\n\nnamespace utils {\n \n/** Separator to split parts in a file path. */\n#define PATH_SEPARATOR\t'/'\n\n/**\n * Copy a file.\n * @param from [in] Absolute path of file to copy.\n * @param to [in] Absolute path of destination file.\n * @return true if copy succeeded, otherwise false.\n */\nbool filecopy(const string &from, const string &to);\n\n/**\n * Strip the path to a file from an absolute file name.\n * @return The remaining file name without the full path.\n */\nstring strip_path_from_filename(const string &filename);\n\n/**\n * Get path to a file from an absolute file name.\n * @return The path name.\n */\nstring get_path_from_filename(const string &filename);\n\n/**\n * Get the extension from a file name.\n * @return The extension (without the initial dot).\n * @return Empty string if the file name does not have an extension.\n */\nstring get_extension_from_filename(const string &filename);\n\n/**\n * Apply a glob expression to a filename.\n * E.g. /tmp/twinkle with glob *.txt gives /tmp/twinkle.txt\n *      /tmp/twinkle with README* give /tmp/READMEtwinkle.txt\n * @param filename [in] The filename.\n * @param glob [in] The glob expression to apply.\n * @return The modified filename.\n */\nstring apply_glob_to_filename(const string &filename, const string &glob);\n\n/**\n * Get the absolute path of the current working directory.\n * @return The absolute path of the current working directory.\n * @return Empty string if the current working directory cannot be determined.\n */\nstring get_working_dir(void);\n\n}; // namespace\n\n#endif\n"
  },
  {
    "path": "src/utils/mime_database.cpp",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#include \"mime_database.h\"\n\n#include <cassert>\n\n#include \"log.h\"\n#include \"sys_settings.h\"\n\nusing namespace utils;\n\n//////////////////////////\n// class t_mime_db_record\n//////////////////////////\n\nbool t_mime_db_record::create_file_record(vector<string> &v) const {\n\t// The mime database is read only. So this function should\n\t// never be called.\n\tassert(false);\n\treturn false;\n}\n\nbool t_mime_db_record::populate_from_file_record(const vector<string> &v) {\n\t// Check number of fields\n\tif (v.size() != 2) return false;\n\t\n\tmimetype = v[0];\n\tfile_glob = v[1];\n\t\n\treturn true;\n}\n\n//////////////////////////\n// class t_mime_database\n//////////////////////////\n\nt_mime_database::t_mime_database() {\n\tset_separator(':');\n\tset_filename(sys_config->get_mime_shared_database());\n\n\tmime_magic_ = magic_open(MAGIC_MIME | MAGIC_ERROR);\n\tif (mime_magic_ == (magic_t)NULL) {\n\t\tlog_file->write_report(\"Failed to open magic number database\", \n\t\t\t\"t_mime_database::t_mime_database\", LOG_NORMAL, LOG_WARNING);\n\t\t\t\n\t\treturn;\n\t}\n\t\n\tmagic_load(mime_magic_, NULL);\n}\n\nt_mime_database::~t_mime_database() {\n\tmagic_close(mime_magic_);\n}\n\nvoid t_mime_database::add_record(const t_mime_db_record &record) {\n\tmap_mime2glob_.insert(make_pair(record.mimetype, record.file_glob));\n}\n\nstring t_mime_database::get_glob(const string &mimetype) const {\n\tmap<string, string>::const_iterator it = map_mime2glob_.find(mimetype);\n\t\n\tif (it != map_mime2glob_.end()) {\n\t\treturn it->second;\n\t}\n\t\n\treturn \"\";\n}\n\nstring t_mime_database::get_mimetype(const string &filename) const {\n\tconst char *mime_desc = magic_file(mime_magic_, filename.c_str());\n\t\n\tif (!mime_desc) return \"\";\n\t\n\t// Sometimes the magic libary adds additional info to the\n\t// returned mime type. Strip this info.\n\tstring mime_type(mime_desc);\n\tstring::size_type end_of_mime = mime_type.find_first_not_of(\n\t\t\t\"abcdefghijklmnopqrstuvwxyz\"\n\t\t\t\"ABCDEFGHIJKLMNOPQSTUVWXYZ\"\n\t\t\t\"0123456789-.!%*_+`'~/\");\n\t\n\tif (end_of_mime != string::npos) {\n\t\tmime_type = mime_type.substr(0, end_of_mime);\n\t}\n\t\n\treturn mime_type;\n}\n"
  },
  {
    "path": "src/utils/mime_database.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * Mime database\n * Conversion between mime types, file content and file extensions.\n */\n\n#ifndef _MIME_DATABASE_H\n#define _MIME_DATABASE_H\n\n#include <map>\n#include <string>\n#include <magic.h>\n\n#include \"record_file.h\"\n\nusing namespace std;\n\nnamespace utils {\n\n/**  Record from the mime database. */\nclass t_mime_db_record : public utils::t_record {\npublic:\n\tstring\tmimetype;\t/**< Mimetype, e.g. text/plain */\n\tstring\tfile_glob;\t/**< File glob expression */\n\t\n\tvirtual bool create_file_record(vector<string> &v) const;\n\tvirtual bool populate_from_file_record(const vector<string> &v);\n};\n\n/**\n * The mime database.\n * The default location for the mime database is /usr/share/mime/globs\n */\nclass t_mime_database : public utils::t_record_file<t_mime_db_record> {\nprivate:\n\t/** Mapping between mimetypes and file globs. */\n\tmap<string, string>\tmap_mime2glob_;\n\t\n\t/** Handle on the magic number database. */\n\tmagic_t mime_magic_;\n\t\nprotected:\n\tvirtual void add_record(const t_mime_db_record &record);\n\t\npublic:\n\t/** Constructor */\n\tt_mime_database();\n\t\n\t/** Destructor */\n\t~t_mime_database();\n\t\n\t/**\n\t * Get a glob expression for a mimetype.\n\t * @param mimetype [in] The mimetype.\n\t * @return Glob expression associated with the mimetype. Empty string\n\t *         if no glob expression can be found.\n\t */\n\tstring get_glob(const string &mimetype) const;\n\t\n\t/**\n\t * Get the mimetype of a file.\n\t * @param filename [in] Name of the file.\n\t * @return The mimetype or empty string if no mimetype can be determined.\n\t */\n\tstring get_mimetype(const string &filename) const;\n};\n\n}; // namespace\n\nextern utils::t_mime_database *mime_database;\n\n#endif\n"
  },
  {
    "path": "src/utils/record_file.h",
    "content": "/*\n    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * File to store data records.\n */\n \n#ifndef _RECORD_FILE_H\n#define _RECORD_FILE_H\n\n#include <list>\n#include <string>\n#include <vector>\n#include <cstdlib>\n#include <sys/stat.h>\n#include <iostream>\n#include <fstream>\n\n#include \"translator.h\"\n#include \"util.h\"\n#include \"threads/mutex.h\"\n\n\nusing namespace std;\n\nnamespace utils {\n\n/** A single record in a file. */\nclass t_record {\npublic:\n\tvirtual ~t_record() {};\n\t\n\t/**\n\t * Create a record to write to a file.\n\t * @param v [out] Vector of fields of record.\n\t * @return true, if record is successfully created.\n\t * @return false, otherwise.\n\t */\n\tvirtual bool create_file_record(vector<string> &v) const = 0;\n\t\n\t/**\n\t * Populate from a file record.\n\t * @param v [in] Vector containing the fields of the record.\n\t * @return true, if record is successfully populated.\n\t * @return false, if file record could not be parsed.\n\t */\n\tvirtual bool populate_from_file_record(const vector<string> &v) = 0;\t\n};\n\n/** \n * A file containing records with a fixed number of fields. \n * @param R Subclass of @ref t_record\n */\ntemplate< class R >\nclass t_record_file {\nprivate:\n\t/** Separator to separate fields in a file record. */\n\tchar\tfield_separator;\n\t\n\t/** Header string to write as comment at start of file. */\n\tstring\theader;\n\t\n\t/** Name of the file containing the records. */\n\tstring\tfilename;\n\t\n\t/**\n\t * Split a record into separate fields.\n\t * @param record [in] A complete record.\n\t * @param v [out] Vector of fields.\n\t */\n\tvoid split_record(const string &record, vector<string> &v) const;\n\t\n\t/**\n\t * Join fields of a record into a string.\n\t * Separator and comment symbols will be escaped.\n\t * @param v [in] Vector of fields.\n\t * @return Joined fields.\n\t */\n\tstring join_fields(const vector<string> &v) const;\n\t\nprotected:\n\t/** Mutex to protect concurrent access/ */\n\tmutable t_recursive_mutex\tmtx_records;\n\t\n\t/** Records in the file. */\n\tlist<R> records;\n\t\n\t/**\n\t * Add a record to the file.\n\t * @param record [in] Record to add.\n\t */\n\tvirtual void add_record(const R &record);\n\npublic:\n\t/** Constructor. */\n\tt_record_file();\n\t\n\t/** Constructor. */\n\tt_record_file(const string &_header, char _field_separator, const string &_filename);\n\t\n\t/** Destructor. */\n\tvirtual ~t_record_file() {};\n\t\n\t/** @name Setters */\n\t//@{\n\tvoid set_header(const string &_header);\n\tvoid set_separator(char _separator);\n\tvoid set_filename(const string &_filename);\n\t//@}\n\t\n\t/** @name Getters */\n\t//@{\n\tlist<R> *get_records();\n\t//@}\n\n\t/** \n\t * Load records from file. \n\t * @param error_msg [out] Error message on failure return.\n\t * @return true, if file was read successfully.\n\t * @return false, if it fails. error_msg is an error to be given to\n\t * the user.\n\t */\n\tvirtual bool load(string &error_msg);\n\t\n\t/** \n\t * Save records to file.\n\t * @param error_msg [out] Error message on failure return.\n\t * @return true, if file was saved successfully.\n\t * @return false, if it fails. error_msg is an error to be given to\n\t * the user.\n\t */\n\tvirtual bool save(string &error_msg) const;\n\t\n\ttypedef typename list<R>::const_iterator record_const_iterator;\n\ttypedef typename list<R>::iterator record_iterator;\n};\n\n#include \"record_file.hpp\"\n\n}; // namespace\n\n#endif\n"
  },
  {
    "path": "src/utils/record_file.hpp",
    "content": "/*\n    Copyright (C) 2005-2007  Michel de Boer <michel@twinklephone.com>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#define COMMENT_SYMBOL\t'#'\n\ntemplate< class R >\nvoid t_record_file<R>::split_record(const string &record, vector<string> &v) const {\n\tv = split_escaped(record, field_separator);\n\t\n\tfor (vector<string>::iterator it = v.begin(); it != v.end(); ++it) {\n\t\t*it = unescape(*it);\n\t}\n}\n\ntemplate< class R >\nstring t_record_file<R>::join_fields(const vector<string> &v) const {\n\tstring s;\n\t\n\tfor (vector<string>::const_iterator it = v.begin(); it != v.end(); ++it) {\n\t\tif (it != v.begin()) s += field_separator;\n\t\t\n\t\t// Escape comment symbol.\n\t\tif (!it->empty() && it->at(0) == COMMENT_SYMBOL) s += '\\\\';\n\t\t\n\t\ts += escape(*it, field_separator);\n\t}\n\t\n\treturn s;\n}\n\ntemplate< class R >\nvoid t_record_file<R>::add_record(const R &record) {\n\trecords.push_back(record);\n}\n\ntemplate< class R >\nt_record_file<R>::t_record_file(): field_separator('|')\n{}\n\ntemplate< class R >\nt_record_file<R>::t_record_file(const string &_header, char _field_separator, \n\t\tconst string &_filename) :\n\theader(_header),\n\tfield_separator(_field_separator),\n\tfilename(_filename)\n{}\n\ntemplate< class R >\nvoid t_record_file<R>::set_header(const string &_header) {\n\theader = _header;\n}\n\ntemplate< class R >\nvoid t_record_file<R>::set_separator(char _separator) {\n\tfield_separator = _separator;\n}\n\ntemplate< class R >\nvoid t_record_file<R>::set_filename(const string &_filename) {\n\tfilename = _filename;\n}\n\ntemplate< class R >\nlist<R> *t_record_file<R>::get_records() {\n\treturn &records;\n}\n\ntemplate< class R >\nbool t_record_file<R>::load(string &error_msg) {\n\tstruct stat stat_buf;\n\t\n\tmtx_records.lock();\n\t\n\trecords.clear();\n\t\n\t// Check if file exists\n\tif (filename.empty() || stat(filename.c_str(), &stat_buf) != 0) {\n\t\t// There is no file.\n\t\tmtx_records.unlock();\n\t\treturn true;\n\t}\n\t\n\t// Open call ile\n\tifstream file(filename.c_str());\n\tif (!file) {\n\t\terror_msg = TRANSLATE(\"Cannot open file for reading: %1\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_records.unlock();\n\t\treturn false;\n\t}\n\t\n\t// Read and parse history file.\n\twhile (!file.eof()) {\n\t\tstring line;\n\t\t\n\t\tgetline(file, line);\n\n\t\t// Check if read operation succeeded\n\t\tif (!file.good() && !file.eof()) {\n\t\t\terror_msg = TRANSLATE(\"File system error while reading file %1 .\");\n\t\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\t\tmtx_records.unlock();\n\t\t\treturn false;\n\t\t}\n\n\t\tline = trim(line);\n\n\t\t// Skip empty lines\n\t\tif (line.size() == 0) continue;\n\n\t\t// Skip comment lines\n\t\tif (line[0] == COMMENT_SYMBOL) continue;\n\t\t\n\t\t// Add record. Skip records that cannot be parsed.\n\t\tR record;\n\t\tvector<string> v;\n\t\tsplit_record(line, v);\n\t\tif (record.populate_from_file_record(v)) {\n\t\t\tadd_record(record);\n\t\t}\n\t}\n\t\n\tmtx_records.unlock();\n\treturn true;\n}\n\ntemplate< class R >\nbool t_record_file<R>::save(string &error_msg) const {\t\n\tif (filename.empty()) return false;\n\t\n\tmtx_records.lock();\n\t\n\t// Open file\n\tofstream file(filename.c_str());\n\tif (!file) {\n\t\terror_msg = TRANSLATE(\"Cannot open file for writing: %1\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\tmtx_records.unlock();\n\t\treturn false;\n\t}\n\t\n\t// Write file header\n\tfile << \"# \" << header << endl;\n\t      \n\t// Write records\n\tfor (record_const_iterator i = records.begin(); i != records.end(); ++i) {\n\t\tvector<string> v;\n\t\tif (i->create_file_record(v)) {\n\t\t\tfile << join_fields(v);\n\t\t\tfile << endl;\n\t\t}\n\t}\n\t\n\tmtx_records.unlock();\n\t \n\tif (!file.good()) {\n\t\terror_msg = TRANSLATE(\"File system error while writing file %1 .\");\n\t\terror_msg = replace_first(error_msg, \"%1\", filename);\n\t\treturn false;\n\t}\n\t\n\treturn true;\n}\n"
  },
  {
    "path": "twinkle.desktop.in",
    "content": "[Desktop Entry]\nName=Twinkle\nGenericName=A SIP softphone\nGenericName[cy]=Ffôn meddal SIP\nGenericName[da]=SIP IP-telefoni\nGenericName[el]=Λογισμικό τηλέφωνο SIP\nGenericName[es]=Un teléfono liviano SIP\nGenericName[et]=SIP-arvutitelefon\nGenericName[eu]=SIP softfono bat\nGenericName[fi]=SIP-ohjelmistopuhelin\nGenericName[fr]=Téléphone logiciel SIP\nGenericName[gl]=Un teléfono SIP por software\nGenericName[hu]=Szoftveres telefon (SIP)\nGenericName[it]=Telefono VoIP SIP\nGenericName[ja]=SIP ビデオ電話\nGenericName[nb]=SIP IP-telefoni\nGenericName[nl]=Een SIP-softphone\nGenericName[nn]=SIP IP-telefoni\nGenericName[pl]=Telefon SIP\nGenericName[pt]=Um softphone SIP\nGenericName[pt_BR]=Um softphone SIP\nGenericName[ro]=Un program de telefonie SIP\nGenericName[ru]=Программный телефон SIP\nGenericName[sl]=Programski telefon za SIP\nGenericName[sv]=En SIP Videotelefon\nGenericName[tr]=Bir SIP telefon\nGenericName[uk]=Програмний телефон SIP\nComment=Voice over Internet Protocol (VoIP) SIP Phone\nComment[cs]=VoIP (Voice over Internet Protocol) telefon SIP\nComment[da]=Voice over Internet Protocol (VoIP) SIP-telefon\nComment[de]=VoIP-SIP-Softwaretelefon\nComment[ko]=Voice over Internet Protocol (VoIP) SIP 폰\nComment[it]=Telefono SIP VoIP (Voice over Internet Protocol)\nComment[ja]=Voice over Internet Protocol (VoIP) SIP 電話\nComment[pl]=Aplikacja telefoniczna do VoIP (Voice over Internet Protocol), wykorzystująca SIP\nComment[pt]=Telefone SIP Voz sobre IP (Voice over Internet Protocol - VoIP)\nComment[pt_BR]=Telefone SIP de voz sobre protocolo de internet (VoIP)\nComment[ru]=VoIP (передача голоса по IP-протоколу) SIP-телефон\nComment[uk]=VoIP (передача голосу по IP-протоколу) SIP-телефон\nType=Application\nExec=twinkle-uri-handler %u\nMimeType=x-scheme-handler/sip;x-scheme-handler/sips;x-scheme-handler/tel;x-scheme-handler/callto;\nIcon=twinkle\nStartupNotify=true\nTerminal=false\nCategories=Qt;Network;Telephony;\n"
  },
  {
    "path": "twinkle.spec.in",
    "content": "Summary:\tA SIP Soft Phone\nName:\t\ttwinkle\nVersion:\t@VERSION@\nRelease:\t%{suserel}1\nLicense:\tGPL\nGroup:\t\tProductivity/Telephony/SIP/Clients\nSource:\t\t%{name}-%{version}.tar.gz\nPrefix:\t\t%{_prefix}\nBuildArch:\ti586\n#BuildArch:\tx86_64\nBuildRoot:\t%{_tmppath}/making_of_%{name}_%{version}\nPackager:\nURL:\t\thttps://mfnboer.home.xs4all.nl/twinkle/\nRequires:\talsa\nRequires:\tucommon\nRequires:\tccrtp >= 1.6.0\nRequires:\tlibzrtpcpp >= 1.3.0\nRequires:\tkdelibs3 >= 3.2.0\nRequires:\tlibsndfile\nRequires:\tlibspeex >= 1.1.99\nRequires:\tlibxml2\nRequires:\tfile\nRequires:\treadline\nBuildRequires:\talsa-devel\nBuildRequires:\tqt3-devel\nBuildRequires:\tupdate-desktop-files\nBuildRequires:  ucommon-devel\nBuildRequires:  libccrtp-devel\nBuildRequires:  libzrtpcpp-devel\nBuildRequires:  kdelibs3-devel\nBuildRequires:\tlibsndfile-devel\nBuildRequires:\tspeex-devel\nBuildRequires:\tboost-devel\nBuildRequires:\tlibxml2-devel\nBuildRequires:\tfile-devel\nBuildRequires:\treadline-devel\n\n%description\nTwinkle is a SIP based softphone for making telephone calls\nand instant messaging over IP networks.\n\n%prep\n\n%setup -q\n\n%build\nautoreconf -fi\n%configure --without-ilbc\n#%configure --without-ilbc --enable-libsuffix=64\n#sed -i -e \"s|(INCPATH *= \\)|\\1-I/opt/kde3/include |\" src/gui/Makefile\nmake\n\n%install\nrm -rf %{buildroot}\n%makeinstall\ninstall -d 755 %{buildroot}%{_datadir}/pixmaps\ninstall -m 644 src/gui/images/twinkle48.png %{buildroot}%{_datadir}/pixmaps/twinkle.png\n%suse_update_desktop_file -c twinkle Twinkle \"A SIP softphone\" twinkle twinkle Network Telephony\n\n%clean\nrm -rf %{buildroot}\n\n%files\n%defattr(-, root, root)\n%doc AUTHORS COPYING README ChangeLog\n%{_bindir}/%{name}\n%{_datadir}/%{name}\n%{_datadir}/pixmaps/twinkle.png\n%{_datadir}/applications/twinkle.desktop\n\n"
  },
  {
    "path": "twinkle_config.h.in",
    "content": "#cmakedefine WITH_DIAMONDCARD\n#cmakedefine HAVE_SPEEX\n#cmakedefine HAVE_ILBC\n#cmakedefine HAVE_ILBC_CPP\n#cmakedefine HAVE_ZRTP\n#cmakedefine HAVE_BCG729\n#cmakedefine HAVE_BCG729_ANNEX_B\n#cmakedefine HAVE_GSM\n\n#cmakedefine HAVE_UNISTD_H\n#cmakedefine HAVE_LINUX_TYPES_H\n#cmakedefine HAVE_LINUX_ERRQUEUE_H\n#cmakedefine HAVE_STRERROR_R\n#cmakedefine STRERROR_R_CHAR_P\n#cmakedefine HAVE_RES_INIT\n#cmakedefine WORDS_BIGENDIAN\n#cmakedefine HAVE_LIBASOUND\n#cmakedefine HAVE_DBUS\n#cmakedefine HAVE_AKONADI\n\n#define VERSION\t\t\"${PRODUCT_VERSION}\"\n#define VERSION_DATE\t\"${PRODUCT_DATE}\"\n\n#define DATADIR\t\t\"${datadir}\"\n"
  }
]