pax_global_header00006660000000000000000000000064131343273760014523gustar00rootroot0000000000000052 comment=74fd021a47989e543e217c9f00c5a3b7bdd7e03b linphone-3.12.0/000077500000000000000000000000001313432737600134225ustar00rootroot00000000000000linphone-3.12.0/.clang-format000066400000000000000000000036721313432737600160050ustar00rootroot00000000000000--- # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IndentCaseLabels: false IndentFunctionDeclarationAfterType: false IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true Language: Cpp MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 4 UseTab: Always ... linphone-3.12.0/.cproject000066400000000000000000000274001313432737600152370ustar00rootroot00000000000000 make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" all true true true make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" true true true make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" all true true true make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" all true true true linphone-3.12.0/.git-pre-commit000077500000000000000000000043501313432737600162650ustar00rootroot00000000000000#!/bin/bash # This hook purpose is to keep coding style consistent between all developers # It is automatically installed in .git/hooks folder by cmake on first run. # From https://github.com/tatsuhiro-t/nghttp2/blob/master/pre-commit function invalid-format-detected { cat git-clang-format.diff echo "*****************" echo "$0: Invalid coding style detected (see git-clang-format.diff for issues). Please correct it using one of the following:" echo "1) Apply patch located at git-clang-format.diff using:" echo " cd $(git rev-parse --show-toplevel) && $1" echo "2) Use clang-format to correctly format source code using:" echo " $2" echo "3) Reformat these lines manually." echo "*** Aborting commit.***" exit 1 } function git-clang-format-diffing { format_diff=$(which git-clang-format) format_diff_options="--style=file" #only diffing commited files, ignored staged one $format_diff $format_diff_options --diff $(git --no-pager diff --cached --name-status | grep -v '^D' | cut -f2) > git-clang-format.diff if ! grep -q -E '(no modified files to format|clang-format did not modify any files)' git-clang-format.diff; then invalid-format-detected "git apply git-clang-format.diff" "clang-format $format_diff_options -i " fi } function clang-format-diff-diffing { format_diff=$(find /usr/bin/ -name 'clang-format-diff*' -type f | tail -n1) format_diff_options="-style file" git diff-index --cached --diff-filter=ACMR -p HEAD -- | $format_diff $format_diff_options -p1 > git-clang-format.diff if [ -s git-clang-format.diff ]; then invalid-format-detected "patch -p0 < git-clang-format.diff" "${format_diff/-diff/} $format_diff_options -i " fi } set -e if which git-clang-format &>/dev/null; then git-clang-format-diffing $@ elif [ ! -z "$(find /usr/bin/ /usr/local/bin/ /opt/bin/ -name 'clang-format-diff*' -type f 2>/dev/null)" ]; then # Warning! We need at least version 1.6... clang-format-diff-diffing $@ else echo "$0: Please install clang-format (coding style checker) - could not find git-clang-format nor clang-format-diff in PATH. Skipping code verification..." exit 0 fi linphone-3.12.0/.gitignore000066400000000000000000000032661313432737600154210ustar00rootroot00000000000000Makefile Makefile.in aclocal.m4 autom4te.cache compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp install-sh intltool-extract intltool-merge intltool-update libtool ltmain.sh missing mkinstalldirs speex stamp-h1 linphone.spec linphone.iss msx264.iss *.o *.exe *.zip *~ linphone-install buddylookup-install msx264-install *.tar.gz intltool-extract.in intltool-merge.in intltool-update.in INSTALL Specfile .anjuta/ .anjuta_sym_db.db gtk-glade/version_date.h share/linphone.desktop share/audio-assistant.desktop Debug/ build/macos/Info-linphone.plist coreapi/help/Doxyfile coreapi/help/buddy_status coreapi/help/chatroom coreapi/help/helloworld coreapi/help/registration coreapi/help/realtimetext_receiver coreapi/help/realtimetext_sender coreapi/test_ecc coreapi/test_lsd gtk/version_date.h daemon/linphone-daemon daemon/linphone-daemon-pipetest *.la *.lo *.deps *.libs share/certdata.txt coreapi/test_numbers specs.c *.orig *.rej *.kdev4 *.swp .deps .libs tools/test_ecc tools/test_lsd tools/test_numbers coreapi/help/notify share/fresh-rootca.pem tester/liblinphone_tester tools/lp-gen-wrappers tools/lpc2xml_test tools/xml2lpc_test coreapi/help/filetransfer tester/receive_file.dump tester/tmp.db .DS_Store Linphone.app *.dmg tester/linphone*.log tester/linphone_log.txt .tx/linphone-gtk.linphonedesktopin/ po/linphone.pot .tx/linphone-gtk.audio-assistantdesktopin/ tester/linphone_log.gz.txt tools/auto_answer tools/lp-autoanswer build/macos/pkg-distribution.xml record_for_lc_*.wav tester/record-call_with_file_player.wav tester/ZIDCache*.xml tester/stereo-record.wav .dirstamp git-clang-format.diff *.log .bc_tester_utils.tmp tools/lp-test-ecc tools/lp-sendmsg *.pyc linphone-3.12.0/.project000066400000000000000000000044651313432737600151020ustar00rootroot00000000000000 linphone org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments CFLAGS="-g -Werror -Wall" CXXFLAGS="-g" V=1 org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd false org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.core.cnature org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature linphone-3.12.0/.tx/000077500000000000000000000000001313432737600141335ustar00rootroot00000000000000linphone-3.12.0/.tx/config000066400000000000000000000006141313432737600153240ustar00rootroot00000000000000[main] host = https://www.transifex.com minimum_perc = 1 [linphone-gtk.linphonepot] file_filter = po/.po source_file = po/linphone.pot source_lang = en type = PO [linphone-gtk.linphonedesktopin] source_file = share/linphone.desktop.in source_lang = en type = DESKTOP [linphone-gtk.audio-assistantdesktopin] source_file = share/audio-assistant.desktop.in source_lang = en type = DESKTOP linphone-3.12.0/ABOUT-NLS000066400000000000000000002333401313432737600146560ustar00rootroot000000000000001 Notes on the Free Translation Project *************************************** Free software is going international! The Free Translation Project is a way to get maintainers of free software, translators, and users all together, so that free software will gradually become able to speak many languages. A few packages already provide translations for their messages. If you found this `ABOUT-NLS' file inside a distribution, you may assume that the distributed package does use GNU `gettext' internally, itself available at your nearest GNU archive site. But you do _not_ need to install GNU `gettext' prior to configuring, installing or using this package with messages translated. Installers will find here some useful hints. These notes also explain how users should proceed for getting the programs to use the available translations. They tell how people wanting to contribute and work on translations can contact the appropriate team. When reporting bugs in the `intl/' directory or bugs which may be related to internationalization, you should tell about the version of `gettext' which is used. The information can be found in the `intl/VERSION' file, in internationalized packages. 1.1 Quick configuration advice ============================== If you want to exploit the full power of internationalization, you should configure it using ./configure --with-included-gettext to force usage of internationalizing routines provided within this package, despite the existence of internationalizing capabilities in the operating system where this package is being installed. So far, only the `gettext' implementation in the GNU C library version 2 provides as many features (such as locale alias, message inheritance, automatic charset conversion or plural form handling) as the implementation here. It is also not possible to offer this additional functionality on top of a `catgets' implementation. Future versions of GNU `gettext' will very likely convey even more functionality. So it might be a good idea to change to GNU `gettext' as soon as possible. So you need _not_ provide this option if you are using GNU libc 2 or you have installed a recent copy of the GNU gettext package with the included `libintl'. 1.2 INSTALL Matters =================== Some packages are "localizable" when properly installed; the programs they contain can be made to speak your own native language. Most such packages use GNU `gettext'. Other packages have their own ways to internationalization, predating GNU `gettext'. By default, this package will be installed to allow translation of messages. It will automatically detect whether the system already provides the GNU `gettext' functions. If not, the included GNU `gettext' library will be used. This library is wholly contained within this package, usually in the `intl/' subdirectory, so prior installation of the GNU `gettext' package is _not_ required. Installers may use special options at configuration time for changing the default behaviour. The commands: ./configure --with-included-gettext ./configure --disable-nls will, respectively, bypass any pre-existing `gettext' to use the internationalizing routines provided within this package, or else, _totally_ disable translation of messages. When you already have GNU `gettext' installed on your system and run configure without an option for your new package, `configure' will probably detect the previously built and installed `libintl.a' file and will decide to use this. This might not be desirable. You should use the more recent version of the GNU `gettext' library. I.e. if the file `intl/VERSION' shows that the library which comes with this package is more recent, you should use ./configure --with-included-gettext to prevent auto-detection. The configuration process will not test for the `catgets' function and therefore it will not be used. The reason is that even an emulation of `gettext' on top of `catgets' could not provide all the extensions of the GNU `gettext' library. Internationalized packages usually have many `po/LL.po' files, where LL gives an ISO 639 two-letter code identifying the language. Unless translations have been forbidden at `configure' time by using the `--disable-nls' switch, all available translations are installed together with the package. However, the environment variable `LINGUAS' may be set, prior to configuration, to limit the installed set. `LINGUAS' should then contain a space separated list of two-letter codes, stating which languages are allowed. 1.3 Using This Package ====================== As a user, if your language has been installed for this package, you only have to set the `LANG' environment variable to the appropriate `LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, and `CC' is an ISO 3166 two-letter country code. For example, let's suppose that you speak German and live in Germany. At the shell prompt, merely execute `setenv LANG de_DE' (in `csh'), `export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). This can be done from your `.login' or `.profile' file, once and for all. You might think that the country code specification is redundant. But in fact, some languages have dialects in different countries. For example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The country code serves to distinguish the dialects. The locale naming convention of `LL_CC', with `LL' denoting the language and `CC' denoting the country, is the one use on systems based on GNU libc. On other systems, some variations of this scheme are used, such as `LL' or `LL_CC.ENCODING'. You can get the list of locales supported by your system for your language by running the command `locale -a | grep '^LL''. Not all programs have translations for all languages. By default, an English message is shown in place of a nonexistent translation. If you understand other languages, you can set up a priority list of languages. This is done through a different environment variable, called `LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' for the purpose of message handling, but you still need to have `LANG' set to the primary language; this is required by other parts of the system libraries. For example, some Swedish users who would rather read translations in German than English for when Swedish is not available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. Special advice for Norwegian users: The language code for Norwegian bokma*l changed from `no' to `nb' recently (in 2003). During the transition period, while some message catalogs for this language are installed under `nb' and some older ones under `no', it's recommended for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and older translations are used. In the `LANGUAGE' environment variable, but not in the `LANG' environment variable, `LL_CC' combinations can be abbreviated as `LL' to denote the language's main dialect. For example, `de' is equivalent to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' (Portuguese as spoken in Portugal) in this context. 1.4 Translating Teams ===================== For the Free Translation Project to be a success, we need interested people who like their own language and write it well, and who are also able to synergize with other translators speaking the same language. Each translation team has its own mailing list. The up-to-date list of teams can be found at the Free Translation Project's homepage, `http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams" area. If you'd like to volunteer to _work_ at translating messages, you should become a member of the translating team for your own language. The subscribing address is _not_ the same as the list itself, it has `-request' appended. For example, speakers of Swedish can send a message to `sv-request@li.org', having this message body: subscribe Keep in mind that team members are expected to participate _actively_ in translations, or at solving translational difficulties, rather than merely lurking around. If your team does not exist yet and you want to start one, or if you are unsure about what to do or how to get started, please write to `translation@iro.umontreal.ca' to reach the coordinator for all translator teams. The English team is special. It works at improving and uniformizing the terminology in use. Proven linguistic skills are praised more than programming skills, here. 1.5 Available Packages ====================== Languages are not equally supported in all packages. The following matrix shows the current state of internationalization, as of October 2006. The matrix shows, in regard of each package, for which languages PO files have been submitted to translation coordination, with a translation percentage of at least 50%. Ready PO files af am ar az be bg bs ca cs cy da de el en en_GB eo +----------------------------------------------------+ GNUnet | [] | a2ps | [] [] [] [] [] | aegis | () | ant-phone | () | anubis | [] | ap-utils | | aspell | [] [] [] [] [] | bash | [] [] [] | batchelor | [] | bfd | | bibshelf | [] | binutils | [] | bison | [] [] | bison-runtime | | bluez-pin | [] [] [] [] [] | cflow | [] | clisp | [] [] | console-tools | [] [] | coreutils | [] [] [] | cpio | | cpplib | [] [] [] | cryptonit | [] | darkstat | [] () [] | dialog | [] [] [] [] [] [] | diffutils | [] [] [] [] [] [] | doodle | [] | e2fsprogs | [] [] | enscript | [] [] [] [] | error | [] [] [] [] | fetchmail | [] [] () [] | fileutils | [] [] | findutils | [] [] [] | flex | [] [] [] | fslint | [] | gas | | gawk | [] [] [] | gbiff | [] | gcal | [] | gcc | [] | gettext-examples | [] [] [] [] [] | gettext-runtime | [] [] [] [] [] | gettext-tools | [] [] | gimp-print | [] [] [] [] | gip | [] | gliv | [] | glunarclock | [] | gmult | [] [] | gnubiff | () | gnucash | () () [] | gnucash-glossary | [] () | gnuedu | | gnulib | [] [] [] [] [] [] | gnunet-gtk | | gnutls | | gpe-aerial | [] [] | gpe-beam | [] [] | gpe-calendar | | gpe-clock | [] [] | gpe-conf | [] [] | gpe-contacts | | gpe-edit | [] | gpe-filemanager | | gpe-go | [] | gpe-login | [] [] | gpe-ownerinfo | [] [] | gpe-package | | gpe-sketchbook | [] [] | gpe-su | [] [] | gpe-taskmanager | [] [] | gpe-timesheet | [] | gpe-today | [] [] | gpe-todo | | gphoto2 | [] [] [] [] | gprof | [] [] | gpsdrive | () () | gramadoir | [] [] | grep | [] [] [] [] [] [] | gretl | | gsasl | | gss | | gst-plugins | [] [] [] [] | gst-plugins-base | [] [] [] | gst-plugins-good | [] [] [] [] [] [] [] | gstreamer | [] [] [] [] [] [] [] | gtick | () | gtkam | [] [] [] | gtkorphan | [] [] | gtkspell | [] [] [] [] | gutenprint | [] | hello | [] [] [] [] [] | id-utils | [] [] | impost | | indent | [] [] [] | iso_3166 | [] [] | iso_3166_2 | | iso_4217 | [] | iso_639 | [] [] | jpilot | [] | jtag | | jwhois | | kbd | [] [] [] [] | keytouch | | keytouch-editor | | keytouch-keyboa... | | latrine | () | ld | [] | leafpad | [] [] [] [] [] | libc | [] [] [] [] [] | libexif | [] | libextractor | [] | libgpewidget | [] [] [] | libgpg-error | [] | libgphoto2 | [] [] | libgphoto2_port | [] [] | libgsasl | | libiconv | [] [] | libidn | [] [] | lifelines | [] () | lilypond | [] | lingoteach | | lynx | [] [] [] [] | m4 | [] [] [] [] | mailutils | [] | make | [] [] | man-db | [] () [] [] | minicom | [] [] [] | mysecretdiary | [] [] | nano | [] [] [] | nano_1_0 | [] () [] [] | opcodes | [] | parted | | pilot-qof | [] | psmisc | [] | pwdutils | | python | | qof | | radius | [] | recode | [] [] [] [] [] [] | rpm | [] [] | screem | | scrollkeeper | [] [] [] [] [] [] [] [] | sed | [] [] [] | sh-utils | [] [] | shared-mime-info | [] [] [] [] | sharutils | [] [] [] [] [] [] | shishi | | silky | | skencil | [] () | sketch | [] () | solfege | | soundtracker | [] [] | sp | [] | stardict | [] | system-tools-ba... | [] [] [] [] [] [] [] [] [] | tar | [] | texinfo | [] [] [] | textutils | [] [] [] | tin | () () | tp-robot | [] | tuxpaint | [] [] [] [] [] | unicode-han-tra... | | unicode-transla... | | util-linux | [] [] [] [] | vorbis-tools | [] [] [] [] | wastesedge | () | wdiff | [] [] [] [] | wget | [] [] | xchat | [] [] [] [] [] [] | xkeyboard-config | | xpad | [] [] | +----------------------------------------------------+ af am ar az be bg bs ca cs cy da de el en en_GB eo 10 0 1 2 9 22 1 42 41 2 60 95 16 1 17 16 es et eu fa fi fr ga gl gu he hi hr hu id is it +--------------------------------------------------+ GNUnet | | a2ps | [] [] [] () | aegis | | ant-phone | [] | anubis | [] | ap-utils | [] [] | aspell | [] [] [] | bash | [] [] [] | batchelor | [] [] | bfd | [] | bibshelf | [] [] [] | binutils | [] [] [] | bison | [] [] [] [] [] [] | bison-runtime | [] [] [] [] [] | bluez-pin | [] [] [] [] [] | cflow | [] | clisp | [] [] | console-tools | | coreutils | [] [] [] [] [] [] | cpio | [] [] [] | cpplib | [] [] | cryptonit | [] | darkstat | [] () [] [] [] | dialog | [] [] [] [] [] [] [] [] | diffutils | [] [] [] [] [] [] [] [] [] | doodle | [] [] | e2fsprogs | [] [] [] | enscript | [] [] [] | error | [] [] [] [] [] | fetchmail | [] | fileutils | [] [] [] [] [] [] | findutils | [] [] [] [] | flex | [] [] [] | fslint | [] | gas | [] [] | gawk | [] [] [] [] | gbiff | [] | gcal | [] [] | gcc | [] | gettext-examples | [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] | gettext-tools | [] [] [] | gimp-print | [] [] | gip | [] [] [] | gliv | () | glunarclock | [] [] [] | gmult | [] [] [] | gnubiff | () () | gnucash | () () () | gnucash-glossary | [] [] | gnuedu | [] | gnulib | [] [] [] [] [] [] [] [] | gnunet-gtk | | gnutls | | gpe-aerial | [] [] | gpe-beam | [] [] | gpe-calendar | | gpe-clock | [] [] [] [] | gpe-conf | [] | gpe-contacts | [] [] | gpe-edit | [] [] [] [] | gpe-filemanager | [] | gpe-go | [] [] [] | gpe-login | [] [] [] | gpe-ownerinfo | [] [] [] [] [] | gpe-package | [] | gpe-sketchbook | [] [] | gpe-su | [] [] [] [] | gpe-taskmanager | [] [] [] | gpe-timesheet | [] [] [] [] | gpe-today | [] [] [] [] | gpe-todo | [] | gphoto2 | [] [] [] [] [] | gprof | [] [] [] [] | gpsdrive | () () [] () | gramadoir | [] [] | grep | [] [] [] [] [] [] [] [] [] [] [] [] | gretl | [] [] [] | gsasl | [] [] | gss | [] | gst-plugins | [] [] [] | gst-plugins-base | [] [] | gst-plugins-good | [] [] [] | gstreamer | [] [] [] | gtick | [] | gtkam | [] [] [] [] | gtkorphan | [] [] | gtkspell | [] [] [] [] [] [] | gutenprint | [] | hello | [] [] [] [] [] [] [] [] [] [] [] [] [] | id-utils | [] [] [] [] [] | impost | [] [] | indent | [] [] [] [] [] [] [] [] [] [] | iso_3166 | [] [] [] | iso_3166_2 | [] | iso_4217 | [] [] [] [] | iso_639 | [] [] [] [] [] | jpilot | [] [] | jtag | [] | jwhois | [] [] [] [] [] | kbd | [] [] | keytouch | [] | keytouch-editor | [] | keytouch-keyboa... | [] | latrine | [] [] [] | ld | [] [] | leafpad | [] [] [] [] [] [] | libc | [] [] [] [] [] | libexif | [] | libextractor | [] | libgpewidget | [] [] [] [] [] | libgpg-error | | libgphoto2 | [] [] [] | libgphoto2_port | [] [] | libgsasl | [] [] | libiconv | [] [] | libidn | [] [] | lifelines | () | lilypond | [] | lingoteach | [] [] [] | lynx | [] [] [] | m4 | [] [] [] [] | mailutils | [] [] | make | [] [] [] [] [] [] [] [] | man-db | () | minicom | [] [] [] [] | mysecretdiary | [] [] [] | nano | [] [] [] [] [] [] | nano_1_0 | [] [] [] [] [] | opcodes | [] [] [] [] | parted | [] [] [] [] | pilot-qof | | psmisc | [] [] [] | pwdutils | | python | | qof | [] | radius | [] [] | recode | [] [] [] [] [] [] [] [] | rpm | [] [] | screem | | scrollkeeper | [] [] [] | sed | [] [] [] [] [] | sh-utils | [] [] [] [] [] [] [] | shared-mime-info | [] [] [] [] [] [] | sharutils | [] [] [] [] [] [] [] [] | shishi | | silky | [] | skencil | [] [] | sketch | [] [] | solfege | [] | soundtracker | [] [] [] | sp | [] | stardict | [] | system-tools-ba... | [] [] [] [] [] [] [] [] | tar | [] [] [] [] [] [] [] | texinfo | [] [] | textutils | [] [] [] [] [] | tin | [] () | tp-robot | [] [] [] [] | tuxpaint | [] [] | unicode-han-tra... | | unicode-transla... | [] [] | util-linux | [] [] [] [] [] [] [] | vorbis-tools | [] [] | wastesedge | () | wdiff | [] [] [] [] [] [] [] [] | wget | [] [] [] [] [] [] [] [] | xchat | [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] | xpad | [] [] [] | +--------------------------------------------------+ es et eu fa fi fr ga gl gu he hi hr hu id is it 88 22 14 2 40 115 61 14 1 8 1 6 59 31 0 52 ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no +-------------------------------------------------+ GNUnet | | a2ps | () [] [] () | aegis | () | ant-phone | [] | anubis | [] [] [] | ap-utils | [] | aspell | [] [] | bash | [] | batchelor | [] [] | bfd | | bibshelf | [] | binutils | | bison | [] [] [] | bison-runtime | [] [] [] | bluez-pin | [] [] [] | cflow | | clisp | [] | console-tools | | coreutils | [] | cpio | | cpplib | [] | cryptonit | [] | darkstat | [] [] | dialog | [] [] | diffutils | [] [] [] | doodle | | e2fsprogs | [] | enscript | [] | error | [] | fetchmail | [] [] | fileutils | [] [] | findutils | [] | flex | [] [] | fslint | [] [] | gas | | gawk | [] [] | gbiff | [] | gcal | | gcc | | gettext-examples | [] [] | gettext-runtime | [] [] [] | gettext-tools | [] [] | gimp-print | [] [] | gip | [] [] | gliv | [] | glunarclock | [] [] | gmult | [] [] | gnubiff | | gnucash | () () | gnucash-glossary | [] | gnuedu | | gnulib | [] [] [] [] | gnunet-gtk | | gnutls | | gpe-aerial | [] | gpe-beam | [] | gpe-calendar | [] | gpe-clock | [] [] [] | gpe-conf | [] [] | gpe-contacts | [] | gpe-edit | [] [] [] | gpe-filemanager | [] [] | gpe-go | [] [] [] | gpe-login | [] [] [] | gpe-ownerinfo | [] [] | gpe-package | [] [] | gpe-sketchbook | [] [] | gpe-su | [] [] [] | gpe-taskmanager | [] [] [] [] | gpe-timesheet | [] | gpe-today | [] [] | gpe-todo | [] | gphoto2 | [] [] | gprof | | gpsdrive | () () () | gramadoir | () | grep | [] [] [] [] | gretl | | gsasl | [] | gss | | gst-plugins | [] | gst-plugins-base | | gst-plugins-good | [] | gstreamer | [] | gtick | | gtkam | [] | gtkorphan | [] | gtkspell | [] [] | gutenprint | | hello | [] [] [] [] [] [] | id-utils | [] | impost | | indent | [] [] | iso_3166 | [] | iso_3166_2 | [] | iso_4217 | [] [] [] | iso_639 | [] [] | jpilot | () () () | jtag | | jwhois | [] | kbd | [] | keytouch | [] | keytouch-editor | | keytouch-keyboa... | | latrine | [] | ld | | leafpad | [] [] | libc | [] [] [] [] [] | libexif | | libextractor | | libgpewidget | [] | libgpg-error | | libgphoto2 | [] | libgphoto2_port | [] | libgsasl | [] | libiconv | | libidn | [] [] | lifelines | [] | lilypond | | lingoteach | [] | lynx | [] [] | m4 | [] [] | mailutils | | make | [] [] [] | man-db | () | minicom | [] | mysecretdiary | [] | nano | [] [] [] | nano_1_0 | [] [] [] | opcodes | [] | parted | [] [] | pilot-qof | | psmisc | [] [] [] | pwdutils | | python | | qof | | radius | | recode | [] | rpm | [] [] | screem | [] | scrollkeeper | [] [] [] [] | sed | [] [] | sh-utils | [] [] | shared-mime-info | [] [] [] [] [] | sharutils | [] [] | shishi | | silky | [] | skencil | | sketch | | solfege | | soundtracker | | sp | () | stardict | [] [] | system-tools-ba... | [] [] [] [] | tar | [] [] [] | texinfo | [] [] [] | textutils | [] [] [] | tin | | tp-robot | [] | tuxpaint | [] | unicode-han-tra... | | unicode-transla... | | util-linux | [] [] | vorbis-tools | [] | wastesedge | [] | wdiff | [] [] | wget | [] [] | xchat | [] [] [] [] | xkeyboard-config | [] | xpad | [] [] [] | +-------------------------------------------------+ ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no 52 24 2 2 1 3 0 2 3 21 0 15 1 97 5 1 nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta +------------------------------------------------------+ GNUnet | | a2ps | () [] [] [] [] [] [] | aegis | () () | ant-phone | [] [] | anubis | [] [] [] | ap-utils | () | aspell | [] [] | bash | [] [] [] | batchelor | [] [] | bfd | | bibshelf | [] | binutils | [] [] | bison | [] [] [] [] [] | bison-runtime | [] [] [] [] | bluez-pin | [] [] [] [] [] [] [] [] [] | cflow | [] | clisp | [] | console-tools | [] | coreutils | [] [] [] [] | cpio | [] [] [] | cpplib | [] | cryptonit | [] [] | darkstat | [] [] [] [] [] [] | dialog | [] [] [] [] [] [] [] [] [] | diffutils | [] [] [] [] [] [] | doodle | [] [] | e2fsprogs | [] [] | enscript | [] [] [] [] [] | error | [] [] [] [] | fetchmail | [] [] [] | fileutils | [] [] [] [] [] | findutils | [] [] [] [] [] [] | flex | [] [] [] [] [] | fslint | [] [] [] [] | gas | | gawk | [] [] [] [] | gbiff | [] | gcal | [] | gcc | [] | gettext-examples | [] [] [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] [] [] | gettext-tools | [] [] [] [] [] [] [] | gimp-print | [] [] | gip | [] [] [] [] | gliv | [] [] [] [] | glunarclock | [] [] [] [] [] [] | gmult | [] [] [] [] | gnubiff | () | gnucash | () [] | gnucash-glossary | [] [] [] | gnuedu | | gnulib | [] [] [] [] [] | gnunet-gtk | [] | gnutls | [] [] | gpe-aerial | [] [] [] [] [] [] [] | gpe-beam | [] [] [] [] [] [] [] | gpe-calendar | [] | gpe-clock | [] [] [] [] [] [] [] [] | gpe-conf | [] [] [] [] [] [] [] | gpe-contacts | [] [] [] [] [] | gpe-edit | [] [] [] [] [] [] [] [] | gpe-filemanager | [] [] | gpe-go | [] [] [] [] [] [] | gpe-login | [] [] [] [] [] [] [] [] | gpe-ownerinfo | [] [] [] [] [] [] [] [] | gpe-package | [] [] | gpe-sketchbook | [] [] [] [] [] [] [] [] | gpe-su | [] [] [] [] [] [] [] [] | gpe-taskmanager | [] [] [] [] [] [] [] [] | gpe-timesheet | [] [] [] [] [] [] [] [] | gpe-today | [] [] [] [] [] [] [] [] | gpe-todo | [] [] [] [] | gphoto2 | [] [] [] [] [] | gprof | [] [] [] | gpsdrive | [] [] [] | gramadoir | [] [] | grep | [] [] [] [] [] [] [] [] | gretl | [] | gsasl | [] [] [] | gss | [] [] [] | gst-plugins | [] [] [] [] | gst-plugins-base | [] | gst-plugins-good | [] [] [] [] | gstreamer | [] [] [] | gtick | [] | gtkam | [] [] [] [] | gtkorphan | [] | gtkspell | [] [] [] [] [] [] [] [] | gutenprint | [] | hello | [] [] [] [] [] [] [] [] | id-utils | [] [] [] [] | impost | [] | indent | [] [] [] [] [] [] | iso_3166 | [] [] [] [] [] [] | iso_3166_2 | | iso_4217 | [] [] [] [] | iso_639 | [] [] [] [] | jpilot | | jtag | [] | jwhois | [] [] [] [] | kbd | [] [] [] | keytouch | [] | keytouch-editor | [] | keytouch-keyboa... | [] | latrine | [] [] | ld | [] | leafpad | [] [] [] [] [] [] | libc | [] [] [] [] [] | libexif | [] | libextractor | [] [] | libgpewidget | [] [] [] [] [] [] [] | libgpg-error | [] [] | libgphoto2 | [] | libgphoto2_port | [] [] [] | libgsasl | [] [] [] [] | libiconv | [] [] | libidn | [] [] () | lifelines | [] [] | lilypond | | lingoteach | [] | lynx | [] [] [] | m4 | [] [] [] [] [] | mailutils | [] [] [] [] | make | [] [] [] [] | man-db | [] [] | minicom | [] [] [] [] [] | mysecretdiary | [] [] [] [] | nano | [] [] [] | nano_1_0 | [] [] [] [] | opcodes | [] [] | parted | [] | pilot-qof | [] | psmisc | [] [] | pwdutils | [] [] | python | | qof | [] [] | radius | [] [] | recode | [] [] [] [] [] [] [] | rpm | [] [] [] [] | screem | | scrollkeeper | [] [] [] [] [] [] [] | sed | [] [] [] [] [] [] [] [] [] | sh-utils | [] [] [] | shared-mime-info | [] [] [] [] [] | sharutils | [] [] [] [] | shishi | [] | silky | [] | skencil | [] [] [] | sketch | [] [] [] | solfege | [] | soundtracker | [] [] | sp | | stardict | [] [] [] | system-tools-ba... | [] [] [] [] [] [] [] [] [] | tar | [] [] [] [] [] | texinfo | [] [] [] [] | textutils | [] [] [] | tin | () | tp-robot | [] | tuxpaint | [] [] [] [] [] | unicode-han-tra... | | unicode-transla... | | util-linux | [] [] [] [] | vorbis-tools | [] [] | wastesedge | | wdiff | [] [] [] [] [] [] | wget | [] [] [] [] | xchat | [] [] [] [] [] [] [] | xkeyboard-config | [] [] | xpad | [] [] [] | +------------------------------------------------------+ nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta 0 2 3 58 30 54 5 73 72 4 40 46 11 50 128 2 tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu +---------------------------------------------------+ GNUnet | [] | 2 a2ps | [] [] [] | 19 aegis | | 0 ant-phone | [] [] | 6 anubis | [] [] [] | 11 ap-utils | () [] | 4 aspell | [] [] [] | 15 bash | [] | 11 batchelor | [] [] | 9 bfd | | 1 bibshelf | [] | 7 binutils | [] [] [] | 9 bison | [] [] [] | 19 bison-runtime | [] [] [] | 15 bluez-pin | [] [] [] [] [] [] | 28 cflow | [] [] | 5 clisp | | 6 console-tools | [] [] | 5 coreutils | [] [] | 16 cpio | [] [] [] | 9 cpplib | [] [] [] [] | 11 cryptonit | | 5 darkstat | [] () () | 15 dialog | [] [] [] [] [] | 30 diffutils | [] [] [] [] | 28 doodle | [] | 6 e2fsprogs | [] [] | 10 enscript | [] [] [] | 16 error | [] [] [] [] | 18 fetchmail | [] [] | 12 fileutils | [] [] [] | 18 findutils | [] [] [] | 17 flex | [] [] | 15 fslint | [] | 9 gas | [] | 3 gawk | [] [] | 15 gbiff | [] | 5 gcal | [] | 5 gcc | [] [] [] | 6 gettext-examples | [] [] [] [] [] [] | 27 gettext-runtime | [] [] [] [] [] [] | 28 gettext-tools | [] [] [] [] [] | 19 gimp-print | [] [] | 12 gip | [] [] | 12 gliv | [] [] | 8 glunarclock | [] [] [] | 15 gmult | [] [] [] [] | 15 gnubiff | [] | 1 gnucash | () | 2 gnucash-glossary | [] [] | 9 gnuedu | [] | 2 gnulib | [] [] [] [] [] | 28 gnunet-gtk | | 1 gnutls | | 2 gpe-aerial | [] [] | 14 gpe-beam | [] [] | 14 gpe-calendar | [] | 3 gpe-clock | [] [] [] [] | 21 gpe-conf | [] [] | 14 gpe-contacts | [] [] | 10 gpe-edit | [] [] [] [] | 20 gpe-filemanager | [] | 6 gpe-go | [] [] | 15 gpe-login | [] [] [] [] [] | 21 gpe-ownerinfo | [] [] [] [] | 21 gpe-package | [] | 6 gpe-sketchbook | [] [] | 16 gpe-su | [] [] [] | 20 gpe-taskmanager | [] [] [] | 20 gpe-timesheet | [] [] [] [] | 18 gpe-today | [] [] [] [] [] | 21 gpe-todo | [] | 7 gphoto2 | [] [] [] [] | 20 gprof | [] [] | 11 gpsdrive | | 4 gramadoir | [] | 7 grep | [] [] [] [] | 34 gretl | | 4 gsasl | [] [] | 8 gss | [] | 5 gst-plugins | [] [] [] | 15 gst-plugins-base | [] [] [] | 9 gst-plugins-good | [] [] [] [] [] | 20 gstreamer | [] [] [] | 17 gtick | [] | 3 gtkam | [] | 13 gtkorphan | [] | 7 gtkspell | [] [] [] [] [] [] | 26 gutenprint | | 3 hello | [] [] [] [] [] | 37 id-utils | [] [] | 14 impost | [] | 4 indent | [] [] [] [] | 25 iso_3166 | [] [] [] [] | 16 iso_3166_2 | | 2 iso_4217 | [] [] | 14 iso_639 | [] | 14 jpilot | [] [] [] [] | 7 jtag | [] | 3 jwhois | [] [] [] | 13 kbd | [] [] | 12 keytouch | [] | 4 keytouch-editor | | 2 keytouch-keyboa... | [] | 3 latrine | [] [] | 8 ld | [] [] [] [] | 8 leafpad | [] [] [] [] | 23 libc | [] [] [] | 23 libexif | [] | 4 libextractor | [] | 5 libgpewidget | [] [] [] | 19 libgpg-error | [] | 4 libgphoto2 | [] | 8 libgphoto2_port | [] [] [] | 11 libgsasl | [] | 8 libiconv | [] | 7 libidn | [] [] | 10 lifelines | | 4 lilypond | | 2 lingoteach | [] | 6 lynx | [] [] [] | 15 m4 | [] [] [] | 18 mailutils | [] | 8 make | [] [] [] | 20 man-db | [] | 6 minicom | [] | 14 mysecretdiary | [] [] | 12 nano | [] [] | 17 nano_1_0 | [] [] [] | 18 opcodes | [] [] | 10 parted | [] [] [] | 10 pilot-qof | [] | 3 psmisc | [] | 10 pwdutils | [] | 3 python | | 0 qof | [] | 4 radius | [] | 6 recode | [] [] [] | 25 rpm | [] [] [] [] | 14 screem | [] | 2 scrollkeeper | [] [] [] [] | 26 sed | [] [] [] | 22 sh-utils | [] | 15 shared-mime-info | [] [] [] [] | 24 sharutils | [] [] [] | 23 shishi | | 1 silky | [] | 4 skencil | [] | 7 sketch | | 6 solfege | | 2 soundtracker | [] [] | 9 sp | [] | 3 stardict | [] [] [] [] | 11 system-tools-ba... | [] [] [] [] [] [] [] | 37 tar | [] [] [] [] | 20 texinfo | [] [] [] | 15 textutils | [] [] [] | 17 tin | | 1 tp-robot | [] [] [] | 10 tuxpaint | [] [] [] | 16 unicode-han-tra... | | 0 unicode-transla... | | 2 util-linux | [] [] [] | 20 vorbis-tools | [] [] | 11 wastesedge | | 1 wdiff | [] [] | 22 wget | [] [] [] | 19 xchat | [] [] [] [] | 29 xkeyboard-config | [] [] [] [] | 11 xpad | [] [] [] | 14 +---------------------------------------------------+ 77 teams tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu 170 domains 0 1 1 77 39 0 136 10 1 48 5 54 0 2028 Some counters in the preceding matrix are higher than the number of visible blocks let us expect. This is because a few extra PO files are used for implementing regional variants of languages, or language dialects. For a PO file in the matrix above to be effective, the package to which it applies should also have been internationalized and distributed as such by its maintainer. There might be an observable lag between the mere existence a PO file and its wide availability in a distribution. If October 2006 seems to be old, you may fetch a more recent copy of this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date matrix with full percentage details can be found at `http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'. 1.6 Using `gettext' in new packages =================================== If you are writing a freely available program and want to internationalize it you are welcome to use GNU `gettext' in your package. Of course you have to respect the GNU Library General Public License which covers the use of the GNU `gettext' library. This means in particular that even non-free programs can use `libintl' as a shared library, whereas only free software can use `libintl' as a static library or use modified versions of `libintl'. Once the sources are changed appropriately and the setup can handle the use of `gettext' the only thing missing are the translations. The Free Translation Project is also available for packages which are not developed inside the GNU project. Therefore the information given above applies also for every other Free Software Project. Contact `translation@iro.umontreal.ca' to make the `.pot' files available to the translation teams. linphone-3.12.0/AUTHORS000066400000000000000000000005471313432737600145000ustar00rootroot00000000000000Main authors: Belledonne Communications SARL team: Simon Morlat, Jehan Monnier, Guillaume Beraudo Contributors: Florian Wintertein < f-win at gmx dot net > originaly wrotes the console version of linphone (linphonec) in console/ directory. Sandro Santilli < strk at keybit dot net > wrote enhancements in the console interface (readline, new commands). linphone-3.12.0/BUGS000066400000000000000000000001471313432737600141070ustar00rootroot00000000000000version 3.0.0: resizing of video window under linux often causes hang or crash (seems to be a SDL bug).linphone-3.12.0/CMakeLists.txt000066400000000000000000000316411313432737600161670ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ cmake_minimum_required(VERSION 3.0) project(linphone VERSION 3.12.0 LANGUAGES C CXX) set(LINPHONE_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}) set(LINPHONE_MINOR_VERSION ${PROJECT_VERSION_MINOR}) set(LINPHONE_MICRO_VERSION ${PROJECT_VERSION_PATCH}) set(LINPHONE_VERSION ${PROJECT_VERSION}) set(LINPHONE_SO_VERSION "9") file(GLOB LINPHONE_PO_FILES RELATIVE "${CMAKE_CURRENT_LIST_DIR}/po" "${CMAKE_CURRENT_LIST_DIR}/po/*.po") string(REGEX REPLACE "([a-zA-Z_]+)\\.po" "\\1" LINPHONE_ALL_LANGS_LIST "${LINPHONE_PO_FILES}") string(REPLACE ";" " " LINPHONE_ALL_LANGS "${LINPHONE_ALL_LANGS_LIST}") include(CMakeDependentOption) option(ENABLE_SHARED "Build shared library." YES) option(ENABLE_STATIC "Build static library." YES) option(ENABLE_CONSOLE_UI "Turn on or off compilation of console interface." YES) option(ENABLE_DATE "Use build date in internal version number." NO) option(ENABLE_DAEMON "Enable the linphone daemon interface." YES) option(ENABLE_DOC "Enable documentation generation with Doxygen." YES) option(ENABLE_JAVADOC "Add a target to generate documentation for Java API" NO) option(ENABLE_GTK_UI "Turn on or off compilation of gtk interface." NO) option(ENABLE_LDAP "Enable LDAP support." NO) option(ENABLE_SQLITE_STORAGE "Turn on compilation sqlite storage, for messages, contacts, history" YES) cmake_dependent_option(ENABLE_LIME "Enable Instant Messaging Encryption." YES "ENABLE_SQLITE_STORAGE" NO) cmake_dependent_option(ENABLE_NOTIFY "Enable libnotify support." YES "ENABLE_GTK_UI;NOT APPLE" NO) option(ENABLE_RELATIVE_PREFIX "Find resources relatively to the installation directory." NO) option(ENABLE_STRICT "Build with strict compile options." YES) option(ENABLE_TOOLS "Turn on or off compilation of tools." YES) option(ENABLE_TUNNEL "Turn on compilation of tunnel support." NO) option(ENABLE_TUTORIALS "Enable compilation of tutorials." YES) option(ENABLE_UNIT_TESTS "Enable compilation of unit tests." YES) option(ENABLE_UPDATE_CHECK "Enable update check." NO) option(ENABLE_VIDEO "Build with video support." YES) cmake_dependent_option(ENABLE_ASSISTANT "Turn on assistant compiling." YES "ENABLE_GTK_UI" NO) option(ENABLE_DEBUG_LOGS "Turn on or off debug level logs." NO) option(ENABLE_NLS "Build with internationalisation support" YES) option(ENABLE_VCARD "Turn on compilation of vcard4 support." YES) option(ENABLE_ROOTCA_DOWNLOAD "Download rootca.pem at build time." YES) option(ENABLE_CXX_WRAPPER "Build the C++ wrapper for Liblinphone." YES) option(ENABLE_CSHARP_WRAPPER "Build the C# wrapper for Liblinphone." OFF) set(CMAKE_CXX_STANDARD 11) if(ENABLE_STATIC) set(LINPHONE_LIBS_FOR_TOOLS linphone-static) else() set(LINPHONE_LIBS_FOR_TOOLS linphone) endif() if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") list(APPEND LINPHONE_LIBS_FOR_TOOLS "Ws2_32") endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(CheckSymbolExists) include(CMakePushCheckState) include(GNUInstallDirs) if(NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}) message(STATUS "Setting install rpath to ${CMAKE_INSTALL_RPATH}") endif() set(MSVC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/MSVC") if(MSVC) list(APPEND CMAKE_REQUIRED_INCLUDES "${MSVC_INCLUDE_DIR}") endif() # find_package should be invoked here to check for libraries - however do NOT # call include_directories here (see below) if (ENABLE_VCARD) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_belcard_CONFIG_DIR}/BelcardConfig.cmake") else() find_package(Belcard) endif() if(NOT BELCARD_FOUND) message(WARNING "Could not find the belcard library!") set(ENABLE_VCARD OFF CACHE BOOL "Enable vcard support." FORCE) else() add_definitions(-DVCARD_ENABLED) endif() endif() if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_bellesip_CONFIG_DIR}/BelleSIPConfig.cmake") include("${EP_ms2_CONFIG_DIR}/Mediastreamer2Config.cmake") include("${EP_ortp_CONFIG_DIR}/ORTPConfig.cmake") set(BcToolbox_FIND_COMPONENTS tester) include("${EP_bctoolbox_CONFIG_DIR}/BcToolboxConfig.cmake") else() find_package(BelleSIP REQUIRED) find_package(Mediastreamer2 REQUIRED) find_package(ORTP REQUIRED) find_package(BcToolbox 0.0.3 REQUIRED OPTIONAL_COMPONENTS tester) endif() find_package(XML2 REQUIRED) find_package(Zlib) if(ENABLE_TUNNEL) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_tunnel_CONFIG_DIR}/TunnelConfig.cmake") else() find_package(Tunnel) endif() if(NOT TUNNEL_FOUND) message(WARNING "Could not find the tunnel library!") set(ENABLE_TUNNEL OFF CACHE BOOL "Enable tunnel support." FORCE) endif() endif() if(ENABLE_SQLITE_STORAGE) find_package(Sqlite3 REQUIRED) endif() if(ENABLE_NOTIFY) find_package(Notify) if(NOTIFY_FOUND) set(HAVE_NOTIFY4 1) else() message(WARNING "Could not find the notify library!") set(ENABLE_NOTIFY OFF CACHE BOOL "Enable libnotify support." FORCE) endif() endif() if(ENABLE_GTK_UI) if(WIN32) set(GTK2_ADDITIONAL_SUFFIXES "../lib/glib-2.0/include" "../lib/gtk-2.0/include") endif() find_package(GTK2 2.18 REQUIRED gtk) if(ENABLE_ASSISTANT AND GTK2_VERSION VERSION_LESS 2.22) message(WARNING "You need at least GTK 2.22 to enable the assistant") set(ENABLE_ASSISTANT OFF CACHE BOOL "Turn on assistant compiling." FORCE) endif() if(APPLE) find_package(GtkMacIntegration) if(GTKMACINTEGRATION_FOUND) set(HAVE_GTK_OSX 1) add_definitions("${GTKMACINTEGRATION_CPPFLAGS}") else() message(WARNING "gtk-mac-integration not found. Please install gtk-osx-application package.") endif() endif() endif() if(ENABLE_ASSISTANT) set(BUILD_WIZARD 1) endif() if(ENABLE_NLS) find_package(Gettext REQUIRED) find_package(Intl REQUIRED) endif() if(ENABLE_LIME) #bzrtp is only required for LIME if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_bzrtp_CONFIG_DIR}/BZRTPConfig.cmake") else() find_package(BZRTP REQUIRED) endif() set(HAVE_LIME 1) endif() if(ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER) find_package(PythonInterp REQUIRED) endif() if(UNIX AND NOT APPLE) include(CheckIncludeFiles) check_include_files(libudev.h HAVE_LIBUDEV_H) endif() set(LINPHONE_LDFLAGS "${BELLESIP_LDFLAGS} ${MEDIASTREAMER2_LDFLAGS}") if(BELCARD_FOUND AND APPLE) set(LINPHONE_LDFLAGS "${LINPHONE_LDFLAGS} -stdlib=libc++") endif() # include_directories must be called only UNDER THIS LINE in order to use our # projects submodules first (we do NOT want to have system headers in first position) include_directories( include/ coreapi/ ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/coreapi/ ) set(LINPHONE_INCLUDE_DIRS ${BELLESIP_INCLUDE_DIRS} ${MEDIASTREAMER2_INCLUDE_DIRS} ${BCTOOLBOX_CORE_INCLUDE_DIRS} ) if (BZRTP_FOUND) list(APPEND LINPHONE_INCLUDE_DIRS ${BZRTP_INCLUDE_DIRS}) endif() if(ANDROID) include_directories(${CMAKE_CURRENT_BINARY_DIR}/java) endif() if(ENABLE_TUNNEL) list(APPEND LINPHONE_INCLUDE_DIRS ${TUNNEL_INCLUDE_DIRS}) endif() if (ENABLE_VCARD) list(APPEND LINPHONE_INCLUDE_DIRS ${BELCARD_INCLUDE_DIRS}) endif() list(APPEND LINPHONE_INCLUDE_DIRS ${XML2_INCLUDE_DIRS}) if(ZLIB_FOUND) list(APPEND LINPHONE_INCLUDE_DIRS ${ZLIB_INCLUDE_DIRS}) set(HAVE_ZLIB 1) endif() if(SQLITE3_FOUND) list(APPEND LINPHONE_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIRS}) if(ENABLE_SQLITE_STORAGE) add_definitions("-DSQLITE_STORAGE_ENABLED") endif() endif() if(INTL_FOUND) set(HAVE_INTL 1) list(APPEND LINPHONE_INCLUDE_DIRS ${INTL_INCLUDE_DIRS}) endif() if(MSVC) include_directories(${MSVC_INCLUDE_DIR}) endif() add_definitions("-DLINPHONE_EXPORTS") set(LINPHONE_CPPFLAGS ${BELCARD_CPPFLAGS} ${BELLESIP_CPPFLAGS} ${MEDIASTREAMER2_CPPFLAGS} ${BCTOOLBOX_CPPFLAGS}) if(ENABLE_STATIC) list(APPEND LINPHONE_CPPFLAGS "-DLINPHONE_STATIC") endif() if(LINPHONE_CPPFLAGS) list(REMOVE_DUPLICATES LINPHONE_CPPFLAGS) add_definitions(${LINPHONE_CPPFLAGS}) endif() if(ENABLE_DEBUG_LOGS) add_definitions("-DDEBUG") endif() set(STRICT_OPTIONS_CPP ) set(STRICT_OPTIONS_C ) set(STRICT_OPTIONS_CXX ) set(STRICT_OPTIONS_OBJC ) if(MSVC) list(APPEND STRICT_OPTIONS_CPP "/wd4995") # Disable "name was marked as #pragma deprecated" warnings list(APPEND STRICT_OPTIONS_CPP "/wd4996") # Disable deprecated function warnings if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_CPP "/WX") endif() else() list(APPEND STRICT_OPTIONS_CPP "-Wall" "-Wuninitialized" "-Wno-error=deprecated-declarations") list(APPEND STRICT_OPTIONS_C "-Wstrict-prototypes" "-Werror=strict-prototypes") if(CMAKE_C_COMPILER_ID STREQUAL "GNU") list(APPEND STRICT_OPTIONS_C "-fno-inline-small-functions") endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang") list(APPEND STRICT_OPTIONS_CPP "-Qunused-arguments" "-Wno-array-bounds") endif() if(APPLE) list(APPEND STRICT_OPTIONS_CPP "-Wno-error=unknown-warning-option" "-Qunused-arguments" "-Wno-tautological-compare" "-Wno-unused-function" "-Wno-array-bounds") endif() if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_C "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-fno-strict-aliasing") list(APPEND STRICT_OPTIONS_CPP "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-fno-strict-aliasing") endif() endif() if(STRICT_OPTIONS_CPP) list(REMOVE_DUPLICATES STRICT_OPTIONS_CPP) endif() if(STRICT_OPTIONS_C) list(REMOVE_DUPLICATES STRICT_OPTIONS_C) endif() set(GETTEXT_PACKAGE "linphone") if(ENABLE_RELATIVE_PREFIX) set(LINPHONE_DATA_DIR ".") else() set(LINPHONE_DATA_DIR "${CMAKE_INSTALL_PREFIX}") endif() set(LINPHONE_PLUGINS_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_LIBDIR}/liblinphone/plugins") if(WIN32) set(LINPHONE_CONFIG_DIR "Linphone") endif() set(PACKAGE_LOCALE_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}/locale") set(PACKAGE_DATA_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}") set(PACKAGE_SOUND_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}/sounds/linphone") set(PACKAGE_RING_DIR "${PACKAGE_SOUND_DIR}/rings") set(PACKAGE_FREEDESKTOP_DIR "${PACKAGE_DATA_DIR}/applications") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/config.h PROPERTIES GENERATED ON) add_definitions(-DHAVE_CONFIG_H) if(ENABLE_VIDEO) add_definitions(-DVIDEO_ENABLED) endif() if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(EXPORT_TARGETS_NAME "LinphoneBuilder") else() set(EXPORT_TARGETS_NAME "Linphone") endif() add_subdirectory(include) add_subdirectory(java) add_subdirectory(coreapi) add_subdirectory(share) if(ENABLE_CONSOLE_UI) add_subdirectory(console) endif() if(ENABLE_DAEMON) add_subdirectory(daemon) endif() if(ENABLE_GTK_UI) add_subdirectory(gtk) add_subdirectory(pixmaps) add_subdirectory(po) endif() if(ENABLE_TOOLS) add_subdirectory(tools) endif() if(ENABLE_UNIT_TESTS AND BCTOOLBOX_TESTER_FOUND) add_subdirectory(tester) endif() if(ENABLE_CXX_WRAPPER) add_subdirectory(wrappers/cpp) endif() if(ENABLE_CSHARP_WRAPPER) add_subdirectory(wrappers/csharp) endif() include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfigVersion.cmake" VERSION ${LINPHONE_VERSION} COMPATIBILITY AnyNewerVersion ) export(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE "${CMAKE_CURRENT_BINARY_DIR}/LinphoneTargets.cmake" ) configure_file(cmake/LinphoneConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfig.cmake" @ONLY ) set(CONFIG_PACKAGE_LOCATION "${CMAKE_INSTALL_DATADIR}/Linphone/cmake") install(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE LinphoneTargets.cmake DESTINATION ${CONFIG_PACKAGE_LOCATION} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfigVersion.cmake" DESTINATION ${CONFIG_PACKAGE_LOCATION} ) # CPack settings set(CPACK_PACKAGE_NAME "linphone") set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_SOURCE_IGNORE_FILES "^${CMAKE_BINARY_DIR}" "/\\\\..+" "^${CMAKE_SOURCE_DIR}/mediastreamer2" "^${CMAKE_SOURCE_DIR}/oRTP" ) include(CPack) linphone-3.12.0/COPYING000066400000000000000000000542571313432737600144720ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ------------------------------------------------------------------------------- ------------------------------------------------------- About The Cisco-Provided Binary of OpenH264 Video Codec ------------------------------------------------------- Cisco provides this program under the terms of the BSD license. Additionally, this binary is licensed under Cisco's AVC/H.264 Patent Portfolio License from MPEG LA, at no cost to you, provided that the requirements and conditions shown below in the AVC/H.264 Patent Portfolio sections are met. As with all AVC/H.264 codecs, you may also obtain your own patent license from MPEG LA or from the individual patent owners, or proceed at your own risk. Your rights from Cisco under the BSD license are not affected by this choice. For more information on the OpenH264 binary licensing, please see the OpenH264 FAQ found at http://www.openh264.org/faq.html#binary A corresponding source code to this binary program is available under the same BSD terms, which can be found at http://www.openh264.org ----------- BSD License ----------- Copyright (C) 2014 Cisco Systems, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS†AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------- AVC/H.264 Patent Portfolio License Notice ----------------------------------------- The binary form of this Software is distributed by Cisco under the AVC/H.264 Patent Portfolio License from MPEG LA, and is subject to the following requirements, which may or may not be applicable to your use of this software: THIS PRODUCT IS LICENSED UNDER THE AVC PATENT PORTFOLIO LICENSE FOR THE PERSONAL USE OF A CONSUMER OR OTHER USES IN WHICH IT DOES NOT RECEIVE REMUNERATION TO (i) ENCODE VIDEO IN COMPLIANCE WITH THE AVC STANDARD (“AVC VIDEOâ€) AND/OR (ii) DECODE AVC VIDEO THAT WAS ENCODED BY A CONSUMER ENGAGED IN A PERSONAL ACTIVITY AND/OR WAS OBTAINED FROM A VIDEO PROVIDER LICENSED TO PROVIDE AVC VIDEO. NO LICENSE IS GRANTED OR SHALL BE IMPLIED FOR ANY OTHER USE. ADDITIONAL INFORMATION MAY BE OBTAINED FROM MPEG LA, L.L.C. SEE HTTP://WWW.MPEGLA.COM Accordingly, please be advised that content providers and broadcasters using AVC/H.264 in their service may be required to obtain a separate use license from MPEG LA, referred to as "(b) sublicenses" in the SUMMARY OF AVC/H.264 LICENSE TERMS from MPEG LA found at http://www.openh264.org/mpegla --------------------------------------------- AVC/H.264 Patent Portfolio License Conditions --------------------------------------------- In addition, the Cisco-provided binary of this Software is licensed under Cisco's license from MPEG LA only if the following conditions are met: 1. The Cisco-provided binary is separately downloaded to an end user’s device, and not integrated into or combined with third party software prior to being downloaded to the end user’s device; 2. The end user must have the ability to control (e.g., to enable, disable, or re-enable) the use of the Cisco-provided binary; 3. Third party software, in the location where end users can control the use of the Cisco-provided binary, must display the following text: "OpenH264 Video Codec provided by Cisco Systems, Inc." 4. Any third-party software that makes use of the Cisco-provided binary must reproduce all of the above text, as well as this last condition, in the EULA and/or in another location where licensing information is to be presented to the end user. v1.0 linphone-3.12.0/ChangeLog000066400000000000000000000521501313432737600151770ustar00rootroot000000000000002008-09-02 Francois-Xavier Kowalski * gtk/Makefile.am (INCLUDES): 1.6 Deprecated automake directive INCLUDES does not work within a conditionnal. 2008-08-22 Francois-Xavier Kowalski * mediastreamer2/src/Makefile.am (libmediastreamer_la_SOURCES+): 1.27 Deliver swscale.h in case ffmpeg does not come with libswscale (in which case the swscale feature is included into libavcodec). Also deliver ffmpeg-priv.h wrapper. * mediastreamer2/src/videoout.c: 1.39 * mediastreamer2/src/videodec.c: 1.24 * mediastreamer2/src/sizeconv.c: 1.14 * mediastreamer2/src/pixconv.c: 1.13 * mediastreamer2/src/nowebcam.c: 1.15 Use ffmpeg-priv,h * mediastreamer2/mediastreamer-config.h.in: 1.13 Remove from CVSm as it is generated by autoheader * mediastreamer2/configure.ac: 1.47 Use pkg-config to check for arts * mediastreamer2/acinclude.m4: 1.12 Remove -I/usr/include and -L/usr/lib(64) from CFLAGS and LIBS. Check for ffmpeg swscale feature into libavcodec or into libswscale 2007-09-26 Francois-Xavier Kowalski * m4/exosip.m4: 1.3 make Linphone buildable with the eXosip/osip version that ships with Fedora. * oRTP/include/ortp/stun_udp.h: 1.9 * mediastreamer2/include/mediastreamer2/msvideo.h: 1.7 * mediastreamer2/include/mediastreamer2/msticker.h: 1.6 * mediastreamer2/include/mediastreamer2/msqueue.h: 1.3 Clean ANSI/C vs. ANSI/C++ differences 2007-08-01 Sandro Santilli * console/commands.c: Clean up commands 'nat', 'stun' and 'firewall' to be more intuitive. 2007-02-03 Francois-Xavier Kowalski * m4/osip.m4 (OSIP_CFLAGS): 1.2 * gnome/Makefile.am (linphone_applet_LDADD): 1.33 * coreapi/Makefile.am (liblinphone_la_CFLAGS): 1.38 * console/Makefile.am (sipomatic_LDADD): 1.45 Cope with osip2-2.2.2 delivered as legacy on FC6. New option "--with-osip-version" 2007-01-18 Francois-Xavier Kowalski * oRTP/Makefile.am: 1.24 * mediastreamer2/Makefile.am: 1.30 * Makefile.am: 1.45 Fix RPM package generattion to cope with 2007-01-17 gettextize * m4/gettext.m4: Upgrade to gettext-0.16.1. * m4/lib-link.m4: Upgrade to gettext-0.16.1. * m4/lib-prefix.m4: Upgrade to gettext-0.16.1. * m4/nls.m4: Upgrade to gettext-0.16.1. * m4/po.m4: Upgrade to gettext-0.16.1. * m4/codeset.m4: Upgrade to gettext-0.16.1. * m4/intl.m4: New file, from gettext-0.16.1. * m4/intldir.m4: New file, from gettext-0.16.1. * m4/intmax.m4: Upgrade to gettext-0.16.1. * m4/inttypes_h.m4: Upgrade to gettext-0.16.1. * m4/inttypes-pri.m4: Upgrade to gettext-0.16.1. * m4/lock.m4: New file, from gettext-0.16.1. * m4/longdouble.m4: Upgrade to gettext-0.16.1. * m4/longlong.m4: Upgrade to gettext-0.16.1. * m4/size_max.m4: Upgrade to gettext-0.16.1. * m4/stdint_h.m4: Upgrade to gettext-0.16.1. * m4/ulonglong.m4: Upgrade to gettext-0.16.1. * m4/visibility.m4: New file, from gettext-0.16.1. * m4/Makefile.am (EXTRA_DIST): Add the new files. 2006-10-18 Sandro Santilli * console/Makefile.am: set osip includes last in the search path. 2006-09-19 Francois-Xavier Kowalski * linphone.spec.in (BuildRequires): 1.8 Add RPM build-time dependency on gettext-devel to define AM_GNU_GETTEXT. 2006-07-26 Sandro Santilli * .cvsignore, developer-docs/mediastreamer/.cvsignore, ipkg/.cvsignore, mediastreamer2/.cvsignore, mediastreamer2/build/.cvsignore, mediastreamer2/build/win32native/.cvsignore, mediastreamer2/include/.cvsignore, mediastreamer2/include/mediastreamer2/.cvsignore, mediastreamer2/plugins/.cvsignore, mediastreamer2/src/.cvsignore, mediastreamer2/tests/.cvsignore, oRTP/build/win32/.cvsignore, oRTP/build/win32native/.cvsignore, oRTP/src/tests/win_receiver/.cvsignore, oRTP/src/tests/win_sender/.cvsignore, po/.cvsignore, share/cs/.cvsignore: Added-fixed .cvsignore files 2006-07-20 gettextize * m4/codeset.m4: New file, from gettext-0.14.6. * m4/gettext.m4: New file, from gettext-0.14.6. * m4/glibc2.m4: New file, from gettext-0.14.6. * m4/glibc21.m4: New file, from gettext-0.14.6. * m4/iconv.m4: New file, from gettext-0.14.6. * m4/intdiv0.m4: New file, from gettext-0.14.6. * m4/intmax.m4: New file, from gettext-0.14.6. * m4/inttypes.m4: New file, from gettext-0.14.6. * m4/inttypes_h.m4: New file, from gettext-0.14.6. * m4/inttypes-pri.m4: New file, from gettext-0.14.6. * m4/isc-posix.m4: New file, from gettext-0.14.6. * m4/lcmessage.m4: New file, from gettext-0.14.6. * m4/lib-ld.m4: New file, from gettext-0.14.6. * m4/lib-link.m4: New file, from gettext-0.14.6. * m4/lib-prefix.m4: New file, from gettext-0.14.6. * m4/longdouble.m4: New file, from gettext-0.14.6. * m4/longlong.m4: New file, from gettext-0.14.6. * m4/nls.m4: New file, from gettext-0.14.6. * m4/po.m4: New file, from gettext-0.14.6. * m4/printf-posix.m4: New file, from gettext-0.14.6. * m4/progtest.m4: New file, from gettext-0.14.6. * m4/signed.m4: New file, from gettext-0.14.6. * m4/size_max.m4: New file, from gettext-0.14.6. * m4/stdint_h.m4: New file, from gettext-0.14.6. * m4/uintmax_t.m4: New file, from gettext-0.14.6. * m4/ulonglong.m4: New file, from gettext-0.14.6. * m4/wchar_t.m4: New file, from gettext-0.14.6. * m4/wint_t.m4: New file, from gettext-0.14.6. * m4/xsize.m4: New file, from gettext-0.14.6. * m4/Makefile.am (EXTRA_DIST): Add the new files. * Makefile.am (EXTRA_DIST): Add config.rpath. 2006-07-07 Francois-Xavier Kowalski * linphone.spec.in (Version): 1.7 Force build of gtk-doc, as the default choice that comes with GTK_DOC_CHECK m4 macro is "no". 2006-05-17 Francois-Xavier Kowalski * configure.in: 1.179 * configure.in: 1.178 Allow rpm users to use oRTP packages built from Linphone root. May 8 2006 : Sandro Santilli * configure.in: lowered libspeex requirement to 1.1.6 undefined yet: linphone-1.4.0 - new audio/video streaming engine (mediastreamer2) that let the following improvements: + split video frame to get them smaller than MTU + less video latency + video supported at CIF and QCIF resolution with variable framerate. + arts sound input/output + improved sound latency with alsa + alsa support works with dmix/dsnoop + old oss drivers that don't like select and non blocking mode should finally work - lpc10-15 support removed (speex does better at 8kbit/s) - jack support unported to mediastreamer2, sorry : volunteer needed - video resolution and framerate set according to bandwidth constraints. - linphonec new "soundcard" command to list and choose sound devices. - new download and upload bandwidth parameters. March 31 2006: linphone-1.3.4 - fix linphonec bug in auto-answer mode: was terminating call after a few seconds. March 28 2006: linphone-1.3.3 - various FedoraCore 4 compile problem solved - fix video bug ('could not mmap': happening on kernel>=2.6.15 with pwc driver) - SIP register were not using route field (submitted patch) March 17 2006: linphone-1.3.2 - fix early media call problem: ack for 200ok was never sent. March 10 2006: linphone-1.3.1 - fix compile errors with gcc-2.95 (thanks Wolfram Gloger) March 8 2006 : linphone-1.3.0 (SUMMARY) - a lots of improvements in linphonec (see strk 's Changelogs below) - telephone event problem with sipomatic. - updated cz translation. - fix bugs around addressbook. - video support with H263-1998/RFC2429 nearly clean. - added timeout for incoming calls February 20 2006 : Sandro Santilli * console/commands.c: added filter support for command 'friend list'. February 13 2006 : Sandro Santilli * console/linphonec.c: fixed unused variable warning. February 02 2006 : Sandro Santilli Console: - Padded vtable with missing callbacks (fixing a segfault on friends subscription) - Handled friends notify (bare version) - Handled text messages receive (bare version) - Printed message on subscription request (bare version) - Added 'friend list' and 'friend call' commands - Allowed for multiple DTMF send in a single line - Added status-specific callback (bare version) January 26 2006 : Sandro Santilli - Core: fixed bug in linphone_core_set_nat_address refusing to set address if use flag was off, added support for NULL addr parameter to only change use flag. - Support: added missing GFileTest enum values - Console: 'nat' commands, cleanups January 25 2006 : Sandro Santilli - Core: added request uri in proxy registration failure message - Console: removed the -t switch (terminate on close is default behaviour) January 20 2006 : Sandro Santilli - Console: linphonec_init() and linphonec_finish() functions. Handled SIGINT and SIGTERM to invoke linphonec_finish(). Handling of auto-termination (-t) moved to linphonec_finish(). Reworked main (input read) loop to not rely on 'terminate' and 'run' variable (dropped). configfile_name allocated on stack using PATH_MAX limit. Changed print_usage signature to allow for an exit_status specification. January 18 2006 : Sandro Santilli - Console: Command completion inhibited in proxy addition and auth request prompts. Avoided use of readline's internal filename completion. January 14 2006 : Sandro Santilli - Console: Reworked commands interface to use a table structure, used by command line parser and help function. Implemented first level of completion (commands). Added notification of invalid "answer" and "terminate" commands (no incoming call, no active call). Forbidden "call" intialization when a call is already active. Cleaned up all commands, adding more feedback and error checks. January 13 2006 : Sandro Santilli - Console: Added linphonec.h. Code layout change (added comments, forward decl, globals on top, copyright notices and Logs). Handled out-of-memory condition on history management. Removed assumption on sizeof(char). Fixed bug in authentication prompt (introduced by readline). Added support for multiple authentication requests (up to MAX_PENDING_AUTH). January 12 2006 : Sandro Santilli - Console: Changed default configuration file to ~/.linphonerc, automatically handling migration from old layout if not present (use ~/.linphonec or ~/.gnome2/linphone). Added compile-time define to show identity in prompt. Used EXIT_SUCCESS and EXIT_FAILURE macros. Made readline use ~/.linphonec_history file for reading/writing. Fixed auto-call handling code. Fixed OOB write of sscanf() in linphonec_parse_command_line(). Simplified control flow in linphonec_main_loop(). Put linphonec_{initialize,finish}_readline() calls out of main_loop(). Removed redundant exit(0) at end of main(). Fixed small leaks. December 14 2005 : linphone-1.2.0 - various ipv6 bugfixes (again) - fix po.pl file (was utf-8 but declared as iso8902) - enum/automatic proxy conflict solved. use "sip:7887488478" for enums, just "382884824" to go through the default proxy. - experimental video support progresses, compliance improved. - remove ilbc from source tree: it is now available as a separete plugin. - updated to work with lastest ffmpeg cvs - fix bug when answering 'busy here'; active call was closed ! - fix Ctrl+H bug in linphonec - added bresilian translation - added swedish translation - uri bar improvements - fix a DoS attack by setting a payload type number > 127 August 24 2005 : linphone-1.1.0 - peer to peer text chat - automatic sip url completion when using a default proxy: when user types 'mymother' in the url bar, linphone calls sip:mymother@[default-proxy] - ilbc 20 and 30 miliseconds frames support - support for setting a soundcard for playback and another for record - ipv6 bugfixes: works with ipv6 local loopback with sipomatic at least ! Needs to be tested in a real network. - gtk interface bugfixes - increase max supported sound devices (was 5). - automatic incoming redirections - experimental video support (needs special compilation procedure, see mailing lists) March 27 2005 : linphone-1.0.1 - patch for NAT traversal (SDP connection address in SDP answer) - patch for writing correct port information when NAT is enabled - patch for always using "rport" extension to traverse NAT with signalling. - patch for saving/restoring correct configuration of proxy: "reg_register" -> "reg_sendregister" and fix for saving "expires" March 21 2005 : linphone-1.0.0 - switch from osipua to eXosip/osip2 for improved sip functionnalities and compliance. - support for presence (busy, online...) for everyone in the address book (uses SUBSCRIBE/NOTIFY) - support for PUBLISH (presence information through sip servers) - support for configuring multiple proxies. - RTP adaptive jitter buffer ( provides lower latency ) - RTCP coumpound messages sent periodically. - on demand digest authentication for INVITE and REGISTER - support for 183 with sdp answers. - add support for jackd (contributed) - call logging - arm build improved - cz and nl translations contributed. January 2004 : linphone-0.12.2 - add enum support (see RFC3241 and RFC3026) Thanks to Rene Bartsch < ml at bartschnet dot de > for its usefull and precious help. - interactive presence box (no more need to click OK to confirm) - update spanish translation - alsa interface: the user can choose precisely the pcm device to be used by setting the sound/alsadev parameter of the config file. - use 1 RTP socket instead of 2: this makes linphone NAT-friendly. Thursday October 2 2003 : linphone-0.12.1 - add support for personalizing rings. - make glib dependency optionnal (but recommended). - add polish translation. - use of libartsc to suspend arts instead of killing him. Tuesday August 19 2003 : linphone-0.12.0 - add support for speex/16000 - re-enable alsa support (0.9.x) - few improvements on gui. - added spanish translation by Jesús Benítez Monday April 05 2003: linphone-0.11.0 - merge ipv6 support patch, contributed by Koichi KUNITAKE < kunitake at linux-ipv6 dot org > (thanks !) - some improvements on graphical interface. Friday Feb 28 2003 : linphone-0.10.1 - add firewall friendly capability - compiles all well on arm-linux - bugfixes. Friday Jan 24 2003 : linphone-0.10.0 - Gnome interface ported to gnome-2 - Unified core engine for both graphical and console interface. - Many bug fix and improvements in the SIP stack. - Japoneese translations and manual added, by Yamaguchi Yoshiya. - updated for speex-1.0rc1 - ported to FreeBSD thanks to Georg Shwarz Monday Oct 21 2002 : linphone-0.9.1 - AMD: add support for the "received" and "rport" parameter in osipua. Monday Oct 21 2002 : linphone-0.9.1 - integration of the patch of Lenaic Huard that adds the ability for linphone to send dtmf tones through rtp (only reported to console interface). - following this patch, a nice keypad has been added to the graphical interface to bring the fonctionnality of the patch gui's users. - integration of the patch of Aymeric Moizard, concerning compliance with RFC3261 (new sip's RFC) using the new dialog_t api of libosip. - updated for use of speex-beta1 (speex codec) - osipua fix by jack@atosc.org 1: reject calls with 603 2: establishement of 1 early dialog for incoming calls. 3: update to libosip CVS and its new OSIP_TRACE MACROs. 4: fix presence handling. 5: fix expires header in REGISTER. Monday Aug 26 2002: linphone-0.9.0 - the sdp rtpmap string for alaw and mulaw codec was incorrect. Sunday Aug 4 2002: linphone-0.9.0pre3 Non visible changes: - Linphone's internal audio architecture is ported to the new mediastreamer architecture. The mediastreamer library is already present in linphone since October 29 2001 but was never used at this time by the core program. Now lots of work has be done inside the mediastreamer so that it is ready to be used by the core program. As a consequence, the old architecture defined by the audio/ directory and the codec.c io.c files is dropped. The mediastreamer architecture provides a modular framework for audio and video processing. It includes various audio and video codecs (or wrappers to third party audio and video codecs). Visible changes: - The Speex codec (http://speex.sourceforge.net) is now availlable to linphone, since it has its mediastreamer wrapper. This patent-free codec provides two bitrates, the lowest being able to work with 56k dialup connections. This is a very important step, because from now only the low quality LPC10 codec was availlable for such connections. - Thanks to Florian Winsterstein (f-win at gmx.de), a console version of linphone called "linphonec" is availlable. Linphonec can be compiled without gnome. Wednesday May 8 2002: linphone-0.8.0 Visible changes: - Updated to libosip-0.8.5, that reflects the lastest sip draft. - Uses the SDP parser the libosip, and uses also a modified version of its SDP negociator. The MediaDesc object is removed, now osipua users have to deal with BodyHandler's, as the SdpHandler that deals with sdp message generation and negociation. The SDP parser and negociator can use the a=rtpmap fields, so that compliance is improved. - LPC10-1.5 codec has been assigned payload type 115 instead of 4 previously. This breaks compatibility with older versions of linphone. - oRTP (the new RTP library) is used in place of the old lprtplib. - fix endianess problems in the audio part. Non visible changes (changes on libraries not currently used by linphone but used later): - mediastreamer has new working objects: webcam image capture, mpeg encoding and decoding thanks to the libavcodec (ffmpeg) library, rtp wrapper thanks to oRTP. Tuesday April 15 2002: linphone-0.7.2 - A crash in property box "apply" fixed. - bugfix in osipua. Tuesday March 12 2002: linphone-0.7.1 - Some bug fixes by Bryan Ogawa in osipua: route, record-route, tags. Friday March 1 2002: linphone-0.7.0 - Digest authentification support added by Aymeric. - Improvements in property box. - Translations in German and Italian by J.J. Sarton and A. Zanoni - Bugfix and improvements in the osipua stack. - Better handling of the registration parameters (the user can set its own address of record). Thu December 6 2001 -osipua.c: Memory allocations. fi call of from_tag_add(...,sgetcopy(ua->fromtag)); -utils.c: Memory allocations. -CallLegs are removed automatically by the osipua layer (eg: when a transaction timed out). Tue November 27 2001 -Bugfix in osipua for proxy support: record-route and route header support, request-uri bug fixed. -New choice "outbound proxy" in the property box. -Fix compilation issues. Mon October 29 2001 -New unithreaded design of the osipua library, based on libosip-0.7.x series. -Asynchronous name resolution in osipua. -To and From tag support added. -Proxy support added. The user can choose between registering for a redirect server, or registering for a proxy server. When he choose proxy, then all requests are sent to the proxy. -New good looking graphical interface. -Mediastreamer is included, but still unused. It has a begin of gtkdoc dcocumentation. Wed September 26 2001 -Add registration and redirection ability in gui and osipua. -Critical bugs in osipua/osip fixed. -Display all sip error strings in appbar. -Documentation translated in French. Tue August 21 2001 - Add address book functionnality - Add ringback - Minor bugfixes in configure.in, src/callbacks.c - Work with libosip-0.6.1 Wed August 1 2001: - Integration with osip sip stack. lpsiplib is no more used and abandonned. osipua provides the session level on top of osip. - added G711 codecs. - configuration structures and api re-written for more clarity. - new codec selection box - new sytem for codec registration - automatic selection of codecs regarding to network connection type. - add resizing ability for icon applet. June 2001: linphone-0.3.0 -Nearly all code in linphone is object oriented. -linphone can be run as a gnome applet, or as a silent dameon, or as a normal application. -bugfixes in sip/sdp messages. -addition of a test program called sipomatic that can automatically answers to call. (for test) -add io_disk implementation of the audio lib. this can be used to replace the sond card by io on file system. Tue May 15 2001: linphone-0.2.1 -Fix a stupid bug in the audio library. Fri May 11 2001: linphone-0.2.0 -add many missing features of version 0.1.0 (for the property box) -audio levels on the main window. -Sip library modified: now it uses one thread, and should be able to handle several calls in the future. -audio library is more performant. It is able to find itself the best blocksize (latency) by testing the driver. -interactive help on the property box and user manual. -add an icon. linphone-0.1.0 : Released on april,19 2001 Initial version. linphone-3.12.0/Makefile.am000066400000000000000000000206751313432737600154700ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in # let make re-run automake upon need ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS) SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \ coreapi console gtk share scripts tools daemon tester include GITVERSION=`cd $(top_srcdir) && git describe --always || echo $(VERSION)` ACLOCAL_FLAGS=-I$(top_srcdir)/m4 OPTIONAL_SOUNDS=\ share/sounds/linphone/rings/synth.wav \ share/sounds/linphone/rings/tapping.wav \ share/sounds/linphone/rings/orig.wav \ share/sounds/linphone/rings/sweet.wav \ share/sounds/linphone/rings/rock.wav INSTALLDIR=$(abs_top_builddir)/linphone-install INSTALLDIR_WITH_PREFIX=$(INSTALLDIR)/$(prefix) ZIPFILE=$(abs_top_builddir)/$(PACKAGE)-win32-$(GITVERSION).zip ZIP_EXCLUDED=include lib \ $(OPTIONAL_SOUNDS) SDK_ZIPFILE=$(abs_top_builddir)/lib$(PACKAGE)-win32-sdk-$(GITVERSION).zip SDK_EXCLUDED= \ bin/linphone.exe \ lib/*.la \ share/linphone \ share/pixmaps \ share/locale \ share/gnome \ $(OPTIONAL_SOUNDS) GTK_PREFIX=/ GTK_THEME=Outcrop GTK_FILELIST=gtk+-2.24.8.filelist GTK_FILELIST_PATH=$(abs_top_srcdir)/$(GTK_FILELIST) LINPHONEDEPS_FILELIST=linphone-deps.filelist WINBINDIST_FILES=`cat $(abs_top_srcdir)/$(LINPHONEDEPS_FILELIST)` ISS_SCRIPT=linphone.iss ISS_SCRIPT_PATH=$(abs_top_srcdir)/$(ISS_SCRIPT) #path to Inno Setup 5 compiler ISCC=ISCC.exe PACKAGE_WIN32_FILELIST=$(PACKAGE)-win32.filelist PACKAGE_BUNDLE_FILE=$(top_srcdir)/build/macos/$(PACKAGE).bundle EXTRA_DIST = \ BUGS \ README.arm \ README.mingw \ README.macos.md \ README.md \ autogen.sh \ linphone.spec \ linphone.spec.in \ $(GTK_FILELIST) \ gen-gtkfilelist.sh \ $(LINPHONEDEPS_FILELIST) \ $(ISS_SCRIPT).in \ CMakeLists.txt \ cmake/FindGtkMacIntegration.cmake \ cmake/FindIconv.cmake \ cmake/FindIntl.cmake \ cmake/FindNotify.cmake \ cmake/FindSqlite3.cmake \ cmake/FindXML2.cmake \ cmake/FindZlib.cmake \ cmake/LinphoneConfig.cmake.in \ config.h.cmake \ console/CMakeLists.txt \ coreapi/CMakeLists.txt \ coreapi/gitversion.cmake \ coreapi/help/CMakeLists.txt \ gtk/CMakeLists.txt \ java/CMakeLists.txt \ pixmaps/CMakeLists.txt \ po/CMakeLists.txt \ share/CMakeLists.txt \ share/rings/CMakeLists.txt \ share/rootca.cmake \ tester/CMakeLists.txt \ tools/CMakeLists.txt DISTCLEANFILES= $(ISS_SCRIPT) $(PACKAGE_WIN32_FILELIST) CLEANFILES=Portfile Portfile-devel # `make rpm' all-local: linphone.spec linphone.iss linphone.spec: linphone.spec.in .phony: rpm rpm-novideo rpm-base rpm-base: $(MAKE) dist -rm -f $(PACKAGE)-$(VERSION).tar gunzip $(PACKAGE)-$(VERSION).tar.gz #remove ms2 and ortp spec file to make sure linphone spec file is used bu rpmbuild tar --delete --file=$(PACKAGE)-$(VERSION).tar $(PACKAGE)-$(VERSION)/mediastreamer2/mediastreamer2.spec tar --delete --file=$(PACKAGE)-$(VERSION).tar $(PACKAGE)-$(VERSION)/oRTP/ortp.spec gzip $(PACKAGE)-$(VERSION).tar rpm: rpm-base # TAR_OPTIONS=--wildcards rpmbuild -ta --clean --rmsource --rmspec $(PACKAGE)-$(VERSION).tar.gz rpm-novideo: rpm-base # TAR_OPTIONS=--wildcards rpmbuild -ta --clean --rmsource --rmspec $(PACKAGE)-$(VERSION).tar.gz --without video #a zip containing win32 binaries, suitable to generate an installer if BUILD_TUNNEL WINBINDIST_FILES+=./bin/libtunnel-0.dll endif other-cherrypick: cd $(GTK_PREFIX) && \ for file in $(WINBINDIST_FILES) ; do \ if test -d $(prefix)/$$file; then \ $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/$$file ;\ else \ cp $(prefix)/$$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\ fi \ done if test -d /mingw/bin ; then \ cp /mingw/bin/libgcc_s*.dll \ /mingw/bin/libstdc++-6.dll \ /mingw/bin/libintl-8.dll \ /mingw/bin/libiconv-2.dll \ /mingw/bin/pthreadGC2.dll \ $(INSTALLDIR_WITH_PREFIX)/bin/. ;\ fi gtk-cherrypick: cd $(GTK_PREFIX) && \ for file in `cat $(GTK_FILELIST_PATH)` ; do \ if test -d $(prefix)/$$file; then \ $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/$$file ;\ else \ cp $(prefix)/$$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\ fi \ done && \ $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/share/themes && \ cp -rf $(prefix)/share/themes/$(GTK_THEME) $(INSTALLDIR_WITH_PREFIX)/share/themes/. zip: rm -f $(ZIPFILE) rm -rf $(INSTALLDIR) $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) #remove unwanted linphone stuff cd $(INSTALLDIR_WITH_PREFIX) && rm -rf $(ZIP_EXCLUDED) #add gtk dlls and files make gtk-cherrypick make other-cherrypick cp -f $(top_srcdir)/gtk/gtkrc $(INSTALLDIR_WITH_PREFIX)/. cp -f $(top_srcdir)/README $(INSTALLDIR_WITH_PREFIX)/. cp -f $(top_srcdir)/COPYING $(INSTALLDIR_WITH_PREFIX)/. cd $(INSTALLDIR_WITH_PREFIX) && zip -r $(ZIPFILE) * sdk: rm -f $(SDK_ZIPFILE) rm -rf $(INSTALLDIR) $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) # remove unwanted stuff (gtk interface) cd $(INSTALLDIR_WITH_PREFIX) && rm -rf $(SDK_EXCLUDED) make other-cherrypick cp -f $(top_srcdir)/README $(INSTALLDIR_WITH_PREFIX)/. cp -f $(top_srcdir)/COPYING $(INSTALLDIR_WITH_PREFIX)/. cd $(INSTALLDIR_WITH_PREFIX) && zip -r $(SDK_ZIPFILE) * filelist: zip cd $(INSTALLDIR_WITH_PREFIX) && \ rm -f $(PACKAGE_WIN32_FILELIST) && \ for file in `find` ; do \ if ! test -d $$file ; then \ echo "Source: $$file; Destdir: {app}\\`dirname $$file`; Flags: ignoreversion" \ >> $(PACKAGE_WIN32_FILELIST) ;\ fi \ done ### LOCALIZATION pull-transifex: tx pull -af push-transifex: $(MAKE) -C po update-po tx push -s -f --no-interactive ### WINDOWS setup.exe: filelist cp $(ISS_SCRIPT) $(INSTALLDIR_WITH_PREFIX)/. cd $(INSTALLDIR_WITH_PREFIX) && \ $(ISCC) $(ISS_SCRIPT) mv $(INSTALLDIR_WITH_PREFIX)/Output/setup.exe $(PACKAGE)-setup-$(GITVERSION).exe rm -rf $(INSTALLDIR_WITH_PREFIX)/Output rm -f $(INSTALLDIR_WITH_PREFIX)/$(PACKAGE_WIN32_FILELIST) rm -f $(INSTALLDIR_WITH_PREFIX)/$(ISS_SCRIPT) ### newdate: cd gtk && $(MAKE) newdate if HAVE_MD5SUM GEN_MD5=`$(MD5SUM) linphone-$(VERSION).tar.gz | awk {'print $$4'}` else GEN_MD5=`$(MD5SUM) linphone-$(VERSION).tar.gz | awk {'print $$1'}` endif Portfile: $(top_srcdir)/scripts/Portfile.tmpl dist sed -e 's/\@VERSION\@/$(LINPHONE_VERSION)/g' \ -e 's/\@LINPHONE_MD5\@/$(GEN_MD5)/' < $< > $@ Portfile-devel: $(top_srcdir)/scripts/Portfile-devel.tmpl dist sed -e 's/\@VERSION\@/$(LINPHONE_VERSION)/g' \ -e 's/\@LINPHONE_MD5\@/$(GEN_MD5)/' < $< > $@ ### MAC MACAPPNAME=Linphone.app MACAPPZIP=$(PACKAGE)-$(GITVERSION).app.zip MACAPPDMG=$(PACKAGE)-$(GITVERSION).dmg MACAPPPKG=$(PACKAGE)-$(GITVERSION).pkg BUNDLEPREFIX=./ BUNDLEDIR=$(BUNDLEPREFIX)$(MACAPPNAME) #a path prefix where additional libs can be cherry-picked by the bundler. LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX=/usr/local Linphone.app: rm -rf $(INSTALLDIR) $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) BUNDLE_PREFIX=$(BUNDLEPREFIX) \ LINPHONE_INSTALL_PREFIX=$(INSTALLDIR_WITH_PREFIX) \ LIBLINPHONE_INSTALL_PREFIX=$(INSTALLDIR_WITH_PREFIX) \ MS2_PLUGINS_INSTALL_PREFIX=$(prefix) \ LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX=$(LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX) \ gtk-mac-bundler $(PACKAGE_BUNDLE_FILE) patch ${BUNDLEDIR}/Contents/Resources/share/themes/Quartz/gtk-2.0/gtkrc ${srcdir}/build/macos/quartz-theme-gtkrc.patch rm -f ${BUNDLEDIR}/Contents/Resources/lib/libopenh264* bundle: $(MACAPPNAME) cd $(BUNDLEDIR)/.. && rm -f $(MACAPPZIP) && zip -r $(MACAPPZIP) $(MACAPPNAME) && cd - cd $(BUNDLEDIR)/.. && rm -f $(MAXAPPDMG) && hdiutil create $(MACAPPDMG) -srcfolder $(MACAPPNAME) -ov && cd - signed-bundle: $(MACAPPNAME) codesign --deep -s "$(BUNDLE_SIGNING_ID)" $(BUNDLEDIR) cd $(BUNDLEDIR)/.. && rm -f $(MAXAPPDMG) && hdiutil create $(MACAPPDMG) -srcfolder $(MACAPPNAME) -ov && cd - pkg: $(MACAPPNAME) rm -rf ./packaging mkdir -p ./packaging cp ${srcdir}/COPYING ./packaging cp ${srcdir}/pixmaps/linphone.png ./packaging pkgbuild --install-location /Applications --scripts ${srcdir}/build/macos/pkg-scripts --component $(MACAPPNAME) ./packaging/linphone.pkg productbuild --resources . --distribution ${srcdir}/build/macos/pkg-distribution.xml --package-path ./packaging $(MACAPPPKG) signed-pkg: pkg mv $(MACAPPPKG) $(MACAPPPKG).tmp productsign --sign "$(BUNDLE_SIGNING_ID)" $(MACAPPPKG).tmp $(MACAPPPKG) rm -f $(MACAPPPKG).tmp ### ### CLEAN clean-local: rm -rf $(BUNDLEDIR) discovery: touch specs.c $(CC) --include $(top_builddir)/config.h \ $(TUNNEL_CFLAGS) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) $(SIPSTACK_CFLAGS) $(BCTOOLBOXTESTER_CFLAGS) -E -P -v -dD specs.c .PHONY: $(MACAPPNAME) pkg linphone-3.12.0/NEWS000066400000000000000000000405301313432737600141230ustar00rootroot00000000000000linphone-3.12.0 -- July 21st, 2017 * C++ and C# wrappers. * Account Creator plugin API. * Xamarin support. linphone-3.11.1 -- March 2nd, 2017 * Bugfix in Autotools scripts linphone-3.11.0 -- February 24th, 2017 * Security vulnerability fix concering TLS. The common name of certificats delivered by SIP proxies were not compared with their hostname. * Improvement of H.264 hardware encoder/decoder on MacOSX. * Fix a bug in P2P presence system. Unchecking `Allow this contact to see my presence` checkbox in contact editing view had no any effect. Then it was not possible to hide presence informations to a contact once it had been allowed to see it. * Performance improvments of the presence list feature. * IM Encription Engine: abstraction for messaging encription protocols. linphone-3.10.2 -- August 30th, 2016 * Fixing linphone python version compilation - fixing vcards linphone-3.10.0 -- August 8th, 2016 * Adding lime_experimental_feature : set to 1 in .linphonerc [GtkUi] to show the Lime menu in the graphical user interface. Caution : Experimental. * Video conference support through a conference server (SDK only) * Disable dummy STUN packets sending when ICE is activated. * Signal AVPF support as AVP : Enable rtcp feedback on RTP/AVP by default * Adding linphone daemon * gtk - Show links to files received in chat by file transfer * gtk - Debug window now stores “scroll to end†preference * gtk - Added button to take screenshot of video call   * Fix - gtk : Fixed issue busy presence not displayed in red * Fix 0002832: Date/time of calls not shown in call history on Windows. * Fix 0002690: Bad SDP when no audio codec has been enabled * Fix 0000750: DTMF RFC2833 event always goes up in the same LinphoneCoreListener linphone-3.9.1 -- November 16th, 2015 * Fix crash when recording video calls with the VP8 codec * Fix H.264 codec support in Mac OS X package * Fix translation of account assistant * Bug fixes linphone-3.9.0 -- November 2nd, 2015 * Video recording of calls in MKV format * Clickable URLs in chat view * Add buttons to change the record and playback volumes during a call * Add button to start chatting without having to create a contact first * Some icon changes * Call logs now stored in database * Bug fixes linphone-3.8.5 -- June 30th, 2015 * Fix bug about status icon on MacOSX. Attention request worked only once * Fix crash at the end of the audio assistant * Fix crash when configuring a remote provisioning * Fix regression in the codec view. Codec which are not usable because bandwidth limits are to low were not greyed anymore * Fix language selection on Windows and MacOSX * Add translation for 'Arabic' and 'Turkish' items in the language selection list linphone-3.8.4 -- June 9th, 2015 * Add a built-in XMLRPC client. Linphone does not depend on libsoup anymore linphone-3.8.3 -- June 4th, 2015 * Fix status icons on all platforms (Windows, MacOS, non-KDE Linux desktop environment) linphone-3.8.2 -- May 7th, 2015 Application level improvements: * add support of the StatusNotifierItem standard to display a status icon on KDE5 * auto-answering can be set through the preferences panel * bug fixes Liblinphone level improvements: * fix audio bug with opus codec * fix ICE corner case not properly handled and resulting bad final ice status * update SO version to 7 (it should have been done in 3.8.0) * bug fixes linphone-3.8.1 -- March 31th, 2015 Application level improvements: * Auto-answer ability * Improvement of UI appearance on Mac OSX * Bug fixes linphone-3.8.0 -- March 11th, 2015 Application level improvements: * The video window has now controls in order to switch fullscreen mode and terminate call. * The out of call video preview feature (to test camera) is moved into the settings and is no longer linked to the in-call video preview feature. * Add an assistant to help users to set audio/video parameters * Some ergonomics improvments (checkbox to set random port for UDP and TCP, ...) * Lots of updated translations. Arabic translation has been added * Experimental feature: play an MKV file by drag-and-dropping it on the video call window Liblinphone level improvements: * Support for RTP/AVPF (RFC4585) for video streams, allowing fast transmission error recovery with VP8 codec only. * API enhancements, most objects can be ref-counted. * Add some getter funtctions to the call information API * Add a function in the API to accept early-media calls * Add a function to set the SIP transport timeout * Add a function to change adaptive rate algorithm at runtime * Add support of file transfer * Call video recording feature, in mkv format (H264 streams only for the moment) * Call playing feature: play an MKV file and send the audio/video stream through a call * Local player API. Play WAV and MKV file and display video on a specified window display * A wrapper for Python has been made * Support of Wake Locks on Android * Support of multicast IP addresses * Support of incoming UPDATEs within dialog (RFC3311) * Support of SRTP by using packages from GNU/Linux distributions linphone-3.7.0 -- February 20th, 2014 Application level improvements: * It is now possible to configure multiple proxy accounts with different transports (UDP, TCP, TLS) * can work with IPv6 and IPv4 simultaneously * User can choose video rendering method on Linux * Video HD formats support added, leveraging on multiple cores for encoding if available * Keyboard can be used for DTMF input * Faster and higly responsive UI thanks to fully asynchronous operation of the liblinphone. * Addon of opus codec * Possibility to specify a remote provisioning http URI for configuration * LDAP search integration for Linux and MacOSX * is-composing notification in chat area Liblinphone level improvements thanks to new "belle-sip" SIP stack: * multiple SIP transports simultaneously now allowed * IP dual stack: can use IPv6 and IPv4 simultaneously * fully asynchronous behavior: no more lengthly DNS or connections * +sip.instance parameter (RFC5626) * alias parameter (RFC5923) * better management of network disconnections * SIP/TLS handled through lightweighted polarssl library (instead of openssl) * SIP transaction state machines improved (RFC6026) * Privacy API (RFC3323, RFC3325) * Full support of rich presence in (RFC4480) * Better handling of sips scheme in URIs. * Messaging: support of is-composing (RFC3994) * Call transfer fixes in error cases * Add API for managing SIP SUBSCRIBES/NOTIFY/PUBLISH (linphonecore/event.h) * bugfixes Requires: mediastreamer2 = 2.10.0, ortp = 0.23.0, belle-sip = 1.3.0 linphone-3.6.1 -- June 17, 2013 * fix memory leak with some video cameras on windows. Requires: mediastreamer2 = 2.9.1 and ortp = 0.22.0 linphone-3.6.0 -- May 27, 2013 UI: * new friend list and chat messaging UI * enhanced call history * call and conference audio recording * persistent chat history * DSCP settings for SIP and RTP * display of call statistics (when clicking on the quality indicator bar) core: * ICE for efficient RTP exchange * fix bug in zRTP support (upgrade required) * call recording * uPnP * call statistics * adaptive bitrate control improvements * faster call quality indicator feedback * DSCP settings for SIP and RTP * detailed call statistics feedback API Requires: mediastreamer2 = 2.9.0 and ortp = 0.22.0 linphone-3.5.2 -- February 22, 2012 * updated oRTP to 0.20.0 * updated mediastreamer2 to 2.8.2 * added ZRTP media encryption * added SILK audio codec linphone-3.5.1 -- February 17, 2012 * gtk - implement friend search by typing into the friendlist, and friend sorting linphone-3.5.0 -- December 22, 2011 * added VP-8 video codec * added G722 audio codec * added SIP/TCP and SIP/TLS * added SRTP media encryption * Audio conferencing * UI: call history tab, menu simplified * UI: cosmetics for incall views * UI: integration with libnotify * UI: show registered SIP accounts * Fixes for MacOS X, and uses GtkQuartz engine linphone-3.4.3 -- March 28, 2011 * Fully ported to mac os x with gtk-osx (menu integration, bundle generation with "make bundle", sound I/O improved) but still audio only * Fix stupid warning "no response" that sometimes arrived at end of calls * limit the size of the log window (to prevent memory drain) * limit the size of the SDP message by removing unnecessary information (for well known codecs, for H264). This is to prevent SIP messages from being discarded by routers on the internet when they exceeds in size the internet MTU. * other sip bugfixes Requires mediastreamer-2.7.3 linphone-3.4.2 -- March 3rd, 2011 * fix problems with webcams on windows Requires mediastreamer-2.7.2 linphone-3.4.1 -- February 17th, 2011 * bugfixes * gtk executable is renamed "linphone" (was linphone-3 before) Requires mediastreamer-2.7.1 linphone-3.4.0 -- February 7th, 2011 * implement multiple calls feature: - call hold (with possibility to play a music file) - call resume - acceptance of 2nd call while putting the others on hold - creation of another outgoing call while already in call - blind call transfer - attended call transfer **CAUTION**: LinphoneCoreVTable has changed: pay attention to this when upgrading an old application to a newer liblinphone. * improve bandwidth management (one b=AS line is used for audio+video) * improvements in the echo limiter performance * implement a echo calibration feature (see linphone_core_start_echo_calibration()). * stun support bugfixes * possibility to use two video windows, one for local preview, one for remote video (linphonec only) * optimize by not re-creating streams when SDP is unchanged during a reinvite * support for sending early media * doxygen doc and javadoc improvements * based on mediastreamer-2.7.0, please refer to mediastreamer NEWS for changes. linphone-3.3.2 -- July 1st, 2010 * fix crash when setting firewall address in gtk interface * fix crash while closing video window on windows * fix un-sent BYE message in some rare cases. Requires: mediastreamer2-2.6.0 ortp-0.16.3 linphone-3.3.1 -- June 3, 2010 * fix bugs when carrying non ascii displaynames in SIP messages * fix crash when codecs are incompatible * fix bug with streams not restarted in case of reinvites Requires: mediastreamer2-2.5.0 ortp-0.16.3 linphone-3.3.0 -- May 19, 2010 * liblinphone is ported to iphoneOS and Google Android * Internal refactoring of liblinphone (code factorisation, encapsulation of signaling) * enhancements made to presence support (SIP/SIMPLE) * new icons * new tabbed ui * be nat friendly using OPTIONS request and using received,rport from responses. * use stun guessed ports even if symmetric is detected (works with freeboxes) * improve bitrate usage of speex codec * allow speex to run with vbr (variable bit rate) mode * add speex/32000 (ultra wide band speex codec) * answer empty SIP INFO requests * reverse order of call logs * optimize video display * manual or automatic network connectivity management (so that REGISTERs are only sent when network is up or refreshed when it comes back) linphone-3.2.1 -- October 5, 2009 * improve graphics and behaviour of mute button * updated translations * windows installer installs reg keys to indicate windows to start linphone clicking of sip uris * workaround a bug Gtk-macos X with modal popup windows, preventing to answer calls linphone-3.2.0 -- September 17, 2009 * new in-call layout * new idle view with two buttons * ability to dial the number from dialpad * improve local IP address detection when having multiple networks (ex: VPNs) * use proxy's received and rport params from via in Contact header when possible * port to mac os X leopard (using gtk/x11), audio only for now * DevC++ support now deprecated, use msys/mingw (see README.mingw for details) * add an option to ask linphone to place a call, whenever an instance is already running or not: should be useful for starting calls from a web browser recognizing the 'sip:' uri scheme. * french and italian translation updated * don't show ffmpeg codecs when encoder are disabled in ffmpeg library. * bugfixes in: - video4linux2 support - alsa support - socket leak in mtu discovery linphone-3.1.2 -- May 5, 2009 * make it work with lastest ffmpeg swscale * improve theora packer * update theora default settings to match performance of 1.0 release. * fix a random crash during video resizing on linux with SDL. linphone-3.1.1 -- April 14, 2009 * fix crash when opening property box, in some rare case * windows version uses the new libmsdscap plugin (video capture using directshow) * improved translations linphone-3.1.0 -- March 16, 2009 * linphone can now send large video pictures: up to SVGA, configurable via the user interface * automatic rescaling of the video windows to the video size of incoming stream * improved webcam support on windows * plenty of user interface cosmetic improvements * set a user friendly gtk theme by default on windows * linphonec can compile and work without libreadline * enable translations on windows * enable lookups of SRV records * new 'linphonecsh' program to send commands to a linphonec running as daemon using unix sockets or tcp. * bugfixing as usual linphone-3.0.0 -- October 13, 2008 * new graphical interface based on Glade/Gtk * systray icon * STUN working for RTP * fully ported to windows * accurate bandwidth management (to let video occupy all remaning bandwidth) * new H264 plugin based on x264 (msx264) * automatic call hangup upon media transmission faillure linphone-2.1.1 -- February 13, 2008 * fix interroperability bug with Asterisk about a BYE not sent by linphone. * fix alsa support weakness (capture underruns not recovered) linphone-2.1.0 -- January 25, 2008 * 4CIF support * enable resizing of video output * hu translation added linphone-2.0.1 -- November 30, 2007 * fix interop issue with asterisk * answer OPTIONS and other SIP messages * allow usage of ALSA user pcm devices with the sound->alsadev config item. linphone-2.0.0 -- November 15, 2007 * port to libeXosip2-3.0.x with libosip2-3.0.x * implements early media * implements incoming re-INVITE * presence support improvements * ipv6 working on windows * implements SDP over 200ok/ACK * add experimental snow codec support * answers to VFU request in SIP-INFO by sending an I-frame. * ffmpeg rate control enabled, improved mpeg4 quality for low bandwidths * separate video grabbing and display in linphonec linphone-1.7.1 -- April 16, 2007 * cz translation * compilation bugfixes (when video support is disabled) * fix IM icons path bug linphone-1.7.0 -- April 11, 2007 * new splash screen when no webcam is detected * new friend commands for linphonec * gnome interface becomes gtk-only * fix issue with codec bitrate settings when no bandwidth limits are given * open rtp sockets before sending SDP offer or answer (so that we don't miss the first I-frame) linphone-1.6.0 -- January 23, 2007 * Video4Linux V2 support with mjpeg cameras * use MPEG4 config string provided in the SDP (if any) * fix bug when choosing an invalid ring sound file * fix bug when using quickcam driver with CIF size * reduce audio bandwidth usage for <128kbit/s connections with video linphone-1.5.1 -- November 14, 2006 * fix translations linphone-1.5.0 -- October 11, 2006 * compliant theora support (using Luca Barbato's draft) * mpeg4 support (compliant with RFC3016) * controls to display and modify video codec list (gnome interface) * banwidth usage improvements * splash screen when no webcam is detected linphone-1.4.1 -- September 18, 2006 * fixes crash when attempting to make two simultaneous calls * fixes crash when having no soundcard * require theora>=1.0.0-alpha7 * do not allow resizing of the gnome interface * do not change mixer settings at startup linphone-1.4.0 -- September 11, 2006 * no more glib dependency at all * new mediastreamer2 framework for audio/video streaming * stable video support with H.263-1998 * echo cancelation * experimental theora support * jack support unported to mediastreamer2, sorry : volunteer needed * video resolution and framerate set according to bandwidth constraints. * linphonec new "soundcard" command to list and choose sound devices. * new download and upload bandwidth parameters used to find suitable video/audio codec choice and parameters. * new 'play' and 'record' functions to linphonec to play and record wav files * arts sound backend linphone-3.12.0/README.M68k.txt000066400000000000000000000110101313432737600156350ustar00rootroot00000000000000 LINPHONE ON M68k-LINUX (by GIAN) ******************************** The console version of linphone works on arm-linux, but also on m68k-linux? I’m trying to provide this: * I used the same toolchain specified in the LTIB menu configuration, that is, on my system: /opt/mtwk/usr/local/m68k-linux/gcc-3.4.0-glibc-2.3.2-v4e * I have created within my home directory a ColdFire-install-environment/ directory, copied into it the fresh tarballs of libogg-1.1.3, libosip2-2.2.2, speex-1.1.12, linphone-1.4.0 readline-5.1 and ncurses-5.5 (readline needs ncurses) Uncompressed all these tarballs. Very important things common to all packages being cross compiled: ****************************************************************** * Copy the ipaq-config.site in the ipkg/ directory of linphone into some safe place, for example: cp /home/gianluca/ColdFire-install-environment/linphone-1.4.0-install/linphone-1.4.0/ipkg/ipag-config.site /home/gianluca/ColdFire-install-environment/linphone-1.4.0-install/ Edit the ipaq-config.site file and replace all the arm-linux strings with m68k-linux one. Add also the –mcfv4e flag to the CFLAGS, CXXFLAGS, and CPPFLAGS labels. * You need a directory that we call M68K_INSTALL_TREE that will own files in the same way they will be installed on the target computer. mkdir /ColdFire-linphonec-1.4.0-mcfv4e export M68K_INSTALL_TREE=/ColdFire-linphonec-1.4.0-mcfv4e export CONFIG_SITE=/home/gianluca/ColdFire-install-environment/linphone-1.4.0-install/ipaq-config.site export PATH=$PATH:/opt/mtwk/usr/local/m68k-linux/gcc-3.4.0-glibc-2.3.2-v4e/bin Cross compiling ncurses for M68k: ******************************** ./configure --prefix=/usr --host=m68k-linux --with-gnu-ld --with-shared make make install DESTDIR=$M68K_INSTALL_TREE Cross compiling readline for M68k: ********************************* ./configure --prefix=/usr --host=m68k-linux --with-gnu-ld --disable-static make make install DESTDIR=$M68K_INSTALL_TREE Cross compiling libosip for M68k: ******************************** ./configure --prefix=/usr --host=m68k-linux --with-gnu-ld --disable-static make make install DESTDIR=$M68K_INSTALL_TREE Cross compiling libogg for M68k: ******************************** ./configure --prefix=/usr --host=m68k-linux --with-gnu-ld --disable-static –enable-fixed-point make make install DESTDIR=$M68K_INSTALL_TREE Cross compiling speex for M68k: ******************************** ./configure --prefix=/usr --host=m68k-linux --with-gnu-ld --disable-static --enable-fixed-point --enable-m68k-asm --with-ogg=/ColdFire-linphonec-1.4.0-mcfv4e/usr --with-ogg-includes=/ColdFire-linphonec-1.4.0-mcfv4e/usr/include –with-ogg-libraries=/ColdFire-linphonec-1.4.0-mcfv4e/usr/lib --disable-oggtest make make install DESTDIR=$M68K_INSTALL_TREE cp /home/gianluca/ColdFire-iinstall-environment/linphone-1.4.0-install/linphone-1.4.0/mediastreamer2/src/.libs/libquickstream.so.0.0.0 /opt/mtwk/usr/local/m68k-linux/gcc-3.4.0-glibc-2.3.2-v4e/m68k-linux/lib cd /opt/mtwk/usr/local/m68k-linux/gcc-3.4.0-glibc-2.3.2-v4e/m68k-linux/lib ln -s libquickstream.so.0.0.0 libquickstream.so.0 ln -s libquickstream.so.0.0.0 libquickstream.so cp /home/gianluca/ColdFire-install-environment/linphone-1.4.0-install/linphone-1.4.0/mediastreamer2/src/.libs/libmediastreamer.so.0.0.0 /opt/mtwk/usr/local/m68k-linux/gcc-3.4.0-glibc-2.3.2-v4e/m68k-linux/lib cd /opt/mtwk/usr/local/m68k-linux/gcc-3.4.0-glibc-2.3.2-v4e/m68k-linux/lib ln -s libmediastreamer.so.0.0.0 libmediastreamer.so.0 ln -s libmediastreamer.so.0.0.0 libmediastreamer.so Cross compiling linphone for M68k: ******************************** First you need to remove all .la files from the M68K_INSTALL_TREE because it confuses libtool and makes the linker use your build machine binaries instead of the m68k-crosscompiled ones. rm -f $M68K_INSTALL_TREE/usr/lib/*.la #for some reason pkg-config doesn't like cross-compiling... export PKG_CONFIG=/usr/bin/pkg-config ./configure --prefix=/usr --host=m68k-linux --with-gnu-ld --disable-static --disable-strict --enable-gnome_ui=no --enable-alsa --disable-glib --disable-video --with-osip=$ARM_INSTALL_TREE/usr --with-osipparser=$ARM_INSTALL_TREE/usr --with-readline=$ARM_INSTALL_TREE/usr SPEEX_CFLAGS="-I$ARM_INSTALL_TREE/usr/include" SPEEX_LIBS="-L$ARM_INSTALL_TREE/usr/lib -lspeex" make make install DESTDIR=$M68K_INSTALL_TREE Binaries can also be stripped with m68k-linux-strip to save more space. Running linphone under the ColdFire board ******************************************** You just have to start linphone from a terminal by typing 'linphonec'. Gianluca Salvador linphone-3.12.0/README.arm000066400000000000000000000112371313432737600150640ustar00rootroot00000000000000 LINPHONE ON ARM-LINUX (HANDHELD COMPUTERS) ****************************************** The console version of linphone works on arm-linux and has been tested on ipaqs under the familiar linux distribution (http://www.handhelds.org). You can find .ipk binary packages on the linphone's download page. If you want to build your own arm-linux packages, here are some instructions to cross compile linphone and its dependencies: readline, speex and libosip. This is my own experience on cross compiling software. As there is no precise step by step documentation (as far as I know) on how to cross-compile on arm, there is no guaranty that the following instructions are the best way to do it. First, be aware that only the console version of linphone can compile on ARM. * You need the lastest arm toolchain from http://www.handhelds.org. Uncompress it in / . It contains all the cross-compilation tools. Be sure that the arm-linux-gcc binaries are in your PATH (export PATH=$PATH:/usr/local/arm/3.4.1/bin/ , for example) * create within your home directory a arm/ directory, copy into it the fresh tarballs of libosip2>=2.2.x, speex>=1.1.6, linphone>=1.2.1 readline>=5.1 and ncurses>=5.5 (readline needs ncurses) Uncompress all these tarballs. Very important things common to all packages being cross compiled: ****************************************************************** * copy the ipaq-config.site in the ipkg/ directory of linphone into some safe place, for example: ~/ipaq-config.site . * You need a directory that we call ARM_INSTALL_TREE that will own files in the same way they will be installed on the target computer. It is also used to build linphone over the arm binaries of its dependencies (speex,osip,ncurses,readline). For example: export CONFIG_SITE=~/ipaq-config.site export ARM_INSTALL_TREE=/armbuild Cross compiling ncurses for ARM: ******************************** ./configure --prefix=/usr --host=arm-linux --with-gnu-ld --with-shared make make install DESTDIR=$ARM_INSTALL_TREE make install DESTDIR=`pwd`/armbuild Cross compiling readline for ARM: ********************************* ./configure --prefix=/usr --host=arm-linux --with-gnu-ld --disable-static make make install DESTDIR=$ARM_INSTALL_TREE make install DESTDIR=`pwd`/armbuild Cross compiling libosip for ARM: ******************************** ./configure --prefix=/usr --host=arm-linux --with-gnu-ld --disable-static make make install DESTDIR=$ARM_INSTALL_TREE make install DESTDIR=`pwd`/armbuild Cross compiling speex for ARM: ******************************** First you need to remove ogg headers from your build system to avoid a dirty conflict between your build machine binaries and the arm binaries. They are usually in a libogg-dev package (rpm or deb). Then: ./configure --prefix=/usr --host=arm-linux --with-gnu-ld --disable-static --enable-fixed-point --enable-arm-asm make make install DESTDIR=$ARM_INSTALL_TREE make install DESTDIR='pwd'/armbuild Cross compiling linphone for ARM ******************************** First you need to remove all .la files from the ARM_INSTALL_TREE because it confuses libtool and makes the linker use your build machine binaries instead of the arm-crosscompiled ones. rm -f $ARM_INSTALL_TREE/usr/lib/*.la #for some reason pkg-config doesn't like cross-compiling... export PKG_CONFIG=/usr/bin/pkg-config ./configure --prefix=/usr --host=arm-linux --with-gnu-ld --disable-static \ --disable-glib --with-osip=$ARM_INSTALL_TREE/usr \ --with-readline=$ARM_INSTALL_TREE/usr \ SPEEX_CFLAGS="-I$ARM_INSTALL_TREE/usr/include" \ SPEEX_LIBS="-L$ARM_INSTALL_TREE/usr/lib -lspeex " make make install DESTDIR='pwd'/armbuild You can use the install trees libosip2-x.x.x/armbuild speex-x.x.x/armbuild and linphone-0.x.x/armbuild/ to make binary packages of each software, as ipkgs for the familiar distribution (http://www.familiar.org). In the ipkg/ directory of linphone you can find .control files for ipkg-build. In order to make the osip ipkg, you have to do the following: - create a directory named CONTROL inside libosip2-2.2.x/armbuild - copy the libosip.control file into CONTROL/ and rename it into "control". - edit the "control" file to adjust version number accordingly. - remove the non essential parts of libosip inside libosip2-2.x.x/armbuild/usr/ : just leave the lib/ directory. This saves space on the destination computer. - then inside libosip2-2.x.x, run ipkg-build -o root -g root armbuild The same procedure applies to make linphone's ipkg. Binaries can also be stripped with arm-linux-strip to save more space. Running linphone under the handheld computer ******************************************** You just have to start linphone from a terminal by typing 'linphonec'. Simon linphone-3.12.0/README.macos.md000066400000000000000000000145021313432737600160040ustar00rootroot00000000000000# Linphone on MacOS X ## Build prerequisite * Xcode (download from apple or using appstore application) * [Java SE](http://www.oracle.com/technetwork/java/javase/downloads/index.html) or openJDK This is required to generate a C sourcefile from SIP grammar using [antlr3](http://www.antlr3.org/) generator. * [HomeBrew](http://brew.sh) or [Macports](http://www.macports.org/). ### Dependencies #### Using MacPorts ##### Multiple MacOS version support In order to enable generation of bundle for older MacOS version, it is recommended to: Edit `/opt/local/etc/macports/macports.conf` to add the following line: > macosx_deployment_target 10.7 > buildfromsource always ##### Linphone library (liblinphone) sudo port install automake autoconf libtool pkgconfig intltool wget bcunit \ antlr3 speex readline sqlite3 openldap libupnp \ ffmpeg-devel -gpl2 ##### Linphone UI (GTK version) Install `GTK`. It is recommended to use the `quartz` backend for better integration. sudo port install gtk2 +quartz +no_x11 sudo port install gtk-osx-application-gtk2 +no_python sudo port install hicolor-icon-theme #### Using HomeBrew ##### Linphone library (liblinphone) brew install intltool libtool wget pkg-config automake libantlr3.4c \ homebrew/versions/antlr3 gettext speex ffmpeg readline libvpx opus ln -s /usr/local/bin/glibtoolize /usr/local/bin/libtoolize brew link --force gettext #readline is required from linphonec.c otherwise compilation will fail brew link readline --force ##### Linphone UI (GTK version) brew install cairo --without-x11 brew install gtk+ --without-x11 brew install gtk-mac-integration hicolor-icon-theme ### Building Linphone The next pieces need to be compiled manually. * To ensure compatibility with multiple MacOS versions it is recommended to do: export MACOSX_DEPLOYMENT_TARGET=10.7 export LDFLAGS="-Wl,-headerpad_max_install_names" * (MacPorts only) Install libantlr3c (library used by belle-sip for parsing) git clone -b linphone git://git.linphone.org/antlr3.git cd antlr3/runtime/C ./autogen.sh ./configure --disable-static --prefix=/opt/local && make sudo make install * Install polarssl (encryption library used by belle-sip) git clone git://git.linphone.org/polarssl.git cd polarssl ./autogen.sh && ./configure --prefix=/opt/local && make sudo make install * Install libvpx (Must be manualy build because the macport recipe does not support 'macosx_deployment_target') git clone https://chromium.googlesource.com/webm/libvpx -b v1.4.0 cd libvpx ./configure --prefix=/opt/local \ --target=x86_64-darwin10-gcc \ --enable-error-concealment \ --enable-multithread \ --enable-realtime-only \ --enable-spatial-resampling \ --enable-vp8 \ --disable-vp9 \ --enable-libs \ --disable-install-docs \ --disable-debug-libs \ --disable-examples \ --disable-unit-tests \ --as=yasm make sudo make install * Install belle-sip (sip stack) git clone git://git.linphone.org/belle-sip.git cd belle-sip ./autogen.sh && ./configure --prefix=/opt/local && make sudo make install * (Optional) Install srtp for call encryption git clone git://git.linphone.org/srtp.git cd srtp && autoconf && ./configure --prefix=/opt/local && make libsrtp.a sudo make install * (Optional) Install zrtp, for unbreakable call encryption git clone git://git.linphone.org/bzrtp.git cd bzrtp && ./autogen.sh && ./configure --prefix=/opt/local && make sudo make install * (Optional) Install gsm codec git clone git://git.linphone.org/gsm.git cd gsm make CCFLAGS="$CFLAGS -c -O2 -DNeedFunctionPrototypes=1" sudo make install INSTALL_ROOT=/opt/local GSM_INSTALL_INC=/opt/local/include * (Optional, proprietary extension only) Compile and install the tunnel library If you got the source code from git, run `./autogen.sh` first. Then or otherwise, do: ./configure --prefix=/opt/local && make && sudo make install * Compile Linphone If you got the source code from git, run `./autogen.sh` first. Then or otherwise, : PKG_CONFIG_PATH=/opt/local/lib/pkgconfig ./configure --prefix=/opt/local --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make * Install on the system sudo make install You are done. ### Generate portable bundle If you want to generate a portable bundle, then install `gtk-mac-bundler` linphone fork: git clone git://git.linphone.org/gtk-mac-bundler.git cd gtk-mac-bundler make install export PATH=$PATH:~/.local/bin # set writing right for owner on the libssl and libcrypto libraries in order gtk-mac-bundler # be able to rewrite their rpath sudo chmod u+w /opt/local/lib/libssl.1.0.0.dylib /opt/local/lib/libcrypto.1.0.0.dylib The bundler file in `build/MacOS/linphone.bundle` expects some plugins to be installed in `/opt/local/lib/mediastreamer/plugins`. If you don't need plugins, remove or comment out this line from the bundler file: ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so If using HomeBrew, this is not working yet. However you will at least need to: brew install shared-mime-info glib-networking hicolor-icon-theme update-mime-database /usr/local/share/mime And modify also: /usr/local Then run, inside Linphone source tree configure as told before but with `--enable-relativeprefix` appended. make && make bundle The resulting bundle is located in Linphone build directory, together with a zipped version. * For a better appearance, you can install `gtk-quartz-engine` (a GTK theme) that makes GTK application more similar to other Mac applications (but not perfect). sudo port install gnome-common git clone https://github.com/jralls/gtk-quartz-engine.git cd gtk-quartz-engine ./autogen.sh ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make sudo make install Generate a new bundle to have it included. linphone-3.12.0/README.md000066400000000000000000000077321313432737600147120ustar00rootroot00000000000000Linphone ======== This is Linphone, a free (GPL) video softphone based on the SIP protocol. **WARNING:** Unless you exactly know what you are doing, you should take at look at *linphone-desktop[1]*. Building Linphone ----------------- ### Required dependencies * *BcToolbox[2]*: portability layer * *BelleSIP[3]*: SIP stack * *Mediastreamer2[4]*: multimedia engine * libxml2 * zlib * libsqlite3: user data storage (disablable) * libnotify: system notification (GNU/Linux only;disablable) * libgtk2: graphical interface (disablable) * gettext and libintl: internationalization support (disablable) ### Opitonal dependencies * *Belcard[5]*: VCard support * gtkmacintegration: integration with MacOSX menu ### Build instructions cmake . -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= make make install ### Supported build opitons * `CMAKE_INSTALL_PREFIX=` : install prefix * `CMAKE_PREFIX_PATH=` : column-separated list of prefixes where to search for dependencies * `ENABLE_SHARED=NO` : do not build the shared library * `ENABLE_STATIC=NO` : do not build the static library * `ENABLE_STRICT=NO` : build without strict compilation flags (-Wall -Werror) * `ENABLE_DOC=NO` : do not generate the reference documentation of liblinphone * `ENABLE_GTK_UI=NO` : do not build the GTK user interface * `ENABLE_UNIT_TESTS=NO` : do not build testing binaries * `ENABLE_VCARD=NO` : disable VCard support * `ENABLE_SQLITE_STORAGE=NO` : disable SQlite user data storage (message, history, contacts list) * `ENABLE_TOOLS=NO` : do not build tool binaries * `ENABLE_NLS=NO` : disable internationalization * `ENABLE_ASSISTANT=NO` : disable account creation wizard ### Note for packagers Our CMake scripts may automatically add some paths into research paths of generated binaries. To ensure that the installed binaries are striped of any rpath, use `-DCMAKE_SKIP_INSTALL_RPATH=ON` while you invoke cmake. Notes for developers -------------------- Here is a short description of the content of the source tree. - **oRTP/** is a poweful implementation of the RTP protocol. See the oRTP/README for more details. It is used by mediastreamer2 to send and receive streams to the network. - **mediastreamer2/** is one of the important part of linphone. It is a framework for audio and video processing. It contains several objects for grabing audio and video and outputing it (through rtp, to file). It contains also codec objects to compress audio and video streams. The mediastream.h files contain routines to easyly setup audio streams. - **coreapi/** is the central point of linphone, which handles relationship between sip signalisation and media streaming. It contains an easy to use api to create a sip phone. - **gtk/** is the directory that contains the gui frontend of linphone. It uses all libraries descibed above. - **console/** * linphonec.c is the main file for the console version of linphone. * sipomatic.c / sipomatic.h contains the code for sipomatic, the test program that auto-answer to linphone calls. * shell.c (program name: linphonecsh) is a small utilities to send interactive commands to a running linphonec daemon. - **share/** contains translation, documentation, rings and hello sound files. ------------------------------ - [1] linphone-desktop: git://git.linphone.org/linphone-desktop.git - [2] bctoolbox: git://git.linphone.org/bctoolbox.git *or* - [3] belle-sip: git://git.linphone.org/belle-sip.git *or* - [4] mediastreamer2: git://git.linphone.org/mediastreamer2.git *or* - [5] belcard: git://git.linphone.org/belcard.git *or* linphone-3.12.0/README.mingw000066400000000000000000000210151313432737600154210ustar00rootroot00000000000000Software to install ******************* Download lastest mingw-get-setup.exe from http://www.mingw.org Run mingw-get-setup.exe. In the package list, select and install: * mingw-developer-toolkit * mingw32-base * mingw32-gcc-g++ * mingw32-pthreads-w32 * msys-base * msys-zip * msys-unzip * msys-wget For more information: http://www.mingw.org/wiki/Getting_Started In mingw shell (also refered as msys), run mkdir -p /opt/perl/bin cp /bin/perl /opt/perl/bin/. cd ~ #Download intltool wget http://ftp.acc.umu.se/pub/GNOME/binaries/win32/intltool/0.40/intltool_0.40.4-1_win32.zip Download lastest linphone-deps-win32 zip from http://download.savannah.gnu.org/releases-noredirect/linphone/misc using your browser. Download gtk+-2.24.10 win32 _bundle_ from http://www.gtk.org, direct link: http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip Install all these three package in /: cd / unzip ~/intltool_0.40.4-1_win32.zip unzip unzip #Install GTK+ Outcrop theme, the one used by linphone for distribution. cd /share/themes wget ftp://ftp.gnome.org/mirror/gnome.org/teams/art.gnome.org/themes/gtk2/GTK2-Outcrop.tar.gz tar -xvzf GTK2-Outcrop.tar.gz #To get the translations working, remove from C:/MinGW/lib : libintl.a libintl.la libintl.dll.a * Download and install Inno Setup Compiler (required only if you run 'make setup.exe'). Add it to your windows Path environment variable. * Install msys-git from (http://msysgit.github.io/). During installation you are asked to make a choice about how line endings are treated by git. Choose "Checkout line endings as they are, commit as they are". THIS CHOICE IS VERY IMPORTANT. OTHERS BREAK AUTOMAKE. General rules for compilation ***************************** * It is recommended that you create a directory somewhere with a path without any spaces or ~ characters, for example c:\sources\. This is the place where source code must be compiled. * git commands (to retrieve source code) must be performed within msys-git terminal. * all other commands (configure, autogen.sh, make) must be done within the mingw shell (msys). In both msys and msys-git windows, change into the directory you created for sources: cd /c/sources * make sure pkg-config works by adding this env variable to your terminal: export PKG_CONFIG_PATH=/usr/lib/pkgconfig Building belle-sip ****************** * make sure that java version 1.6 is available in the PATH. java-1.7 will not work with antlr generator. * download the sources with msys-git shell using the following command: $ git clone git://git.linphone.org/belle-sip.git * compile and install $ ./autogen.sh $ ./configure --prefix=/usr --enable-shared --disable-static $ make && make install Building Linphone ***************** * download the sources using the following command: $ git clone git://git.linphone.org/linphone.git --recursive * compile #always run autogen.sh after a git checkout or update $ ./autogen.sh $ ./configure --prefix=/usr --enable-shared --disable-static #note: in order to use the tunnel (commercial extension), append #--enable-tunnel to the configure line above. $ make $ make install #Option: make a portable binary zip of linphone $ make zip #additionally you can make binary installer if you have Inno Setup 5 installed in its default path $ make setup.exe #now you're done, you have a fresh linphone windows installer in the current directory. Building plugins (optional) *************************** This the example for msx264 (H264 plugin), the same applies for other linphone plugins. $ git clone git://git.linphone.org/msx264.git $ cd msx264 $ ./autogen.sh $ PKG_CONFIG_PATH=/usr/lib/pkgconfig ./configure --prefix=/usr --enable-shared --disable-static #make a binary zip of this plugin $ make zip #or make an installer $ make setup.exe ****************************************************** * Notes about linphone-deps generation * ****************************************************** Linphone-deps is a collection of linphone dependencies, that are for some of them difficult to find as windows binaries. These notes are useful if you want to upgrade part of the software that is included in the linphone-deps packages. List of software included in linphone-deps: antlr3c (compiled) bzrtp (compiled) polarssl (compiled libsrtp (compiled) libavcodec, libavutil, libavformat, libavdevice, libswscale (compiled, all these from ffmpeg) libtheora (from the web) libx264 (compiled from the version distributed from linphone's web site) libogg (from the web) libspeex, libspeexdsp (compiled) libgnutls (from the web) libgsm (from the web) libxml2 (compiled) libsoup (compiled) libsqlite3 (compiled) Remarks: For every package compiled that goes into linphone-deps, .la files (libtool files) must be removed to avoid libtool errors. When running "make install DESTDIR=", somepath must be absolute and should not contain any ~ or space. - building antlr3c * download the sources with: $ git clone -b linphone git://git.linphone.org/antlr3.git * compile and install $ cd runtime/C $ ./autogen.sh $ ./configure --prefix=/usr --enable-shared --disable-static $ make $ make install $ make install DESTDIR=/home//antlr3c-install $ cp - building polarssl * download the sources with: $ git clone -b linphone git://git.linphone.org/polarssl.git * compile and install: $ cd polarssl $ make lib SHARED=1 WINDOWS=1 $ make install DESTDIR=/usr $ make install DESTDIR=/home//polarssl-install - building libsrtp * download the sources with $ git clone git://git.linphone.org/srtp.git * compile with $ autoconf $ ./configure --prefix=/usr $ make libsrtp.a $ make install $ make install DESTDIR=/home//libsrtp-install - building bzrtp * download the sources with msys-git shell using the following command: $ git clone git://git.linphone.org/bzrtp.git * compile and install $ ./autogen.sh $ ./configure --prefix=/usr --enable-shared --disable-static $ make && make install - building sqlite3 * download the sources on the following website: http://www.sqlite.org/download.html (choose the sqlite-autoconf-3XXX.tar.gz) * install: ./configure make && make install DESTDIR=/home//sqlite3-install then copy the content of ~/sqlite3-install/usr/local/ into linphone-deps/. - building ffmpeg ./configure --enable-shared --disable-static --enable-memalign-hack --extra-cflags="-fno-common" --enable-gpl && make make install DESTDIR=/home//ffmpeg-install Copy to ~/ffmpeg-install/usr/local/* to linphone-deps/. Copy also all *.dll.a files from the build tree to lib/ directort of linphone-deps. These are the implibs necessary to link a program against the dlls. - building libxml2: the binaries found on the internet are generated with MSVC++, and for obscure reason they are not suitable for building libsoup (that requires libxml2). ./configure --enable-shared --disable-static && make && make install DESTDIR=/home//libxml2-install copy ~/libxml2-install/usr/local/* into linphone-deps/. - building x264: * download yasm normal version windows executable from yasm project page: http://www.tortall.net/projects/yasm/wiki/Download copy it as /usr/local/bin/yasm.exe cd into x264/ dir then run: ./configure --enable-pic make make install DESTDIR=/home//x264-install then copy the content of ~/x264-install/usr/local/ into linphone-deps/. - libgnutls (required for libsoup https support) - download binary zip from http://josefsson.org/gnutls4win.org - add to linphone-deps - building libsoup (only required for buddylookup plugin) - download source from gnome ftp (warning: at the time of the writing only version 2.26.x can compile with the glib version supplied in the gtk-bundle, 2.27 requires a new version of glib) - uncompress libgnutls zip in / - make sure you have libxml2 installed in / - apply a bugfix patch (fix gnutls support on windows, completely broken otherwise). The patch is in linphone-deps/src, apply it this way: cd libsoup-2.26.* cd libsoup patch -p0 < libsoup-gnutls-bugfix.patch - run: ./configure --prefix=/usr --enable-shared --disable-static make make install make install DESTDIR=/home//libsoup-install - copy ~/libsoup-install/usr/* into linphone-deps/ Once you have everything in linphone-deps, remove .la files from lib: cd lib && rm -f *.la linphone-3.12.0/TODO000066400000000000000000000007031313432737600141120ustar00rootroot00000000000000hot stuff: --------- * ice support * run a user given command upon incoming calls * SIP/TLS low priority: ------------- * zeroconf support for advertising services (cool stuff!) * have the possibility to define several profiles (config files) and switch between them * help tips for the registration box * 2. There could be a sound effect for "busy" - a short "beep-beep-beep" would be sufficient (try http://www.google.com/search?q=busy.wav) linphone-3.12.0/autogen.sh000077500000000000000000000033351313432737600154270ustar00rootroot00000000000000#!/bin/sh srcdir=`dirname $0` test -z "$srcdir" && srcdir=. THEDIR=`pwd` cd $srcdir #AM_VERSION="1.10" if ! type aclocal-$AM_VERSION 1>/dev/null 2>&1; then # automake-1.10 (recommended) is not available on Fedora 8 AUTOMAKE=automake ACLOCAL=aclocal else ACLOCAL=aclocal-${AM_VERSION} AUTOMAKE=automake-${AM_VERSION} fi LIBTOOLIZE="libtoolize" for lt in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do if test -x /usr/bin/$lt ; then LIBTOOLIZE=$lt ; break fi if test -x /usr/local/bin/$lt ; then LIBTOOLIZE=$lt ; break fi if test -x /opt/local/bin/$lt ; then LIBTOOLIZE=$lt ; break fi done if test -d /opt/local/share/aclocal ; then ACLOCAL_ARGS="-I /opt/local/share/aclocal" fi if test -d /share/aclocal ; then ACLOCAL_ARGS="$ACLOCAL_ARGS -I /share/aclocal" fi INTLTOOLIZE=$(which intltoolize) #workaround for mingw bug in intltoolize script. if test "$INTLTOOLIZE" = "/bin/intltoolize" ; then INTLTOOLIZE=/usr/bin/intltoolize fi echo "Generating build scripts in linphone..." set -x $LIBTOOLIZE --copy --force $INTLTOOLIZE -c --force --automake $ACLOCAL -I m4 $ACLOCAL_ARGS autoheader $AUTOMAKE --force-missing --add-missing --copy autoconf set +x #install git pre-commit hooks if possible if [ -d .git/hooks ] && [ ! -f .git/hooks/pre-commit ]; then cp .git-pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit fi if [ "$srcdir" = "." ]; then if [ -x oRTP/autogen.sh ]; then echo "Generating build scripts in oRTP..." ( cd oRTP && ./autogen.sh ) fi if [ -x mediastreamer2/autogen.sh ]; then echo "Generating build scripts in mediastreamer2..." ( cd mediastreamer2 && ./autogen.sh ) fi fi cd $THEDIR linphone-3.12.0/build/000077500000000000000000000000001313432737600145215ustar00rootroot00000000000000linphone-3.12.0/build/Makefile.am000066400000000000000000000000521313432737600165520ustar00rootroot00000000000000SUBDIRS=macos EXTRA_DIST = openembedded linphone-3.12.0/build/android/000077500000000000000000000000001313432737600161415ustar00rootroot00000000000000linphone-3.12.0/build/android/Android.mk000077500000000000000000000157671313432737600200750ustar00rootroot00000000000000## ## Android.mk -Android build script- ## ## ## Copyright (C) 2010 Belledonne Communications, Grenoble, France ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ## LOCAL_PATH:= $(call my-dir)/../../coreapi include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := .cc LOCAL_SRC_FILES := \ account_creator.c \ account_creator_request_engine.c \ address.c \ authentication.c \ bellesip_sal/sal_address_impl.c \ bellesip_sal/sal_impl.c \ bellesip_sal/sal_op_call.c \ bellesip_sal/sal_op_call_transfer.c \ bellesip_sal/sal_op_events.c \ bellesip_sal/sal_op_impl.c \ bellesip_sal/sal_op_info.c \ bellesip_sal/sal_op_message.c \ bellesip_sal/sal_op_presence.c \ bellesip_sal/sal_op_publish.c \ bellesip_sal/sal_op_registration.c \ bellesip_sal/sal_sdp.c \ buffer.c \ callbacks.c \ call_log.c \ call_params.c \ carddav.c \ chat.c \ chat_file_transfer.c \ conference.cc \ content.c \ ec-calibrator.c \ enum.c \ event.c \ friend.c \ friendlist.c \ info.c \ linphonecall.c \ linphonecore.c \ linphonecore_jni.cc \ linphone_tunnel_config.c \ localplayer.c \ lpc2xml.c \ lime.c \ lpconfig.c \ message_storage.c \ misc.c \ offeranswer.c \ player.c \ presence.c \ proxy.c \ quality_reporting.c \ remote_provisioning.c \ sal.c \ siplogin.c \ sipsetup.c \ xml2lpc.c \ xml.c \ xmlrpc.c \ vtables.c \ ringtoneplayer.c ifndef LIBLINPHONE_VERSION LIBLINPHONE_VERSION = "Devel" endif LOCAL_CFLAGS += \ -Wno-error=deprecated-declarations \ -D_BYTE_ORDER=_LITTLE_ENDIAN \ -DORTP_INET6 \ -DINET6 \ -DENABLE_TRACE \ -DHAVE_CONFIG_H \ -DLIBLINPHONE_VERSION=\"$(LIBLINPHONE_VERSION)\" \ -DLINPHONE_PLUGINS_DIR=\"\\tmp\" \ -DUSE_BELLESIP \ -DHAVE_ZLIB LOCAL_CFLAGS += -DIN_LINPHONE ifeq ($(_BUILD_VIDEO),1) LOCAL_CFLAGS += -DVIDEO_ENABLED ifeq ($(BUILD_X264),1) LOCAL_CFLAGS += -DHAVE_X264 endif ifeq ($(BUILD_OPENH264),1) LOCAL_CFLAGS += -DHAVE_OPENH264 endif endif ifeq ($(BUILD_CONTACT_HEADER),1) LOCAL_CFLAGS += -DSAL_OP_CALL_FORCE_CONTACT_IN_RINGING endif ifeq ($(USE_JAVAH),1) LOCAL_CFLAGS += -DUSE_JAVAH endif LOCAL_C_INCLUDES += \ $(LOCAL_PATH) \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../build/android \ $(LOCAL_PATH)/../oRTP/include \ $(LOCAL_PATH)/../mediastreamer2/include \ $(LOCAL_PATH)/../mediastreamer2/src/audiofilters/ \ $(LOCAL_PATH)/../../bctoolbox/include \ $(LOCAL_PATH)/../../belle-sip/include \ $(LOCAL_PATH)/../../../gen \ $(LOCAL_PATH)/../../externals/libxml2/include \ $(LOCAL_PATH)/../../externals/build/libxml2 \ $(LOCAL_PATH)/../../externals/polarssl/include \ LOCAL_LDLIBS += -llog -ldl -lz LOCAL_STATIC_LIBRARIES := \ cpufeatures \ libmediastreamer2 \ libortp \ libbellesip \ libbctoolbox \ libgsm \ liblpxml2 ifeq ($(BUILD_TUNNEL),1) LOCAL_CFLAGS +=-DTUNNEL_ENABLED LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../tunnel/include $(LOCAL_PATH)/../../tunnel/src LOCAL_SRC_FILES += linphone_tunnel.cc TunnelManager.cc LOCAL_STATIC_LIBRARIES += libtunnelclient else LOCAL_SRC_FILES += linphone_tunnel_stubs.c endif _BUILD_AMR=0 ifneq ($(BUILD_AMRNB), 0) _BUILD_AMR=1 endif ifneq ($(BUILD_AMRWB), 0) _BUILD_AMR=1 endif ifneq ($(_BUILD_AMR), 0) LOCAL_CFLAGS += -DHAVE_AMR LOCAL_STATIC_LIBRARIES += \ libmsamr \ libopencoreamr endif ifneq ($(BUILD_AMRWB), 0) LOCAL_STATIC_LIBRARIES += \ libvoamrwbenc endif ifeq ($(BUILD_SILK),1) LOCAL_CFLAGS += -DHAVE_SILK LOCAL_STATIC_LIBRARIES += libmssilk endif ifeq ($(BUILD_CODEC2),1) LOCAL_CFLAGS += -DHAVE_CODEC2 LOCAL_STATIC_LIBRARIES += libcodec2 libmscodec2 endif ifneq ($(BUILD_WEBRTC_AECM)$(BUILD_WEBRTC_ISAC)$(BUILD_ILBC),000) LOCAL_CFLAGS += -DHAVE_WEBRTC LOCAL_STATIC_LIBRARIES += libmswebrtc endif ifneq ($(BUILD_WEBRTC_AECM),0) LOCAL_STATIC_LIBRARIES += \ libwebrtc_aecm ifeq ($(TARGET_ARCH_ABI), armeabi-v7a) LOCAL_STATIC_LIBRARIES += \ libwebrtc_aecm_neon endif endif ifneq ($(BUILD_WEBRTC_ISAC),0) LOCAL_STATIC_LIBRARIES += \ libwebrtc_isacfix ifeq ($(TARGET_ARCH_ABI), armeabi-v7a) LOCAL_STATIC_LIBRARIES += \ libwebrtc_isacfix_neon endif endif ifneq ($(BUILD_ILBC),0) LOCAL_STATIC_LIBRARIES += \ libwebrtc_ilbc endif ifneq ($(BUILD_WEBRTC_AECM)$(BUILD_WEBRTC_ISAC)$(BUILD_ILBC),000) LOCAL_STATIC_LIBRARIES += \ libwebrtc_apm_utility \ libwebrtc_system_wrappers \ libwebrtc_apm_utility \ libwebrtc_spl \ libwebrtc_system_wrappers ifeq ($(TARGET_ARCH_ABI), armeabi-v7a) LOCAL_STATIC_LIBRARIES += \ libwebrtc_spl_neon endif endif ifeq ($(BUILD_G729),1) LOCAL_CFLAGS += -DHAVE_G729 LOCAL_STATIC_LIBRARIES += libbcg729 libmsbcg729 endif ifeq ($(_BUILD_VIDEO),1) LOCAL_LDLIBS += -lGLESv2 LOCAL_STATIC_LIBRARIES += libvpx ifeq ($(BUILD_X264),1) LOCAL_STATIC_LIBRARIES += \ libmsx264 \ libx264 endif ifeq ($(BUILD_OPENH264),1) LOCAL_STATIC_LIBRARIES += \ libmsopenh264 \ libopenh264 endif endif ifeq ($(BUILD_UPNP),1) LOCAL_CFLAGS += -DBUILD_UPNP LOCAL_SRC_FILES += upnp.c endif LOCAL_STATIC_LIBRARIES += libspeex ifeq ($(BUILD_SRTP), 1) LOCAL_C_INCLUDES += $(SRTP_C_INCLUDE) endif ifeq ($(BUILD_VCARD),1) LOCAL_C_INCLUDES += $(VCARD_C_INCLUDE) endif ifeq ($(BUILD_ILBC), 1) ifneq ($(TARGET_ARCH_ABI),armeabi) LOCAL_CFLAGS += -DHAVE_ILBC=1 LOCAL_STATIC_LIBRARIES += libmsilbc endif endif LOCAL_C_INCLUDES += $(LIBLINPHONE_EXTENDED_C_INCLUDES) LOCAL_WHOLE_STATIC_LIBRARIES += $(LIBLINPHONE_EXTENDED_STATIC_LIBS) LOCAL_SRC_FILES += $(LIBLINPHONE_EXTENDED_SRC_FILES) LOCAL_CFLAGS += $(LIBLINPHONE_EXTENDED_CFLAGS) ifeq ($(BUILD_ZRTP),1) LOCAL_STATIC_LIBRARIES += libbzrtp endif ifeq ($(BUILD_SRTP),1) LOCAL_STATIC_LIBRARIES += libsrtp endif ifeq ($(BUILD_VCARD),1) LOCAL_CFLAGS += -DVCARD_ENABLED LOCAL_SRC_FILES += vcard.cc LOCAL_STATIC_LIBRARIES += libbelr libbelcard else LOCAL_SRC_FILES += vcard_stubs.c endif ifeq ($(BUILD_SQLITE),1) LOCAL_CFLAGS += -DMSG_STORAGE_ENABLED -DCALL_LOGS_STORAGE_ENABLED -DFRIENDS_SQL_STORAGE_ENABLED LOCAL_STATIC_LIBRARIES += liblinsqlite LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/../../externals/sqlite3/ endif ifeq ($(BUILD_OPUS),1) LOCAL_STATIC_LIBRARIES += libopus endif LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_CFLAGS := $(LOCAL_CFLAGS) ifeq ($(_BUILD_VIDEO),1) LOCAL_SHARED_LIBRARIES += \ libffmpeg-linphone endif LOCAL_MODULE := liblinphone LOCAL_MODULE_FILENAME := liblinphone-$(TARGET_ARCH_ABI) include $(BUILD_SHARED_LIBRARY) LOCAL_CPPFLAGS += $(LOCAL_CFLAGS) LOCAL_CFLAGS += -Wdeclaration-after-statement LOCAL_LDFLAGS := -Wl,-soname,$(LOCAL_MODULE_FILENAME).so $(call import-module,android/cpufeatures) linphone-3.12.0/build/android/config.h000066400000000000000000000146101313432737600175610ustar00rootroot00000000000000/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* Define if tools enabled */ /* #undef BUILD_TOOLS */ /* Define if wizard enabled */ /* #undef BUILD_WIZARD */ /* Defined when using gsm at nonstandard rates */ /* #undef ENABLE_NONSTANDARD_GSM */ /* The name of the gettext package name */ /* #undef GETTEXT_PACKAGE */ /* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the CoreFoundation framework. */ /* #undef HAVE_CFLOCALECOPYCURRENT */ /* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in the CoreFoundation framework. */ /* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ /* Define if the GNU dcgettext() function is already present or preinstalled. */ /* #undef HAVE_DCGETTEXT */ /* Define to 1 if you have the header file. */ /* #undef HAVE_DLFCN_H */ /* Define if exosip dscp available */ /* #def HAVE_EXOSIP_DSCP */ /* Defined when eXosip_get_version is available */ /* #undef HAVE_EXOSIP_GET_VERSION */ /* Defined when eXosip_reset_transports is available */ /* #undef HAVE_EXOSIP_RESET_TRANSPORTS */ /* Defined when eXosip_tls_verify_certificate is available */ /* #undef HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE */ /* Defined when eXosip_tls_verify_certificate is available */ /* #undef HAVE_EXOSIP_TLS_VERIFY_CN */ /* Defined when eXosip_get_socket is available */ /* #undef HAVE_EXOSIP_TRYLOCK */ /* If present, the getenv function allows fim to read environment variables. */ #define HAVE_GETENV 1 /* Define to 1 if you have the `getifaddrs' function. */ /* #undef HAVE_GETIFADDRS */ /* Tells wheter localisation is possible */ /* #undef HAVE_INTL */ /* Define to 1 if you have the `get_current_dir_name' function. */ #define HAVE_GET_CURRENT_DIR_NAME 1 /* Defined when gtk osx is used */ /* #undef HAVE_GTK_OSX */ /* Define to 1 if you have the header file. */ /* #undef HAVE_HISTORY_H */ /* Define if you have the iconv() function. */ /* #undef HAVE_ICONV */ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the `eXosip2' library (-leXosip2). */ /* #define HAVE_LIBEXOSIP2 */ /* Define to 1 if you have the `osip2' library (-losip2). */ /* #undef HAVE_LIBOSIP2 */ /* Define to 1 if you have the `osipparser2' library (-losipparser2). */ /* #undef HAVE_LIBOSIPPARSER2 */ /* Define to 1 if you have the `udev' library (-ludev). */ /* #undef HAVE_LIBUDEV */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LIBUDEV_H */ /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* NOTIFY1 support */ /* #undef HAVE_NOTIFY1 */ /* NOTIFY4 support */ /* #undef HAVE_NOTIFY4 */ /* defined when compiling with readline support */ /* #undef HAVE_READLINE */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_HISTORY_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_READLINE_H */ /* Define if sighandler_t available */ /* #undef HAVE_SIGHANDLER_T */ /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `stpcpy' function. */ #define HAVE_STPCPY 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strndup' function. */ #define HAVE_STRNDUP 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the header file. */ #define HAVE_X11_XLIB_H 1 /* All supported languages */ /* #undef LINPHONE_ALL_LANGS */ /* Windows appdata subdir where linphonerc can be found */ /* #undef LINPHONE_CONFIG_DIR */ /* path of liblinphone plugins, not mediastreamer2 plugins */ /* #undef LINPHONE_PLUGINS_DIR */ /* Linphone's version number */ /* #undef LINPHONE_VERSION */ /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Define to 1 if your C compiler doesn't accept -c and -o together. */ /* #undef NO_MINUS_C_MINUS_O */ /* Name of package */ #define PACKAGE "linphone" /* Define to the address where bug reports for this package should be sent. */ /* #undef PACKAGE_BUGREPORT */ /* Defines the place where data are found */ /* #undef PACKAGE_DATA_DIR */ /* Defines the place where locales can be found */ /* #undef PACKAGE_LOCALE_DIR */ /* Define to the full name of this package. */ #define PACKAGE_NAME "linphone" /* Defines the place where linphone sounds are found */ /* #undef PACKAGE_SOUND_DIR */ /* Define to the full name and version of this package. */ /* #undef PACKAGE_STRING */ /* Define to the one symbol short name of this package. */ /* #undef PACKAGE_TARNAME */ /* Define to the home page for this package. */ /* #undef PACKAGE_URL */ /* Define to the version of this package. */ /* #undef PACKAGE_VERSION */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Tell whether date_version.h must be used */ /* #undef USE_BUILDDATE_VERSION */ /* Version number of package */ /* #undef VERSION */ /* defined if video support is available */ /* #undef VIDEO_ENABLED */ /* Tell whether RSVP support should be compiled. */ /* #undef VINCENT_MAURY_RSVP */ /* Defined when LIME support is compiled */ #define HAVE_LIME 1 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN /* # undef WORDS_BIGENDIAN */ # endif #endif /* Defined if we are compiling for arm processor */ /* #undef __ARM__ */ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus /* #undef inline */ #endif linphone-3.12.0/build/android/liblinphone_tester.mk000066400000000000000000000024071313432737600223660ustar00rootroot00000000000000LOCAL_PATH := $(call my-dir)/../../tester common_SRC_FILES := \ accountmanager.c \ call_tester.c \ dtmf_tester.c \ eventapi_tester.c \ flexisip_tester.c \ liblinphone_tester.c \ log_collection_tester.c \ message_tester.c \ multi_call_tester.c \ offeranswer_tester.c \ player_tester.c \ presence_tester.c \ proxy_config_tester.c \ quality_reporting_tester.c \ register_tester.c \ remote_provisioning_tester.c \ setup_tester.c \ stun_tester.c \ tester.c \ tunnel_tester.c \ upnp_tester.c \ multicast_call_tester.c \ vcard_tester.c \ complex_sip_call_tester.c \ common_C_INCLUDES += \ $(LOCAL_PATH) \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../coreapi \ $(LOCAL_PATH)/../oRTP/include \ $(LOCAL_PATH)/../mediastreamer2/include include $(CLEAR_VARS) LOCAL_MODULE := liblinphone_tester LOCAL_MODULE_FILENAME := liblinphone_tester-$(TARGET_ARCH_ABI) LOCAL_SRC_FILES += $(common_SRC_FILES) LOCAL_C_INCLUDES = $(common_C_INCLUDES) LOCAL_CFLAGS = -DIN_LINPHONE -DBC_CONFIG_FILE=\"config.h\" LOCAL_LDLIBS := -llog -lz ifeq ($(BUILD_MATROSKA), 1) LOCAL_CFLAGS += -DHAVE_MATROSKA -DHAVE_ZLIB endif LOCAL_STATIC_LIBRARIES := bctoolbox_tester LOCAL_SHARED_LIBRARIES := bcunit liblinphone include $(BUILD_SHARED_LIBRARY) #end linphone-3.12.0/build/macos/000077500000000000000000000000001313432737600156235ustar00rootroot00000000000000linphone-3.12.0/build/macos/Info-linphone.plist.in000066400000000000000000000022221313432737600220100ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable Linphone CFBundleGetInfoString @PACKAGE_VERSION@, (C) 2011 The linphone team http://www.linphone.org CFBundleIconFile linphone.icns CFBundleIdentifier org.linphone.linphone CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString @PACKAGE_VERSION@ CFBundleSignature ???? CFBundleVersion @PACKAGE_VERSION@ NSHumanReadableCopyright Copyright 2011 Belledonne Communications LSMinimumSystemVersion 10.7 NSAppSleepDisabled YES linphone-3.12.0/build/macos/Makefile.am000066400000000000000000000002021313432737600176510ustar00rootroot00000000000000EXTRA_DIST= \ linphone.bundle \ environment.sh \ Info-linphone.plist.in \ pkg-scripts/postinstall \ pkg-distribution.xml.in linphone-3.12.0/build/macos/environment.sh000066400000000000000000000015361313432737600205300ustar00rootroot00000000000000#export EXTRA_ARGS="--workdir $bundle_res" export LINPHONE_WORKDIR="$bundle_res" export GIO_EXTRA_MODULES="$bundle_lib/gio/modules" export PANGO_LIBDIR="$bundle_lib" export PANGO_SYSCONFDIR="$bundle_etc" export GDK_PIXBUF_MODULE_FILE="$bundle_lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" #this is very important not to force a shared library path so that native frameworks can find their dependencies by themselves, #and not be forced to use the few libraries we have in the bundle that have the same name as native libs (ex: libiconv) export DYLD_LIBRARY_PATH= #the fucking script of the gtk-mac-bundler resets LANG due to obscure bugs. Set it back. LANG=`defaults read .GlobalPreferences AppleLocale` case "$LANG" in *.UTF-8) ;; *) if test -d /usr/share/locale/${LANG}.UTF-8 ; then LANG=${LANG}.UTF-8 fi ;; esac export LANG echo "LANG is $LANG" linphone-3.12.0/build/macos/libiconv-macos.patch000066400000000000000000000013341313432737600215520ustar00rootroot00000000000000--- libiconv-1.14.orig/lib/iconv.c 2013-03-14 16:30:50.000000000 +0100 +++ libiconv-1.14/lib/iconv.c 2013-03-15 10:24:38.000000000 +0100 @@ -607,4 +607,23 @@ strong_alias (libiconv_close, iconv_close) #endif +#undef iconv_open +#undef iconv +#undef iconv_close + +LIBICONV_DLL_EXPORTED iconv_t iconv_open (const char* tocode, const char* fromcode){ + return libiconv_open(tocode,fromcode); +} + +LIBICONV_DLL_EXPORTED size_t iconv (iconv_t icd, + ICONV_CONST char* * inbuf, size_t *inbytesleft, + char* * outbuf, size_t *outbytesleft){ + return libiconv(icd,inbuf,inbytesleft,outbuf,outbytesleft); +} + +LIBICONV_DLL_EXPORTED int iconv_close (iconv_t icd){ + return libiconv_close(icd); +} + + #endif linphone-3.12.0/build/macos/linphone.bundle000066400000000000000000000170401313432737600206340ustar00rootroot00000000000000 /opt/local ${env:LINPHONE_INSTALL_PREFIX} ${env:MS2_PLUGINS_INSTALL_PREFIX} ${env:LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX} ${env:BUNDLE_PREFIX} gtk+-2.0 ${project}/Info-linphone.plist ${prefix:linphone}/bin/linphone ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so ${prefix:linphone}/lib/*.*.dylib ${prefix}/lib/${gtkdir}/modules/*.so ${prefix}/share/mime/mime.cache ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/engines/*.so ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/printbackends/*.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-bmp.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-gif.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-icns.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-ico.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-jpeg.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-png.so ${prefix}/share/locale ${prefix:linphone}/share/locale ${prefix}/share/locale ${prefix}/share/locale ${prefix:linphone}/share/linphone ${prefix:linphone}/share/pixmaps/linphone ${prefix:linphone}/share/images ${prefix:linphone}/share/icons ${prefix}/share/themes ${project}/../../pixmaps/linphone.icns ${project}/environment.sh ${project}/../../gtk/gtkrc.mac ${prefix:linphone}/share/sounds/linphone/rings/oldphone-mono.wav ${prefix:linphone}/share/sounds/linphone/toy-mono.wav ${prefix:linphone}/share/sounds/linphone/ringback.wav ${prefix:linphone}/share/sounds/linphone/incoming_chat.wav linphone-3.12.0/build/macos/pkg-distribution.xml.in000066400000000000000000000013701313432737600222510ustar00rootroot00000000000000 Linphone linphone.pkg linphone-3.12.0/build/macos/pkg-scripts/000077500000000000000000000000001313432737600200715ustar00rootroot00000000000000linphone-3.12.0/build/macos/pkg-scripts/postinstall000077500000000000000000000006161313432737600223760ustar00rootroot00000000000000#!/bin/bash CURL=/usr/bin/curl BUNZIP2=/usr/bin/bunzip2 VERSION=1.4.0 BASENAME=libopenh264-${VERSION}-osx64 FILENAME=${BASENAME}.dylib.bz2 TMPDIR=/tmp/linphone_installer mkdir ${TMPDIR} cd ${TMPDIR} ${CURL} http://ciscobinary.openh264.org/${FILENAME} > ${FILENAME} ${BUNZIP2} ${FILENAME} cp ${BASENAME}.dylib /Applications/Linphone.app/Contents/Resources/lib/libopenh264.0.dylib rm -r ${TMPDIR} linphone-3.12.0/build/macos/quartz-theme-gtkrc.patch000066400000000000000000000011701313432737600224010ustar00rootroot00000000000000--- /opt/local/share/themes/Quartz/gtk-2.0/gtkrc 2015-03-25 15:29:53.000000000 +0100 +++ gtkrc 2015-10-29 13:43:15.000000000 +0100 @@ -12,7 +12,7 @@ gtk-menu-images = 0 gtk-toolbar-style = 0 gtk-enable-mnemonics = 0 -gtk-icon-sizes = "gtk-small-toolbar=16,16:gtk-large-toolbar=22,22" +gtk-icon-sizes = "gtk-menu=12,12:gtk-button=16,16:gtk-small-toolbar=16,16:gtk-large-toolbar=22,22" gtk-toolbar-icon-size = large-toolbar gtk-error-bell = 0 gtk-show-input-method-menu = 0 @@ -82,7 +82,7 @@ engine "quartz" { - buttontype = "aqua" + buttontype = "textured" } } linphone-3.12.0/build/openembedded/000077500000000000000000000000001313432737600171345ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/README000066400000000000000000000037511313432737600200220ustar00rootroot00000000000000Recipes for open embedded: http://www.openembedded.org Documentations: http://docs.openembedded.org/usermanual/ http://bitbake.berlios.de/manual/ Instructions for compilation from sources: (requires 10 Go of free space) - Choose a distribution and build it. For example, to build Angstrom follow the guide at http://www.angstrom-distribution.org/building-angstrom For IGEPv2 use environment variable MACHINE=igep0020 It is possible to use MACHINE=qemuarm to build an image that can be run on a computer with qemu. - Add linphone recipes to the pool with an higher priority: Edit conf/bblayers.conf to set EXTRALAYERS to point to the source repository of linphone. Search the EXTRALAYERS definition in conf/bblayers.conf and modify it like this: # Add your overlay location to EXTRALAYERS # Make sure to have a conf/layers.conf in there EXTRALAYERS ?= "/home/smorlat/sources/git/linphone-daemon/build/openembedded" This additional layer gives access to the various linphone recipes but also to a recipe to build an entire image containing linphone. To build this image based on the generic console image you will need to use: bitbake console-linphone-image - Prepare compilation Source appropriate environment with "~/.oe/enviro*" Change directory to where you launched Angstrom install script. - Compile linphone bitbake -c clean linphone bitbake linphone - If you want additional codecs (e.g. iLBC or AMR) compile linphone-plugins bitbake -c clean linphone-plugins bitbake linphone-plugins - Find the generated packages "*.ipk" Example: /Data/work/angstrom/angstrom-setup-scripts/build/tmp-angstrom_2008_1/deploy/glibc/ipk/armv7a/ Installation - check network connectivity * ping linphone.org * see "route -n" * see "/etc/resolv.conf" - update package list * opkg update - copy ipk files to install to /tmp - eventually remove previously installed packages - install with "opkg install libortp*.ipk libmediastreamer*.ipk liblinphone*.ipk linphonec*.ipk" linphone-3.12.0/build/openembedded/antlr3/000077500000000000000000000000001313432737600203375ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/antlr3/antlr3c.inc000066400000000000000000000004001313432737600223720ustar00rootroot00000000000000DESCRIPTION = "Linphone version of antlr3" LICENSE = "GPL" PROVIDES = "antlr3c antlr3c-dev" ALLOW_EMPTY_${PN} = "1" S = "${WORKDIR}/git/runtime/C" inherit autotools pkgconfig lib_package do_fetch_append() { import os os.system("autogen.sh") } linphone-3.12.0/build/openembedded/antlr3/antlr3c_linphone.bb000066400000000000000000000003061313432737600241050ustar00rootroot00000000000000require antlr3c.inc SRCREV="f0dbcbbcd22a7fd9a479ff68d4daa9225fb2f3b1" PR="R3" SRC_URI = "git://git.linphone.org/antlr3.git" LIC_FILES_CHKSUM= "file://COPYING;md5=13c502aaa9b2ca91d01a3aae44d899b4" linphone-3.12.0/build/openembedded/belle-sip/000077500000000000000000000000001313432737600210105ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/belle-sip/belle-sip.inc000066400000000000000000000007111313432737600233560ustar00rootroot00000000000000DESCRIPTION = "SIP stack from Belledonne Communications" LICENSE = "GPL" DEPENDS_${PN} = "polarssl-dev antlr3c-dev" DEPENDS = "polarssl-dev antlr3c-dev" RDEPENDS_${PN} = "polarssl-dev antlr3c-dev" EXTRA_OECONF += "--disable-strict --with-antlr=${STAGING_DIR_HOST}${layout_exec_prefix}/usr --with-polarssl=${STAGING_DIR_HOST}${layout_exec_prefix}/usr" INSANE_SKIP_belle-sip += "dev-deps" inherit autotools pkgconfig do_autoreconf () { ./autogen.sh } linphone-3.12.0/build/openembedded/belle-sip/belle-sip_master.bb000066400000000000000000000003641313432737600245470ustar00rootroot00000000000000require belle-sip.inc SRCREV="af93922ac91cf3cbf5ceed0328bf43d08d37714e" S = "${WORKDIR}/git" PR="R1" SRC_URI = "git://git.linphone.org/belle-sip.git;commit=${SRCREV}" LIC_FILES_CHKSUM = "file://COPYING;md5=9f9938e31db89d55a796e86808c96848" linphone-3.12.0/build/openembedded/conf/000077500000000000000000000000001313432737600200615ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/conf/layer.conf000066400000000000000000000004611313432737600220450ustar00rootroot00000000000000# We have a conf and classes directory, append to BBPATH BBPATH .= ":${LAYERDIR}" # We have a recipes directory, add to BBFILES BBFILES += "${LAYERDIR}/*.bb ${LAYERDIR}/*/*.bb" BBFILE_COLLECTIONS += "linphone-layer" BBFILE_PATTERN_linphone-layer := "^${LAYERDIR}/" BBFILE_PRIORITY_linphone-layer = "50" linphone-3.12.0/build/openembedded/files/000077500000000000000000000000001313432737600202365ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/files/igep0020/000077500000000000000000000000001313432737600214645ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/files/igep0020/alsa_8khz.patch000066400000000000000000000010441313432737600243700ustar00rootroot00000000000000--- linphone/mediastreamer2/src/alsa.c_orig 2011-05-24 12:39:33.824600109 +0200 +++ linphone/mediastreamer2/src/alsa.c 2011-05-24 12:40:04.760407404 +0200 @@ -32,8 +32,8 @@ /*in case of troubles with a particular driver, try incrementing ALSA_PERIOD_SIZE to 512, 1024, 2048, 4096... then try incrementing the number of periods*/ -#define ALSA_PERIODS 8 -#define ALSA_PERIOD_SIZE 256 +#define ALSA_PERIODS 4 +#define ALSA_PERIOD_SIZE 512 /*uncomment the following line if you have problems with an alsa driver having sound quality trouble:*/ linphone-3.12.0/build/openembedded/libgsm/000077500000000000000000000000001313432737600204115ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/libgsm/libgsm-1.0.13/000077500000000000000000000000001313432737600224045ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/libgsm/libgsm-1.0.13/01_makefile.patch000066400000000000000000000034611313432737600255060ustar00rootroot00000000000000diff -urNad libgsm-1.0.12~/Makefile libgsm-1.0.12/Makefile --- libgsm-1.0.12~/Makefile 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.12/Makefile 2007-11-01 15:43:06.000000000 +0100 @@ -96,7 +96,7 @@ # Other tools SHELL = /bin/sh -LN = ln +LN = ln -s BASENAME = basename AR = ar ARFLAGS = cr @@ -140,6 +140,7 @@ # Targets LIBGSM = $(LIB)/libgsm.a +LIBGSMSO= $(LIB)/libgsm.so TOAST = $(BIN)/toast UNTOAST = $(BIN)/untoast @@ -279,7 +280,7 @@ # Target rules -all: $(LIBGSM) $(TOAST) $(TCAT) $(UNTOAST) +all: $(LIBGSM) $(LIBGSMSO) $(TOAST) $(TCAT) $(UNTOAST) @-echo $(ROOT): Done. tst: $(TST)/lin2cod $(TST)/cod2lin $(TOAST) $(TST)/test-result @@ -299,6 +300,11 @@ # The basic API: libgsm +$(LIBGSMSO): $(LIB) $(GSM_OBJECTS) + $(LD) -o $@.1.0.12 -shared -Xlinker -soname -Xlinker libgsm.so.1 $(GSM_OBJECTS) -lc $(LDFLAGS) + ln -fs libgsm.so.1.0.12 lib/libgsm.so.1 + ln -fs libgsm.so.1.0.12 lib/libgsm.so + $(LIBGSM): $(LIB) $(GSM_OBJECTS) -rm $(RMFLAGS) $(LIBGSM) $(AR) $(ARFLAGS) $(LIBGSM) $(GSM_OBJECTS) @@ -308,15 +314,15 @@ # Toast, Untoast and Tcat -- the compress-like frontends to gsm. $(TOAST): $(BIN) $(TOAST_OBJECTS) $(LIBGSM) - $(LD) $(LFLAGS) -o $(TOAST) $(TOAST_OBJECTS) $(LIBGSM) $(LDLIB) + $(LD) $(LFLAGS) -o $(TOAST) $(TOAST_OBJECTS) $(LIBGSMSO) $(LDLIB) $(UNTOAST): $(BIN) $(TOAST) -rm $(RMFLAGS) $(UNTOAST) - $(LN) $(TOAST) $(UNTOAST) + $(LN) toast $(UNTOAST) $(TCAT): $(BIN) $(TOAST) -rm $(RMFLAGS) $(TCAT) - $(LN) $(TOAST) $(TCAT) + $(LN) toast $(TCAT) # The local bin and lib directories @@ -426,7 +432,9 @@ clean: semi-clean -rm $(RMFLAGS) $(LIBGSM) $(ADDTST)/add \ - $(TOAST) $(TCAT) $(UNTOAST) \ + $(LIBGSMSO) $(LIB)/libgsm.so.1.0.12 \ + $(LIB)libgsm.so.1 \ + $(TOAST) $(TCAT) $(UNTOAST) \ $(ROOT)/gsm-1.0.tar.Z linphone-3.12.0/build/openembedded/libgsm/libgsm-1.0.13/02_cplusplus.patch000066400000000000000000000011571313432737600257640ustar00rootroot00000000000000diff -urNad libgsm-1.0.10~/inc/gsm.h libgsm-1.0.10/inc/gsm.h --- libgsm-1.0.10~/inc/gsm.h 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/inc/gsm.h 2007-11-01 15:44:52.000000000 +0100 @@ -54,6 +54,10 @@ #define GSM_OPT_FRAME_INDEX 5 #define GSM_OPT_FRAME_CHAIN 6 +#ifdef __cplusplus +extern "C" { +#endif + extern gsm gsm_create GSM_P((void)); extern void gsm_destroy GSM_P((gsm)); @@ -66,6 +70,10 @@ extern int gsm_explode GSM_P((gsm, gsm_byte *, gsm_signal *)); extern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte *)); +#ifdef __cplusplus +} +#endif + #undef GSM_P #endif /* GSM_H */ linphone-3.12.0/build/openembedded/libgsm/libgsm-1.0.13/03_config.patch000066400000000000000000000131561313432737600252020ustar00rootroot00000000000000diff -urNad libgsm-1.0.10~/Makefile libgsm-1.0.10/Makefile --- libgsm-1.0.10~/Makefile 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/Makefile 2007-11-01 15:48:02.000000000 +0100 @@ -151,7 +151,7 @@ HEADERS = $(INC)/proto.h \ $(INC)/unproto.h \ - $(INC)/config.h \ + $(INC)/gsm_config.h \ $(INC)/private.h \ $(INC)/gsm.h \ $(INC)/toast.h \ diff -urNad libgsm-1.0.10~/inc/config.h libgsm-1.0.10/inc/config.h --- libgsm-1.0.10~/inc/config.h 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/inc/config.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,37 +0,0 @@ -/* - * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische - * Universitaet Berlin. See the accompanying file "COPYRIGHT" for - * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. - */ - -/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/ - -#ifndef CONFIG_H -#define CONFIG_H - -/*efine SIGHANDLER_T int /* signal handlers are void */ -/*efine HAS_SYSV_SIGNAL 1 /* sigs not blocked/reset? */ - -#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */ -#define HAS_LIMITS_H 1 /* /usr/include/limits.h */ -#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */ -#define HAS_ERRNO_DECL 1 /* errno.h declares errno */ - -#define HAS_FSTAT 1 /* fstat syscall */ -#define HAS_FCHMOD 1 /* fchmod syscall */ -#define HAS_CHMOD 1 /* chmod syscall */ -#define HAS_FCHOWN 1 /* fchown syscall */ -#define HAS_CHOWN 1 /* chown syscall */ -/*efine HAS__FSETMODE 1 /* _fsetmode -- set file mode */ - -#define HAS_STRING_H 1 /* /usr/include/string.h */ -/*efine HAS_STRINGS_H 1 /* /usr/include/strings.h */ - -#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */ -#define HAS_UTIME 1 /* POSIX utime(path, times) */ -/*efine HAS_UTIMES 1 /* use utimes() syscall instead */ -#define HAS_UTIME_H 1 /* UTIME header file */ -#define HAS_UTIMBUF 1 /* struct utimbuf */ -/*efine HAS_UTIMEUSEC 1 /* microseconds in utimbuf? */ - -#endif /* CONFIG_H */ diff -urNad libgsm-1.0.10~/inc/gsm_config.h libgsm-1.0.10/inc/gsm_config.h --- libgsm-1.0.10~/inc/gsm_config.h 1970-01-01 01:00:00.000000000 +0100 +++ libgsm-1.0.10/inc/gsm_config.h 2007-11-01 15:46:19.000000000 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/ + +#ifndef CONFIG_H +#define CONFIG_H + +/*efine SIGHANDLER_T int -* signal handlers are void */ +/*efine HAS_SYSV_SIGNAL 1 -* sigs not blocked/reset? */ + +#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */ +#define HAS_STDIO_H 1 /* /usr/include/stdio.h */ +/*efine HAS_LIMITS_H 1 -* /usr/include/limits.h */ +#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */ + +#define HAS_FSTAT 1 /* fstat syscall */ +#define HAS_FCHMOD 1 /* fchmod syscall */ +#define HAS_CHMOD 1 /* chmod syscall */ +#define HAS_FCHOWN 1 /* fchown syscall */ +#define HAS_CHOWN 1 /* chown syscall */ +/*efine HAS__FSETMODE 1 -* _fsetmode -- set file mode */ + +#define HAS_STRING_H 1 /* /usr/include/string.h */ +/*efine HAS_STRINGS_H 1 -* /usr/include/strings.h */ + +#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */ +#define HAS_UTIME 1 /* POSIX utime(path, times) */ +/*efine HAS_UTIMES 1 -* use utimes() syscall instead */ +#define HAS_UTIME_H 1 /* UTIME header file */ +/*efine HAS_UTIMBUF 1 -* struct utimbuf */ +/*efine HAS_UTIMEUSEC 1 -* microseconds in utimbuf? */ + +#endif /* CONFIG_H */ diff -urNad libgsm-1.0.10~/inc/toast.h libgsm-1.0.10/inc/toast.h --- libgsm-1.0.10~/inc/toast.h 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/inc/toast.h 2007-11-01 15:48:17.000000000 +0100 @@ -9,7 +9,7 @@ #ifndef TOAST_H #define TOAST_H /* Guard against multiple includes */ -#include "config.h" +#include "gsm_config.h" #include #include diff -urNad libgsm-1.0.10~/src/code.c libgsm-1.0.10/src/code.c --- libgsm-1.0.10~/src/code.c 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/src/code.c 2007-11-01 15:48:34.000000000 +0100 @@ -6,7 +6,7 @@ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/code.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */ -#include "config.h" +#include "gsm_config.h" #ifdef HAS_STDLIB_H diff -urNad libgsm-1.0.10~/src/gsm_create.c libgsm-1.0.10/src/gsm_create.c --- libgsm-1.0.10~/src/gsm_create.c 1996-07-02 16:32:44.000000000 +0200 +++ libgsm-1.0.10/src/gsm_create.c 2007-11-01 15:48:48.000000000 +0100 @@ -6,7 +6,7 @@ static char const ident[] = "$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_create.c,v 1.4 1996/07/02 09:59:05 jutta Exp $"; -#include "config.h" +#include "gsm_config.h" #ifdef HAS_STRING_H #include diff -urNad libgsm-1.0.10~/src/gsm_destroy.c libgsm-1.0.10/src/gsm_destroy.c --- libgsm-1.0.10~/src/gsm_destroy.c 1996-07-02 16:32:39.000000000 +0200 +++ libgsm-1.0.10/src/gsm_destroy.c 2007-11-01 15:48:57.000000000 +0100 @@ -7,7 +7,7 @@ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_destroy.c,v 1.3 1994/11/28 19:52:25 jutta Exp $ */ #include "gsm.h" -#include "config.h" +#include "gsm_config.h" #include "proto.h" #ifdef HAS_STDLIB_H diff -urNad libgsm-1.0.10~/tls/taste.c libgsm-1.0.10/tls/taste.c --- libgsm-1.0.10~/tls/taste.c 1996-07-02 16:33:05.000000000 +0200 +++ libgsm-1.0.10/tls/taste.c 2007-11-01 15:49:54.000000000 +0100 @@ -10,7 +10,7 @@ #include #include -#include "config.h" +#include "gsm_config.h" #ifdef HAS_STDLIB_H # include linphone-3.12.0/build/openembedded/libgsm/libgsm-1.0.13/04_includes.patch000066400000000000000000000016771313432737600255510ustar00rootroot00000000000000diff -urNad libgsm-1.0.10~/inc/toast.h libgsm-1.0.10/inc/toast.h --- libgsm-1.0.10~/inc/toast.h 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/inc/toast.h 2007-11-01 15:52:33.000000000 +0100 @@ -16,11 +16,12 @@ #include #include +#include #include #include -#ifndef HAS_ERRNO_DECL - extern int errno; +#ifndef errno + extern int errno; #endif #ifdef HAS_LIMITS_H @@ -37,6 +38,10 @@ # endif #endif +#ifdef HAS_STDIO_H +# include +#endif + #include "gsm.h" #ifndef S_ISREG diff -urNad libgsm-1.0.10~/src/code.c libgsm-1.0.10/src/code.c --- libgsm-1.0.10~/src/code.c 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/src/code.c 2007-11-01 15:52:33.000000000 +0100 @@ -9,8 +9,8 @@ #include "config.h" -#ifdef HAS_STDLIB_H -#include +#ifdef HAS_STRING_H +#include #else # include "proto.h" extern char * memcpy P((char *, char *, int)); linphone-3.12.0/build/openembedded/libgsm/libgsm-1.0.13/05_compiler_warnings.patch000066400000000000000000000051531313432737600274570ustar00rootroot00000000000000diff -urNad libgsm-1.0.10~/src/debug.c libgsm-1.0.10/src/debug.c --- libgsm-1.0.10~/src/debug.c 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/src/debug.c 2007-11-01 15:53:42.000000000 +0100 @@ -49,7 +49,7 @@ fprintf( stderr, "%s [%d .. %d]: ", name, from, to ); while (from <= to) { - fprintf(stderr, "%d ", ptr[ from ] ); + fprintf(stderr, "%ld ", ptr[ from ] ); from++; if (nprinted++ >= 7) { nprinted = 0; @@ -63,14 +63,14 @@ char * name, longword value ) { - fprintf(stderr, "%s: %d\n", name, (long)value ); + fprintf(stderr, "%s: %ld\n", name, (long)value ); } void gsm_debug_word P2( (name, value), char * name, word value ) { - fprintf(stderr, "%s: %d\n", name, (long)value); + fprintf(stderr, "%s: %ld\n", name, (long)value); } #endif diff -urNad libgsm-1.0.10~/src/toast.c libgsm-1.0.10/src/toast.c --- libgsm-1.0.10~/src/toast.c 2007-11-01 15:37:52.000000000 +0100 +++ libgsm-1.0.10/src/toast.c 2007-11-01 15:53:42.000000000 +0100 @@ -251,8 +251,8 @@ { char * s; if (!(s = malloc(len))) { - fprintf(stderr, "%s: failed to malloc %d bytes -- abort\n", - progname, len); + fprintf(stderr, "%s: failed to malloc %ld bytes -- abort\n", + progname, (long) len); onintr(); exit(1); } @@ -270,7 +270,7 @@ maxlen = strlen(name) + 1 + strlen(want) + strlen(cut); p = strcpy(emalloc(maxlen), name); - if (s = suffix(p, cut)) strcpy(s, want); + if ((s = suffix(p, cut))) strcpy(s, want); else if (*want && !suffix(p, want)) strcat(p, want); return p; @@ -386,7 +386,7 @@ ut[0] = instat.st_atime; ut[1] = instat.st_mtime; - (void) utime(outname, ut); + (void) utime(outname, (struct utimbuf *)ut); #endif /* UTIMBUF */ } @@ -416,7 +416,7 @@ } if (st->st_nlink > 1 && !f_cat && !f_precious) { fprintf(stderr, - "%s: \"%s\" has %s other link%s -- unchanged.\n", + "%s: \"%s\" has %d other link%s -- unchanged.\n", progname,name,st->st_nlink - 1,"s" + (st->st_nlink<=2)); return 0; } @@ -585,8 +585,8 @@ if (cc != sizeof(s)) { if (cc >= 0) fprintf(stderr, - "%s: incomplete frame (%d byte%s missing) from %s\n", - progname, sizeof(s) - cc, + "%s: incomplete frame (%ld byte%s missing) from %s\n", + progname, (long) sizeof(s) - cc, "s" + (sizeof(s) - cc == 1), inname ? inname : "stdin" ); gsm_destroy(r); @@ -624,8 +624,6 @@ static int process P1((name), char * name) { - int step = 0; - out = (FILE *)0; in = (FILE *)0; @@ -779,7 +777,6 @@ case 'h': help(); exit(0); default: - usage: fprintf(stderr, "Usage: %s [-fcpdhvuaslFC] [files...] (-h for help)\n", progname); linphone-3.12.0/build/openembedded/libgsm/libgsm.inc000066400000000000000000000015601313432737600223630ustar00rootroot00000000000000DESCRIPTION = "GSM Audio Library" SECTION = "libs" PRIORITY = "optional" LICENSE = "libgsm" INC_PR = "r2" SRC_URI = "http://www.quut.com/gsm/gsm-${PV}.tar.gz \ file://01_makefile.patch \ file://02_cplusplus.patch \ file://03_config.patch \ file://04_includes.patch \ file://05_compiler_warnings.patch \ " CFLAGS += "-c -g -fPIC -Wall -D_GNU_SOURCE -D_REENTRANT -DNeedFunctionPrototypes=1 -DWAV49 -I./inc" PARALLEL_MAKE = "" do_compile() { unset LD oe_runmake CCFLAGS="${CFLAGS}" } do_install() { oe_libinstall -a -C lib libgsm ${D}${libdir} oe_libinstall -so -C lib libgsm ${D}${libdir} install -d ${D}${includedir}/gsm install -m 0644 ${S}/inc/gsm.h ${D}${includedir}/gsm/ cd ${D}${includedir} ln -s gsm/gsm.h gsm.h } LIC_FILES_CHKSUM = "file://COPYRIGHT;md5=fc1372895b173aaf543a122db37e04f5"linphone-3.12.0/build/openembedded/libgsm/libgsm_1.0.13.bb000066400000000000000000000003251313432737600227730ustar00rootroot00000000000000require libgsm.inc PR = "${INC_PR}.0" S = "${WORKDIR}/gsm-1.0-pl13/" SRC_URI[md5sum] = "c1ba392ce61dc4aff1c29ea4e92f6df4" SRC_URI[sha256sum] = "52c518244d428c2e56c543b98c9135f4a76ff780c32455580b793f60a0a092ad" linphone-3.12.0/build/openembedded/libilbc-rfc3951_git.bb000066400000000000000000000005621313432737600230010ustar00rootroot00000000000000DESCRIPTION = "iLBC codec as published in IETF RFC 3951" SECTION = "libs" PRIORITY = "optional" LICENSE = "LGPLv3" PR = "r1" SRC_URI = "git://git.linphone.org/libilbc-rfc3951.git;protocol=git" SRCREV = "b9490e0cbdda6a4ec29f7c47d81d3997004fedba" S = "${WORKDIR}/git" LIC_FILES_CHKSUM = "file://COPYING;md5=586c8a6efdeabd095cc4206ce4d0699b" inherit autotools pkgconfig linphone-3.12.0/build/openembedded/linphone-plugins.bb000066400000000000000000000006151313432737600227360ustar00rootroot00000000000000DESCRIPTION = "Plugins for linphone to have additional codecs." LICENSE = "" ALLOW_EMPTY_${PN} = "1" PACKAGES = "${PN}" DEPENDS_${PN} = "linphone msamr msilbc msx264" RDEPENDS_${PN} = "linphonec msamr msilbc msx264" LIC_FILES_CHKSUM = "file://${COREBASE}/LICENSE;md5=3f40d7994397109285ec7b81fdeb3b58 \ file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" linphone-3.12.0/build/openembedded/linphone/000077500000000000000000000000001313432737600207505ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/linphone/linphone-common.inc000066400000000000000000000055371313432737600245570ustar00rootroot00000000000000SECTION = "x11/network" SECTION_liblinphone = "libs/network" SECTION_libmediastreamer = "libs/network" SECTION_libortp = "libs/network" SECTION_linphonec = "console/network" SRC_URI_append_igep0020 = " file://alsa_8khz.patch" DEPENDS_${PN} = "intltool-native speex alsa-lib spandsp belle-sip liblinphone libxv ffmpeg libv4l libgsm" DEPENDS_liblinphone = "libmediastreamer libortp" DEPENDS_libmediastreamer = "speex alsa-lib libortp" PROVIDES = "linphone linphonec liblinphone libmediastreamer libortp" inherit autotools pkgconfig gettext INSANE_SKIP_linphone += "dev-deps" INSANE_SKIP_liblinphone += "dev-deps" do_install_append(){ install -d ${D}${bindir} } EXTRA_OECONF = " \ --disable-tests \ --with-ffmpeg=${STAGING_DIR_HOST}${layout_exec_prefix} --enable-video --disable-vp8 \ --disable-glx \ --enable-alsa --disable-pulseaudio \ --without-readline \ --with-speex=${STAGING_DIR_HOST}${layout_exec_prefix} \ --disable-manual --enable-tests=yes \ --enable-console_ui=no \ --enable-gtk_ui=no \ --with-realprefix=/usr \ " EXTRA_OEMAKE = " V=1" PACKAGES = "${PN}-dbg ${PN}-dev ${PN}-doc ${PN}c ${PN}-common linphone-rings liblinphone libmediastreamer-bin libmediastreamer libortp ${PN}-utils ${PN}-tests" FILES_${PN}-common = "\ ${bindir}/lp-gen-wrappers \ ${datadir}/pixmaps \ ${datadir}/applications \ ${datadir}/gnome \ ${datadir}/tutorials \ ${datadir}/linphone \ ${datadir}/sounds/linphone/hello8000.wav \ ${datadir}/sounds/linphone/hello16000.wav \ ${datadir}/sounds/linphone/incoming_chat.wav \ ${datadir}/sounds/linphone/ringback.wav \ ${datadir}/images/nowebcamCIF.jpg \ ${datadir}/appdata/linphone.appdata.xml \ ${datadir}/icons \ " FILES_${PN}-tests = "${bindir}/xml2lpc_test ${bindir}/lpc2xml_test" FILES_${PN} = "${bindir}/linphone" FILES_${PN}c = "${bindir}/linphonec ${bindir}/linphone-daemon ${bindir}/linphone-daemon-pipetest ${bindir}/linphonecsh ${bindir}/sipomatic ${bindir}/auto_answer" FILES_${PN}-rings = "${datadir}/sounds/linphone/rings" FILES_liblinphone = "${libdir}/liblinphone.so.*" FILES_libmediastreamer-bin = "${bindir}/mediastream ${bindir}/msaudiocmp" FILES_libmediastreamer = "${libdir}/libmediastreamer_base.so.* ${libdir}/libmediastreamer_voip.so.* ${libdir}/mediastreamer/ ${libdir}/mediastreamer/plugins" FILES_libortp = "${libdir}/libortp.so.*" FILES_${PN}-dev += "${libdir}/*.a ${libdir}/*.la ${libdir}/pkgconfig ${includedir}" FILES_${PN}-utils = "${bindir}/test_ecc ${bindir}/test_lsd" FILES_${PN}-doc = "${docdir}/ortp-0.24.1 ${docdir}/mediastreamer-2.11.1 ${docdir}/linphone-3.8.1-linphone-daemon ${mandir}" linphone-3.12.0/build/openembedded/linphone/linphone-common_git.inc000066400000000000000000000023631313432737600254140ustar00rootroot00000000000000 SRCREV = "cfa356c8680d26eec970c7b54beea0717b91f2b0" L_GIT_SRC_URI = "gitosis@git.linphone.org:linphone-daemon" PR_append = "+gitr${SRCREV}" LINPHONE_TMP_DIR="/tmp/LINPHONE_TMP_${SRCREV}" SRC_URI = "file://${LINPHONE_TMP_DIR}/linphone.tar.gz" S = "${WORKDIR}/linphone" # bitbake git fetcher currently doesn't handle git submodules # There is also a problem with autogen and AC_SUBST # note: don't use a ssh key with password, it does not work. do_fetch_prepend () { import os,bb bb.note("Hack preparing clone in %s" %"${LINPHONE_TMP_DIR}") os.system("rm -rf ${LINPHONE_TMP_DIR}") os.system("mkdir -p ${LINPHONE_TMP_DIR}") bb.note("Hack cloning linphone !recursively") os.system("cd ${LINPHONE_TMP_DIR}; git clone --recursive ${L_GIT_SRC_URI} linphone") bb.note("Hack launching autogen.sh manually") os.system("cd ${LINPHONE_TMP_DIR}/linphone; ./autogen.sh") bb.note("Hack preparing linphone.tar.gz") # we need to keep the .git since the versioning in linphone is done through `git describe` os.system("cd ${LINPHONE_TMP_DIR}; tar czf linphone.tar.gz linphone") } require linphone-common.inc #Required to avoid compile errors on May 2011. EXTRA_OECONF +=" --disable-strict" LIC_FILES_CHKSUM = "file://COPYING;md5=9f9938e31db89d55a796e86808c96848" linphone-3.12.0/build/openembedded/linphone/linphone-common_local.inc000066400000000000000000000005561313432737600257250ustar00rootroot00000000000000SRC_URI = "file://${HOME}/linphone-3.6.1.tar.gz" S = "${WORKDIR}/linphone-3.6.1" require linphone-common.inc do_configure_prepend () { ./autogen.sh libtoolize --copy --force } #Required to avoid compile errors on May 2011. EXTRA_OECONF +=" --disable-strict --disable-glx" LIC_FILES_CHKSUM = "file://COPYING;md5=94d55d512a9ba36caa9b7df079bae19f" linphone-3.12.0/build/openembedded/linphone/linphone_+git-nogtk-gsm-video-x11.bb000066400000000000000000000005561313432737600274340ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r0" DEFAULT_PREFERENCE = "3" OVERRIDES_append = ":console" OVERRIDES_append = ":gsm" OVERRIDES_append = ":video" OVERRIDES_append = ":x11" #PARALLEL_MAKE="V=1" require linphone-common_git.inc linphone-3.12.0/build/openembedded/linphone/linphone_+git-nogtk-gsm-video.bb000066400000000000000000000005251313432737600270210ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r14" DEFAULT_PREFERENCE = "3" OVERRIDES_append = ":console" OVERRIDES_append = ":gsm" OVERRIDES_append = ":video" #PARALLEL_MAKE="V=1" require linphone-common_git.inc linphone-3.12.0/build/openembedded/linphone/linphone_+git-nogtk-gsm.bb000066400000000000000000000004251313432737600257140ustar00rootroot00000000000000 DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r15" DEFAULT_PREFERENCE = "3" OVERRIDES_append = ":console" OVERRIDES_append = ":gsm" #PARALLEL_MAKE="V=1" require linphone-common_git.inc linphone-3.12.0/build/openembedded/linphone/linphone_+git-nogtk.bb000066400000000000000000000004371313432737600251330ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r14" DEFAULT_PREFERENCE = "3" OVERRIDES_append = ":console" #PARALLEL_MAKE="V=1" require linphone-common_git.inc linphone-3.12.0/build/openembedded/linphone/linphone_+local-nogtk-gsm-video-x11.bb000066400000000000000000000005611313432737600277370ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r9" DEFAULT_PREFERENCE = "-1" OVERRIDES_append = ":console" OVERRIDES_append = ":gsm" OVERRIDES_append = ":video" OVERRIDES_append = ":x11" #PARALLEL_MAKE="V=1" require linphone-common_local.inc linphone-3.12.0/build/openembedded/linphone/linphone_+local-nogtk-gsm-video.bb000066400000000000000000000005271313432737600273320ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r9" DEFAULT_PREFERENCE = "-1" OVERRIDES_append = ":console" OVERRIDES_append = ":gsm" OVERRIDES_append = ":video" #PARALLEL_MAKE="V=1" require linphone-common_local.inc linphone-3.12.0/build/openembedded/linphone/linphone_+local-nogtk-gsm.bb000066400000000000000000000004731313432737600262260ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r9" DEFAULT_PREFERENCE = "-1" OVERRIDES_append = ":console" OVERRIDES_append = ":gsm" #PARALLEL_MAKE="V=1" require linphone-common_local.inc linphone-3.12.0/build/openembedded/linphone/linphone_+local-nogtk.bb000066400000000000000000000004411313432737600254350ustar00rootroot00000000000000## THIS unusable work in progress ## DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" PR="r9" DEFAULT_PREFERENCE = "-1" OVERRIDES_append = ":console" #PARALLEL_MAKE="V=1" require linphone-common_local.inc linphone-3.12.0/build/openembedded/linphone/linphone_git.bb000066400000000000000000000026721313432737600237430ustar00rootroot00000000000000DESCRIPTION = "Audio/video SIP-based IP phone (console edition)" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv2" SRCREV = "855f3aa1b83fd979bb8ff4c6373522bc72fe77ec" L_GIT_SRC_URI = "gitosis@git.linphone.org:linphone-daemon" LINPHONE_TMP_DIR="/tmp/LINPHONE_TMP_${SRCREV}" SRC_URI = "file://${LINPHONE_TMP_DIR}/linphone.tar.gz" S = "${WORKDIR}/linphone" # bitbake git fetcher currently doesn't handle git submodules # There is also a problem with autogen and AC_SUBST # note: don't use a ssh key with password, it does not work. do_fetch_prepend () { import os,bb bb.note("Hack preparing clone in %s" %"${LINPHONE_TMP_DIR}") os.system("rm -rf ${LINPHONE_TMP_DIR}") os.system("mkdir -p ${LINPHONE_TMP_DIR}") bb.note("Hack cloning linphone !recursively") os.system("cd ${LINPHONE_TMP_DIR}; git clone ${L_GIT_SRC_URI} linphone; cd linphone; git checkout ${SRCREV}; git submodule update --recursive --init") bb.note("Hack launching autogen.sh manually") os.system("cd ${LINPHONE_TMP_DIR}/linphone; ./autogen.sh") bb.note("Hack preparing linphone.tar.gz") # we need to keep the .git since the versioning in linphone is done through `git describe` os.system("cd ${LINPHONE_TMP_DIR}; tar czf linphone.tar.gz linphone") } require linphone-common.inc #Required to avoid compile errors on May 2011. EXTRA_OECONF +=" --disable-strict" LIC_FILES_CHKSUM = "file://COPYING;md5=9f9938e31db89d55a796e86808c96848" linphone-3.12.0/build/openembedded/msamr/000077500000000000000000000000001313432737600202535ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/msamr/msamr-common.inc000066400000000000000000000011271313432737600233540ustar00rootroot00000000000000DESCRIPTION = "Mediastreamer2 plugin adding support for AMR codec" SECTION = "libs" PRIORITY = "optional" LICENSE = "GPLv3" LIC_FILES_CHKSUM = "file://COPYING;md5=d32239bcb673463ab874e80d47fae504" DEPENDS = "linphone opencore-amr" DEPENDS_append_wideband = " vo-amrwbenc" MSAMR_WIDEBAND = "" MSAMR_WIDEBAND_wideband = "--enable-wideband" EXTRA_OECONF = "\ ${MSAMR_WIDEBAND} \ " FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*" FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so" inherit autotools pkgconfig linphone-3.12.0/build/openembedded/msamr/msamr_git+wb.bb000066400000000000000000000003101313432737600231400ustar00rootroot00000000000000PR = "r2" SRC_URI = "git://git.linphone.org/msamr.git;protocol=git" SRCREV = "6ed342ed00526c21e85f8a06538fe3da2c7a24f4" S = "${WORKDIR}/git" OVERRIDES_append = ":wideband" require msamr-common.inc linphone-3.12.0/build/openembedded/msamr/msamr_git.bb000066400000000000000000000002501313432737600225370ustar00rootroot00000000000000PR = "r2" SRC_URI = "git://git.linphone.org/msamr.git;protocol=git" SRCREV = "6ed342ed00526c21e85f8a06538fe3da2c7a24f4" S = "${WORKDIR}/git" require msamr-common.inc linphone-3.12.0/build/openembedded/msamr/msamr_local+wb.bb000066400000000000000000000003321313432737600234530ustar00rootroot00000000000000PR = "r1" SRC_URI = "file://${HOME}/msamr-0.0.2.tar.gz" S = "${WORKDIR}/msamr-0.0.2" do_configure_prepend () { ./autogen.sh } OVERRIDES_append = ":wideband" DEFAULT_PREFERENCE="-1" require msamr-common.inc linphone-3.12.0/build/openembedded/msamr/msamr_local.bb000066400000000000000000000002721313432737600230520ustar00rootroot00000000000000PR = "r1" SRC_URI = "file://${HOME}/msamr-0.0.2.tar.gz" S = "${WORKDIR}/msamr-0.0.2" do_configure_prepend () { ./autogen.sh } DEFAULT_PREFERENCE="-1" require msamr-common.inc linphone-3.12.0/build/openembedded/msilbc/000077500000000000000000000000001313432737600204055ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/msilbc/msilbc-common.inc000066400000000000000000000006371313432737600236450ustar00rootroot00000000000000DESCRIPTION = "Mediastreamer2 plugin adding support for ILBC codec" SECTION = "libs" PRIORITY = "optional" LICENSE = "GPLv2" DEPENDS = "linphone libilbc-rfc3951" FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*" FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so" LIC_FILES_CHKSUM = "file://COPYING;md5=59530bdf33659b29e73d4adb9f9f6552" inherit autotools pkgconfig linphone-3.12.0/build/openembedded/msilbc/msilbc_git.bb000066400000000000000000000002511313432737600230240ustar00rootroot00000000000000PR = "r1" SRC_URI = "git://git.linphone.org/msilbc.git;protocol=git" SRCREV = "2bf845d7f537eb671dd32ca5b0cc932e8bb48952" S = "${WORKDIR}/git" require msilbc-common.inc linphone-3.12.0/build/openembedded/msilbc/msilbc_local.bb000066400000000000000000000002751313432737600233410ustar00rootroot00000000000000PR = "r0" SRC_URI = "file://${HOME}/msilbc-2.0.3.tar.gz" S = "${WORKDIR}/msilbc-2.0.3" do_configure_prepend () { ./autogen.sh } DEFAULT_PREFERENCE="-1" require msilbc-common.inc linphone-3.12.0/build/openembedded/msimx6vpu-h264/000077500000000000000000000000001313432737600215735ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/msimx6vpu-h264/msimx6vpu-h264.inc000066400000000000000000000006441313432737600247310ustar00rootroot00000000000000SECTION = "libs" DEPENDS_${PN} = "libmediastreamer imx-lib imx-vpu" DEPENDS = "libmediastreamer imx-lib imx-vpu" PROVIDES = "msimx6vpu-h264 msimx6vpu-h264-dbg" inherit autotools gettext FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*" FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so" FILES_${PN}-dbg = "${libdir}/mediastreamer/plugins/.debug/*.so.* /usr/src/debug" linphone-3.12.0/build/openembedded/msimx6vpu-h264/msimx6vpu-h264_git.bb000066400000000000000000000014271313432737600254060ustar00rootroot00000000000000DESCRIPTION = "A H264 encoder/decoder plugin for mediastreamer using Freescale's IMX6's VPU" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv3+" GIT_SRC_URI = "gitosis@git.linphone.org:msimx6vpu-h264.git" SRCREV = "master" LIC_FILES_CHKSUM = "file://COPYING;md5=c46082167a314d785d012a244748d803" TMP_DIR="/tmp/TMP_MSIMX6VPUH264_${SRCREV}" SRC_URI = "file://${TMP_DIR}/msimx6vpu-h264.tar.gz" S = "${WORKDIR}/msimx6vpu-h264" # note: don't use a ssh key with password, it does not work. do_fetch_prepend () { import os,bb os.system("rm -rf ${TMP_DIR}") os.system("mkdir -p ${TMP_DIR}") os.system("cd ${TMP_DIR}; git clone ${GIT_SRC_URI} msimx6vpu-h264") os.system("cd ${TMP_DIR}; tar czf msimx6vpu-h264.tar.gz msimx6vpu-h264") } require msimx6vpu-h264.inc linphone-3.12.0/build/openembedded/msv4l2-display/000077500000000000000000000000001313432737600217265ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/msv4l2-display/msv4l2-display-common.inc000066400000000000000000000006471313432737600265100ustar00rootroot00000000000000SECTION = "libs" DEPENDS_${PN} = "libmediastreamer" DEPENDS = "libmediastreamer" PROVIDES = "msv4l2-display" EXTRA_OECONF += ' CFLAGS="-DOUTPUT_VIDEO_DEVICE=17"' inherit autotools gettext FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*" FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so" FILES_${PN}-dbg = "${libdir}/mediastreamer/plugins/.debug/*.so.* /usr/src/debug" linphone-3.12.0/build/openembedded/msv4l2-display/msv4l2-display_git.bb000066400000000000000000000014071313432737600256720ustar00rootroot00000000000000DESCRIPTION = "V4L2 display filter plugin for mediastreamer/linphone" HOMEPAGE = "http://www.linphone.org/?lang=us" LICENSE = "GPLv3+" GIT_SRC_URI = "gitosis@git.linphone.org:msv4l2-display.git" SRCREV = "master" LIC_FILES_CHKSUM = "file://COPYING;md5=c46082167a314d785d012a244748d803" TMP_DIR="/tmp/TMP_MSV4L2Display_${SRCREV}" SRC_URI = "file://${TMP_DIR}/msv4l2-display.tar.gz" S = "${WORKDIR}/msv4l2-display" # note: don't use a ssh key with password, it does not work. do_fetch_prepend () { import os,bb os.system("rm -rf ${TMP_DIR}") os.system("mkdir -p ${TMP_DIR}") os.system("cd ${TMP_DIR}; git clone ${GIT_SRC_URI} msv4l2-display") os.system("cd ${TMP_DIR}; tar czf msv4l2-display.tar.gz msv4l2-display") } require msv4l2-display-common.inc linphone-3.12.0/build/openembedded/mswebrtc/000077500000000000000000000000001313432737600207625ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/mswebrtc/mswebrtc-common.inc000066400000000000000000000010331313432737600245660ustar00rootroot00000000000000DESCRIPTION = "Mediastreamer2 plugin adding support for WebRTC features (iSAC codec, AEC...)" SECTION = "libs" PRIORITY = "optional" LICENSE = "GPLv2" DEPENDS = "linphone" EXTRA_OECONF = "--disable-isac" FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*" FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so" FILES_${PN}-dbg = "${libdir}/mediastreamer/plugins/.debug/*.so.* /usr/src/debug" LIC_FILES_CHKSUM = "file://COPYING;md5=59530bdf33659b29e73d4adb9f9f6552" inherit autotools pkgconfig linphone-3.12.0/build/openembedded/mswebrtc/mswebrtc_git.bb000066400000000000000000000002551313432737600237620ustar00rootroot00000000000000PR = "r3" SRC_URI = "git://git.linphone.org/mswebrtc.git;protocol=git" SRCREV = "a9b5929928dd58299ceaed0aeb507c82bae80b55" S = "${WORKDIR}/git" require mswebrtc-common.inc linphone-3.12.0/build/openembedded/mswebrtc/mswebrtc_local.bb000066400000000000000000000002771313432737600242750ustar00rootroot00000000000000PR = "r0" SRC_URI = "file://${HOME}/mswebrtc-1.0.tar.gz" S = "${WORKDIR}/mswebrtc-1.0" do_configure_prepend () { ./autogen.sh } DEFAULT_PREFERENCE="-1" require mswebrtc-common.inc linphone-3.12.0/build/openembedded/msx264/000077500000000000000000000000001313432737600201775ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/msx264/msx264-common.inc000066400000000000000000000006241313432737600232250ustar00rootroot00000000000000DESCRIPTION = "Mediastreamer2 plugin adding support for H264 codec" SECTION = "libs" PRIORITY = "optional" LICENSE = "GPLv3" LIC_FILES_CHKSUM = "file://COPYING;md5=59530bdf33659b29e73d4adb9f9f6552" DEPENDS = "linphone x264" FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*" FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so" inherit autotools pkgconfig linphone-3.12.0/build/openembedded/msx264/msx264_git.bb000066400000000000000000000002511313432737600224100ustar00rootroot00000000000000PR = "r1" SRC_URI = "git://git.linphone.org/msx264.git;protocol=git" SRCREV = "f1fd3d6be817dd5c1b8a46f68de04421f75cf056" S = "${WORKDIR}/git" require msx264-common.inc linphone-3.12.0/build/openembedded/msx264/msx264_local.bb000066400000000000000000000002751313432737600227250ustar00rootroot00000000000000PR = "r1" SRC_URI = "file://${HOME}/msx264-1.4.2.tar.gz" S = "${WORKDIR}/msx264-1.4.2" do_configure_prepend () { ./autogen.sh } DEFAULT_PREFERENCE="-1" require msx264-common.inc linphone-3.12.0/build/openembedded/opencore-amr_0.1.3.bb000066400000000000000000000007211313432737600225470ustar00rootroot00000000000000DESCRIPTION = "OpenCORE Adaptive Multi Rate (AMR) speech codec library implementation" SECTION = "libs" PRIORITY = "optional" LICENSE = "Apache" PR = "r1" SRC_URI = "${SOURCEFORGE_MIRROR}/opencore-amr/${PN}-${PV}.tar.gz" inherit autotools pkgconfig SRC_URI[md5sum] = "09d2c5dfb43a9f6e9fec8b1ae678e725" SRC_URI[sha256sum] = "106bf811c1f36444d7671d8fd2589f8b2e0cca58a2c764da62ffc4a070595385" LIC_FILES_CHKSUM = "file://COPYING;md5=dd2c2486aca02190153cf399e508c7e7"linphone-3.12.0/build/openembedded/polarssl/000077500000000000000000000000001313432737600207735ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/polarssl/polarssl-linphone/000077500000000000000000000000001313432737600244445ustar00rootroot00000000000000linphone-3.12.0/build/openembedded/polarssl/polarssl-linphone/darwin.patch000066400000000000000000000005151313432737600267520ustar00rootroot00000000000000diff -urN a/library/Makefile b/library/Makefile --- a/library/Makefile 2013-07-29 17:26:14.000000000 +0200 +++ b/library/Makefile 2013-07-29 17:26:58.000000000 +0200 @@ -26,7 +26,9 @@ DLEXT=so # OSX shared library extension: -# DLEXT=dylib +ifdef DARWIN +DLEXT=dylib +endif # Windows shared library extension: ifdef WINDOWS linphone-3.12.0/build/openembedded/polarssl/polarssl-linphone/soname.patch000066400000000000000000000031271313432737600267520ustar00rootroot00000000000000diff -urN polarssl.orig/library/Makefile polarssl.new/library/Makefile --- polarssl.orig/library/Makefile 2013-08-22 10:24:46.353700982 +0200 +++ polarssl.new/library/Makefile 2013-08-22 10:21:43.933733318 +0200 @@ -28,12 +28,14 @@ # OSX shared library extension: ifdef DARWIN DLEXT=dylib +SONAME=libpolarssl.0.dylib endif # Windows shared library extension: ifdef WINDOWS DLEXT=dll LDFLAGS += -lws2_32 +SONAME=libpolarssl-0.dll endif OBJS= aes.o arc4.o asn1parse.o \ @@ -73,15 +75,17 @@ libpolarssl.so: libpolarssl.a echo " LD $@" - $(CC) ${LDFLAGS} -shared -Wl,-soname,$(SONAME) -o $@ $(OBJS) + $(CC) ${LDFLAGS} -shared -Wl,-soname,$(SONAME) -o $(SONAME) $(OBJS) + ln -s $(SONAME) $@ libpolarssl.dylib: libpolarssl.a echo " LD $@" - $(CC) ${LDFLAGS} -dynamiclib -o $@ $(OBJS) + $(CC) ${LDFLAGS} -dynamiclib -Wl,-install_name,$(SONAME) -o $(SONAME) $(OBJS) + ln -s $(SONAME) $@ libpolarssl.dll: libpolarssl.a echo " LD $@" - $(CC) -shared -Wl,-soname,$@,--out-implib,$@.a -o $@ $(OBJS) -lws2_32 -lwinmm -lgdi32 + $(CC) -shared -Wl,-soname,$(SONAME),--out-implib,$@.a -o $(SONAME) $(OBJS) -lws2_32 -lwinmm -lgdi32 .c.o: echo " CC $<" diff -urN polarssl.orig/Makefile polarssl.new/Makefile --- polarssl.orig/Makefile 2013-08-22 10:24:34.585703377 +0200 +++ polarssl.new/Makefile 2013-08-22 10:19:26.801749343 +0200 @@ -21,7 +21,7 @@ cp -r include/polarssl $(DESTDIR)/include mkdir -p $(DESTDIR)/lib - cp library/libpolarssl.* $(DESTDIR)/lib + cp -d library/libpolarssl* $(DESTDIR)/lib mkdir -p $(DESTDIR)/bin cp library/libpolarssl*.dll $(DESTDIR)/bin linphone-3.12.0/build/openembedded/polarssl/polarssl.inc000066400000000000000000000005601313432737600233260ustar00rootroot00000000000000DESCRIPTION = "SSL/TLS library" LICENSE = "GPL" inherit pkgconfig EXTRA_OEMAKE += " SHARED=1" PROVIDES = "polarssl polarssl-dev" ALLOW_EMPTY_${PN} = "1" PACKAGES += "${PN}-utils" MAKE_DESTDIR = "DESTDIR=${D}/${prefix}" FILES_${PN} += "${sharedlibdir}/*${SOLIBSDEV}" FILES_${PN}-dev += "!${sharedlibdir}/*${SOLIBSDEV}" FILES_${PN}-utils += "${bindir}/polarssl_*" linphone-3.12.0/build/openembedded/polarssl/polarssl_linphone.bb000066400000000000000000000004551313432737600250370ustar00rootroot00000000000000require polarssl.inc SRCREV="cecb44e4f13f42f793dde34b42793e1ebcce91a5" S = "${WORKDIR}/git" do_fetch_prepend () { import bb bb.note("Will checkout in %s" % "${S}" ) } SRC_URI = "git://git.linphone.org/polarssl.git" LIC_FILES_CHKSUM = "file://LICENSE;md5=751419260aa954499f7abaabaa882bbe" linphone-3.12.0/build/openembedded/spandsp_0.0.6-pre18.bb000066400000000000000000000011321313432737600225640ustar00rootroot00000000000000PR = "r0" SRC_URI = "http://www.soft-switch.org/downloads/spandsp/${PN}-0.0.6pre18.tgz" S = "${WORKDIR}/spandsp-0.0.6" # *cough* do_configure_append() { rm config.log } DESCRIPTION = "A library of many DSP functions for telephony." HOMEPAGE = "http://www.soft-switch.org" SECTION = "libs" LICENSE = "LGPL" DEPENDS_${PN} = "tiff libxml2" inherit autotools #PARALLEL_MAKE = "" SRC_URI[md5sum] = "98330bc00a581ed8d71ebe34afabbcf9" SRC_URI[sha256sum] = "835cd886105e4e39791f0e8cfe004c39b069f2e6dcb0795a68a6c79b5d14af2c" LIC_FILES_CHKSUM = "file://COPYING;md5=8791c23ddf418deb5be264cffb5fa6bc" linphone-3.12.0/build/openembedded/vo-amrwbenc_0.1.2.bb000066400000000000000000000007031313432737600223770ustar00rootroot00000000000000DESCRIPTION = "VisualOn AMR-WB encoder library" SECTION = "libs" PRIORITY = "optional" LICENSE = "Apache" DEPENDS = "opencore-amr" PR = "r1" SRC_URI = "${SOURCEFORGE_MIRROR}/opencore-amr/${PN}-${PV}.tar.gz" inherit autotools pkgconfig SRC_URI[md5sum] = "588205f686adc23532e31fe3646ddcb6" SRC_URI[sha256sum] = "dd8c33e57bc415754f31fbb1b1536563bf731fc14e55f8182564e4c0fbb26435" LIC_FILES_CHKSUM = "file://COPYING;md5=dd2c2486aca02190153cf399e508c7e7"linphone-3.12.0/build/openembedded/x264_git.bb000066400000000000000000000011131313432737600210030ustar00rootroot00000000000000DESCRIPTION = "x264 is a free software library and application for encoding video streams into the H.264/MPEG-4 AVC format." SECTION = "libs/multimedia" PRIORITY = "optional" LICENSE = "GPLv2" HOMEPAGE = "http://www.videolan.org/developers/x264.html" PR = "r1" SRC_URI = "git://git.videolan.org/x264.git;protocol=git" SRCREV = "e89c4cfc9f37d0b7684507974b333545b5bcc37a" S = "${WORKDIR}/git" EXTRA_OECONF += "--disable-lavf --enable-pic" EXTRA_OEMAKE = "" AS = "${TARGET_PREFIX}gcc" LIC_FILES_CHKSUM = "file://COPYING;md5=94d55d512a9ba36caa9b7df079bae19f" inherit autotools pkgconfig linphone-3.12.0/build/osx/000077500000000000000000000000001313432737600153325ustar00rootroot00000000000000linphone-3.12.0/build/osx/Info.plist.in000066400000000000000000000025671313432737600177210ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable linphone CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_FRAMEWORK_IDENTIFIER} LSMinimumSystemVersion ${MIN_OS} MinimumOSVersion ${MIN_OS} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType FMWK CFBundleShortVersionString ${LINPHONE_VERSION} CFBundleSignature ???? CFBundleVersion ${LINPHONE_VERSION} CSResourcesFileMapped NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} NSPrincipalClass NSApplication NSHighResolutionCapable True linphone-3.12.0/build/redhat/000077500000000000000000000000001313432737600157705ustar00rootroot00000000000000linphone-3.12.0/build/redhat/INSTALL000066400000000000000000000003451313432737600170230ustar00rootroot00000000000000INSTALL : Download and install the repo rpmforge : http://repoforge.org/use/ $ sudo rpm -Uvh Download the linphone-release rpm $ sudo rpm -Uvh $ sudo yum install linphone linphone-3.12.0/build/redhat/README000066400000000000000000000036021313432737600166510ustar00rootroot00000000000000********************************** * Compiling linphone on RedHat * ********************************** Download and install the repo rpmforge : http://repoforge.org/use/ $ sudo rpm -Uhv $ yum -y update - Install build time dependencies $ sudo yum install libtool intltool - Install others dependencies $ sudo yum install gtk2-devel $ sudo yum install ffmpeg-devel $ sudo yum install openldap-devel - Install antlr3 $ git clone git://git.linphone.org/antlr3.git $ cd antlr3 $ sudo cp antlr-3.4-complete.jar /usr/share/java/antlr3.jar - Download and install packages $ sudo rpm -Uhv ftp://ftp.icm.edu.pl/vol/rzm2/linux-fedora/linux/epel/6/x86_64/polarssl-1.3.2-1.el6.x86_64.rpm $ sudo rpm -Uvh ftp://ftp.icm.edu.pl/vol/rzm2/linux-fedora/linux/epel/6/x86_64/polarssl-devel-1.3.2-1.el6.x86_64.rpm $ sudo rpm -Uhv ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/15/Everything/x86_64/os/Packages/antlr3-C-3.2-14.fc15.x86_64.rpm $ sudo rpm -Uhv ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/15/Everything/x86_64/os/Packages/antlr3-C-devel-3.2-14.fc15.x86_64.rpm - Git repository Belle-sip : git clone git://git.linphone.org/belle-sip.git oRTP : git clone git://git.linphone.org/ortp.git Mediastreamer : git clone git://git.linphone.org/mediastreamer2.git - Compile Belle-sip / oRTP / mediastreamer $ ./autogen.sh $ ./configure $ make && make rpm $ sudo rpm -Uvh - Compile Linphone $ ./autogen.sh $ ./configure $ make && make rpm - Compile msx264 $ ./autogen.sh && ./configure && make $ make rpm -Create yum repo : $ cd rpmbuild/RPMS/*arch*/ $ createrepo . Create a file "linphone-release.repo" in /etc/yum.repos.d/ with : [linphone-release] name = Linphone for redhat baseurl = file/// *path to the new repo* enabled = 1 gpgcheck = 0 $ sudo yum install linphone linphone-3.12.0/build/wp8/000077500000000000000000000000001313432737600152375ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphone.vcxproj000066400000000000000000000277201313432737600210670ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {08dd0d38-d9b5-4626-b60d-b4d76b571142} LibLinphone en-US 11.0 DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ false Level4 $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\zlib;$(ProjectDir)..\..\..\sqlite\;$(ProjectDir);%(AdditionalIncludeDirectories) __STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="\\linphone\\plugins";UNICODE;_XKEYCHECK_H;HAVE_ZLIB;HAVE_CONFIG_H;%(PreprocessorDefinitions) Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) Console false false belle-sip.lib;mediastreamer2.lib;ws2_32.lib;ortp.lib;gsm.lib;speex.lib;speexdsp.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib version.bat Batch script to get the git version _DEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions) true NDEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions) MaxSpeed true true true false true true false {1db09afe-fc9b-472e-a746-0e33f8ef8883} {4c225a82-800b-427b-ba7b-61686a9b347f} {9924ac72-f96c-4e56-94d9-2b025da43c6b} {072fad20-7007-4da2-b2e7-16ce2b219f67} {36b528f9-fb79-4078-a16b-0a7442581bb7} {d22bd217-d0f8-4274-9b3a-f3f35f46482c} {b16b81a9-bef2-44c9-b603-1065183ae844} {0565952a-ea62-46a2-8261-f5b4b490da42} {a45d63b9-60de-476c-8836-f8eedbe139d0} {59500dd1-b192-4ddf-a402-8a8e3739e032} {027bad0e-9179-48c1-9733-7aa7e2c2ec70} {ffc7b532-0502-4d88-ac98-9e89071cbc97} {5dfa07b4-0be9-46a9-ba32-fdf5a55c580b} {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f} linphone-3.12.0/build/wp8/LibLinphoneTester-native/000077500000000000000000000000001313432737600221155ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj000066400000000000000000000205361313432737600304160ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {5e94a00b-b14a-4e42-8284-8cb0ef099534} linphone_tester_native en-US 11.0 true DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false false Level4 $(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\oRTP\include;$(ProjectDir)..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tester;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\bcunit\build\wp8\bcunit\$(Platform)\$(Configuration);$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;HAVE_CU_GET_SUITE;IN_LINPHONE;%(PreprocessorDefinitions) Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) Console false true ole32.lib;%(IgnoreSpecificDefaultLibraries) WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;ws2_32.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration) _DEBUG;%(PreprocessorDefinitions) true NDEBUG;%(PreprocessorDefinitions) MaxSpeed true true true false true false true {1db09afe-fc9b-472e-a746-0e33f8ef8883} {4c225a82-800b-427b-ba7b-61686a9b347f} {902daf1d-ebf1-4d03-b598-143500a50ab4} {9924ac72-f96c-4e56-94d9-2b025da43c6b} {072fad20-7007-4da2-b2e7-16ce2b219f67} {36b528f9-fb79-4078-a16b-0a7442581bb7} {d22bd217-d0f8-4274-9b3a-f3f35f46482c} {b16b81a9-bef2-44c9-b603-1065183ae844} {0565952a-ea62-46a2-8261-f5b4b490da42} {027bad0e-9179-48c1-9733-7aa7e2c2ec70} {ffc7b532-0502-4d88-ac98-9e89071cbc97} {08dd0d38-d9b5-4626-b60d-b4d76b571142} linphone-3.12.0/build/wp8/LibLinphoneTester-native/linphone-tester-native.cpp000066400000000000000000000073441313432737600272350ustar00rootroot00000000000000#include #include "linphone-tester-native.h" #include "ortp/logging.h" #include "bcunit/Util.h" using namespace linphone_tester_native; using namespace Platform; #define MAX_TRACE_SIZE 512 #define MAX_SUITE_NAME_SIZE 128 static OutputTraceListener^ sTraceListener; static void nativeOutputTraceHandler(OutputTraceLevel lev, const char *fmt, va_list args) { if (sTraceListener) { wchar_t wstr[MAX_TRACE_SIZE]; std::string str; str.resize(MAX_TRACE_SIZE); vsnprintf((char *)str.c_str(), MAX_TRACE_SIZE, fmt, args); mbstowcs(wstr, str.c_str(), sizeof(wstr)); String^ msg = ref new String(wstr); sTraceListener->outputTrace(lev, msg); } } static void LinphoneNativeGenericOutputTraceHandler(OrtpLogLevel lev, const char *fmt, va_list args) { OutputTraceLevel level = Message; char fmt2[MAX_TRACE_SIZE]; snprintf(fmt2, MAX_TRACE_SIZE, "%s\n", fmt); if (lev == ORTP_DEBUG) level = Debug; else if (lev == ORTP_MESSAGE) level = Message; else if (lev == ORTP_TRACE) level = Message; else if (lev == ORTP_WARNING) level = Warning; else if (lev == ORTP_ERROR) level = Error; else if (lev == ORTP_FATAL) level = Error; nativeOutputTraceHandler(level, fmt2, args); } static void LinphoneNativeOutputTraceHandler(OrtpLogLevel lev, const char *fmt, va_list args) { if (lev >= ORTP_WARNING) { LinphoneNativeGenericOutputTraceHandler(lev, fmt, args); } } static void LinphoneNativeVerboseOutputTraceHandler(OrtpLogLevel lev, const char *fmt, va_list args) { LinphoneNativeGenericOutputTraceHandler(lev, fmt, args); } static void BCUnitNativeOutputTraceHandler(int lev, const char *fmt, va_list args) { nativeOutputTraceHandler(Raw, fmt, args); } LinphoneTesterNative::LinphoneTesterNative() { liblinphone_tester_init(); } LinphoneTesterNative::~LinphoneTesterNative() { liblinphone_tester_uninit(); } void LinphoneTesterNative::setOutputTraceListener(OutputTraceListener^ traceListener) { sTraceListener = traceListener; } void LinphoneTesterNative::run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose) { std::wstring all(L"ALL"); std::wstring wssuitename = suiteName->Data(); std::wstring wscasename = caseName->Data(); char csuitename[MAX_SUITE_NAME_SIZE] = { 0 }; char ccasename[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(csuitename, wssuitename.c_str(), sizeof(csuitename)); wcstombs(ccasename, wscasename.c_str(), sizeof(ccasename)); if (verbose) { linphone_core_enable_logs_with_cb(LinphoneNativeVerboseOutputTraceHandler); } else { linphone_core_enable_logs_with_cb(LinphoneNativeOutputTraceHandler); } CU_set_trace_handler(BCUnitNativeOutputTraceHandler); liblinphone_tester_run_tests(wssuitename == all ? 0 : csuitename, wscasename == all ? 0 : ccasename); } unsigned int LinphoneTesterNative::nbTestSuites() { return liblinphone_tester_nb_test_suites(); } unsigned int LinphoneTesterNative::nbTests(Platform::String^ suiteName) { std::wstring suitename = suiteName->Data(); char cname[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(cname, suitename.c_str(), sizeof(cname)); return liblinphone_tester_nb_tests(cname); } Platform::String^ LinphoneTesterNative::testSuiteName(int index) { const char *cname = liblinphone_tester_test_suite_name(index); wchar_t wcname[MAX_SUITE_NAME_SIZE]; mbstowcs(wcname, cname, sizeof(wcname)); return ref new String(wcname); } Platform::String^ LinphoneTesterNative::testName(Platform::String^ suiteName, int testIndex) { std::wstring suitename = suiteName->Data(); char csuitename[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(csuitename, suitename.c_str(), sizeof(csuitename)); const char *cname = liblinphone_tester_test_name(csuitename, testIndex); wchar_t wcname[MAX_SUITE_NAME_SIZE]; mbstowcs(wcname, cname, sizeof(wcname)); return ref new String(wcname); } linphone-3.12.0/build/wp8/LibLinphoneTester-native/linphone-tester-native.h000066400000000000000000000014171313432737600266750ustar00rootroot00000000000000#pragma once #include "liblinphone_tester.h" namespace linphone_tester_native { enum OutputTraceLevel { Debug, Message, Warning, Error, Raw }; public interface class OutputTraceListener { public: void outputTrace(int level, Platform::String^ msg); }; public ref class LinphoneTesterNative sealed { public: LinphoneTesterNative(); virtual ~LinphoneTesterNative(); void setOutputTraceListener(OutputTraceListener^ traceListener); unsigned int nbTestSuites(); unsigned int nbTests(Platform::String^ suiteName); Platform::String^ testSuiteName(int index); Platform::String^ testName(Platform::String^ suiteName, int testIndex); void run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose); }; } linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/000077500000000000000000000000001313432737600213455ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/App.xaml000066400000000000000000000016721313432737600227560ustar00rootroot00000000000000 linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/App.xaml.cs000066400000000000000000000226111313432737600233560ustar00rootroot00000000000000using System; using System.Diagnostics; using System.Resources; using System.Windows; using System.Windows.Markup; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using LibLinphoneTester_wp8.Resources; using linphone_tester_native; namespace LibLinphoneTester_wp8 { public partial class App : Application { /// /// Provides easy access to the root frame of the Phone Application. /// /// The root frame of the Phone Application. public static PhoneApplicationFrame RootFrame { get; private set; } /// /// Constructor for the Application object. /// public App() { // Global handler for uncaught exceptions. UnhandledException += Application_UnhandledException; // Standard XAML initialization InitializeComponent(); // Phone-specific initialization InitializePhoneApplication(); // Language display initialization InitializeLanguage(); // Show graphics profiling information while debugging. if (Debugger.IsAttached) { // Display the current frame rate counters. Application.Current.Host.Settings.EnableFrameRateCounter = true; // Show the areas of the app that are being redrawn in each frame. //Application.Current.Host.Settings.EnableRedrawRegions = true; // Enable non-production analysis visualization mode, // which shows areas of a page that are handed off to GPU with a colored overlay. //Application.Current.Host.Settings.EnableCacheVisualization = true; // Prevent the screen from turning off while under the debugger by disabling // the application's idle detection. // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run // and consume battery power when the user is not using the phone. PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled; } tester = new LinphoneTesterNative(); suite = null; } // Code to execute when the application is launching (eg, from Start) // This code will not execute when the application is reactivated private void Application_Launching(object sender, LaunchingEventArgs e) { } // Code to execute when the application is activated (brought to foreground) // This code will not execute when the application is first launched private void Application_Activated(object sender, ActivatedEventArgs e) { } // Code to execute when the application is deactivated (sent to background) // This code will not execute when the application is closing private void Application_Deactivated(object sender, DeactivatedEventArgs e) { } // Code to execute when the application is closing (eg, user hit Back) // This code will not execute when the application is deactivated private void Application_Closing(object sender, ClosingEventArgs e) { } // Code to execute if a navigation fails private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) { if (Debugger.IsAttached) { // A navigation has failed; break into the debugger Debugger.Break(); } } // Code to execute on Unhandled Exceptions private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { if (Debugger.IsAttached) { // An unhandled exception has occurred; break into the debugger Debugger.Break(); } } #region Phone application initialization // Avoid double-initialization private bool phoneApplicationInitialized = false; // Do not add any additional code to this method private void InitializePhoneApplication() { if (phoneApplicationInitialized) return; // Create the frame but don't set it as RootVisual yet; this allows the splash // screen to remain active until the application is ready to render. RootFrame = new PhoneApplicationFrame(); RootFrame.Navigated += CompleteInitializePhoneApplication; // Handle navigation failures RootFrame.NavigationFailed += RootFrame_NavigationFailed; // Handle reset requests for clearing the backstack RootFrame.Navigated += CheckForResetNavigation; // Ensure we don't initialize again phoneApplicationInitialized = true; } // Do not add any additional code to this method private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e) { // Set the root visual to allow the application to render if (RootVisual != RootFrame) RootVisual = RootFrame; // Remove this handler since it is no longer needed RootFrame.Navigated -= CompleteInitializePhoneApplication; } private void CheckForResetNavigation(object sender, NavigationEventArgs e) { // If the app has received a 'reset' navigation, then we need to check // on the next navigation to see if the page stack should be reset if (e.NavigationMode == NavigationMode.Reset) RootFrame.Navigated += ClearBackStackAfterReset; } private void ClearBackStackAfterReset(object sender, NavigationEventArgs e) { // Unregister the event so it doesn't get called again RootFrame.Navigated -= ClearBackStackAfterReset; // Only clear the stack for 'new' (forward) and 'refresh' navigations if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh) return; // For UI consistency, clear the entire page stack while (RootFrame.RemoveBackEntry() != null) { ; // do nothing } } #endregion // Initialize the app's font and flow direction as defined in its localized resource strings. // // To ensure that the font of your application is aligned with its supported languages and that the // FlowDirection for each of those languages follows its traditional direction, ResourceLanguage // and ResourceFlowDirection should be initialized in each resx file to match these values with that // file's culture. For example: // // AppResources.es-ES.resx // ResourceLanguage's value should be "es-ES" // ResourceFlowDirection's value should be "LeftToRight" // // AppResources.ar-SA.resx // ResourceLanguage's value should be "ar-SA" // ResourceFlowDirection's value should be "RightToLeft" // // For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072. // private void InitializeLanguage() { try { // Set the font to match the display language defined by the // ResourceLanguage resource string for each supported language. // // Fall back to the font of the neutral language if the Display // language of the phone is not supported. // // If a compiler error is hit then ResourceLanguage is missing from // the resource file. RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage); // Set the FlowDirection of all elements under the root frame based // on the ResourceFlowDirection resource string for each // supported language. // // If a compiler error is hit then ResourceFlowDirection is missing from // the resource file. FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection); RootFrame.FlowDirection = flow; } catch { // If an exception is caught here it is most likely due to either // ResourceLangauge not being correctly set to a supported language // code or ResourceFlowDirection is set to a value other than LeftToRight // or RightToLeft. if (Debugger.IsAttached) { Debugger.Break(); } throw; } } public bool suiteRunning() { return (suite != null) && (suite.running); } public LinphoneTesterNative tester { get; set; } public UnitTestSuite suite { get; set; } } }linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/000077500000000000000000000000001313432737600226075ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/AlignmentGrid.png000066400000000000000000000215221313432737600260430ustar00rootroot00000000000000‰PNG  IHDRðQ5´ pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF}IDATxÚìÙ Ü0DÁ(¸ ÷â:Ý‹;Ú4!ŽíLZa ­$€]@€ @ @€ € @€@€ @€ @€ € @€@€ @€ @€  Ø5út÷zFŸïËk«­¶ÚzôÖé{›¶úŽmµµû_¼‘("@€ @ @€ € @€@ø/­$nJx@€ @€ @Øì}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € à7®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € à7®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € à7®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € @›]£Ow¯gôù¾¼¶Új«­Go¾·i«ïØV[»ÿÅy€" @€ € @€@€ @€ €ÿÒJâ „€ @€@€ €Í®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € à7®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € à7®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € à7®Ñ§»×3ú|_^[mµÕÖ£·NßÛ´Õwl«­Ýÿâ¼@€ @€@€ @€ @€ @€f[IÜ”ð€ @€@€~ã}º{=£Ï÷åµÕV[m=zëô½M[}ǶÚÚý/ÞÈ  @€ € @€@€ @ `¶•Ä-@ / @€ € @›]£Ow¯gôù¾¼¶Új«­Go¾·i«ïØV[»ÿÅy€" @€ € @€@€ @€ ̶’¸(á @€ € üÆ5út÷zFŸïËk«­¶ÚzôÖé{›¶úŽmµµû_¼‘("@€ @ @€ € @€@Àl+‰[€^@€ @ @Ào\£Ow¯gôù¾¼¶Új«­Go¾·i«ïØV[»ÿÅy€" @€ € @€@€ @€ ̶’¸(á @€ € @€àÿ›W`g“&IEND®B`‚linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/ApplicationIcon.png000066400000000000000000000065001313432737600263720ustar00rootroot00000000000000‰PNG  IHDRddpâ•TsRGB®ÎégAMA± üa pHYs.#.#x¥?v ÕIDATx^í]{tUŸ<š&-¡o‘RµzDWQëz_+]+¥ìÊ9+¨kÕŽ‹® ÈžÍîqñQW÷, ²¨È>Ú"/[E^-"²-}µ¥¥Ï¤MÓ$Ís23É$Ùï–öœò˜ffÒIÍüÍw¿ûû¾ß½÷»ß{‚3IØd,‹?cï±TŒaE?L{$qC.€‘ø#$Ä €%NHœy@`pâ=$NˆÀ< 08ñ'D`œx‰"0 N¼‡Ä ˜'æzÈmÅÉL}8CÕ$Ãæ—2•†\Œ­öVw9Š´Ø–0q^Cã½²Z}YAÈ qK„–ÈtV—ÖNº_Ô™nåÄc?Xž¤Ýo«Ž,„³™€ˆ5BÊj­wè°:[Vÿ§mb ;÷5ò”ÛŽäLÚºðýÎTþ1áWD0„̯H<Öb}»h¼½ ñ\Ð’{.Ñqéi×S1LuY|ù³¾ÌA;Ýé+ºŸ¥÷ÅÏ:&•ÕZVòëmÚCÈo“-ÚÛgv6—Ö[ï ½ð“¶,é&|þ}¦ ß0"‚¸zÐYå/Ó¡'«»C%þºÉ¶êÖµß0p¿"B!dÕç=Ù†üHºÜ®zµ£Ø¿·œhµ­öw4ú?òÐGžBA< átÓªýê\_o®ß£ŸŒâ‹{x ì0P§ùõ6íB!dÃÞžHçppöèå½E%î6P5œþ-|ðÄëză=µÝ„ê’KTâã-Ög ŒÉW¶SO50p¿"B!dëQí=.úJgŽô–åêñbá*®`QœqѨ‚Ðè­ôWŒQ LÐåöbÕ­Ö#Ñ€5BÎtÛA<†Mœ×¡·Ñ]/~t²sÅFU¯hê v÷`4Œâº á®ÂJ—º¹ÖHŸhèÕ?§kÙVöüNõ”t¥7M®HPdÈ%‰I EJ²8)Q,NJ’‹’%"‘âÚ Ù³SÓe3ÙÖíòµÝøg8å=E{D¸ÝEãá"씈°9Ý ‹²Q˜£ÇHhKŸ»Á —ÓÞƒ¶%0eô_ÞíD46äQžb°¹Œë÷ë&±"æré¤àvåžNY:ö¯ÝX»eT €¬^ãX½qÁ$ݨ æ2†x·|Õ»ÒBxô\‹íz羇oIùœ+¼\‚mzúFýévó*Ôj~ ÄÁÿ³<¶rf0§„ ™QÚ1@îï„ F× qü«¡jÄ_œ‚ZËîãJX*1ŒgR: ®Š¹7Mü”kù ÛPpMoM¾f¼]ÐØÌûë œU|ö +,‘œï&Ú ìçº AŸÖâªõb"Æ[ZG‡™Ã‹OBrxªÃþ ,§7„ÚpFh”°ÏÛ‹¾=Öl+ZSÒ”>:§‘æ"1Ì.:“t Á¼´µŸü@²~÷‹d â´£EK–”TE£CÄä°"¤°IVÙbù‹ÞæÒÅ¢ùÀì†ÕÓ^Õûõyûï#"…U¦^:ÃÙm&Ë`VÀný&"äÂ,$†n¯èb³†Ø)BV³¬å÷N:»vwÿ½Ýgy¤ÆK9Þ™œÓ8¶,*=ûКG³X,ÖrÔ窤µ]øÎñɃŒsV’¶¿`þëÆÅ*†¨ýðyó/œ…ñY¨:ugë®ZÛ-¬É@ Xž5uÏ®ÓÝz;]?Þ—³PÒÛ6@•í8¤¾û׳”œmCeCµŠ¢ÙY­M}äǰ—“F#T%VÒckÒÚ‹_]Êò¥”Ÿœ’»º-ñœß:;7y“fãùIUH”ßœzâd›õníä"¨¢7ª§ôšœ'„:Öó… ÍcšµÄ§áN1"« ~°Ñü ÙAkø2:ôBc¬ûä[ÝõŒLˆ}PW‰¿o·¿ôÈtå×) IÈÆ@áìÔ„;ß™Q}¤Œ ÜÈc,Ôõ¬Ûžw}òßdRqŒÎgã²àeS’$³såÿ`~=Ò"'–N6Öÿñt;¾Vcr5Äê&¸Hç_Ž€7gõ©‹Ž*ÎÛ¶F®—‹ +œ[* wŸU;6ÿ˜Q@ï39»àÜË_ß=d¸9r†KrÔ}\ZŽ7¢åørÒé¦b! #,·ãp¾åß»k?gµÜîÏ „ŒÔñv…&njӆÑ òHØ ïå>Ñb?Ë ÖI½¢Å³27£ Ǻ PÁu™²ù•­ø > EÔC 9Ò‚/»>K¶€ÀBÐ)†ª?™"ïÇ59\ã᜔±ßuMâßèñü@ïOÍŸ‘¹ läÔP® -¹+ëý”$iæx&cĶi²ücÍö'¸´•SB¾i2/ɽJVÈ%@!ëBgßïœ*ßÄÉÎ÷aC9#¤®Eºçº [ÈÓCWúò<å‡\ÙÌÙ²3¦å3s €$‰Xœ,¼IFb1¦Š¨D¥\¬œ(¹ìÒ® áSÁFÛ w@"èòzEœÌ#0‘‡]&Cÿ†{XÈõæ ë ²[Xáà3ñ&Âà½;ܸS y†?Æ“mö×8Mƒ±Æ_rEÞ¹=‘š©”ÄÜq6dÉÕ¥FëŒ!g1$\W}5ÿêù±8\!»²”Ò9¼ßâÀuPGÈÔ4ÙCád„úûD…$᩼œŸE_tz¬§ÊÅs¢a_uÜ’­XÈ—n_½Q!ä­s¦g*¦EàHë·I&U.y =*„Üó„üÉåù E{1µÉÙy^K~XÙfçl)!6ÊcßYmZÖÔçØÞoquù¿pƒ82 5¬Hõ3-B2'Hæ¡7êPíÔÑSñ—7WéfLM3wf¶âœð¹Æ•©ìå`o®kÅ»-ûn’\4y펛^?Ø{[u±.^«Ä)5¨û§OÈg_S8 \¼1 U¼{‡k\ßÙSgzüñ·[^±·«Æ2¬ó“~‹s¬Lù©ª)ý‹Óâϫφs'«ß£˜†Ä¹½Êðß„„ºdÕ Ûxº¢#B:"ЉaH8IãÁ. ²8Üæšnü¿¬Z†‹š)¸;q ,sÐtÁ^]$¶õ°-•¤™ lè|…ï-ƒ;vË·UõÏ,>¤[iÂݶ`zP|ÒYé7HôÙè/gMM^µ¿Ñ2¯ßêºà¯‹vcføÛ˜oH!˜#}g5¨Wœh·?•{•¼`]~Žݲ£³¹ö#¤m€Üõñ‘ÞÛ´®@2ˆ°óÇNôÛÒ»ÒOþi—áÎ5ñ®“öR>òcÞ;.aá;¨‡ëðû¢ÍÍpöÛ‹ÎèÁ‚/Ñ ÿbÿú®ÿ@ç µ7«†?[q Ñr/<%ýãúæć+ööoõ³ vÀÑ ô¥ò0¨üŠ%¨cp?.L/MÇ[­+ÀâÀ/T Û‡•¾Î¶Âýí;NÜè륑¹øÊë#>æÉÜÕ‰5¸ ®É8À¯·h!%ô pÏtâq´Óåö½`]¨ lØÛ4"bÞmÇúóÂé~¤øâUádxÿ]0„0´}r}z9»v +6ü‘¡£=FªiÌïtghߥð!€Â/’ƒÛ*; ä¡p9Ã;åW£/í|ßi_7ýc*‹„”Õ æo¬Ð2º_½ñ¾×Ëû®S'¦òX$d4öÅœ¬P2õ˜s€…‘òh`¬©Ž"0Æâ„Ä ˜'ÞCâ„̃ï!qBæÁ‰÷8!ó€ÀàÄ{ˆÀa¿=7 Sqb„@Œ$aâÿ&²™[è3UIEND®B`‚linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/Tiles/000077500000000000000000000000001313432737600236675ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/Tiles/FlipCycleTileLarge.png000066400000000000000000000233121313432737600300410ustar00rootroot00000000000000‰PNG  IHDR³P¬ßöª pHYs.#.#x¥?v OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFõIDATxÚìÝ}¬d÷Yðç93÷}_¼»Y{µX§II”@œÚ$ÈqÒ'iœÔy!B©‚¥¹»T‚ºR S5‰Á© ‰³­ ì­±yIƒTBÈÚ P°BbßÝ묽Ùû²3s~ýÃ×îe¹ï3÷Þ½w>)’wæÌ9sŸ3ùïùÍ3çd)%`'ª”a„Yff@˜a„Y„Yf@˜@˜a„Yff@˜aa„Yf@˜@˜a„Y„Yf@˜aa„Yff@˜a„Y„Yf@˜@˜a„Yff@˜aa„Yf@˜@˜a„Yff@˜aa„Yf@˜@˜a„Y„Yf@˜aa„Yff@˜a„Yv‹¦À`ËLEXƒéé齇>¿Û*¥Œeæì–³c€gf`õÐø²C‡ݽÛšœœ¬"â7K)ר<€0 зe曺ÝîäfoèCúÐŽˆ›êº¾MÙV—¾¦‚´¬¨”2RJy"3”RJfÞœ™lÒ¶~8">½ðÏ/gæw®²¼ <3³+ûÁÌ<°ü3"îž››ûöM²/ŽˆO,zè…¥”—)?€0 ЋÛ.ù÷á‘‘‘ÿYJë×Î;w0ž™‘[eÛ³kÓjµ®.¥Ü´ÄS/®ëú£ýØÆäädµo߾߈ˆ^ú\)奔{`yzfaÐ=³Ë*¥üTDܾÜóu]ÿD£ÑøH/Ûèv»?WUÕWXä–Ì<±Ìû³“Ç1ƒ!³,VOeæÑÂn;3_›™mdýNç‡Æo®²Ø}™ùa@˜vY˜m·Û7EÄ…¡¡¡?ê÷º~|õù5,:=;;ûÝããã_[ÏúçççÿñðððCqÅ*ï£;;;ûü‰‰‰'6áo¼ivvö‘ñññ¿Û©ŸÇ0@Ï,°c5›Íok4—R~±Ÿ?ÈZpÛ—;<::úéõlzzzïððð‰Õ‚ìÂÉFc||üÖ~þagÏžÝ_JùxD|flllŸO Ìlƒº®÷/\.ë'"âÏÛíö÷õc½ ×–}ÇZ—ÏÌïˆÿ²–e'''«…»‰½pïç¶~Õ¬”rÓW\ñ¥ˆxß³aÜ' f¶c«ªÅ¿ôAgiŸ»¶ì:üH§Óyÿj }øÃþ`f¾i=+ÎÌ£ סݰų±™ùÍ>=€0 °ýö]úú5K{ÛÃõ¯\¼xñåË=ßét~¨”ò6øž6Üj°Älìbffa`›,w Ö ÏÒ®pmÙUeæÐÐÐн­Vëê%å5Fã×rƒ¿¸+¥¼ëäÉ“Íõ¼Æl, ÌìÌ0»áYÚñññ[3³ÑÃ{ºfllìÞÅÁszzzoDÜkøÁ× ϑ믿~Í!{•ÙØÅü f¶ÉZ‚غfiûñc«Ì¼þÕ¯~õÏF<󃯃~<"¾«×õ6±Ú2˜u‡1`Gk*°ƒ­)ˆ-š¥}c»Ý~Ïr×¥]¸¶ìѾÌTÕ;ΗÆ?Šˆ[úô÷þ`)å`fž]æýß´d×ÜRP×õ~#`'33 ìdëýŠ|µYÚÛú:ÀVÕGK)ÇûÞß~都ôÆ^rEa`«”RF×ûšåzi×{mÙ5nk¼ÇþÛX-p¯£7¶_'Â,@ŸÂb/AìïÍÒv»Ý×nàÚ²Ûáeóóó×õñJffMÏ,°“õÄ÷Ò6'vÊ=44ô³øžˆø–>¬ÎÌ, Ìl“~±,üoGÈÌ7_.'ÛM›°“ b—Ï €0 ˆ9!fËJ»Ýþ¾¥nñÚ‹RŠÖŸ:öý:³ßøÆ7ž777÷ª ³ÀîhªêÆÇǯëú/ºÝî/¶Ûíמ:uj¸ÇÕš•íƒÌìù¤àäÉ“Ív»}}·Ûý¹RÊç÷ìÙsfddä%ª lÉ8VJQì0³éÛh·Û×7›Í‡?VJ™É̇뺾¯Ýnß7::ú×ëYçÌÌÌóÇÆÆ¾böì«™ùüõ¾h¡þ¯/¥Ü¯^|Y³RJ·Õj}ÓÞ½{Ïlö›w „Yf·d;¥”3qx…E«ëú÷J)÷?õÔSŸ=|øðùUÖw]D|ÉìÙtf^¹†ý7ÖívoÈÌ›3ó2ó;WXöªªºa‹>Wö 8—æ¶ÊƒñÃ+<MUUˆˆ:t¨]×õÿ.¥Ü×ívþ?K,¯g¶?–mטŸŸ¿®ÙlÞTUÕë"âUFclóAe¶Š™YôA`ëffo‹ˆ_Ûàk§K)¿_UÕýçÏŸÿ̾}ûž,¥Ü'íÁ¾|r¡ÎWDÄÔu}sUU¯‰ß”áå™ù§[ô¹²A˜5€0»ùΟ?åž={N÷!¼”ˆøbDüuf¾Åì]]×?Ÿ™¯Žˆ—eæP«[SÛ‚0 ³ÀŽ ³ ¡éTfUõ]íDfÞ"Ì[Å¥¹€­ ο« »Þ}J³€ ÃŽtáÂûØRÚ `Ð-l3X¸k×¹p%‚]©”òHUU×mñ6œ™Y`+ƒó|D|V%víþÕF³ÀîV×µ¯¡w/ûfÝ­Óé<¨ »Ò|Dü‰2Â,°«ŒŒœŠˆ/«ÄîRJ¹o¡@˜v=}³»/ÌÚ§€0 Œ•`wÑ>³ÀÀ8}úô}ñL%»Ã—ÚG„Y`÷;räH«”ò9•Ø5´Â,0XôXîÝn×¾¶;€Á [x°Åæçç¯þ’=°ãÍGÄþíº’c`fØ =–Ó*±³•R>ç’\Àv23 ƒ>¬aföÔ©SÃGŸ¨ªj¢ªªñÌœh6›ãÝnw""F"b¢ÑhŒ×u=1UU—R&"b"3Ÿûï…ç¯ÎÌ!{aG‡Ù™ˆ8™­…ÿnef+"f#b¶®ë™ˆhEÄLD´ªªju»Ý™ˆh5V§Ó™)¥´J)3¥”ÖÈÈHëÉ'Ÿœ¹ãŽ;Z“““õ¶o'€0k avÕÀ𙈸IµØBwfæû…Y@˜úf¿%"þ<"®P1¶ÀW"â%™ù´0 ¬FÏ,°–Àû•ˆø7*ÁùÀZ‚,€0 ¬'ÐÞ¨›ìÎÌô9Ö~|ò |H]ó²Ú Ødkn/Xô™T5pffõ_íl&í€0 lz ÕnÀfÐ^lì¸ä+øpºî×h7 ÏÖÝ^°è³¨z0àÌÌ ÀÚ è'í€0 ly Õn@?Ü£½èéxä+øPºá×j7 G§#â;{™•u Ì̽aíôB{Ðû±ÈY- | íy¥”ÏDÄMªÉ:Ü“™ïêÃgO%A˜5€0Ûs ÐnÀzôÜ^ ÌÏÒfô#k7`=´ý;9«…¢}[—vÖ /í‹>s* ¬„Ù¾ í¬¤oíÂ,ð,m@?ƒ±vV¢½fËÛ… ~§”òU•`±RÊßFÄTfË1¨Œu:·•R~gbbâñÌüfUa±Ì¼¶”òX)åd§ÓyïÔÔÔ„ªÂ,°­Úíö+K)wDÄãFãSñÆÌl¨ ËÚŒˆÆW^yåT)åívûµ“““ŽEÀÆÇÍó0ðc]ËÏÍÍ}ëÐÐЭ™ykf¾@éU)åñÌüÕùùù»GGGÿr¯U@f  Ì®lzzzïóž÷¼7GÄ{"âUcƒíç2óîóçÏzß¾}O ³Àj|µ,éäÉ“ÍRÊMÝn÷×:4ŸdÙ‚“«WDÄ{÷îýj·Û½·”òÆ“'O6UXvÜpV þÞ¿ççç¿«ÙlÞš™ïÍÌ#*Äv+¥Lgæ'#âÎÌüâ%Ï)³ô0{þüù+÷ìÙóöRÊ»3ó{T…Ë8Øþy)å¿ÏÍÍ}rbbâ Ç0@˜þIDüqDŒ);(Ô~#3¯ˆ/ª 6=³0èg´™_èt:¯)¥<®ì û·ívûU—¶Â,0 †††þdvvö¥¥”?V .ó û{.\xùÈÈÈ_¨ ÌÏ™˜˜xâÑG½1">¦\¦þÓñãÇo^Ë%»€Á¡g}Xâ:³NçUU}<3ÇUˆíVJ™©ëú½ÍfóÞ%žS f  ÌþCóóó× ýNf^«Jl£¿ºxñâ[–k+p ´K9uþüù—–R>£l“§OŸþný±€0 lÈþýûÏ?~ü u]ß^L±EJ)¥®ë;vìíGŽi©°m0èƒÀ2m—êt:o¨ªê™yHÕØÄ ûT]×ïl6›÷¯qyEƒgfX“f³ù»/^|y)åKªÁ&ÙG.^¼øÒµYaX—ÑÑÑ¿9sæÌ+êºþ´jÐg'Μ9óòÑÑÑ¿Q @˜6Í‘#GZFãmñÓúhéU)¥]×õOè6JÏ, ú °ÆžÙ¥´ÛíëƧ2ó›T’ Ù©n·ûÖ¡¡¡?êa ÎÌ,°aCCC» . ¡Ÿ›››{i/A@˜z611ñD)å>•`=2ówo¿ýö'Tèy<ñ |¨Øðk§¦¦&>ü«UUÝ¢’lÀ‰Ó§O¿§—^YÇ0@˜avC¯›ŸŸ¿nxxø7#âªHaô‘Ìü¡Ìü+aØmÀFÄ }^¥'SGK)_èt:oS @˜6ÕäädÕívÿc)åÞÌWúhÇƧJ)·ONN:.ëC|E$ִܹsçîÝ»÷×3óuªÆf)¥üÞ… Þ¹oß¾'׸¼¢0k aveívû•ÍfóSñ-*ÆÚ¿m·Ûoù aX¯s€u:÷7?dÙ¬k‡††>§f ;uêÔp)å£Fãc™9¤"lq o4Ÿêv»9uêÔ°ŠË޾¢ ÿà±V«uõØØØÿÈÌïU!¶ÛÂÝÂÞ:>>þµ%žS f  Ìþívû¦F£qwfV.£@;ÕívßzéíoÃmÀâ`ðSFã~A–Ëð¤ëH£Ñ8YJùÕ„Yàššèv»÷FÄíÙËýmasíPD|´Ûí~\-ðÜØà+lóóóß:<<ü[™ù"Õ`§x¶vllìkªƒÍÌ, ¸™™™¯—R~9"þP5Ø)A63ïît:󪘙…Au”R^X×õ»«ªº5\W–Ë+À>^J¹§ÓéÜ522òè¢Çœ™Y`q°ýr£Ñø™cÇŽ]7FÄ=¥”•a›ÌÖuýén·ûÆãÇ?¿ÑhüÛÅA ÂÌ,Vù½×ôôôÞ¼¥Ñh¼/"^¥bl¶gÛ"â72óéU–U0f  Ì®ÍÜÜÜ· ÝZUÕ{"âÕ£vÉ6‚5¼Nñ`Ài3ÖlttôoÆñcÇŽ}[<Ó†pg)å*Ãi#zff}èñ²²SSSW]uÕ[J)ïwZV³ž6‚5¬KAA˜5€0Û·rM]×ïÎÌw1.ùl77÷’ñññ¯õy½Š NÏ,Ð7“““Õðð卿,qÒthllìWTèûøâ¬>dôm]Ýn÷'«ªúUeïÊÌ{úµ2Ç0@˜a¶/ëÑ^ÀÃg_Û Ãm@Ï´°Ž“'í@Çgµ0ðá¢çuh/`úÒnà³ Ìöôzíl0„ö¥ÝÀ1 Ðfl˜öz8‰ÒnôgªêÛggg¿¹Ûí¾'"NDÄÓ=„Ù£­VëjU„Y`Wi6›ën1(¥üY]×?ßétn|ôÑGdæ›2ó¿ŽŽþe<óc#úd||ükÍfó®Ì¼åÁ<¯¨ëúX)åse¿²ÕjlW3€A¶èju]ŸÊÌ£«„ׯ—R¨ªêþ™™™ÏNLL<±Â²×E„+#ôn:3¯\isçÎÜ·oßk꺾93oÎÌ#«¬óDfÞ²oÞ1 03 lºV«uõRA¶”Ò-¥üa]×ÿ."^~üøñ+Æ;3óž•‚lDÄüü¼™Ù-²ÿþ³™y¢Ñh¼·ªª«/^¼øâº®ÿuD|¶”Ò^â%7œlÁÌl·Ûý±ªª~y!À~µ”r_)åþV«õÐþýûÏnd¥”ÃqÆìÙ#™¹áÛÐNMMM\uÕU¯ªëú ™ùúÌü¶…§nÌÌ7ûÍ;†Â,³›¾RÊ×u]u:ßy´OëÜçìÁí ³Kì—k"â¦n·û•f³y¿0 ³À޳›d `½{03oÜÁŸ{œžY`'Ó7 ÌìXßP‚žM+ Ìl¾ÍÌ–R.ì”?º”ò”] Ì;_Ï3³¥”¯FÄë2óøNù£ëº~WDÜRJéǬª™Y@˜Ø¥”^gfï|úé§_”™ÌÌÌÜSJéyêá‡~ 3O´Z­Å3·ŸfvšÌÜÐ¥¹ÍƾÿàÁƒç""&&&žÈÌv@˜=qã7v""öîÝ{fáN[½ÌÒš™„Y€m²‘™Ùçfc—xî®>Ïß*¥üi_íªº{‰Po–fv 5÷Ì.5»„ßî㫾xæÌ™wÎÍͽ¹”òxŸÂñ#™¹d8îa–Ö]Ôaàrµpc…•fcŸ“™ó™ùÉ>lóBDÜräÈ‘Öøøø×ºÝî;J)í^×›™w­a™­VëEu]ÿ†½³—·Õf ÿªÛí^¿Êlì¥îêå •RJ]×·dæ—Ÿ}lhhè¡Ìü±×Û™™¹g-ËîÝ»÷L£ÑxgD¼naFz%®Õ ³—“…ÙØ_Šˆ— ýÑz^›™ZJyd£ÛÎÌŸk6›÷/ñøÇ"âžÖûÀÄÄÄë}ÍÓO?ý¢ˆ¸s…ÅÜE f¶ÉR3³ÏÎÆþdfÎn08ÞµÁý[ÇŽûÐrÏ?òÈ#ïëáazO<—™ïågiÍÌ;Z>3 ì ¹cß{)e2">¼ðß%3??³Ñû¬V«uõØØØßefc/{,"¾;3Ÿ^å=_SJù“Ì<²Ž¿ó©Ì¼:3{šE={öìþüBD¼oÑÃ7fæƒ;ø3àÿÄ0àÌÌ;Ù³¿Äïy6v±õ^svá_o^-È.œ<<Öívß¶ž4dæ'{ ²ËÎÒš™„Y€íP×õ†{c×à®u¼ÍÌ/¬uù¡¡¡‡J)?½Öå;Χúù‡]ÒK«gØÑ´À ;¸Í`rr²šœœ¬7cÝ¥”‘RÊ™y`• ûKFã'7²n·ûëUUýóU{,3¯]æ=úÏÌ,°“Ãl½Yë^Ë5gK)?ôÐC?½ÑmLOO`µ„Õuý {`…ñÚ™= ø °ƒgf7[)åeñùež~lffæ{×{¹¬%¶qMDüYD\±Ì"×fæc˼ÖNž™Y€åƒþ’ל-¥´ÛíöÛz ² Ûx,"ÞºÌÂ\.È Ì¬%lÞuécu]ÿËáááÏ÷qŸ-¥,u}Z-«¡¾¦‚kа‚%®9û±ÌüÀflkñÂJ)3gΜ¹òÈ‘#­å–7~˜™XÑâkΖRþ8"þÕfmëìÙ³?.lëÄJAa`­NDÄôÜÜÜ-ý¸)Ãr>|>"^OWUu—²¬N› ú  Í`USSS‡zåÐÐÐÿÚŠí•R^“™Ÿ]Ãrvà8f0`§Òf€0 Â,³³ Ì€0 Â,Â,³ Ì Ì€0 Â,³³ Ì€0 €0 Â,³ Ì Ì€0 Â,Â,³ Ì€0 €0 Â,³³ Ì€0 Â,Â,³ Ì Ì€0 Â,³³ Ì€0 €0 Â,³ Ì Ì€0 Â,³³ Ì€0 €0 Â,³ Ì Ì€0 Â,Â,³ Ì€0 €0 Â,³³ Ì€0 Â,Â,³ Ì Ì€0 Â,³³pYøëKœÐ­|G¤IEND®B`‚linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/Tiles/FlipCycleTileMedium.png000066400000000000000000000215561313432737600302370ustar00rootroot00000000000000‰PNG  IHDRPPéè&Ù pHYs.#.#x¥?v OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF™IDATxÚìÝÜu}Çñ÷ûóýîíÝn.¿sI¨8`•9«È5p b-‚¨®Ú‘qm‹?f(— B¡£•±¶EÐ` HBÑb ¡CT­£ÈÑŽ0bÍyùu·{··ûý¼û›ô¼Þåîö»{·ßÝçc&Éí}w÷ýÝïëû~¿ßÝU3Àì9J( @€ @€P @( @€ (€P @( @€ (€P @€ @€ (€P @€ @€ ( P @€ @€ ( P @€@€ ( P @€ðÿ„” U¥Ól_±bÅÐ\ÝŸ™µ©ê•Ÿ¶NõnéÒ¥_2³us §ŠÈfªŽ9i Ø ÑÖR¶££c@D ªz¦ª>_Ãð\af?‘Õ###Çf³Ù½¬:P:P$ÖòåË/VÕŒª.‘-fÖV‹ûéëëk‘{Uõ5ª´¶¶®§ú @‘hAtûë©Þû;jq?'tÒ­"rþ‘¶sÝTŒðŒð‰•ËåV·µµ½¤ªÁø÷Þ"‚¿¯â(º^D¶Lü÷±±±SÒétk‚ž‰“Édº'†gy§s«™]¥8UDîžìg---d-€ER;œ+¦èÚS"ò@>Ÿÿƒ˜Ë_afŠHÛ?ÿóÞÞ^.ÕŠÄ…ç[UuÍQn²¢µµµâ“J===ND6«êNuU]µvíÚu¬  ¨EÈuïÛ·oQÅt7PÕ³DäK•,üú믿ID¦ Ç j2Æ÷õõµ”J¥wñ*jnœDŠ[ÀŸD2³^3{½ª^­ªÛ«¸Ü´™í-_º4­(Š® ÃðΙ.¿T*]Á·fx󂈣ªûªøüÞdf›Tu¿ªž›àõÏLŠP«ª¾FD1³;«Ø^<ÓðqÎ}ell쌥a¡ðÆ ¾>‹Ç’‘T«ëŒ¢¨ÇÌv«êif¶ˆWŠæížŽûëU‹/~¶o¹4³?›åãH¥R©ûs¹Üê£Ýnpp°½¥¥e«ˆ,®å㙪ë<ùä“w;çn(ŸUMó*"@ѼÒ‚,v7jfKUõ¢ ~õ¸¶¶¶û§:kÞÓÓã–-[¶EDN¬`Gñv3;®]ç„/ä%ÄÇŸ¾îl Þû—*éFÍì£CE_˜b¹Ÿ¹ÜžJºNïýO޲ضýæþCJ:U×Vi7zE¬‘ȹO”J¥ËÆÿ[©TºÄÌ>s¹3>?M×IŠÿÛN8û8b’;›áí~3“3õ…B¡³¥¥åÙ*<®aU=CUŸ=!N?%³<î9…sUuÇt]gù ûi3\ÿšàõÏhóº™ñ ™v£Õz뤪.‘mù|þØt:ýP•ÂS¼÷WT¡ëœXGºP:P4[jf+Dd ‚ß›´ííí Ï9眗TuUæ¡jŽÉf–èXµjU.N×9A‡ªÒÒ¢‰ŒŒŒ´V¸Ã˜´]»víº*‡§H•1ªjfåÊ•ëãv[p-(ŠfÓÖÖ7œ~ïºÑZ½e²>x¸ëœx]g%Òé4ׂ hB±7üñݨˆ\œçÝef§ë¬e—Œd᣾šW57ü«öÜÿ²ŠÇ®é@é@A :P `ÃgG"¯¾¼T*]Xƒo³dïÓÑØØØ›gs.æׯ-à\ê½ï+òú.Ù>66¶-î—¥™ÙGEäXƒ±]«ª_‰³€C‡-Ïd2ë‚ ¸ÀÌÞ¡ªªvÔú³íÓ63{¼Ü1ž'"·¶´´<ë½ÿŸ(Š6™Ùz3[\A(·RÙªìÜf}hoooX,×FQt³™í^°`Á@÷ˆH·ª®òÞ?Ne PTk%9÷à$ï1åÈØbf/›Ù“Qõ‹Å3Ëß4Ý29Zu3£Q;ŸÏkfñÞûœsÎÃp§sîÓ"ò։履l}ƒž¾ò4-"e†Ç-Íl¿ª>EÑ#…Báál6»w’ÛÜ,"Ÿf ÆöEUýä$õm‹¢¨KU/TÕóUõ¤™.pxxxe{{ûÀ¼®X{1qh2BºPã/šáí—ˆÈú Ög2ñÞ?kfßóÞoûÅ/~±³³³sL8 _-Gvj…B¡3 Ãuι DäíATrâïé¹OÐ6M*"Eѵι/W¡ëÈ«êf¶\UßÂŒ]ÏÝfÖçœ;OD^wyÞû AôÌÑcg Í Õú¬MÔ½i?³”%@ ÐÊ^ð/ˆÈqT½a»ÙýªºZU h2p>Y¶Q‚†Þ?6Wá ´í Ë{ÿ}ªÀÏ_»‘k©ˆüVxf£:^U_œÃ×§mª°ÞgfOR‰Æcf{æ2϶OÚ¬÷ˆ™=ÉåK(æHEŒ{ –!@1WFFFø¼ÈR*•XŸ Å1иœ‡c ""ÞûgTõ¬ÄwŸýιÕót߬:PÆ>$zÌz$@1ç+ιT¡!°á᦯¯¯eÍš5™‘‘‘¬s.ëœË¨j6 ÃLEYÉAñÞgE$+"É:ç2Þû¬ªfU5cfG~®ª Dd5k ñ~gf‡D$/"9UÍ™YÞÌrιœ÷>/"¹Ã?wÎå¢(Ê‹H.‚\©TÊ›YÎ{Ÿ÷ÞçÚÚÚr{öìÉ—?4›žMv€:thù‚ ö¨*×mbN”?4ûͪú<ÊŸh .|YU¯¥˜Ã½~ºðš¤.u«ˆl¥˜»vîÜy;e`„oˆ~\W°ÂÌúå1ߣ;#<hƒvQŒî(åÁèFø¹áåQ/£;#<(£<ÀèN€2ÊŒîŒðŒð³íVŠÈÏEd1UD…¯¡b±X<-N?WáïSD:ÐÄïïDäcT1ô†JÃh#„è="ò•@žÞ¹sçm”¾)GxFyÌ×èÎOÊ(Fw `”£;#<#<£<1º3ÂÓ2ʃÑ(åÁèN€¢*¢(º‹*`"ïý·¨B6=‰Ý5Æ^F.—[ÝÚÚz¹ªv«ê)TSŒðƒªzŸˆÜ¥ªÏTay•Mf€šY[EAÐmfªj@51‹×ÏOÍ죣£÷e³Ù½(ÚZ,Ï Ãð*3[¯ª © bi$"ßóÞßÁwTµ@€   £££¯K¥RÝåý T 5rÀ{¿Å9·YUw hbtpp°}ùòå—ŠÈ•"ÒE¥0Çž÷Þo.‡é¯ ÐÚà,|õôô83[Eѽ˖-ë‘» OÌ“sŸ3³ͬ·T*}¨¿¿?KYª+¤ñ …“Ã0ìvÎu‹Èkc¿„º™TDº‚ èêèèø’™=X*•6ßtÓMÿ!"ž 1ÂÏ+ïýGœsÿD%0w‰ÈÕ”~^AðÏQ]cfEª„¸mÆ ¦ t õ0"‰È«—'Að€ªCUPÌ,ï½ÿP†÷—ÿNQè@ëC*•úÑÈÈÈéföCª: ÏŠÅâÛ‡'к“Íf÷>÷ÜsçŠÈW©ê(<>#NÿŒj u­³³sLU?EÑ5"2BE0ÁiÞû[7nÜxáÂ… _¦"ÕÇ1и<Ê;‘ …Bg*•úŽªO¥0Çá¹ß{Y†å6Š´~¥Ó龡¡¡ÓÍìª9 Ï=ccc§-joëÀÀÀ­­­¿¢Œð‰á'é Ö›Ù&UÍP9T¹ë43ûÌ7Þx[OOŸáïP849*ÂqQÔ$<§=ÞI€2Â7„qÇEùŠTÃSï$@›Ê¢E‹ö9çÞ+"×q\•òÞßµgÏž³9ÞÉß4#üDÅbq]÷ªê2ª‰ŽÞEU½VU¿s9“4ÙòùüÓ"ò •À,ü¦P(Z·šËåV·µµ= ªgQ%ÌSˆ¾P,ß3Õ'ѳí3Â×¥b±xf[[Ûž˜çûñ©TêÉR©ô~ªA€&B©Tº:‚ïó휨“ÍAðMŽ‹2Â×õÏñN$`¤ÿ½ã¢lûh](Ç;‘ =r\”mŸ­‡‘ýÌ `dG‚B4ï½ÿP|G|L‰) ܪ~ÛÌ^¡H@xEd{9ªAZ#¼È«Ç@O<ñÄ‹œsݪúnISÔQp>©ª›‡††¶p ”­»ïàÁƒK.\Ømf—«ê[¨æ)4kf÷”J¥Métú¹I~N‘áëÏ¢E‹ö©êíιÓÇÆÆÖxï?ofýTsšyùF©T:ãÆÇAð©ÉÂt uÛN¦··7ìêê:Ï{¹ª^ÂGÙ¡Š¡iªú}¹gÿþý,]ºôà â ÉÐñÛ—,Yò§A\%"o§Š¨08_0³MιMªúë ~Ÿ" É ÐñFGG_—J¥ºUµ[Uß@E1Þû-ιͪº+fSM4Ù:áÝ彿Ü9w%•Å„×F¯÷þ«A<¨ª…*-“ÂÆÄI¤ú ã"ò_T“„ÝSaÞW­ðh#v Ç™Y'˜0Ék#*•Jgµ´´ì®â2),hcèééqfö5ÂS쨃0 ï2³6ªAJ:AE×:ç¾LEq4Þû[‚ ø (J€2ºcGy¶}FxFw0ʃ´Y;PFwÌ×(϶O€&:@Ý1Ÿ£<Û>#|Ò7FwÄå{{{CªAÚt¨™]%"wRAÄå7AÐCJ€6M€šÙkEä§"²˜ "æSTÕ3Tõ'(#|³¸ƒðD•và)¹›Qž´):PFwÔË(϶O€&*@ÝQO£<Û>#<£;À(OÚè(£;êm”gÛ'@ Œî¨ÇQžmŸžÑ`”§mÔthh¨#“É\)"YɈHÖ9—‘6i3³¬ˆdU5cfGþ[þ7¥ÂMßQšˆäT5gfùÃÿ=üo"2""#Þû¼ˆäD$/"¹b±¸µµµõWt hâGøOÛÐÐP6•Je[[[3Åb1›J¥2QeUµÓ9wk ñ>EÑ® rÅb1ŸJ¥r£££ùb±˜kooÏ©êH __T?&Úüúç‘r‡ñòÄŸEQô:*”|Þûb†S‰dâhRWœsçP…†ØIvQFxFø9dfi3ÛÇ'9%Ÿ™E¹\î˜öööy¸oVhS:“ðl˜p°`Á¦ sÅ{ÏØ×XXŸŒðŒðs Ï¨ê©~ÃŒñýιՌðt ¨±¡¡¡³áv« …B'• @QcmmmçQ…Ɔ!ë•E­Ap)UhÈ.ô½T!ëã ±_øszf6 "+¨|Ã)ˆÈ"U-Ìák‰ªÓ63;•ðlXéR©´–2 ¨ï=c^ ÃÃ3(jx¸ ‹*4ô„Áú%@Q ýýýYU}•hèäš\.·šJ ¨²•+W^$"é*.²@U«â™«µ°ÖÖVÓ ¨X㙑‡Dä/Däx¹‹’VÅ=KDä=Þû;Ìì—±6H>e+Qø<Ðä¸h–i"òŒ™=ì½ß¶k×®{î¹¥q?§­’U«VåÊ;§‡DDFGG_ŸJ¥.RÕu"r¶ª.˜Åz{‡™¥çòr&  ­ü6¿ãf°ñ šÙ÷œs?²pá—6zRÙªœd ÿo¹]Dnïëëk9á„ÎÃðföNU=íh SÕ%"r¦ˆì ´(ª±’°kŠÀ,ŠÈSf¶-Š¢í---?žé2½÷ç8‚Skc"òXùÏu¹\nu:¾0‚wšÙ;TuÙ$릋M¶ $¬$çÆï/zïèÒW^ye™snm7Ï&<ËËv›ÙÊ?EÑ_Oœ‡——6³½åĈó¸þÍ9wɸ¿o‘î˜O· "Ǩê¾Üÿ:3ûGU=~ë_¼þÙ€é@Ãt]èÖááágžå0)¨ê}1Ó3—ÿ‡={ö\efqÏò?4“ð<ܪj§ˆ|qš Ý'({¡&î@'ýŠd3TÕk+y‹¢™½UDvWøx†UõtU}~’Ÿgf?®´»¢èÝa~w¶¿W,ÿ8 ï‹È&Û©j((è@t¹\î”Jßß­ªO›ÙžJ'xïß7Yx–—ûbE—TrRÉÌúŸx≊Np¥R©ˆÈiSt£t (ðj×)¯~0ÆûÚÛÛc]š£ª›*ø›Ã0|xš0Ûif×U°ìÍã?¿‚ßQÕOFQ´VDÆeŸ'@€¢‰ T£ëœ(ŸÏß3ËNqÛŽ;n˜É ƒ ø‚÷þ_fóxÆÆÆî®ÆóšØ–¿g MŒc q ˜ìc [Ddk->ŽÍ{ÿ˜ªž7ƒ›¾("¤ªf|Üap°}ùòå;EäM3xŽ?sÎZíçW>6úAUýp‚×?0(*µaÆÔê³,UõžlÀÃ"rélÂSDdÅŠC"r©ˆ˜ÁãØ\‹ç—J¥~äð(hëïïÏvtt ¨jfªÛDQÔ†á½1:¨óÌl»ªSü<96›ÍîeÐÒ"1V­Z•3³­Gñ¿'<Ë;¯ÇUõoŽòóí„'P$óÅåܦ):Ÿ'vîÜy]5îcÆ ·xï§új’M¬0Â3Â'yL|A~ÿ;í_ÌçógU³3,ŸTzJDNw¿ûUuµªr©#<(’É{÷¸ ¶X,ß_í±º|Ré"wRIUï#„ßižþrN@ú[q‹Ñì÷Š-á%|wWXœcccð÷žOxgdd¤ž¿·ˆå0F«_ZÇqn@Ø)¯xÜÿerc£8EðûJƒ°òõ¦¨ù¿ávÀõD Kÿ‘*„0Oéôà·?@<3"êFñáû'‚Ââ·CQpÀÿ=Hï¿nœ_–~3$àÁL[ATöpˆ€äçíÞ¢á»u-f}”+ðԵvõá8` §åwŸ>Wh+ˆ0_3Þ¬Œf­•ÿ®„;k6lÎÏÓÚQ|ZešÔXg€¶‚3h" ñ#\½ÃÃËð÷Ópðç;ç× µ«¶Ï´üô—!ÔXWüú‚‡Z€0£D$<2ÀhÝÐÚQ|ê(VÒ/^¼ÅPDW[A˜Ñû Ã4í­6çgÐÚÕ§õX, d—j¬ÇT!u­à>Ó0ÍúCšÄþýº‘¬I”-)¾Ø“±v%)µNSߟÁ¿?…»ß4L³þdiþüù/áó³âêŽà77^[Q|m¹¡_ðŒªŸôÝd^–¬"Ö‘qË1Þ,½wEM\ø%±'1jºQü«À} óôòÒ¥K?A§è \?íM–™ ÙòO;nÖõÐ<‡þämmmïÏ›7ï+QÄ@¿uñMoo\¦8dĈïe÷É/0|·Ñ¤Ÿ3?»ü„!7#Xü ey®ê-Óùóç[²¯hå&‘9»)(ü=)&ÆÝ=üX΄e’Ý=\n6¸ÜÍ®'CØ [©¤&‘ÑžÚ p+Èœñ‚<üÿc$ùeµ…Š‘1”KVFNÁ­AÙæF± ~aè7oÚÅ×ð"¦ë2¡“ïD×‚Ûæ"¨p4À›€;¨ Ò¼M _ÿTš·«ÙR->É%útoh‡¨-°¯Ê«-_Þü¨¦þ¡¢¶üA|¹`¦^|˜¯’g"Žk«`ÍùÉË «^|nßOuÇY“åF•VSžå%âüV.̈† ×>­g” ña™m?2­¾­A|¸Q_Õ“<äB|2rƒ9yÛ¤@÷357SSÖ'™e lË–-åeË–•°6Y†°Ä•äsÁ‚%ܹòpuK˜f™ý®„Eòïà“ åw—¬ðÀí„—*œfxÎþ ž²‰UÖwØAßÐ9sæLuppÐA½LÛ¾q­‹ÿ ùÛez  NÞÅÍß wÁ&'ëfwš<\Í“›lÖrpky-â#¶…'Y².¾®®®ó0+a_øl !ضhÑ¢#Y€°nvk…„Ñ'iÉc²¨È8ifenkyµÞòÕÆ£ƒ¿¢ù#™d€}fæ6sñÑü&#¢¸±dinkyÎÌìÒüÆ•Móá²6·™·|4¿Í‹(N Ì­ñÑüÆ‘Pü0Ì­³Kó_HQCj1·jZ>šß¨Šç_“¹U'>šßx¢2 ¥ÉܪŸ{ŠûCXüþµ<‹j ”þÌ `ùl&öŸ‘W5˜‡jQŸîÞòš©]rlXÐÐü-Yà}TN:Ó÷³”—õy>Üy«°uj#–Ö6¢àŸË²ðEO[ú`ðwˆñX¿Ù~âÍŠøPÈ%(àP¸‡eëNÑ+]iùÿ‡:ÚƒFaêè-yLM|rÎq¥R¹Â{…ù SÈÓ£lTbÒiÈš;Ünñ¨»’Ž?õǪU«äz?Dæ ðÒª¾tâE}îGØç—껂SkùjXpÉ[}žq M‡cM”êëm.ù`©T’ÉR»RßRÑmÃÈj= $'HñRN@'Nœ¸;má †Ô[¾kÜI7cÇì¾V;¾L¹–Œ³‡Æau³îÆšô˜zËWËßâÅ‹?½CFTMæ™Á&áÀ:É‘jÖ„gµå«çÅ~`Âêi":[ý;¿,Zkùêg?° µ$ÔfÿÎ/ÛÖú|~‰³˜ ’"D•EÿNMËÇ~`¥$ì5«þ:ñÕ2„™ô©„3º ÙÍA%%Å›]¼7ãyRžó~IK+<>0__Û1½²C^X"ÖG»²…þ%¶ð¼KÓ)c5!€îÎ#Xƒ-a&[Û¬šÝsçÎ]ßÑÑñˆî»&pèLjï¬lÁ>Ë×ì¤x%k-Ÿ¼' Â{‡Â³Y½fi¡N®C+¸_,’X&³PÍû²ÒòáÎ’—ôí”C ›Ï2cH™ÀÞÓ§Oo^±b…“r:é¶|Ò™•‡V ¼¿RxiWebñ÷uwwA}!±D”ZË'¯Û,—Ë/Ctw¦]ÆŸ<ˆ¯Š~à&ô÷&{Ê}>ôïFÑêýɼ€‚|šVoòP_2Ö_­Vßø'©µ|õ™AaÚ!Ä 2´Ç÷ßâ®æ8U•nYvƒû3¶ÎïÆAáÿN7µ+±[_}Ap7ÝØÞÞ¾Ù}°(õ~… ˆ9Nc¦uŸ<±644t`íÚµV—9­‹ÏÓ"V𿼖¾­agŽ+1WYwÍê‹è—ïéìì<“Uæ3_­Ð###ååË—ËSn›áD¼&…Y +‚ ñÕ2 @߀B+hú*÷°òñw—ÌëÓXÒ|R5âs[¿Η4j•¼¸;YäE/Gµ”ÉÚRJXqDÚÓ^¥ø¿»;Y^”“$âÇ’lH-Ím²•ÌïS0¿öRlœRæâ£¹µ+Mæ7s³KskW|šÌo¦-Í­]áÕ§¦Áüf&>šÛì„')k0¿™™]šÛlŧÁüfÒòÑÜf+<-æ×ºøhnõ/kókÝìÒÜê_–æ×zË'[ëW¯^]ºpáB[yÄ• Œ³%ìù+£jʘ-a46û7œ<÷Q†Ÿu|Æ7X¸r'Ünø’ç/ªò ¾¸Îþ ®öëUáÇ_Ù©ì`{›süøñjOOÏ%Û·…uñÅ- @Ɇƒ»ã†/H¸ ”óZp’Oõ—u³‡îÔ6½#NØ‚…iC+÷í¼”9âÐû”¯R0PLë½ÞTxÉ…øt Z9ȬD%ÙœÍb.Ä—' YW<º'«q:Ä­YçÃ$}õâÔ¤0ôs…fra)Ô‹// 5 _¦¥4å§Q^Ô‹óTœ^‰¨$ˆOÞy§~€¦Z|.7S5’¦wÞš4H"nÕâ€r|—iAÝþwMýçÅÊuî-8y?®é¥~ÊEµø°ª8m •!•¿Á²Ñ[·ní†XoZ;yñ‡2½ƒîÇ7ñ¹ y~åÝŽÏ‚ò.27Ê+.@~ç\øî&wÊ)pK¼qã;9¡Õ®]~ åÀMy]= û:œ¬Ùι䤰¸ì NÀ Iˆí>Âõ ì0(®([M|ƒånGë¿b|Ì>øûÇaáø»¼èÀÜ066Ö˜ßßjÊCyBÅçe$üpâ¿êAGnvµ˜ %ýÄTÏ•3ÍK‚þžBo ÁøTD¥zÀ“P.¶Å,[KkEñŸ‚ŠVrR!ÄçK¶jUèNÈHÇ"¤9Áon¼¶œø0(1jù¤òaÊzåèðO¶j éî]¸páa¼J¾×þ¤­´™NÊP¹]A=c8PÿÚ'9<ße "S>p¯D ãú•>éÕ ßÝŽxކÄÓ—26FŸTbg£Št+ùv¿tæ·¦Bãêãoym€éuÒ/m÷•ÒË´’ßEñ%!qxkϯµóæCæM„>Û£µð³î¬IXiqƒÊÐ Îi-m0d1  /ÖÄÔÚy£G“VL–¶æ\·éLpeX‘´‚_8-¿C£&­ˆúCú‹‡ñ{{³ýDˆø"Í=zZÁЕ-ì ŸTœ¬uúöí‚à8ŽsC@ 6*¯qh^^˜2Ùµbj­ V+¾5,ýçD»ß¯Ïˆïî +Ž,Âß{>á9&$,</8GvË̹0PxÜ .·y ² Ç4<ý˜„Óó9^SŸ¼ >*ÙáÑ/ Q!Õ?÷¼+žcaldzn¾s{aáù{A @41c€qS\2AÂæöâÆÏp-L ‰½p_W3na¼, €!ÿ©õÌì£ÝlIEND®B`‚linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/Tiles/IconicTileMediumLarge.png000066400000000000000000000115111313432737600305320ustar00rootroot00000000000000‰PNG  IHDR†Ê`ã8† pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFtIDATxÚìëqâJF{(ÀfÀf "0Ž`µ,ŽàÚ,ŽßŒ#G`ÈÀdÌýAëše%¡‘4 Ω¢\åG{Ôó©#d¬µpÊ„ @€0a„„ @€0a„„ @€0a´ kí§µvØ ½¡µöÏv[S{ iÐf¢6§x¸»ÂXÚ/^°÷rdo‰‡»)Š‘ý›i {Ó {#<Ý=a<Øl¢ ¶¢[xº{ÂHr&Ó©M‹Í[ ž¾à™Þ€¼3<åÝÁÖû[Q޹°z¶G5lÌíyæ—²sFÀ‰å)¼¥…‘2«hcgË1u,6³ØUãìØ3ï& çèa­myvY¶õLÞ9؉«D „QONÑãdí¢ £gŠÍ<–U¢¨·öP:zè¤VaYCX)C×(ÁšHyaLJNÂÌqí¢”Í¢3º®Qâ„ ¨/ŒÌèQâ¬ôIR!J´V]¾ì‰H’F ÅÑ5Ç“¦SråñÔâ&€@óÛZûCD¶-˼E'Ñ£ “âÏÖ¥’>Wæ#„0ú%ŒtM@ÛFúrÿ]\¤¾^6½HÖt±Ò<k!¶‘µˆ¼‰ÈÚ³g:k a("ù¡_G'¾o­0>N °‘ˆLõ#ÖÚµþÎʳaªËEõé­Š¡È÷­ŽE—¡'úùm­Ýëï§B!š|E…T±ˆ |߯Ã}V,¤6G­g©sü[cÌ÷¶w%뎯E\‹:Ç¿jz0>„ñFB¸8Mô‘J†"²c®.Ê·¦k´Æ#†pÍ\] /˾V>I'—˯} ƒˆqÁˆáèñ5Z½{zȼy¥ñ6ÕwÄðÒBÁå"³Oa|0oÝkS/‘Jh[;ئz:à sçÏëK7ŽQ Ê)(ó¾OñéaÎ6мrïrEÛˆ`*"/ø?hî1 çC0Áav4Ƙ»Jŧޞ—‚c/"cc̶Rñ©ø„ƒã©H¥ÛURJRˆ«0H)=I!¥R )¥)Ä)bRú“Bª ƒ”x qJ%¤”þ¤Jƒ”~ ©+ RJ )¤R*!¥„ŸBjE RJ¸)¤VÄPQDÒŽÇA6Û:ÝvmW‡rعþKú½°KlDäUD.7ö”]U 1~î4+y5Ƭ* CÃÐ/táu+ Éæ¬0´ Eäé÷ó°zU‹ˆÈ¿rxFÉöaèm|?H¤y3Æ,L@ÀQd "cá6ø³‹Œ1{cÌX‹è7 cÌØ³ÿËs/"÷Z±Bÿº”{Õ@v»ªmê’º£W]ÉÏÓ¶5sCW8—ÂuÐY«(þÊ™×J´î¸‘g|,ÏÆ˜»¼eò³KâºÆ1V?C«'VE¿TöZI$‡}¬~í|+z_fssé««Zw¼«£]e¥¢(Õu–¾C뎟Â[]äÉóÓå²ûŸA©„¶'©Ä¥ødÑ«ûl%c1«R*ÑvõQÁHDÞuN«G kí\Dðg<c„A=ÑÖ’³$ÎE4Ȭ;õD‚(¨;Ì‘(^Dßv½e‘Þ“a´žx®ƒÀˆÜq30dÖ}ߎð øZýž·áˆ½©ýJ¯’µá( ¶(Í^\¶(ˆ$"1>í|ª¨·©9G CùÚÛJªéNªH÷¦îËþQå'êh=2'Š´::Gn­M„µ_lôÁ½^0E1‘óç•o.we¹àó‰:1óæo>ö)Œ[æÍ;Þ|ì3•ìhu½³Õ c݈Ý 4˨Λ¯‘JhQ/ǤKÂøÁ|] /¾n¼Æ M £mõ1H#¤ жúH%UIJѯQO'·Îñ7Þ¶ÅÈA{9lÅ¿×9‘·Ÿõoêƒoê“…”?ÝH}ß7 \|æçk9\\UÙÕ´ˆ\¤'®SÄrþJu, ¾‘ªiaœæº­ŠáMDÖ¾.ø.”Mšf´ã›h79‰Î·mF,‡í÷iTØ2µG“•~Ò扊"nòÝ4« ëÌØfy˜ywa$.÷5Zkc‡ ÝeÙÖ3|ç`'v_fô`æÝ„1«h£ì¤N lLËŠ«âgÃ]I»Ÿ­µó:¿”2у™?﬇lDg&óÝÁÖû[QŽÊ;;)Sl–°STŒò\².Fž¦Îð‚Ä™Țȴ†½)káˆãxMã¥{/®kÐNaL›®Žj—)î¶8œŠÍ²Åhè~3´ÐÅ„ @€0a„„ @€0a„„ @€0a@×øoÌÇöúhWIEND®B`‚linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/Tiles/IconicTileSmall.png000066400000000000000000000072141313432737600274140ustar00rootroot00000000000000‰PNG  IHDRGnuá pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF·IDATxÚìÝuâ0…ïøP€K ˜ „t*XSAB¦Ø’ pq¸ƒÙkŒ­Ÿ]C,4÷œ<tléËŒf4²bfˆš #pŽÀ8Gà#8Gà#pŽÀÝF̼`æÔ²mÊÌ‹à|óQSC»©j÷ ˜„ÿêÀ̉¦Ý¡Ò6ñ}à©E›Œ/µgæ¸Ö&VŸW•uqÿŸ„sl¢isàke€Ì̃5î¹ÏûLµÁ¤ ßϸ]‹ÊdݦYË„}–/p®¬ˆ™·¬Wjø~Ûd-½‡£æ Ý ‡Ü†ˆqáL ƒ:tÇt‰rܳëtG%n[5©=K1†ªß[—„ràpÀTý€™ ;_D´ë!)€±êoõ¹»ÅÍf_ßs?dêÇÌvÌäbšaóDDE§sŽº`î9˜ÜÌ¿D«OÏá8õßΗçpœúO®Wg"$"§ñLKICH÷±¶’7dÏ9•Öõ[©Ö­˜yï©etÉF¦ yŽ057F+"ʬ³Rã¶‹V¹×•;Ùä9óPÝÉ'÷jt'«$P-6÷÷,0ÝQ%€‘n­iÊ[µ ÔjbçžÖrÔ.Á›*ÅO%Ž…°õÉÕHí½á²Zº ëHY‰€¹Ô@LÊ¥¦²@ݩɽæD´£ZdÚº®ªV^O,ªä5€g›@Ál Ÿ —ü ºê”õé?è2÷#ìÜðü[Œ äú”kæùôâ2áõ÷Væã Qá pÓ òfþÁí>î5ýïðž,¨:CìúɨöOøéøH÷ýFúàžÿRÓe‚ \û‰Ú ïßÞ1Öûÿ茞 p œåñyXØôæÒÛÖsõGã3 \ý¯ 4ð$ÿkâŸêZã'á|ïÓåüõ8 @ Ý æù3ÿGþúÛ˜µëâåÐêÇýÎß)ã‘ù"ä–$»²8õ0^]4Xòë ùå‚ì­ôØç>)z*$cÌ%8ça‚Ú‘ÞZìhÞHûó’ïý™˜¯  ‡ ™÷ò`ë¬ï\@ï} ¿ìñú’ùæ`òÖç®ä?cì8#ä ¨!;(ƒë­æ÷Çç#äoŒî½ˆ ™ì¡rá;ö‡Û Û(íßO˜ôÒ¶ £¦}dÞL6ëuúXërç¨àòe4ô;)ö6'Èqâ„óºéß' $õÑ ÎÑ) êŸ ‘ÍRÝgß«Ïuý>é*ßþ.hö…û2òÓõÂë™ðåÿéWííäk{ôe4 fðgCÛoèÆßþضè )Ý:½1x$€8÷¡_êì‹ ‘ñ.us7´3ý“2šï¾ ðßçBýƒäë(Ž 7+;0äË#Kn 8}Øÿg ÝÝ÷ÖÝ7Ø–ücç‘ O õS ¼Ë¢æëËNÆ.édÓ ºóþ²ÛþÕðàÐóößÛP 'êC.Ž-³üm ê‹ôSò¼éP-úú,BÕ/31õM+éº÷ÿú‰úøújýÔ# ÑœL!Aú6ýÙ±îuÑsÔRäbÔÁHî Öóï¦ÄÂây¿ ½ŠòþË}9xö;áØ÷ÿÛÝÚñ£á1þ ÷¯ý›þûÔûÓü—ûÅ·õù(÷I 8: )J3î;F°+*øJ nëMñÑÿ^îhî ï+Í'Q.(QïD ÞÙçWíÎàðy÷ô"ñ} "!Ñèº ßÓá‚î/âðúEô·õûÊøÖóôšûBòMôòü¡ëªï·ÿvîÓ´ "ûòÕAóºîE ®øU7\“6i?Œù“'=ØýèÌë4Ûä4ø_#‰]m ûÒ «ê1õÐéèïø í³ ÿ~³Ù¸¼~Fòøþ'çríïë®æäþ¨ðÄ™H  t³ô¯þïýð†êñ80rs#•2„íåǤÝyÙ·Æe>é4 u °òÀ=àfë(ã#Þÿò¸èDLøZ Ë¿s …ÿ\ùï¨ûòã¤ê+æ-ävøøèA–ñ,—%TF+Üñëç æâ=õx@€,ª5Ž?¾o-võø ŒðÿðÛìô¼¶ E'¸LöžÿòyôÎð—ñ@î8ð,ì=í¸ðíûð0ò‹èžî+âêæ¸âhà ö“çÕžü° çøÒÆÅÒäñÒ-Á¯åH0¤„š/(íg ”ðTè=!úÓ1u‡=n9ý+†:ƒÓ$¦ýN úfúý5ûFþýý¡¹½y ƒ.ä÷ Eáµî†è2à#ð• e {õ, ÍÌçB¸ÅÂUĹùèýÍð_ô¡ìúý²ÔïâcÜÒÞÿ€åQ á)»añ|þ$ñ±ížE÷ž¶' !ç'©(Å%ª'{Ý#»ûPª þü2ö$*þ8øù5p×Ýê­â&Ø«‘íÅ y / £ëóúÚËä£ÚÈ×XäÝ]í‘çIî½îDíQírñÁí_öƒò]ý¬÷bÿ y€‹ Ó_úÁõöeøi® *0'çW(²ïÜ 1ß­æAžæå'‹¡'Ð,e¾ !ãŽúlØ¢ÜyàTÙ>÷Àæ6dÿÖù) ö’÷…ù ÷=ùúáìö†ëIé‡ìðŠ. ’ >‘àýyÞÝÖB^æ­"¨êŽ%bú`]éòò†ï«è¤ öWÈn 'ò( h߬éÎååÞ³ú"ë> •˜ #ÿ ¥ï@ú{êÃìTíPëqõDïç:ùbY }$ Fÿ zâžøŽãÚÖïï–Di-ò$&A“ûùLì²óù©ìQÃ"“ #ÅþÆîõÕ÷ãSùj( tÀ Mõ°þPñ`ó$îõñ*à­ê`Ú<ÜÉìÝ? 5öi müõ9ÈßAíÝõÚdûkâÊ%" 4+,õÍ"'âaùÏãÜP ñí -$8e3|(`7¤ RÊNGz K÷ÊU[O M ßf Ü ïøÔµãç8ÓAIöŠÐîå /¼2Ӫ΢¹Ôó\Ü®/ú–ü]èÙ óVÇπܹȒóçh=vžü¬ôËö§üªô Pð$|Œ4-+5ÿ6e%Û1—“•¥t+] *!°-ÕíÑÎÓ4á˜áìÕ®þêL< 8VzêÑ^àÛáÌ™þ~åýdºß4öáÈ=ÔÙ@ÉÎ÷mãþlúñàþø¬ š$Å+#qüG pùÛÚ 7]*°%M6?tñý‹öðƒÿ44'$Å-,7Xˆ&bòT5ç\ìñîÉçáùÚòœüZýªïÎúÜãëœçÈã’èêçÉä çéåÎâÐ÷ éPÿñøtÞ?ðÆÞóÖY ¡çï%?Ë£$jú Æî ô~ ò¥#E²/†*Ï4,èÞ6ÁÙÖÕ­ùNâÏíQ.°\ ¨I =ôýæ‡îÀìÅå‚RóK ¤²[ déûÕ àŠëÔÕ‘øh2|$â"Ë1ƒþçÅö×÷°ÿ÷`$>¦úËsáðð®â¯ÝR Âì4(•ÔÂ$þ¹Þý®ý ®ÿ)ôqþÚãIí¨îDãs>õHßï ¢×Fåþß=ÖÐÿ#çO+S ö3Z3Ì ¦)ä.úfì…ßÜÝ÷˜&*$x v÷šÀ ^ñ W · Ê   ÿ j JúŽüïÖö<íÿí£÷Èî#ý4ÿÉÜâ÷èϬڊÖzÏhïïÛÝ[öŸùÍþÛ’ò$ƯÐéâïÇo¦ò$IQ $ Jðrý‚þþï½ ŽX»Ïc& !óv%• º³Ð².*+ªˆ3°1_!è1/õmGÚ:é›ê‘Û óøÿ» ì\üÉÔïãÙÓPÑ}æ@×Zî¨yÃÜ…÷Í%ÓóÞ¨Ð=ôžäüÝ÷uøïøËøŒ |Ý õN5½©-6VAX6û9PBmÃ1ô3 ‚ôvðØü£9®%æ,+<®B Zïœùà÷hï»'ý¹•ºòŽü¯æïá¸äÅã7áñæåqææ1æYåiìŒæ5òÆî ìòãyçÊç>áýì,‘cì ýòûyøHóDþÏ3¿<µ:òæ6ðýeãðä”/êY:âÀ1 Šýàþ¬ÿÊþpú*üþõú¶µÿà å Å åêlù²ÒÓâYÜ©Î! šèÎû©ìÞðÜìšÝîJúˆ$Í`½!û Ïísôù(ïÞÄ8É úˆücü+û±aþ÷ç 5Šç Aiªô;Oç§î¨òûçŽñúæ%ãã´&œõ$Ó—åŠã3Òû iðlÁn*÷üвø¨ R5úJÛ GúçŸø6ùTùû“ìÖöÙäîèŠìRån€òJ—óáú—ÞÜþÜæÍi ÂÀÞKûÞÆQÓmÊ‘ÄléµÐê )öp\úç¡éåï„ùsê "ßD þÿÉ~ýá Xż Pö }[e(u#ƒT&jö ÛéªïZýïì…)¤ý—ÚòŸÍÎÒlà6ÐÔý6êØ÷_ùCÕÀíÉÇ/ÌåzÎþ_îþ†#ÿÇÛ`ý" [   ÿ¤ ê¶ L8¿ E)CŸ-_Aš Í!Ï÷±0Ãø%6Ž6š.~(4#òInèèXÇî¡ß ª  ëËÚÕ¼á¹ÔÿÔ"Þ}ÖæÄá“ÛUäÔ]֢ߚդö…æ ÷¤ûŽá2ðDððÝ›üÑ"7DùÿãÒékô?×C)’’A‡5]2ê@8'vÿ%cþÅ”8'÷#)4$9üN îð|÷øð¸ïuößòüúô÷[ÿüï]ÅþÎô>üfݘíÌyÔÓIÊãõÜB æÿ8뎷Ó6à#ã¥Ôsîb×Éòïó(ÿâíñuIðE" M#í& ¤÷¢Ô¬ùf{Òâ!ó±÷¬Æ(7+{îÚ ™ÕŽß¤ô•Ù$È&l)Õ 4ëM\Ý â^önáÙÕ¢qö×»ÛŽêä^Ùàû íl ?–. Mʲ ¹ V œJÿw •ëw÷ºñýèåÝ÷oÿ7ÜÜö)ÒÓ± wÞ!.ÿ`8,TôñÙæyê˜ÚóNòcÏ / ¬yàÉûPÕ>Ô ïXØ~'ûX‹®ô¯!én KôƒØŸGÕ}qæ= #ä÷1âqàñøÃç– Ò„…gà÷ÇÕCÙÁãÑ×-û3êË¥¢ô´¨Ù†ê)ÏtÒ;çdÔpÿ¡ðÛö»þ3èxðíìSå¶ÿdò˜n {¦Þ Ï>ì$ÄL‚J–&ÛF„ý9ûÓýó Äþø#!%Z)?v ñ!FòÐê‹ü6%Hœ%$cÿm÷û¶øø,ò©ö4ìðÀé©éñëöê¸ëÃëñÇì»ûiô9×þ×ÿ& mÜìñ›ßˆÖ ‰ët*ïP ,úRÞù–õÁïÿ"ÿ+_"„ .ûPDõÇöZû:õªÿ€2×V/69þº"ÖËÆêÐÒªÄÍã`%/¥e%íå âϘݑÑÍKí’ÙÝoùa¶¢ÀÕoù;¯NÅø¶j«líÎÄ8#ŒÞ/.üQ/ßç{ ²ÑûÚïyØ› 3úòCüñ ¡ó0øÀöô=Îø9 {$$c3Aê.ô1ÞB2Ý&¤éQû¼÷Sê• $ÒôuÄϤçäËdÉEá·ÒÝáaçÕ­Ü&àŒÔ(îÏ+´#iï´Ä÷ÜšÏ=Å^ù×Üa!îù69ñ°ö‹ýkñ»uêF÷( eðT¦[Õfª.*NWýÁýùõRòÿ'EÜ%,7ûáÖlëwÒ.ÐðÚ6Õeã·ÑÂRÙ2¿ gêöÐú?ÿßùc¼ "ТüìÌ%Ã1ÒÞ%‚µú$ÂäÊßfÁïÒó„@¿*@˜E"Y7Ç áÐ÷&‰øwõªùßúòôùÏëóà0èÝäÞØÞvÜLóþâÿ’ùõ ûShØÚí®ì×fËúÃ,A(8ï³ +¯ÆÕqÂŽ¯ôéÛÐüþUö*þ”ÿÄ àÿÒàù8á9Õ´'·ö¯88ž+w×;ô7á…ÒeÇñíögú^þÈŒû4¯{ìúPèæñýïêU"²"ùqAM üÌþ† ƒKko© ‰çŽü¸é áìõ´+Á"ëY+j½Dþ(ÏÈÿØmùyü5ü$&µÒt%Ô_öh»qÄõñmÃ<&wŒñ% ¦ ¹õ¾Aø ÖîèçíÔÏ÷!™)·ßøØë͈?å%J›,c6:K,ü¶"ßþöôÒiÉq\÷¸ D4oËífünä/ç=é¿ ˆºû> ýßãðDÙcÚÖá¥Ú´ônçøƒú1äOôæÊ*Ý_¼¤Â È7¼âæŽÐžÖñb  ½ óùÔJþg÷>…M*ö ;$©+,þ¹Œð²ð„åøâ/%¿!£, Æ¿­< hóǤ@ñ‰+ Ù1AÔO¡Ž»y¿f¡¼×‡Óþ WëCùÂßvæ5ãÑß ëìãYñs 1îÊ ñþÓþ.ö"ëÊ#"%ì2"U,'°ÓI42ËLdN1ŒFŠ| )òüõùòÃÿU÷APE½ÿ _Þÿþ½ÛÎgΰ¼î Þ9%{猪Æ|Þ(ÎNÆÅè÷Ô†ÿEòý èÑ8D"}%ÿT~ÑVïÖ§ÈMËâ÷)ü#P+ðv kò6ìʯ÷ÎA Í èÛ%¢ûr€ÜÐò Ì}ÕWÚÌËqëäñaËû_ÝÌï"Õ×3ðŠÚ]°úY#­s«#8ò4 ¬Ò*æ ÉÌ Ý Ì®¯ê­- úöO à%îxâsÛ =ìg+`º,SÝŠ Û¿ÉͯÜÅ.øèèðÿÏý‡ýÞÐб I üsd÷vø¼Öú†2?Z83Gç­ ÕWÛ–ÚÚ@<©8LC W,è]FûG©þû–-Ó¾=®5i!+;6å+nÔfÕyü‰Üé&î ¥$+2ÿÔ´ç˜÷¯×„ã2ÐóÓ×vÏ2ìÛAÚõi€„ì°þkÝeãšø#à ¬…&C 9Ö&Zøþê/ñîç%% ¬(D,áhþëöÑôàêtúýß#x($I)-)ý®#‡è±æÃúؤʿ¿–ê=Ô@øò–äRõ£ÎãÚCÕ¨ÌèÚïûWí! ­ ¯SêiAÓàòçÓj¥õ³7'í| ¢ÒyáŽõÚ”&PÜ })öÚÿ}¦ý¼”é: .ÓM–ëýçhäÙñvëÑä]Äô %ï¿)q*d #€ðuì>êLµòQ$Ó"³!~2 tûÛ ûåcósáâQïôãå¡õDÇÿ-hæ ÎÅ A÷£´ ¡ ,  º' ðïÊéýè2òÁ,üx;, âÙ*éÚÚwÅùi?/ƒ>²å~æßÑÛTã‘ã‚߬á8åJà!èëpm h×J÷éÈ$Ê=ñÐጠÓÄù¥±ñ.õîò†òÄä4î2âúßtô‹ã¼!ü“ôïºÎèÑÊ<ÈãóVÑÈ m>èDùçÆèà÷¼ëm|Cß DXðœÜÓå®ÚnÚåÜ&ýµë¯¶$‡ó´"ÎóŒ «ähëAÿêç)å 9&l.ØûTÕ%íVÏÏ ÜfÒKê“áÈüñó i¯Æƒ³ yü¨ÿ óÿ¬ }µ#¦ð% PæGé°ü6ìÏš "z!‚¨!·Uq"?|×  H" XG0?;ñI+-xÓJìIåзßô°&Ìd"´&Q¶³èøGÞ6âfèÉÞ9ùêíì.þ©_ tý¾óîýù'à¢éƒåÞüVëÖRùMßãšòÑâ›àãöç¾ 5ýð³r«iþ ÕóŽùmöáàó"«© Hè?‡ÇUÚBÕ¶ÄídäÍðÆå‚ü žøŒö±ò¼ø ÜöììâÓÙ ìÿî³,®ÒØüª•¤âc·æöç•p ¹Úù0bôÁõ7ÿŽö“ 2§ èK¡ømÖððöŸýó¢Ò+œ!k(-& "ëý¦ ½þƒýp™=&$6*)ß&pù+Lå8òñTæ‹òüÂ7Š(ž.°:š ‡$"áaû«ÝÛ¬LæÁ&y³*¨.™ü#oý“ù úuùÆøô4ø>ùÔñÔJ DïW4ÝÑå÷õNÞX K‘!z&n\Éì»úañ‚ëß„öË Ò c Ü Êòðþ„ã î©íèãûø$8j"¡.q;¥¶Ýá*ñæåÝ ûçê])ÝÿSØvîáû°Ó»äòÇêÊÂé&ÍŸßøL"íæoÜßô_Ó=ÔÓßéÑ;Kꀴît ±Ò0æôÒNÐîáÚ¦Ôö1׌"’ýÅýþXwþ~$.<;,^9…=ƒó.´äþæîØâ/‚ù×.; ò(/["¬ýø °òñ÷Sõxñ¨æúî3Ã2W9ÐX(jÖzô—Ö~ÎÌøTàDÚ­î-1ßâèÌì´áNùFótþwûJv= &ì!r f"íÇÿîép ô‰#±Ž9#Žcsðÿ#õ¡ì½÷ü4?!À6h7èt1%ùU»ß?î¨ãÝQü3êôzÞ YÇ÷`¿ß×íMÐs×+ÔûÎæZØÔQï¼ ¥ú àÜKñÑ—ÓrèÁÔòæVŒ ßXüäÕ,ÔCûúÜ #¼0&øï =ßèï¤âF$÷ÀT («{9^à}ýæÌÄÕ[íúѪúú"tíÿåç>÷ ç,åJú‰ë"þb1‘"´5\9@½.ÎÞ-ÿtÇ ÑõèØÊ:;ýÊ"p(ߟóüSó/¦‡ø&~ä§h®ìküÝã²ä›ógå\ Xû69˜þ|í¡øŒé¹é-¼ì†.S2ð6÷ K*_Ú†øáÓßÒRôÇÛgÅò*‰!¬$b,: šËð ÿ<ïbëÕPð)½ ~6¨1:/#ñHjßç”çÃÞ:÷ÁìßýQ me ÕçÛû=Ë…Ü+רÆbú»ß¶ 3qëÝ "¿ûÛ´Ò¿Ûûà¡É <ó(Gá˜ìòÜÅÝVéDÝ-”íJ ù•¶Búë7ý$ì æÝ ZòN*ü3­/[^0¼î ìÏà ÒËøì­Ùàùb •ð}´ÖŽå•ÜXÔõüÉäx±ê„2ó*ÜÕîR׈×îå|ÚX÷‹íNÿ)ýëý"ºþaÿ”Äÿmd2'7»8É*8×9ŽE/Fë– Ývßà¶æóE%›7}Gó &*›éTýåZä¦òKçøÌ˜ IäiQàÝ£÷¸ÎdÔíämÔöNð¥ D áúà ví>÷ëkëðë¾ûgó? œù¢C½Fôëäî›íB ù8(Æ#Ñ5×î™]ÑàáäÔTîó uH¡þ²Ûççôäã®âcôç¶_ùÛ ÞU¬°ÂÙúöS¸,˽Ç`¶ùö8Öø: W* Šà¼û©ážÛRùÞç¹Ö!]™Û"LJìJø>ç²è¯úséË9z#hùÝÝí>óDÞé!Q=+'øN'T(»û¦Áî9÷úèÉèéú2ë (Ú44+ô[&ÊÀ Ý#Ëy¿E[Úá8ï}?·@Å&:lþ4ðóêõÙ!1š$&( Æ!ø¬èóñ‚æIä^òÔèÏøÅÝèU“L ötúÛÂôì ©¾ÿWßçù7Ç8ÔLÔáÅ~ðß/ ) %a#“åÞ}øtåÇÙ”óHp+™6?HÁÓ'œóþð»ð÷ñð¶úÿñiì4Ž%Û$#5&è¦À½ÑùîvÈ"Dh%µ(m· ZÞ¯øNÒ¡Ö¥ÞdÒâö=äù`ýîðÝþ»ÙÑêlÎ#ÔÈÚ*Ï0ýÅã>Âi a•ø?åíóóVùî4<ÿz2¢I9 5Å .37ü¢dáæí‡ï_àN‘ýÂ?Û)Z?*DÖò3™óF xåÍìõüÓèÛ"Ù Ë)bõÉÌÉ1æ3Ñ ÆâíTÚ×ð…òóòü{ö%ÞuúGwéò€ñç/^ùZ¢iðq'ÔôàRè6ÕðöŒ;¶$§:l="33N8Ñùøþg ýU#'Ë09+/#!1ÛòêmÏ{ä˜ß^ÐbýÚéM¸A~8ñèüqÝIèêØ ØÈæíÛy¤ðY k êòõýÙ£èoÔªÕ®íÁÙ» ù"§[ÿ¡CÛmóaè<Úv_÷4r%Œ4óãW¼èáÓúâò­ýÀÁv —ásØåôÏòÍ“ûÕ×Ó$ª Ñ(˜*å !që;ÿlÛäªÙ×.ë;ÝóM82ø¡‹ÍÚç—È3Å{ÿÂÒö2ÜB5iü'rúcü“IþÁ B Uã ËåÙô%ø2–à­ñÈâŠÛêå%u"&É)w£AäÒô‹ÝäÝ2øLáè&ìY!ãø0ϬçmÃaŃ׵Ãzä%û&È&QX"4üð .ö ö Äú‰)å13SÔ,9üTÅï øýò½ñGô ÷ ûù{ Âgì ÚñsËßdèAùÝáC!ŒÀð%¤àÕ³ËzË3²ÖñÚ«ÿ²÷ùýÏó“ömônôHìñ§îoèv òÊ389¯:¡f/iï¼içtæôU쎠™&L ‘&›)gòÐûõð ˜úÏ#*@"A)öÍà<ò…ÜMÜbæ,àCíuê—æî–ÞoåÆÖ:Ý×^Õ‹ãÞØºô8æo'úÈ É k ÁóýIñ…îÛ Ñöu-‘þ?Â6O(8Ñä òåÞƒó§Fú0Æ4RF"¸*¢ËÑ=C{þàþµN'¿ Y¹ß'ü6ÌžÔ•ØFΕêðß´íŽï ë³ìiôíyø¥ö“÷÷»ü2÷=ÉŠÐÔØçõmÚÔп è8z'c%²8Ñû¦`îÕór ÷ª$j_6Ñ)Ò3R7éD-ìQèjøJáwãÑæÁá'ý`웟c ùæ“ÿWÆûØÔ·Ä*þ"áø•<Šê&ûêÛ#äùß4Ú»ðžâoÕølGJûø÷ïÎ÷Úôòî`áù&*^),…ý†ÈìÊöh¼ïk «Í± à  ã¯åÀþÉ×EæÿÊ ‚õ×&š#í[ÚzðqÙûÖ î§Ý[ øø¶¯V ÿz™ë;ùîå•ç:óhç@ýVj–#íIÖ§ÊÓ¼L×!s!¼†ø1 èóñêæšî ìiÕöà Fß÷ì{âÚ´€éy)Z',*(€ÓºòÑZΰñ¥Úyü›4+Q="¦!ÎUþ'(†AKº7ð+XHI÷ÕÔâ@ì¾ùèH •Þ)!öêQíô'ìíIêré5ò²çºýŠó9þWå•ûÈtÛ€ÎDÄàð÷ÔyûùüçBì0õóè_é/ðÎé6ùòÈbùš%ú º3ð,bñ1 ÷X¦ìí¡Ìó0(,:<8ðƒ8œóÜêÆçÛÞŠñ= åÀ %þdÿ7ÿø¡ý.ëÝò%ë›é7ôxðtø?ûëÝ:õĈԙрÅ[íàÚŠuõ@U›õ`þ ôÐó³öc*_ Dþ1í7¸C+-@òLìzí£Eó³5\e6Œ9œ2.Ÿl bÿ¯kó“Jòÿ Ô h W"¦ñÏïÉ_ânÉ~Åé»Òûõ±ëŸúæÛåg×W×é*ØÄ¤ð‰– p í>ô9$òòÞ~õ~Gô·üríê£öíšè¯þšïH S±.ê'0*iýäßçhôæäòÏç„ øU t ÎüîÚ>õs·OÌÀë²ùðãÍ šè (0ßû äÅÚÜÿêö s ý“ bû]ýÜQ”GúU üñ®ð”¥ù29(w1kù¨ ñÏï?Üö—™H ÅítüÛÝhèyÞ³Úgò–á`ÑùYmúÞŠÕ´ì¤Ð©Î"ðïÖZtýÔ"ÁîåÏúÏ€ï×õû÷qïd°G&M %!*š Äíý³ÇüVŽ-~".²ýÏjß½îçö ÞX.{ËÝÝïøäÄ=ÔÉÐÄ*ÚÍï?ßõº.  ·áùþáÉðÓ\݂˸ðç%o‡Î ò³ù6ñÀî‰LöÎ*Þ”@P5r8sC9n+úÝñ‘ùßßqô²yBðWfÙ<çëá’Ù£êüãZê¨ê•é=éŽö3îõóúùLßœí’Ï Ö€ÝåÍÊèý» êV—ìkþ]àk䦸âq"“ÙA&/¼)YA&þœòéö,õ.Ô ‚9ß)?†@ê,ü=5å$y÷¹åó¬ñ Iø Ô G÷òçlï‚âãë}ãIû§ñÆ 0Ž úFô DÛéá× Ô!ÿìÚ¼Ï CïMÏá|áÑ¡¼î*<â%)8Z=-)4±V&x Ôõ®l<ý@ M/'m“+àõ9ã ëIè"âÏçîQh ÔÜ ®Mßšõ}ÌÛÕuÕ-ËPïFݱù´÷ßéù:ÒäÿÏÎÍ êŸÕ7+ò: ‘P` xûþ´ø® .ü˲Éér_^¡ò ìûnÿ9ú³q-ü –#x.VðÙ{̰ܴìÑÎc§ûm$‰ö"îõâ ŽáWïñÔÎÜaÔSÓHæTÙÌþ ð»Ü>õnÿ-åšéºôyâ$aþÂ"ͺ%N& X$ƒù´*¡¿e ÑÁ )$§÷-˜îœðó€ô¿:Œ5ã?k)?æú0àãÄçÁàÿñoëÖø(õüºý>ôQý€ÞßíÐÂ×Mæ¥Ô Tó;,·Ô1Ü/ûÇ'OëùÓä ãb ÁêK)_É w,oloû½T=ûÔ jF| dÅ á ù’ˆìñó%éø<öK BWòU /ÔÆé°Î™Ïp×]ÑnãVÛ°ð+èWÿCõÜE&íwû—Òúâ§á0Í&#ÑïbHu4_+‡E0ïMØÙ]ãÁô¡ß­›ý—"”%Ì%?S"… 0Q•| <¼Y!ò #7ú! žãóIØõÞ{Ý4Úéæâ]î¯íéðÞ$çÁÚòÚåîqÚÀ÷#Å«`ÒìòÏÓŒÍ?öÜ Qï2á(c0åþõñ¬÷ÖIõ<5Sƒ8Í:A-ú5 â'µUÿøÙ ‹êWó ý‡î“ » ÿ!Ÿ(:õ°šÐbç5Ð<Ìê1×Cúñbòªù¨åõï¶Û âàÒÚäòúâ#ž÷!8žóxA×ÀìuÐ ÎÛöˆÖÖ$;' !9#¢ Ö5» bk +1Q ;j5÷™8æ¢ï¹~ÖÑ̦¹³Ý%­Áäôá ÔØ ê’ÝüÕñóÔã sü¢öÈWãøñŠÚߣ×êÖ®Üì×öè}àkÿÓî‘w" ˆ $ƒó Ç1žÁ;Ä6$^7aþ(eæœóícån õé"€õ'»&ª&ññ rÝ+ê³é@Þ°Lõ&ô w n&jë)ÌÛCäêàSÝ(çyç½ï"ëê^÷$A F|ßàöõÜ«ÕÜcå—,\1'¤1¼U[šüí“Y!•h'¶$Õ¶%õö<å‚ñéçâ¸ûòëEQ¸ÿ=`çBùëàçä¸ðÙå:Æ÷1\ `™üäùüßÄ?ÚeºÙ½LÒ’½ÑÞ.½] !.©ÿ2sóøcjò| ²ë-´'#°,Xï<WßâÜú<ánȾ¹%Pj´1b÷;Dí ñZögîŸ ƒþ݃Fâ$þ ÉÚ€ÐÓÈó¼Øa±¶æö%Ù·ìbãqÕCîÍ.èÅ-Ê1± …&õáJüˆÝ³ÙñÿäG4L1A@é);9G" s?öÈüŸõôóöú- >Ý#·%¨ Ò žïi‘æYë*ï¯ës÷†õÆô÷û'ÝQòƒËPÖîÜ—Ëzä¨0ý× çÁô$è<ã‡ý³éÜ ÉY/ë&Ó,)oúj Çé~ñ@ñèröñ6ý%?’8’/?;¡º$è†ùÅê«æ‰Áô4 GÝoû»/ÐâÌ׸Và1ú5MÕÍìùá©Í¬ÿZëÇï¡þeÞæ-ç à‚ïUÆ‘õz*ö|ïÁ ¹ùå$>A)2*Wi$HTÐXJ+d ¡G8á&D#ï1ÛØ¥ã÷œß>ó!¬ÅÛýöÚźÎqÒ3ÄDñ0ÝR ÁúøÕ! ØDù…Ÿæ°ñßmà?èÚßÅýîü9Å»ý ›î=öŽü9é3.óñQž;‘;kTíûÐ(íºîÛöx0’$¨ ±0jÑù÷ÕÐiòYãÏ-ûºÄ i  x©êëþÿÐAáâÓzÏ¢ô€Þ0=ü$î#•ø·çÅ,åáÍÀvøwÕp%¶e3â.“-/ðÑÒàªãþÍâ« ÓE&ñ 䩳 a : öÿ£ëxôîðwç£#ö" `æø5 Eå-òŽèøäPøÎì`À<Ÿßj]Á%Ô×ÃNÃéß0_øŠ~Øóê›àwÕF´ê¿|23§*6¢8È'h2! ¨óêðÃíÿ@õ±go ’˜ôd€èë×÷èåŸÛþ“ Ô{ ´è±Å;ÛÌx¨îÖ׸÷Ò÷¥Ýèô+ÉoÔ»ßEÉÆ{èN#ô'%Ý´ ÕÙ ÿý0ÿªÿysµ’¬$Œ$Ù&CõýjôÿêÀ"+ýBª1¼-çA?ô+ŠÑä`ܒЭô‹äÁ%H Ø£ø§ áöñ„ÎàҀʅÑظÉ+èú܇ìì4ë÷ëTíçè<ùûì ùúªP Ô’!É õú{wÄù "ù $5Ü,\¨3«ãôÑå#Þ[ÅóXBÞ-°4ÈH^ 2,°íäÛºä­×Ñ×ÝeÚÔï@æ™þ‘ûcý/îêŒøMÒNÞ;ÎfÊyäÈÏ^yð$+¢"{%BÞ¥öÄÙUÕõJÛíýxm= ð¡ú±“ÿôÁ#©v7ð*¾)Ë6‰"Óðèÿ ó,ñ`"ú×}Dë »üÄcë(÷sà8æ’Ü?ÞðLàOÿѯñê­A¹{ØmÄø³mÅÕxÊÐø" þLæéñ×äÁÝÍò¥ážÑöÍ3Ò 6Ü ù ô @Z ¥·ˆ ‰†} êÏý˜¿ì ùiã.èØë¿ã þñn 滌 ãíkOâkèÿ-èH%¿½)B/pþM"`Ñóì Ì1Ê.æØÑ–‘õn,G&•à+=ø ò ò\™óÀºlUÊ$Ñâh#¨ä©ÐuÔ¿í]Ñ/_üQ"#Fð£˜ß‰æcçxÞCþªê‘ ÄÏÿ2 RãWùXѨÝõâžÔhâòzË ’¿ôÓâçëäìaâ" óøØ'¯×/g/<)./º5$­ÿ{…î÷•ôÒë"—ÿRD‚2¹¯?Âà{ ò¾3ÏÔܾÀÿ=ã ëÄFë]ÌÓ³ßáÏÒìôå–õÐô~åñ6àãâûæ7ãîîÏêeókð»ñõ¸  â&ý®ÞÄFÌ)µIUK/»B¥œ"”íWýóHí›/ý`-PP45ÈG2j÷?]ãQê˜ð‹á?QúűIï7ÿµáåëQÕøÝ¬Ò,ÑÃæiÓ”~î “ò]%ÎÂâÒè»Í¥ ô±*@ž|(Ÿ3 Õõ4|•@;:#8%ûœëlò÷ùñ© ¥uÙhJ¿…ûÈ æUõ§áIäRðéæ°ªøŽÇ=êœþº¾¥Ûú©Vµ2¾q®Æð’Ï4¹¬i½÷Y gÞì·ç‡ÖØòîø#$ ,7ÜùãMÙà òƒâ.³ iÕZ÷úF÷dþŸ]÷×Qÿ‚cÚTò É;×ÙÕëÏÕÿýèS"<Mú&\ö²ÓÙäÏÙOÎø'à®¶L¦X¥Gï+þYíuéÂý îõô™&fð'Ð&²„"x ÕRÁ’…ý• n ‡*¹$*}'Úùçáëô–ýòð*ø¯ˆÿöHؾòÓpÒ4õvÛ3)š %»0WüAÝð`õ,óÍïäùõ„ý!úW™ÿ­Å:Z$õ *Û€æ¶éæ×Lù2h"%5f8½‰-¨ëŠä â†úæa wÿ7 oàôöùËÔÚ~ÊhÊÏéIÓcsõåù´çÒõ¥àÞäõíDå÷róŒó<÷‘ôòêü8õ¾ý3ÿhèâùUâÚã…ÿ‹ê¥+“Ï?O8i'ø‹™ ƒêö †ÞôîêÕœØDázØìûsêT9xøì JÅÅçĨµ#ÇϨ-ø€×¤môK3ìòuñNêAû4ï·ÿcõÓ,üa鑿4Å~ Ž Î U ŠÍ§Ü‚rˆæóPÁ÷;õ¡r bTh ?r¡íPúé8ë@ô‘ìQòûœÁ|õÖÿ-ä2îÊÞŸÝîÞþ Aó¡åk,V Kpú¬ˆñ»î]ù^ìèœþæ%u™(¼*¾‚%Zþ¥ ¸ ûa4fy?);. 81éÍçäYúiíýüèø×ëå«ÎI×)ØðÒ¶òá^dþ9rÞú-ÖÜ‚ð´Ó Ôâé¢Õ wôR"êK"'æ ÆEû#Ô_øp{z?2*è?ûIØ¥9™é×çcá©àé<ï©ÿáþëgÖPáMΜճÆ-ËAÅ!ÁâѹÆ-ßo×¼îuçÒóqô…ô‘ò`ÿQôK3±.´ U3-ì‡ ~ÛîàDøÐà´(ß'F$®)Ѭ"»!X1$!^($¥0Ñ+ô.þ3£ -íõ[WÝÙëÛ“Ø çQÝõeîàüýøáþ"èäð$Û~ÞàÜ ÕõùCàl1 ÄíL¡Üæßù$ÞÚ‘¢ PèÖÿUËìhð+å4©,B1{0c$Y+Dµûiå þ ¸I/Ç'(d4-!ºÐñËôÉÇìSÖÿRøÑøëËß&ñG̨ӗÓMÆäð1Øšü÷ ;Fð ¥Ú çÓ6ÖÜÑDî`Ýÿ¯ðÞ ­3 à  9 ? ñª† $õ+§)U"â+6üIÞÙåï.åbÜ· ö}pêùlä×$îyÕbÓ¦çöÙ>ûHò†ÿ0ÑøÔ‡æ1òZÔŸÜ™ÐäΰáÚÒ®î씇 Ö”& ( ÷æRúÔïL_ý†BG°7cöË£â~í Ü‹Ý)ãÒÚ>ù˜è¿«¢ÂãNÙ³™ÐgÌ—³ß÷‘ÙÜäê,JÌ“á>Ñ,ËÊèP×: èôM!AŸ!/#ÏMÓ ý «ú ´(æ/6T,7/4§(ò§Çô ù¼÷ôf#Á ø=Â/=0eA?È$;ãìõÀ×°Øvæ<Öìÿîqͺí¾(ÓÛåFÄË ÒÑ‹ôÜJ ÿòÿº EàÌóTßÙßú›äT'ÿ˜CÈÚÀ{‰ +üü]û#,Å eHú<1$¦D…ò"ånèÝãèK#+ò#‘*{a$9ôÀ ßIé0ߪÛ?ñ}åú)þA[ ‚âÿk¼/Öð¿1¹Úá>È[ÕîBFö ÎÏõ!iã ï¥ä•ßù¦åkÃk! #s B ç„åöߨ Eï!,¯ê2Ü3ÅÈ/¿õ‘ ÅèàëÊ÷è5^zS!Xõ4 á í‡ÛLÝ\ã¨ÞsñºçÁ úB…Ÿçq ‡ÂµÓ™ÞgÁ:#ìÕ"“<©#Eé½cÔ1ݼâÏ| Ié1f¡1½4#’'Äö~æTë|ÿOéC% ¦ó›ø ÷çØñ‘ñëhÿù+¨µ -)ï ô¢åCÖÕ2Ûï÷}Û±1 g:•=Ò ?/ädúýØÓÚƒñÜÚ:ÀÿÓ)Í$1J(õ %çðäø1ä [þ”%ím#%R SñóúÁæÛîúêOçáûëïtš .ë—wØDéýÚÚôþ–åj)EŸ'ë19è ±Ý ø×ÏØÎðwát‘©æþ%,ÛôíSåØÔ ¤ó{(¤b+)™! $¸¤^þT‹êóÄðSäÔBøP*ò Vt*½Ý;Ê;Ó±álΖþÆëß Ø“á…ÿçÃNÖ¶Â^¿Û‹Èþ8æ ¾ Rò— ×Ôšç¤ÕNÔôçÞ Úÿi }Žà*ÿ^YUüš …£!K 83#=ð6e,2Ìî\ ÒçåNþ‡ë!ù þ/ .Cu.oâ&ÅEÒ¡èiÇp¥ýR#"(fѵãhø8Õ‚Ý)ÓÕÔ7ÜÖŒïËáÓeøð9 Ÿð.ýæ\ìÎøæ½Ëÿª/X Ê b' §ú÷Ð6û–öí!6+ˆ+’/kd'Xð ¶ê­éù>ø H¨1‚=ÈL¶W+~Ûqðçâ¤ÝZòhë7úÀ ϯí­×â|þ`ƼØÚoÅ ¤æ]ª'ÍïÿVÙ}åaëhؾÝö&,Œ ² (oá-ýçÑNÖ”âìÔhþ¬ê¸æ xúiKïR÷ùò4ðo\øgÍ v& äŸûWÍÖÛ?ÙÎ8îåJ$…ƒ%œ,š8!¸ê>úôñèç^êö{4(4ÿ8²ÿÔ#DÌ èƒÅ0¿aí¡Ì ‰ù.¹ â k òÿÙôáú†íˆí÷5èÆ šö dLÏßûBÔ>ÜèÝÇ ¥õ*RJj0Aõ¢UâtìšøS凧¨)a%Õ_&ù·Àã¾îˆöæò!GY:n/@8¿=S…0òø”è îÈðÅã òÁç Ú < § èÀñ.çè½îûó+êJVþ1!ù 7"«ÔÿýP¸ŸÅFا¼óçÇY gö ¯âøï"êÇä¸úïã§ëHÞ wàñ™vÙ™èØ•Ö•öPÞ›_ 37+ %B4NÌÅêçú(õÓçÓþŸ*÷!ô„+8õ=SÏ@çîËÈÑìæÒw{þš Ç&eéèÅøôÞ å¯æ¸Ý‹ö9ç²þ¦õCü¿üóî3ùã;ëeã4߆ùíã$"p£m!&õf ]âþèkúÍá ,߸?9 á8í&àêMè¢ ³ò›1=5;¤<˜#E9E¾áçêõÐézãØíYÍ [*l#|h(ïÿ ñðÈùƒòíuïò¯aL3ûÙ /Þ ó Ì3Ô&â ÊÅwò<0ƒ)C,Åß~þïàÕj ðí2"ÁYD!:þ[øá ÷˜ÝÃàÐïlä ¢ú=!ÏÀ"6'› •!›òþråMéñì¹áí mõË„£ "!'â„ȨÕÚmÈáÿ(å» >ï9óT¯ËãèÈæÀ…ã|Ê–(éïšãÿ] æ„÷¦Û>ßYë Û–ÿLðΕ_þ^IõÇÿhò†õvòÓð‘Móþü 40#*¹é2&æ ‰Ö!ØÐ@ÞÅ0/;-*–à÷÷lÙ4Ûð}ÛÈ-÷&¦&P)‡Ò"‘ý ³íPñ5ð„ã÷þì| MêsÝúU–âVð8é”ÜÎaìù¶ ô!Æ%ðG:á‡ú$Ô¢ÕZñwÓ¨þïJ!íú™®ÞÖóÑÛ Û÷þ×ãb)yO3V4{1¶òj ç9íÖî‡æ ÿñ.y~'jG#Q1€óTžÎåŠÝˆÊê `ïÏA5*?Zݤ¤¶Ë¸ÅD­ôûŒÌ$*qL"Æ0|î6Ì!áÃÑMÉVîÖ (õÅB^ó) ׎ê‹×ÞÔ¾ñ3ÜCÔù;'l@%ˆ-ƒ V#Ñï!á™ê_ú‚æ'ƒ ¡0ø4[î.p×UøÞÊÒ¦ jæ 6…¬D?ì&¯?6‚ïëô`ðƒåñûôîóúJ ýL­é ù(á«Ütý‰ß¨¤$û#Ç Xä&ú.ÛKÛ0éÂØWäï’Š&—ëÞüQëIèñ*¹B7/&k3ë˜ì,÷¸ò’èç |ø[+!ç%þ ¼!šïÔißæê‘ÝÃ.ò“$¢‡(+íg˜ÏAß•Ñ÷Èýù•Øõ$¦ “$ò.(ùi"I¸‰âi¿¸°¶ôïÏLªüVøõ¹ã¿ðß/ß&ëÕßIþ¾îh©ñ(Žð#ù)wþ‹.ÚïÀÛTÕíæò$E'*Fà#=ôà7ò¢ì"ùŒ@R)ÂC!JÕ$@B÷ßȽ¼uÌcã-ÃÆî÷Â,Å+ý&?Öaï–ßSÖt÷˜å¨ø+ÏýIÌ⪵õì¹Ïöã—Ô ÇšæÝs °DCüîPôʱîÐ$¤·>„/_?•á5wÓmÓäíoØ‹ù¼#±%&¢+@Â'÷þ4;ä÷+éàÑïó“. Í1á YÌuÓã³Íªú®ä² ûû ô ±(Ú‘4Ü$óÙÈUÍ|øIÇÕ)tfå(ÿÄÍèX½º7ûÇ++øò*ôIœì\þ"ãˆìªÜTæ æ5á§tôä#™ˆ½'üò‹ÐæLíøŸêà!Ð…Aó/â1äD†ì'Õhï{æ=ÏkòA+Ý Šæ* Pëv÷×ê™ã„kì@I*²!Ž„'Rå³Ë»ÚÖnÉï]å“s ³†îõ‰íóözëèòÚðCïuøb"Ö"]'+ð´—ËPÝ@û(Ó=°áI|F„«=8éßé¯ä%üïëóZâ%/˜#&¹¾÷ÇvæiìOôøäX7üã& \&kù”äÔAñuЊÒüêÜØ¡ù÷SQï€/×óçJØ~Ó‹÷cÝÅ+Ž ô𬠓ÒhâiÐnÎZí Øð ühõãUeç û]ì÷á+vð{ß ¾_K'ë¦÷«ËÚï'áBÙZñAú0Á#¯D|ë« Ò©ÝNîñÓ§&øíiL³Üõ íá”í1æÞß»ùýêK û̶ s[ã˜ó»ÞÝÛyðná\cóã ÿ TXÿâðjúæéì±ü!ê¶&©{8 42v7,Ö|7ÎÀÍ×6Ýê,;9àÍ<‹ô‰+çí(þÁéM"k I%s(’!  ØðÑù§ä•ê”Þ™Ütò=Ü(DøŠrþö \Ö£âþÛ2Ê úÓÛ¸Aüï½ êž[ÆÝßΟÆÔóÊÛà<ìgû¸Êâ§÷üÜ!áÓÿOæ™#m3 Æ+Sû«ì¾þîÆïOóœó„ü øyÊ`.Ø&4”ñ¼ ÊêÛÅØžÀPãe,Ý÷ îcèþ±ÏÜ©ÙsÊöüIß±ä‚Í*ê®ýàÚåÞéÛíï2åæjú8t ˜÷ûÿÿöbþ£ü6 Š!9Þ'üÿrãHøˆ×žÛOÜÝ;ì;À?Ü2àñM Jè÷æ„_ç@$(w03*ú /+;âçûbÜÑÚðëà¤%öOïuשUn ÷> ;ëFî}þí´ †Y4ñØÜ¥émç¾ÛÁuî TäìûŸå(î¼í4âã%ò² ×Mø®KÛKî#Ø-Ö™òÞŸ üœ—–\àM–êÒ÷@ïŒåËøŒ5 #¦$Î<ˆïuSÝ æ ïrá¶ ôJtÛ ×ÆåZ^ÒdÝ æöÒ\ïýCôãùDóXó>ìÉñ^è0é,õ$êóül ï‡ÝÞ(è;ìãÞ¾\ó4(è>ž?Ñ…;…õÿ“ñˆíR}óÊ1æø.°8æƒ/0ê 9âåàæ ´ç<.]/ä0Š5"âþÏI ¸kú]{äWí›âÜ˪éL 8 îb<ÎPâþÏ¡ÆÉôiÔÁ&ýÿu¥ýlßÙï0ê–ØSWòr%Ï¢2(qò 8äÖíó¨æ>ûñ/ÝÐ*°5 8'Çù*PóâöÄùòdrúÐö£7ä ¢õÓ Ö’ç6ÔÉÉáë4Ô8ò·C ßç·CÅ<ÚÁË"¾SøÄÑc‹§ü¡™ÜbñÜ Ú(õ£ã©Dþ´ù²èø‹þ´ùº;ø ñôýTkú”$Ó´8 .j/Ù:ø_)ÃóÿìîGíÍkðî&¨*Z1w“) ñ¾äÝæÀöŠß\üJ&Y’ m£è þOåëÛêû‹åóù ýÉûáü“ýUF þ ñ·ü ïðìîÿ*òò¢ýÓáðþÌì7í„|÷Ò6ù#Ô';¦ö:ØÙæò¶ØÎ¹ý sÞ» –ö\ ¸égðî…å7ÿùîÃù‘»j€³Éuòøùoç çíÛàM.î®QT'ƒêùíçQ Ôò»%½é)¤àä GÂ-ÒåÜjÆÛêëj~Ÿè…`Ûšå’áÔßšómé ïû| K J ø îþÃŒò)øßæóêpë}â6äî®" 5º- ƒ6ëY;õSçžaþ@!+O3âAË3*úô $ð ðJcòŽw¨ZÀ‘ó­óÞó~òNò î ïÁï<ìVóžï¤ôXóæìðñÊåçhë¥âtù„ëDƒ5Œ ñt nÒãÞÕÌ"áê2<% -‡ïÕôë Üûh6›$½;A=*ƒ=ý W#…þ{ù¦ø¢ùe÷®üÒûË‘-¯ â ú3óá ÜUë èPØæQò0 É ïÏšÌãºû¾ÝÊc·<îTÒgþ>õîç*û ÐòÜ á'Ðr-냨Xü+‰ï‰øäò.î­5øîâ£!–#«#F*-4'Óþ[*ö–ù¶ëþÑ/x &$=2AóÖ\Ø…è>ëÍÚµ0ñ¤ Aüý Jê#ú¡Þ,åäÞí×ðêݨñÐë‚ñð}ò…ïõèï´ú²ôÛ˜ú œ¤x 3ÇÀrGöJåöoô1 ÝüÌ û)ž(:˜'$ë^Äå5æ¨-í-?%Á/ƒPÃã¦ø\Ù[Úçê×YþÐî®ãFÿ¯ÿýäü ú®ýóùåð¼ñðþçÓ Àð¤p:¶dÞyöoÝ\سèó–‘ ¶ ÿóÁ ¤äûð©ï?ã` nõ7K3 i¶éîÙà®æòÙ¼ûÇîƒ ¥Zƒø[Æìáô;ëléƒñÅç÷îíN÷` …8%#%Ìl>Û&õÎçzÖ?!ùøå5¯.Ãy5Éð'çÒíëüî°â3"..'1÷þ È÷ö¡úÅ ;jüÎbðíøíïYèGí“Ü!æ˜Ü½ÙaõVà“üš ôÂOàÍèXéÛ9‹ê©)Î0+.¢À#4áø‚ë ãW ù¸5Ä#d8B´"+;ì * - ÷E ƒúå p>‚2 ÂA Œ²é Ô¹ÞåçÓq‡÷‡qÜרÿÛÅIÍß×ZÄ]ýÊÚ[ TüN÷_RÚÒëɶιè°ÉÜö›Š!ê M"úõË =òÐõ—ýÐözó×'ßæ4X-;1_5Vˆ,‚„ý 8"ÿ{¥ ë#¤ 8 #óèî¨Ñ&ßâ•ÑWâê|ý× Ýá:úýÎbÒPã6ËS«é4ÿÖØòË7Îgâ?ÏHú>êauÿm p½cÓ!w!X Žó¨ s÷™'™‚071'-/&üé®ë¡ú'úÌñgŠ2';'Æ7éP"Jî þ[çJååðÈâõÿªñ@ÿýÛò\øõê£ì©í è`îŸìÚè‹ê¬ëUæÀüoîpªú‚ ãÝÏîcðjÜm[ù#(”¼u%Wó µñÛõÞù‡&Í*±2°5(ÛÖÐù‰Ô}ÑúxÞC>}·š &  .ŠtðJý†äûéNðáå=¤ô¶þýc cäõ’Ü Þ“óÛI!;úŠ3l&I^-ý¤µó´÷Ù+ôXâ=7¯ò }ÒÑé²âÄÔú *òÈ8 À÷î-ø ÷ ú°ûEì“ø¹áßè„ï°à|Êõ9xæ†ù\Õ›ãæàwØx %ì€.éh03 „(kâòõßösá,èþÙ.”$‘+ñd†R¶\ÉL —‡ }¾ i{ m÷Ë”í ø¡íÒìAükïAæýûzôÞÈ÷ÏÛۇܺÒðñäáZ9÷™÷Ui×áí»Í Í´ìzÍt&øù,K.ãQ"*ùøþÔÿÙù±jþÐÆŠþ"öi&“%O’$u¡×Óì*+Œ¼0+Ãx,bì  ÛËå6ê|Úg¬î‡ýóæwøÁÓUà|Ð ÎÃîñÒ݈ô^úðÝDî0φÓpãçÑ<ù,ì&ü›ý@ü4ýI‘þN 3 Ýß yý ½ ’—0îAù5æ,ã<— Ç%Üî%|ö'ëXÙýô7’(J%‹9‚ù·¯é°ñôøÞêÒpù&üÍþÀï>ûä—íDâã§å+áÉê&æté"ê4égçÝîêÃò5ò_î&÷ãRî7ì(ãWÇò¢¡ Òý' Œðïîù õÒÓ4Ê"<ñ=¨‰:¥áyaãëÝW0íhïÒˆ:ÿ{ ³*3ˆ<ûÿ üûGøØ— ·ð §èËùÃÙÝRìI×ç óÑau÷–|ãÝòœùþå±ÿG%Ú [ ô8¬é’ïö‡ëæ ñý&xÔõOÛõPø_þû—HFu= ‰å Úœ—ñaþhìVë%ú`ër¢ý.)•ß(oñÝÔ–áí.ÌP¯ñObÔëuölþøÔj ÁÈ 3Lû uôOùÌôÝõ€ñövè®òÈÜ¿æeì àµþç÷’ôÛõâ‡÷ ãýãðñéî ýÊöÚö ÄûÒÕÎçÀáñÕ³ÿFfcùŽåðJó¨‚ò:ê¨"«²!ÿEñ zCa…ÿjÿÍ Ütt© Ã#x^!çòë îKìå§ïð¾Çö)xÓTî Ð­Îè©ÒÝîŒ ¢ Cñ§ ÙÔûì›ÒîÒêëùÖÐô¹?T¹ Fb¬O ‡<üä" 8™%ß>Ò=ñ%=²/áøü%õd(ë Õ6Ï0¬„0±îM æßÞÂåÖå ÕýË•ãúrÑÛáSÕîÑ\ãqÙåäÜÐãÑÒä×qç·ÔÑüCñ­ô1ýcçîÕò¢ä«ðûÕ·!ë`È× çÍø)à.ð Þ<‹< (»> ¼%€ö«7¨ô#èÇÃK¾ø­tïŽõ9òïùöŠð'üÝô<þCúÿwþ-WtöývâÄëÈПÕêÝoÌ”þçW°ìg× æ`îkÚczú³è'ÕçZþíë;ö½‹èÚ$Ò•øÍi „þUY!]ä ®"ñ)vX>*Žç€ hÛLÙ@hß§#j æ)y& !7ð¿üîèðàþ7ãùS˜éwðGí˜æ§oõ½ôK¶Óíݧӻ 8éõ/©ôó.iìü ÝßáùëÚšð÷w^0ä‘ü{ܵÜqúEÞWu7]E«£l ž»ç OÙü (è« ãcú# ¨åònäsßAû¯éZ<öj ×4óÑ×pèyصÿgô“ Vï^oÕõî׉ÔñkÛÿöÅ÷¬ê<õó¬ìÒ®ùâ ä¥ ÷!; HÎ-¦DEJ›$Ï:†xÎý‘ô=Òûºz#"&Í"Øê›ú¨÷oèàýÑ%®õ½"þPEú7øôúõ8õõ¾ï3ðÂìïjëhñäìµóÔî~ôø} p Rúg âÔñ2æÞ|†ðR+í>ù#*ú(¼ýøïÆ-L Ï!4ïmÌ Öö–öìññ•ûîòžFÕ3çY+ø/Óó¹»É£ÜسÃѨæt,y&¤y+ÃìYÓèá[ÕÓÑñ4ãõzr†Ï ôv©ù¾N½Ìª}õjÑ(9 l2I1 ñ%Šâ™ýãÓ¯Ýãìªß~I::óô„ìjðd÷•ðÃþÍüKn.j@ 2 2Õ;%º†ï=ö/èñz ç 2ðb !Î,âAÒ ÉÌã%ØCçžîYÖÇçkïˆÖ%"güÆ$ßì˳ÂIÞïÓºÄÍüÀæ ^ Ú«ñ°òîÂïåýÝóÂ#( áL1$d›RFWš_=%´C¹³®û—ûY"*-ß k#Þ'Ò3,Î7è±áÊ|hñ2 bLÛa÷ּʞãLÁh õ;(èùvt åô%Wóý”MÊJìòÉEÆCãÛ qëø qË Ô»âÒÅhõ^>Ö38HAC!2›ÿTõjü•÷Fýyûÿ õñÿ§êqóáåè¯ÜÝCærÞ¬õyëx’ÿ)q÷T Ùïé´çIÙ\"zÿü!É/cãÝÙ¶KÏlƆ± ìѧù‡ò¡ú¹úÄYÿÌA‚ù͹ÑèìéeÔ<-.0Ë/ÕüY(˜ÔÎêøíØ"ÿxÈ FM¿þïþß´¤Õ Áïþ\ìéY iól"І°ßÿ[5þ½ü°¯Æu ç¿ïeïSÝ}HôØ( ë !1 ÜSüèüÿrúžØøáóß5î&«šÍ%ýÊö~È}¼žãÕÍ_ v!$î £„ äF¡óË•Éê†êÐÙ'ƒ]2¨â ÿÔ× ÒâOâ¸Hî/Œ&¬?3þ|/ýôz\}Á]¸þÚTl³ØœëóSîOßOÅ싸Œ†úÊ ?æBó.ØoàÞãÔÞžòyôgó—ßÌùÈuݿƎËXÀòìC×¶PúC { Cü= kô6÷ßýõåÆ3/¯"Z>1¦ó› óõ3ñŒøüæ-¿a±'‚ õ=™ ‚$eÍ ;²ó{õ° ò¶0?Äå2ÆÏïþ©±·aÔP±´ôæš.8ù»Øæ˜ùâLê}åÃá¿ñ å£ô$ ª÷C«^üäE´ûÆ—¢dÉ"íµ Ú )/:ÑO10¡RMå, CÁ Ôû¤ýqý,òO=ùûÕBjýÐòà­ÿM¸Ð ß µVîÎé"ðøÆ$âýÒ|Ç6ð'Úý¯ñ«ÿYâÛ&kñhõÊ áœÒ®Á‚ß (Ŧ \!ÏòÇÈñî<~ó¬™ ùF.!ÿø9Ú§ôÌÓçãÓÏ_ùò‹&Úø"Üó˜ßMÝ£õã8Zû• —%‘"jíœ ¬Î÷àlÉ ÆtßuÌaë÷ðÙ MBñ2ßfäÞìoØÇMï¹4«“}3ýÒ{þÅ›ÊóãÀͬÇîÕª€ÿ,àv  _‘ýÝ ;ýc÷'ƒP5 +– Ø:EÚû ¢ÓwÏHVæ3>)2÷5;<*jÖ#þ…ý=%öü¢û´/ù=­6¨2#ßyþ£ÕsÒÚ"(;¦R#ú…ã*ö”Û|àæØçÕëÞÒEñâ²mý‰qPå¹ü_å áÁúhêƒ<%•&J&ïõ ëä+î=”ç(-ô )¹ø¼¼á¹îéùhæõþ q­"ºÝ'q _"áÑÿþÅÓ¶Íò¿žñ¨ÕñôGûÛÛhóçÑCÙ±ÛâÕ5ñDâ1I÷‚ Š–‡ËáBöÏû×ãí8Õ¢Ñü_)Þå çÏJ×Ýü6àçf ÖDGnŸ ’+ñŒ¨ ê ´Ï,°!‹ývôë?ö:õõç0'ûù)Ôv.-¥9( òÞüôäíù‰ö'Ëz"£#@ 6Ìó|äã)ð6âmâôòâá Ï÷q6n A.úR T~ùjåYC¬ 9³© p H 6îîöxî<ãžâòÜ2[$pö)‡åÄüê#Ø(4¿û¹Eu@Œ 5¼èļޓêIæ0çQâ–ågæ×ÞíîZèúÐ&ÌNê ÐäÁHöÖL&ã™úð·LðÜóÕì„ð?á±åà Úö¥âÝ~ý²òŸÂÞàÌä»kdÞ³>¸Îéݺêâìy÷µ» ¥ÅÍG¦ì–ÜÜ!ìµÞéܬì1àÑýbï’¯KYø‹æðÑkçäm[é *$¹!–(›ì&ÇË\ÜiÐNʪ߹Ôjï9áþ¸òå 5 „r) ¦Ãýxjö)GvO'ÿë yâÚèOµîZZúTb6iê"@ ¶®A%M8/Ê5nPÞìÇ+Ð-ÜèÞÕ‰)ðA#ëUÇûï Àãnó;ä¦Û&ñúÝ)ÿì©Éÿy aûÇ{ìVùyæ`éî,çô˜ÏôÏ?ä˜ð‹çÔáþ0ê¨È¨‘S EÇõÿ 5î8÷Dü÷ð¤Æv<VaÙLÂqÎßÝÉÃQ Ùæ¨ dñÌõ úóùJña÷¼âùèCèDÞî ôìá‘çb«‘Õ=µX£Ã÷ðÅÔÀªh åäîô¹…øÆ÷ñžúy r¾ pÿEéî,Më ñû†îüÔ,,ê$«Q+K ‡ŽüÁ%pøÜâ­&3v'Ï&5~#.÷ fåí[ûiä&=Þ=ì+,2;T!!jçžùê´âO•íä,rÙ"©+ﮈöïÏýÅø ù‡ûzðóùóÞÜRÕ &è;üäÞæÝ+þ5áí!ÇM$ÔõöNæUî”ñÍç¶ÀôH ±å ° Ð ró¡–êïï•ùßç?!1;;]-*E?h÷S! à•éðñÞ$ñôüÃO ˆûÅìsùpÓ:àˆÎ“Ç9ïJÏ\\üû’[öôIÑÅêªË¸ÌiéÔ„ÓñßסæÅ'ÎaàpÚóБ÷Åáà Óý è Çf ²})®þ‹ÁŽ)ƒ)?œ5w1gCc¥)ää¹úöˆäNÛÑ.v#[#o,# RÌö¸ð—õTû‚î;½üH/sÊ% 1{ô6 ϼæ۟ˇ6á)}æãù:ÜÆâªìnåZ¡ôBøAe_"“5#a&uÝJéÑúÛòèæý>&†™Ñ) ø7wìÑúú“ñ ½@5Í!/Ž5h»+Ñð~ ÕÝwç1çÚÕJ–æù Cvíä‚Û{çþÖ Ô Þ\ÏWðÛdlóU ‹ /ø ’ÞþîµÙ†×ï=ÚõûA~Ôÿ-ÛÙù¸Ø´Øäê%ÅC&Ùä7 •Ùåñïæú•ê s“Åü9§Ùñ™Ò1Ðñ“Ô”ßüI€¤úô Cé±ó—îÅä­ þî)é ©9ì)1A9É +WÞþþ†ÓkÒRúëÕ…%ÁV-:ùí®ù’Ùö¦e }?Ò|ãFåõÞ-å²òßÅá‹òqãA #þg âY÷„žáªï:ë€ÜŒßìJ2Í™1‡1i$úÜó-á²×`Èä#"" g-g)‡u2Ø€ ¢ò“ÿdôtêBãó*-ˆM-m0z ì(žéå¡ßæ>êâ}ø¶îÏÿÕüZ¢áò»®ÚïlËÚÒQ×÷Æ\ýëÝ‹ØÝgúPÀvÐØ¿p åŽ `š"·ðK Ká‡íÍåØßñö‰ä–)øÆ ß" b&¶ïí ý¾óeëuÂùj.[0V6ˆD/äà eÍÿÞØnÌò_Ü~'÷Ì‹å þXÑžÞ”áªÏõåì[ùÓÑç;¾Øïæ^ÛdØÑîÝC$òw-Ü )©Õ N› ‰/`=ð4˜3Ì>`0<á¢èäŸàÙÍô–=(|.#AI£$‚åì÷Îçìá)÷tâøYñí!P·öW †ÚõìÑ Ïpë(ÐÅ’ñŽ‘?÷‹‰ìôxðïêIûíš Lø®™ Su» $§õí¼û|òšpß8Ø-™:€ä‹Ï‘ÞoäºÒN àí >×}œõÚ ¬ã×ï çŒÞÒõÅጷô>c †.ÓöÉÊјíx¹ÇYÍ+µ§ úÙ9!ô»úè®ù†ëwãYfìk!T %6"tp(tûæ"ýêkçÅñ"éÿ^$ñ2GÞÃètú…ãÓ @s&˜&—_$ÓýàÊò…æêßòdäbåÑå+v ¢$×,ÍòNUÄܙٿ¼O=ãx>°ØCáC `BƒøLÂõFô=›ö+ŠÅ!(­ iáñ¦®åLïbåQä[ôé}ÆüiP ìõóébòðó@|/NûÞ×òñQŞ̒Ù{ÄÞ˜âj'X¬%k-úç[ã™òêî\Øm)êó“GI1¹/RBò:&ìõ•iôööó™ÿüõí²9*Î6=ØÙËÍ ÎòßÓ¦!7˜k(­õÆÇÕßò+É_ÕÎÞõͰøhç™ÿ9þ¢ìCýÍÕGå†Ð÷δãÏ5¹êÿ1hošù} ÖòÖö$ õM z=Ö%;¦@ R7pûësìRó¼úê?#—ˆ?“0›3ÁD$L2üèo ™åÄä‚$é³"ØC%êÞùÀÊÖ\ÒµÈ{éƒÙ•ñ—ëæñð²ú¤ö2“™ù{žéšïLûÚáB{rn!4ñj Ùé˜ôƒÜ‘#ù¡ô!ÿô3ö öûÐ(µW3?-‚(-5ñk+óÉöâµñ«Ü/ßZê‡ß¢þ*ôø Mî'NÛQ÷»È+ÏÛßÄËÅá[÷€þÊ µæK÷¨ÛJâÚäîÚÅú(è‚ µþ" 'üDƒðZù/ü£ï¯Ÿä.U$îç1lð'èð<»øŸý©Ž·TYþÑ‘ÝBõZÌÎ{ï8ʪ"þî"ï'”ù ÓëÜ6Іý´áoñùôý°3þ† ðöñBäy®édz†$k ¾']Š*?—k‰!¤?s"ÝJ¤ðtëãÃîÄé<ä`ƒîHåÀ™vìÀ 8×CáêdÖ… çí|(q!U* õ¸½ÑÎéíÖK;oÝÇ™yUjR‰: «®ü 2¡ Eí;¤ 1Cúê&¾áë`ú°ïê ;@ü^¨ètüÑäµñgâì´æ„ä‡ðååHüõòdøÚÿÞÜóô:Â]Ô‹ÐIÀÃ÷ZÝ‚€²ù8ÕæóœìÜåZù|íÞÜôŠþž-ŠU8ë3P¶5‡÷®?ò)ð’øŒ:•)À2ÊDÇ`4×èÈrÙ ê®íSâI:ö—Nü¼ZúµýýðüK÷¾÷ë>í»çéåKõîrð€ùI×têØÊ»ÐfÙÃÊW÷Hà_ªùÛw"üìýnúúõS÷U9§ûFÏ:±5xEW00‚ðrµôôî)ý86&å,0?< Ÿ&uûùzú® ýð?ûË ) Qñè¤òÄ]Ù ÎM¾ÚìTÔH÷íðnçqòtÔ©ÛLÛÞÍVòÛÚVWôuayÃõåÎöCõø,ù¸Ÿ €@c¾"!øy]èö«ì£è|øõ¡!*X g)2ò} ¼äMñ¨åkâòqæ¹þãõ}¯fò´Ñ¤é̹<ÃÍÉä±€ÿÑ/Á O8à|óBìÕÙ¥ ð ùÊ ëù&ÿ‹¶„öù1 Tþø—´ Ô0™-2ó5$öF€÷²ðÜ/ûÁ ã…"ýû$YåúšÜã)ãÚ¿ù(â¹4ý”¿õp ÔAãÃÚ ÇÃý¾Ø¨ÛþP%oÏ3pû0ëø£ócõ “œ&7ñ’(; È<ù†ÎKùÕ ($9>/2ë×XÞƒæîøÖåç `Bú³ YÒ–í5ÀšÊÉRÀBÝ»ÊSïFÝÁÑó§€¶nØáÏõnϲÐÌç©Êb ï*“Óþc 6óžú†÷ˆð!cü/€ßA¢;2+ÔEòúm!zãyñ7ùxçÖ ü ” ãØÅÐJãtÞË×ßébæyæ„éšéèùðËïó˜òfàèÕ7Ðç Ò{nð¾’N¢î.úèhåþ¤ãû6Ä :Dª?T%©>‚<¶û:ü9™ût)º'<47;G£&gAõÚ$*ófýšó·íL@ú  û yÅïñµü^æ:è¬äqß.êRâûFïn 1, í xöÕÁà ßÐÈSææ¬ Ó «ïÆ™ÓýàïðÒ§&ßû=?j-a6Á@6"Û5i´%îà‡ÿí"ÿâ_"ú)´ ï%hð†ÎÝç±ædÜêÿ—ïe0 ¬öÛôÇ<Õ„æõÍRË#ÙÊîÞË÷õaçpöÜÔÝ„ÙãËbñ,×V®ôÖ ] mÍ ;ý/Ñ÷o7;u%²q$O õHk €ûÿYÿü©u0ê%oz8ëâ( ÒØ-îýÕŒ˜ýg±"`šXòî®á¢ëµÙ:Ö(ÝýÌ~îØ‚Kó1 b÷ÔþñÌçJ.ëlL&x'‘)í é&”ÞúþTþÿ-ÇE ñ‰&£úW,ðwôI¼÷¢4f%[$;ûp IÝ ôŽÝkÚKæ]Ü=ðñåùõUõˆ÷Ëüßï(ùXà+æ†ÞÕÑïUÖ!Íñ£.®±.Ô.n¶#å‘û é6Øšhõ´,‹"~ Ø-¹Òý}ÚùýÛ î0 ~½¨¼ñùpçríŽí1å­ßöuÿX ÎærõÓSáhÑÚË&Ú•ÌÑäiØ~ñ¼çADöŠrüë6ú`ÔäÔŸíàÌ`+ÏûyM0:Ð$KG(ðþårçøÀè3©˜%¤ ù#`, Œ(Ï Ž~»# QšZ&‰"¼ •%ôåÅÞìjÕ¡Û—ÚYÚé¹ä¨ïðÞê#î€áÁßtáÑÕõvÜíCùcŒú¡Á×Èê«×šÏ¤ùÞ˜&_‰1™1JL7–öôñø“ð9éþ½4x'ž2·="ã8®)x Ï…òº<å$ï8þíÎ m0a(ñ` JÊ ÙmÕkÁòìÕÖÑõ°ê…ñÔïÁâRéÍÙéÚ®â0Ö\öòâÓùj î‰îÖëà°Ý[ÌŠýFà* # *g$& =£ l ^Q ¢ßÙ-ë%V6è8"6gØî¦¹ŽÇ"Öo½‘ä‡[ à.ïÂÏÙ±âçÞûÏDûžßYlþ¦ñ¨þœáFëaÜÛÝæÖÔà0ÛøíçòøA£ äë ~kê •4˜"Ã;EAói=*c9Íêä®ôdêÊàá|ó‡ 4 oÿ3Ó²óºoÀÔ†¶é%ßöίìoÒOØ£îòÑ«ý)ëQó4õøÝ\é@ê£âîkú† Zû7÷\óNÈ&F#j+0ú³(L~| õ¸-ϱ@ƒ:¶†Anã6ÚÍÞ—ü%â„+š!Oõ½ÓÔìì›ÉÆÍÜÙÇRø.ÜD âû‡þ÷ Ú<ùQëuïäâëÄâ›§ö¤T—u<4öïùíôË3½”OñJ½-ÌTìó "çPêæ"`õ_.\5hõl*²ÍyñhÐØóõᨠmúé ùº¯¦Ó… CÔéúö ÕåÓ4ÙgʯùAÛ/#=Ÿ!’%Põ”ñÏâ¸ÖæÃLÙV+Ê =2M0%=/Ôé låæÛWéq>í»%÷;†é hÊø:bèÁííë¨æÞ+öÉÙùUøãèèìä(ÙþkèHü9]Þmú7ÅCÊjæÄ©‰ñ–![ %õk Ù ç\ìÜÄ nö’(K2”.È/™8S!e4 •(ôNØîñrûùB À Kb_;ëdþ ãÚäõ/çÈ «ÿaÄ—uãüÒÇ[Ò0Ñ®À_õ5Ú`øÿIÚ-ô}ÏÕÒèécÐ4Êî*q˜$((ül#”|¾­¯©hõÛ â'‰"\+‰ l"+ó;?øßð€€<¯.z!<èíá…Ôß Þ2ÐÄõ ä) ýª õÇ•àµè'ÔiÌ…Óó¿ðÝóÈ#ê/ÝÄð+ë€ó¶ë ù›ê |ñø¾§{œ!>&˜?(Á —þÖX˜þt"ë+¬1> É.›Ü°%ç6Ú%Íùz?g0,m@ÌýÒ"uä@ö²Û°ßrØXÓà¦Ódë ãŠûõZû>ÿÐëmñØãڜ͚È5ðzϾûÿ*I(… æ"ãàøäTÙHý:êЊ \z:q õtTù¥"; N6Ÿ/ q<{û§ïy3óªó"øÊ & •F ø?ÑíœòLßâ9àÔÑõ¬àÖw]›è §ÂþΗÕ!»)á{&P3':ý?ÒæDõ@åÄÞ>ù#æPtÉ !Å ¥¤ Ää Œ ï ' Ù véŸ ú;>éÚð%ãáÞæíKßBÿbî[ =$CQò§ö}ìUá åI.BŽ(ë/ƒûjÏhêÍ@Æ%ô+Ö¢N*L(€f+½÷ ‹øŸôRÎù"] ° ŸÉÅd%# 7!ÌÜoüßÅwÐèí£ÑÛ> Ššïò§Þ.àWíG×aÜëœ Û¾¦ ØÙ¹õÞÐðÔ®înׂ šô¸t 8IòK9è°ïbö³ê/?#+$³.Ä4&840‘%@þ½ë@ù¥÷î™@ ð3á3²Ñ7WÕª&¿ȸ×ÈĬþ èqƒz7ƒçÜûÒٌبçÿÍSúcáôiñüæ¤ìšçÄã½òñæ¥÷"ïoûôñ±üÆÐ8''¬Èy Eå'¦ ’Dä/F#Ih&«@UâëWýõñêîUþ+‡ ,6¢*ôôh‘åoäøÌß`bûÌÛÇÿÐ ýï«÷äPåaÙKÖßÕdÍçóÒ€ò£Jó¼$ÞÊà9îÝ׈Ðú&¡&Sà(õUªûÿ}N5}#×8–<ü„4(ñ†bíuóµû%ô "U—ç Íéöçåûíjà Ü+òGâ›ù4þüåäõÂLÖL¯EµÉ@²´÷&Ü ¼ -–ô ~â|åïÜØø|#a$äúË%}Ü7ølëæÜ ùü-!E ¸^  mšYTýäfõBñ ª÷_ b FüV ×ÙðÎ9ÑáÞѳYëª+šA&uö“ BÚVÛkå!̰æM «! Ãôòo~ð˜îƒ ô Ê E"? ƒ"H*`¢#T<H¾ÿ%< B![ Y K"`ószíÀíûèêžü<‚ ýR êØÓê8Ö–Ês CÜr,‚Tà$s¦ˆó¾ó#údïþböOþù3kéS >#‰ë• 1Üåä ë‡ã Äÿ¸+ø(<(œ7wL&ðâ½üôàðÛƒø>èÈvGó$ Õô‡ÅGÓKÓîÇûïä×`ô:ûÿ_ètñ´è|à/øääÿwñø`õªöNïwþrùMûÒ íÞù®ç«ëòð§.ja7û:â$a;ûŒé±ñnÿâïÿ+à 9îFCÿk9Eמòcã’Ú]÷Øçýiù7õKú¨ø¨÷óþ§þ,óùœÜnßWß:ÅHü5×umþ)ýs-õÇïƒû)ê€ õ× õ(NÕ¯ú³ÿýïýD(u|P°8ÊKzX?øBlþ@üÿ(ÿµ m% ï·ÿlOü °ö°ÿJîÊîÚ÷åçb>ûát±–âõÍßΓ΃¾;î`Θ5ù>øÒ ŠÔõó(Æ8Ë^çÅ4 äîcÝ ¡ )“Hd 9Y—Ñ Âœ“e!Â6"tôzðÁû:ÿføËÌi·þ˜ñ9iáÙì˜Û(ÙTê¢ÖŠòìÁ¨¡÷8 p½gâh¯5¤`Ø5«ñý¾àè†ð#<í!ò˜÷ÈðAyõ"úƒÛÜ …ápr› ÿÖC êGð %þ.ŒóÿéÿNó¤jM"Üšÿ‚Hð`ðcïÜãVú`é=÷QPÚõ,úÂé>ê:çݧù4ßò5ùSÚ°;úóÿ@ï‰ðÃù?ó* ÆÐˆú/ ¸"ùû#âú±:f€8ÄD=É-ÔÝOøÞãߟÿËé7iNø¼þæËð×àhÔ?ÑÆâ˜ÏÓÿ~àÖ1ÿøè¥öF.Ø‹èOÛ0ÕbïçÝ' ù™¹§Š'pùõ¡Uü‹"( &=r7Æ2©OÁ³8ÍÝoTì„âq¾õ…ÿµÈìüû6Ú»êjÕ"ÜüÐkÓBÍ¿ÅÍÀ1ÕUÆ:å$Úåðäê,üñµÿhóâ dòâ#ðe/þ"›T*åæTà#ÝõÀè”!à•&#.k/\•$N!%ë#)%ù&Â&Z*-ø%44r+üïŽ vÛ£çlÜçÔè†Ý7òqî½öMùó½÷oè¹éÔàoÖ«ç ЂŠàb¼L ¹9êÖûÜâìØoÝv%4n—#¯µ­f †S*g#/“.Ð)Ì49N-ÝÒóg ÃÎ*(í$K#01ôÁ}зæÓÓ.Éóðƒ×eVø§ò³ÿëÝÄè1ÑÎOÝñÀ:UØh"„·ÙìØææ‡Ö[ÕâiÖzñÌãÿŠø/ƒ á XE ô½ Š ä9î"3²'€.Öx/rô•"Ú—ïÐëÞ¶ÿ\Ü UõÅbÔIêÿÜÌ»ôØÛìþóñÃZþÕö¯ý†æ}ìÒ×8ÙƒÖ;ÌéØÔ ïïáÉa^&÷ö‚þþüÁïRRy2-Þ ;|ê 'öBý: øz¥%ã%³/gÌï6¤î‡îùø´ïë’û@ þ²ü¤ì@ònêáçø¤á°òÇpÏ — ÷ýì¶íÓñ7äÌß÷„Þ’¥ü€0n&¨/ã1éÍØŸÆíÜ: &ò)bE ‚.Dþ¯Ãøt‰ûéù×ý‰lVI FòèÝSí¶ÖöÔqçOÑùúåfþìþPEÞ»üoÏÐÙ1ß1Ï6ûåã Ÿm£ +îUúÎåêâöø?Þ}$èý*?Ä6. IA º-Gñ¡ 5õûòav7j*3…Euì7Óðr%âÃí¿àÂÜ+çï݆ùqëôU ¸ÝÛT§¾ Ì$ÓQ¶^Ú™(€â0ÿ ÌŠÜ)ÕQÆñôkÒ'²÷‚$°!›"J¡ËçI ä*„†0ì,'4Uç(æûJ¸ó;ü¬êõ~)%8º6ã#;‹ÿNâ2ñ+Ý Ó­î_Ô*ìïæþ‚áæªÿTÌß%ȘÁÿÚ˜ÂýÙÜÜ dL÷Ýôâé}éòÖ8ã‚Fþz¬_<‰ ÎÃR|õwAø 2Àî>Ê<œ©?ší^kíªé¸ >ôb$¾²!Û-|['­îžYÞkåå%ØùLèªuýÓ ÜÛ øÂzÑEÊwºõï¥ÌXëõ±î:ßÒïáPééÁÛLïé*uH$øþÅ!ÒâEý°ëCæ ø&2Ü#ñ,…=Næ0Jï. 1å–æŒ¸éSx . âðï ß8ì—ßPݹèÞeösém Äù$HZr!BߊüðÊËÀå¯ÂE'ërõ(ñBàzþ©ÏžÙç•ÌNðš2ž!Š+$:õ è(Ôë§[ìåèMÓðÉ’ T )iñí èuòZô(ïE«ü§/³ˆ ¥ Qìôú…Ý”Ùý öÚì7Óâ1,6ŽN%ÐÞ½ô‹ßˆÓ'úÝ£þ7*¢*± Y+`ì¶ëè‡þÛêïª4#Çí£)7ð>‚å´òÝíUëà®÷‘ l þBréoNÛ”ëæAÜ ^ìx,Q(S1¦þ„YßåíîÝ­ÕÀö¹Û9%m_ô¬#Ý€íÞë Ý­aöf'86$œ(EÝ"Pœùúf «ê ñkó™éÉÿï&*'Ï=-ýÖþаØJå/Õ Ÿîb3ý²¤ÚÕùÚÁ†ÎÈ ¶¹çþÅl‹ëš~¬î<þ%ÔeÞ,ݶËú3ßÿ 1üo Áë†6@þËÿüz& 90(-4Ñ:Ч-–ïÓèüèÇî#]*?1^ 0™ÒvXÊXÅðø¹Ît$öô"%Ýûä2áJï!Ù¸×ÀٚоâøÒ[öÎáË ûÜ‘ ˆï°æ†çöÆå(Z/² H J'ÈÕ9÷ÿ„ÿãú– e)(‹(z:L È1Âëu|ò¢î¼"Jµ7¸-ÚL9ø –ßîLèWà¢ùÊí×Jý ·ÚÔ´S¸Ú;÷uËóÊòçÕÆ< øè˜í sŽé@ÿ?ÚâhîüØÝ¶ü^* /w1(DÕDùÌÍZÖ׿×ñÿ ò½ ã Äô`öa òìø]ôrò– °ý¼¨×Å÷ÿFîÜÓñ™Ó1ÕÄã{Ï2 Øæ¿'‘Ûg(±ÿ´ë òVôüà ò;9®.o2zóº‡ÀàÚ·ÎCº§õšÖ• øüa/·&äú yðlý±íÞï¾÷„êlÞúŒäAûydáÛøµØâ`ñÄà¨þ5,™! =.ðí(æ ä"˜ã½(c¸.%÷F"bô©NëméšRë2&> :ß1ƒ/•> ¹+ýò …æÁìõ°å6ÄùJtlî$ÃÒ/ík 5é)ò5ù>ó³»Q"¶ fN$.Ì€ó7¸ºÀ7áÌ»¹ìð ÔbõN¥éoîGîùä¦Tíˆ<P/£Ö*îýÖÍâyÛóÐÊÿœâu"ª ä.*‘–2kü¢êëúÞø‘îê@)å&h -—겜ÉäTÍÁijûEÕŽ/ @'"‘þý—å½óÄá$Ü•ìÚñú!äáñò³ýøDð(ô²ãÅãé3Ù!åc²!J‰ ·ñâôåj¨áÃ3Kû:v=¤Ã;œß+ìÑåŸ05%ä3C>(ï9¼÷–Ãçßð‹ïÞâ pð/%½ Ù)6'~}%}ü9 ðšôM÷ë…Jñ+Å™ ßö FØìÏäÊjîÊÌí!iø<7V4™ÿ¦2’Õùò-êKÝ[ u÷W @Õº(¼ôÈ1àlùöÝþåùô¦ëg¢Æ!xAÏ,õX ïá›ç™æ¡ögä-ýø‹ÌÀÚ±øÒÊÐúáÉ&`å* Ø ^p^èñžË$ÙCÍ¿óë-Êvwî¿ ñ˜ü- pà"øñÛðÞVìáâXÿ:÷—ìwùµ æóíÿãð–÷.öeñ\(öÙ# 0-e.”áŽÜ׸Ñ-(Ø9Œñ&ˆ8!ý< ÞVòCáÈÔ£úéÚ©÷$(y®"­&ÍböøÁüíxíÂñ¿ä Xð› ì Tâ†óC„â“õë¸å° °õ' 8 ž*äÊ!ãØÇù‚ÔÜÌ û צe²u ²öaà0î@çÛ™®èí/Ÿ/Z5†´'Òðø©éä|öáãòFôÇ ø"Q#w«.èì¸ÑËžäëWÐz(wµ<¨=& ‘A Í)®´SÁLÌ[±óÙ ,ˆ]C4EçÁ Í(á Ô—Ë`õ_×Ëaúth•ò`åքߌß!ʪþܨIýh)¡¿"›'u'‚í1þ9å䘕äÒ,8 *L;–ø|*pÑ ðYàµÐW#ë94!É8ô?æM<È÷%Rë2ñCò)æÍü_ï@ÿýÿã¥ü¤ùf!é{ñeé˜ÞH’äv#еÅ"02)åð*ÝéÒmñÅÒ æõ&äUüo6äÉûaîræ¬ øx&ÔÒ0«2–H4¤þ†Óîìò>ø°éÈ hýx\Ž.w,"¯ì—vã°ç2ð©â„Gø)"*5%‹êχ×ÂÞ^Ã[¡ÞÜ,Z…'á8Oå »sÕÂɸÚñêÓ‰\ý÷ðЃàáîýßÂàÃìCâahó¦J •'%Ÿ*höäÖçêoä(Õ{ –î”$)ì%=+ý )#(õ¼ÿ“û™é¯!ûø¢H9,ÍDNô5ßîü^È“ÆñîÁí&Rû‚$,Vó‰Ñ‡ç8á‰Ò|øôèEÅùkK  å{è~°ÌÝÝ Î(“è\jÈj%÷áì,÷a˜é¿2 ¬9¯9Ñi1ŸãÌÿMÜÒ:ú­×¿±ûº&%C%’*µ€%ª÷Õ‚ãèé^ì¶Û«jñ+0÷ „ x+QÛøüèÎÔ¬åÒJüêa ½˜WÚŒgûÜEÈàðËqÀ¹üÚò |¥ÿÂ"À«êñ̸5 )Ù,÷ðß-<þh±ïÞúzäüè£àßÞmé~á ó· ý÷g Ðõ9 Áçsê ä±( C@³/ð.%GÆïø$ªÒHßåíðÐÃzú£(Ñ$*ã-|ù>Iè*ö2íÿå¶ñî˜%’ )y)6'òÞ°ÄûÐ\â5ÈþíÏö pO 9ôÿêì‰ôãìÌìõôëÝ ¥óz.X".¬çÙ †ÓZÖsÿ²Ö¤BQå:¤J÷ 2íåVJåDä—ÿÑéæXì$ÚV *ú ùlïæ×çê7ý êÉ¢š(¸©é(HñP+ÓìÝÓàÍvö¤×µîþi¶²êp§ÖTãñÜáЄ’Þu ãDúÿë1 ÄËßFÓÊOïÝEgÿJ2"Îö"`ãöî£èn¹õ%` ~™ÁÆ =Ð!õÎ×ðì£ì Ô%Vû[DZ8Š1E&à—Û¨ÕðúŠÙIj÷¸"k)»9òç2áÊæçGÛûIè,©‡,õº ­Ý|ðcÛ¯ÜñÐáyîö 5 ÍŒû ,ííû®ë«éëxî,+}P9õ7µÚ5mØhø?ÞÙÑ’ =áë6PT8Á<^œ4Pò›ºä±àz §â})¤U! #! F~ö qé9ôÞåoÝeØdô»Ý®Mþ ÷èþêÍÝÛ ÈQ¿ÞÛ"(Û4ä„ÒÉèÞ«Õ‡ÊÒÿ_╹ eª!üЌހô3æ´×Ž ×èù)Ï")âÿ~ï_û‰ñ;ðöòÀþeõGÛ+?&_²5žãøˆ»7ÑO䙺—˜î½ó<ˆÈÞEËέÙE߂ϑ¥æ£À ¹³ïæ ýÜ¥ã÷ãcÜÕùéˆ þÄ:; è¯þö1û‘ûê÷œìZ"Ü$Yúš„Ùï ß<Óh ÃäÚ3¯»4¡;ð ú0vëlè æg°åN*c 3)˜3ˆþ÷%ÛÔ ø,×ÁÙ ôqã§1û¥ óáüÊá; eõ“µòBé 'îæ Á® êÌáãrí‰Û¨ í® d¢ôi ÅäuèóïÝ 2ñçˆRíÿ“УëÚצôþãì ®ýZÔVį%ZþÈ·íxöÇóÌãÎ&ýýö<ñ4³ñ:6ôÇGãòé¢úߪŽú¬³Y¸ÞþÖoÔÿìLÔé𥠴»÷æ&òˆöÄîÂòÎéÌì€éöæôõ/ì» þuçØÐÛ¬æ§î&ÜtÙöJ9v#Ý8?s7Ãõµ ¶ü¿éê®úñ2½h/¦9Ðj/Mæ†ßâ0ß"åÃ> ’&‚2Žr†’ôÿxøËüžßèÑäÛªþò cßè ŸÁÙ3ÒúÃ~õ!Û«»ÿA ôí —âVê@ò ÚrVõ%4!O #'¾í© ÉãlêÜùšâ¿ ÿûŽ4!Û(Þ/JH"¿úºúôÙnñû ùcB Þ<$ÒêþNÑ}Ý!Ó+ÈÑê5Ôþàø ú¼ òÞáÍÀØ.Ó ¾ ß- —øk-ÔríUäÌÔNíl¬ý®þƒÜùU•ü–û£Gü£ú`{úk0 ?ù1-Ö; þ"¶òlÿõ§çT ñž"þ*“/D'0ç\)â9âÅ÷Ãáþ^%Œ$8ýÀ!Þ›ô©èWß.ûQè(úÂù÷Öúúÿ¤âþz­óuúÌóXê( Ìò‹ÖOI0AóÓønûë÷Iý¢4&kM9›ê¼bÙNÝi÷üÖÂEþ•!½-°ìëßäçì‹îäggï^3ç' ŽIý \ïöëÖâëóˆßúñJM[ýù~õöö÷ùè.™õ)÷©º&ÉÛÂrɱéLÁ øYäRÿëÖ$çÁæ*ÝÄøê:ú, ‚Þ! \øÃê\øèèçBî©ä°Ýð &‰ *¾8C¿0só*lUêà-¢æA«2d0o?v(Wõªqø£í²øEïöN"÷ú¯ò½ÿýó„ôÈòÃï€ï[ì íwìËï'ñäíˆõ•è¤ð&äå¼éàÁü”ïB ™I Ô£éË<ÐÿÝ~êßÍ› öã2n,Ï Š/úìQeþYéý%eB&û;l>¤%Ç7B Åý¼düèö¯ûHöIÿÝøuOX ú&éGªÛ@ÜOí ØQyñÞÃfâOÿ‰ÄžÙr¹%¼µËÍ·)òŒ× ù`þáà_úÄÎmÚöå‚Îcì (úQÿÐñúcü5îúUûT"av%R&D"+À –)4v {R tÚøZ÷ÿÂ.v" 3Jê !Ùìå¼í|Ûö9õùó UöV â úAÙ‡à˜á9دë âKñîîàð;õõò õ¤øøõ”=ùð£þÇ'&qÖFçÒºúvŠþ¾ô„3þw$Ä%¡*‚ Æ$Æçwë᜗ñ/NŒ01óV¡ÙëîÚ¯Ù<éƒÚü,îaý»‚øÚùYýPùúþñùî ïÿôvá8ù™„ïíÝTõÓëuØ•ó¶ qïÙéŽçí÷‰àæîðá!ÑÀã}ú®Û"àÓèÜVþNí,žÿ| 0ïÖmå:ðPëfäÞñ^ã¯ûIé ÃûÀm!'';þ!1ãTît÷·Þ#'-b6U5)²5ñe ¶íqîâìW! Q"Æ%8 $ùä âüyö[ Oû  5úd½ì'øjêÃðSä™ï¹Ûeâ]ßËØˆ÷‹ã‘ Þë¿{íêŒäQè¤ïwÜ„î=.—–%a4 ý€%Ý­÷;õâE"¯L8ô,ê7ä@:' Šc Ž d Å äŒ*l  ̓'çÿ9ÌñØÇîÑÔd \ý+ŒÕÏöŽÄÛÉçânÁëÿJáÿü„ò’’ÍýëRÌSÇ òÖÎëµû˜T!—Võ8›öæôxØõ´w0Aê7Í.¨/\6ï®)ê×îÿ ûV7û« A¸ÀñÞ@ûÚÓäÕæ“Ó Gíš , Øõc¬ØJñ¨Í³ÐžædÌ/Œìõ¤ÌÔ—ìÌÍѸçÕ&ðÍÌî¾ ¶­h&Á( ì!qü‹~zûD+W>2ø/0Ô/¢ù"åîÒù{þðÄ!›,16.¹ 8hü’çÝ÷€çÃâXóîåmü¦ôøTÿTêgú¸åïõêÕì¨íÓîè ïèés;ôE  Öøã…é¤íX÷žâµ*þ7#Ä#¯ !€õÑÝô€òÑA÷±,v7,c0¸ý²%~ÑõîÙâpÍ< zåEý BÞß'i˜ ýµ!ìKö¼á·âYòÅán5úû¼ èöƒøâRòGà©Ú“üÎÝÐ"à.,*o°+…ûÝ gûtñú aö ¶M §ÍêfÃÙ`åTîeÛ/ö?ø3G!öÜæ÷gõütøwêóaã1ÞÆósâu$ú‡ùÊ"ÝòøJÕWÝ6ç\Û° ðÂ1°&Ü:¹ü¬ Éà;ò•ýß-9å*›0In)+!ª 7 Í E U ù Þ 6  ¦Z c­ }÷ ñÐôúñ’îû×òL Eñ×¼ÙFðlÑ£ØÑÝÒföúãþÉþÃñÀ€Ñ§ïCÉÉ>ý¢Îä(É *$®*Tƒ/öt¦wúUíXóÛä&`o'È)Bp"- È ? fŠê†-5i*d+Ãg'¿é“ÞõÞðLÜ ÿ9ôJ÷!Ü8ø/ÊÚ®Ô=Ëãî¶×!óòô„ÿãÔfé‡ÐAÐêèòÔWþîËý|þûyü3Qq "¯ ÐÞ b8mð>ó4í,›8¦ qó¯ùQÿÜë~%Ìí<Î2©{>GíÀéðâõkëCÿ¶øËõÍè6øµßÍìDÞòá¡æFâê¸éÉçøìØâ‡ìƒç í+ðòõxëë÷Vé—ë‘ô2éÖãøëÜ cú' SöúúM1õR!PzC2)¿9íDš ž-TâŽüŽçÇÜx0ï±ØÚ³€û ^ü½p(´þÁÿ>üøø‘¿ú† ˆ÷ ®ý– [éÄö¨ÝPà+òÝ)”ÿšH×ñ)äíõÝà½)¨ô%¯!Ê Gqô;ñë¿Çì~y)¸¥+ñuïóÒö­pøë„þ…5þj š# / [þÙ °ð0ûFí[ë_ÿÜìgº! ðÉ+¦â> ´Ô©Õ§öyѱ?ù·LÕüKðûãú7Þý¡0 ÜSOÒ!ùã8ñùŠõ×ñ9÷µõèð¾áöá?êƒæö½ùÌï`äà½óòëmà-ï/ ´òÇØÍRðq™ʔÒmÂæù¹Û É |"ô §ñ‘õ Iöi + %ƒ¸Œ ˜qç©a ®Ãïd¿" åáX%J â ô]6ó„è– }ñå Œèì9 ×ÍéÐêÌlñîÕdéö¸D UèŽqÍÎênÔ+Ñ–óxÛ% :÷• q‘œ+ ¼:¸ Ѹ Cóö" œ;’' 88@ð¤4«‹/û™õ‚$ò%-Ô7ž3ßÙ/\âýdîÅÙL Oïk‹ 2ø* ׯ÷ÃÍ.Ü‹ÔqÔåûØ6èèÞÓ»å\Ñ'Õ0æOÜMü°ñªøü(ëêâýöä$=Ϙ ¨å³ ”Ü ÞÚãû6U¼>wBILA6¯^û5ÿ% y÷σ’ú;—¥òÐëÏò@ðï:÷ÀòÈù;øfù½ýÅøÇeø¹òú]à^ç½Ô Óâ"Ñ~ÿõîØë ç0ýÚá7ôàØ—ÿ¨è!JøÀXí¢ú´ôüì|`õ–$aŽË!¦ÿFXöÑüõþê ä|möí v'” µ'Øåþ¹ãÜaVçè#dž,-Kë¶û©æ Úøkä±~ îÌÞúåbñøÝ¼âñT# VÞ Ž«öä»ýúÿ0èìë«òƒçOýüûòîÀëÕ‚è*â Òaæð‹'ä"c ¾+#àa%Ú×|õwÛ?‹ùtÉû*ÙÜ×÷Œß&Ý^þ{åwè|ãÍÌξbÇ&Û÷ìúŸÛ1 œ¹÷Hç»ëè€Þ=ü¡ëð$ èìý vÒë×7Ôðí…Ý-¯öÿüÔ ñå¤àÑìÙRÕò÷ƒßñ¸ürôtFå"ôæðñë$ lýÌ SÄœ+Ž®Dä2A—H™16΋ YÎô”ü–%›y$.3þ~Zê›ú[úÃê…&#­¶³øZjù™÷ü}ôÒ÷ÿñïï#ï“êíí@éÙô?èÕöƒôõóüÓ ¹#øé2Þœë›ëHÝy ôë3uñ!×ùÛùÜïs ´ ü!]#ô"šôöîGñåù´òâ{:6,"I(.òí0»ÍÐÜ"¼<—ùÕ 2#/ 6"—çìý¼ÓåÚE×ÙÕÌñ¸ç% á‚ÃTüȼçà§y³Â~¬9bÖÂ,s.Å)Ü ™ñßöŒØÃÖKñšå« ±èäkð þªðúí˜ö8ô8ÿ:ýÅ·-=t?ï4‹*y8‹ýˆê²ô]z÷b¤që¶!ÊßlÏXÅ>êeÝß×òÕÛàkýŸß9'‘F ì$Ôä7 xÃgÖÐ×®Èï`ëKµÌ Áö¸ùèï,ðS,õÞ'ý Rø3vfDVQU\n?‰ôtúYùÐ, U.0"|+¹ïÐ [ÒGáaä¨Ôþó­;ÓÓí À ÁåèÆ®”…ÜéüÝø€íùd$¤õã9Æ#é!Ø ÅÐë¦U§é;xÉ’ÉîÚÇF"qiB :<6þF–Ì&Ï^ñòœþæõ±üŽôµöñÛü"çõÌß”äWݳÝ/â¥Üûóêº.²0÷ð rÛæ²öêàGÎ '-çØ™ ö¸~ÓÐB´wõÙÔ§ü ùªöú3Üü¸Î÷8 $Ù)éëÞ F ™"R6•ð4íÑŠã5ö×Úh e,#‰ýd›ù˜ ¹ƒb üîe÷¢ñì‘aù% $Ú!8ÕßÌüÖöxÖŽX GéSî ÷ÞŠ•úØ"¬ø l5ý3 ú· KüýGõgù¢$Ý&~#àüÓ =ÏÔè ÒåÃ@õÝdöp"G ؼY Õ?å{ÊLÚ^ò•ß!! ÄÎ%oÞÉøƒéËž)õðÆF2Ö#:cù¹ÉÒñS tÅûOµüîÿªÀSûì(ïKïHíøáÏï0Síø ÉâðÁÝà!ågæò}ùuí½’Û™õ4ÉÿÙ%ŃÂpÕ\Â=óZÚ3 zþZ?ÿ¥ö:øéñ¨Ë &%ƒ'¾ñP Øù\ðÿÂ+ë"#(àÖžd f"1 ²ô øŽ Œöc.Eß Â0ÈÐö’²+¸wÜ®¹K Pñüåµô¦ Ïã ÷µå ä±íñàaú´ä {öµ^ XŽh@‹Äÿ| þùª­u—N'ANi8´4˜ÕQ›5ŽGžLã$=«5 ûÛüªüuô²Rû¯Ú ËVê&¥Ð­÷ëÁFÁàé°Á°ñìÔEæÏ ¿É—ØÛwÈ›ö Ûõñ .ÿlè¸$GíÏׯŠÙŠâÂ]ûïHÊbp òë\Bööí© †ú‰”Ç!I m!Œî Õ@íAÑMÔèýÖ Y÷× —òw ´ÛïUáZÙoâ6‰ÿÑ"ŸE ú"ä¡FÏÉÚ3Ð"Ç›ç]ÒÕó7Úh™í$ÿ˜ßä”îhÕý!6ôò.¸*ù:,ÌúôÄ™Ëì¹Óû@õ}þþã T B ¸Ÿ°®±ö&F×?¤0þ —7ÙÜ`ûtã`×ZÑì9!+Ó5ã "µÿD †þûZÅõŠ·ýa5a5´8J /âØû‘àèÑÚ Ðç™%Æ #wðð •ß)ò»Úúß ÜÿÒâåÒ®ø€äàóøú êÉõØèà >ç ž Û$è!óæ#(ìö&ëØä“ ¼î:/×òÄ)ð ^çÿìmýXìYÃ"f%ð!:+Šv ŒÙIe½½Ë¶Õ•½§òxãÛîù+Ù˜ñÐ5ÚÍà-Õ›÷’äáÝù9Ê äC-þh:Ý™ö»ÏÂÓôó&ÕqA7‘DÛßû2×bÚ×2å9%Ý Ö È º7 ‘¤ g%É­ˆ­¨?ìôÁæë&ñûTîR—2Ì ”(‡4! ð$ñê^ªõ çY\úv'9òÎ Û‚Äð’âßí³èžßtú2æ©oøg† êúk7 ûÂùe{B p#\ n ¦ýS ©ègô÷cáÀ¥þb5(' n.øÕÌó5ÿ±Õù6' ì;€7æ ý-ñâþÿ8â"è5çUèwã‡ã·ç,Üq åî™_DòlÍ¢âãÓðÁfد9k‘ðFCîÁõ\ëÍð’àÆãîÞ ÚEöÛä÷Íâ©ÊÆ”Ù-Ú{Égç‘òÿDYèþ¥ìÒñD ÷êQ (q#RåGÿRÜ6æòá£Ý³ò¤ßÂKó°f ù) Ïøî”ü6éã{žå›4  14á¡ôÈÞuÐlÍHãòÖ+òÃæ9ÿ6øŽ ñ¿þw ìÚù{ë,ë"µß(åQpêõé' 4öšwö¬+›²õ˦T –å ù²4M×N¥;(WBsë½›Ï+ܧñ{Óæ~ø¡Jß¾ñÞ â+í8ä¸Ú·õÞúAów¤ÃM˜õ©ñêvøzæýèÆósçÈŒù) jñŠÆàì7î¸Ý&í„¢Á@ì÷òcŒñÄñW.ò(-É):ýÀoÔ€÷¸¼~Êuá—¿!ð®(œ±uõ}Cöãú¥îxöâžæfìâ«ú¡D"™Ö…ˆ¥vÄ;¿²¥Ôÿ÷Ð7& |$Áÿx÷±ú™ö^Mù u( Ó$ý ¼ìÇÖêˆî„üî!V )x#lv) $IýÙ÷ùUÀ¹(M¿#5+ Â# î[WæpëÿËèÛ+]ˆ9?1•$35“ÿ‰ýãòóÙò¨Ý½óF1 4!©/ýø©öqúzüxöú¸ó+ôñMû0ô'ë|ÿb ³å´ö[áØ4 Áá&†8Ä%ÆíÅ ¦à ìOõ®çßùê Ø¿"/þÊWð!Zìaí@þfêq'ûgCí6„ DïG+á`é²ðà=µòø$_ÿ÷0ø’¿æûôÌÎ4ÙÊÑ/À„÷HÒŸgUÿ"©ê„¨Ð¦áÔOÑ6ìÚŸ­øýuDß„ÿ‰Êbà•Ü!Ñ©þ¦ç Ú¡ ùèè ù„z £Ek/Ãá>.9ß+ÖC´øÂ#ã2ï úBã†'²25Î(¥å*¼ åömwôƒñ:ÀífÏþÞ/L ˆG/Oë.UÅêÜÏâÆÜÚíÛ4&äÏûÎÙ`æñØçó ùžíÿ³ë$ûý*œÛäåõõ“ã’°þ:$O$Iô"û÷Fí!ööêì#â;7÷ ª-22ƒ¤%Œë†ŒÜæÜåêåÓ~eë8 ¿ü¢OçBý’Ù¼åÈÖ½ÓÄà ÑÔñxá´û¨»wóŒ ÿ×tìTÜWÔ<÷Ôä(Ľ”qøn×Úäïeáb×FXç•+rÒ µ&áÿšÛoáÀõ…äM ”üóI ¿ViWöŸÖHêUÖçËëø¡ÛØÐÜ ‰ñ æî±ô¤é;÷½(ª5ž/4)::{&DÚý÷¸ÔÉÍéOÙ£(±ñt.=ó³ ë¾óÑ @ôI| I„n½ã4mô&±â]í!âRÝ>õ§ä» eQ¼ïýíåÊéØðsàßîð3±ö'µ0eûõ½Ù`í å#Ö]“ê<$Mž+B.Å©2 µóÂùÓü·ëw£÷*/2(+0*C!âå+ü«à_à¯íÂà™ùŸð³ÿÕþ/ûâ„ï@ÿ ÚíêÎ4Ï]àʼÿäå­ÿÚ@ÔÚ÷Í¿ŽÈßßíÂcoì¦#ð‰„"åê1€áî•è@âVüÜçþ!"×m'ª’Eì÷XøBå• ýå/!ý)#3 ¯)Gà=ÑëÚÄÜuÐ×ôHá´ý°ú àCúºÏÜËå¥ÎU %ë†W þ«päf¡ÕèÞàÚeðÑäÖÿ¢ö³êfÿÊbb Sù Œ ‡3w!]<¼3n-<¨^&³ã¿üŒínáB"ìùM?/>'¼;&ú:&ã«íÒìJÛÆüUã ô›<“òðð};ÖÌè Õ?Ï$ï®ÖH?øÅ… NñQœê%ó>ò*ìt‘ñN >ýÌå äL ­²öÌÿ‘óð#)z ë90tî7µ×ÄÎWÍ ñNÖ•ò¶ Ë ®ûò­ääëêbÛÀúÃã" œöµ œµð ôÈÍìÆ¶]Ä1ا¾©éâýgËå÷Dðläê 'òt"bl$e& ¸(‚ò¿æµöÎìåé‡EôqE èì€ôà·âÌ„ãÉ'© '‰&ì ƒ ,õ— ­ò¥ûxçßñìê¨äµäîS&ÎÏ8+`äJ ¨Ä Ó«ßHÁ©Šê D<+¼:ôIز< ôAùÍòãŠüˆ(ì¹E%^©Jï¥ÿçåê«ë|âù„ê“ 'ü­YýÀöÌø‚ð! ÚósVô/ KÒõë-Ä‚ÉbÞßÃàíä(ý("ð*¢þùùãï.ûáÞ^- $Dõ5­&8AoDô°NøAôùö[çõ»#Q 2&0ˆÁ-|Þ%ÿTÒYÍ+ª×k Í ¶l$Žî½5ÍèìÈÊÑÊá8ÒXýtë¨ü:æ…ûŠÓÔãåÒcÒœè©×»Üóå‡8L‡ô Dð;øA¥õG*» ‚=%-j5àüÜÛÿâÒâ2ØEõ~ãaÏÿqÅrçœÈSáóßIÍ&$îw4º#,ˆúM Óþý!LN 3ø™ý­ äíBþÚjæ™êR×¢ ~òë!´f šó ‘Þ8èà×fpÞ%&ã¯Ý* åk [ÈêßmÍŠÄ2í¸ÍñÈ)w&(pLýÁì—óV€ 8o"Ù+8›º%…ô2åñ ôšõQñö©ñìþB÷!ŸrýïÒâàûÕÚÛÚà% ? ‹Ç<ö¡¯ÿ¼¡Õ0¹¯÷¡ßßü”ø­ö þ¨ô3û&óHúöïï¬ù?êCýÞ@v(â1]BX Ü&”ñ •öAî#kùR!y$Ò&åº*õù ïž÷5ûÊëM#ý`! ø""ëÑ ×é>Ûݯæ´ã é<î¡ÝÓíòÔéäªÐØÚ¨ÜêÔ’ïxÝG{î‰ sÑ SQà 6øUþŠõ2NÝ9F#>²F‹K5Þyï\ù¤Öâ7·ÿLñ8K)@£ Ø"·I—Îúü\ýÑÚø$8`ôU}Ë‘å~Ð¥ÃÍåÐÅðaâ2í§ëë“ìîó´ñãúÃø©ûïø9ø¿j ±æ%uú_ÔÙåµéåÏd+Eúè85%»0ñö+ Êöˆó^çûO-ì‡7*¿'v4Ï Õ"ÄïxÂá)ëÓáœÞèëAã¢tôñŠ ¯ùQ ¥ÖÃóšÇ©Ê²èuÅf ì]Ç eùƒ ¥ápöÝÍä é‘áüð~ ü ›÷Xîó%õ»èòU$o­8A,Ñ”2¸î¶ ‡íõò(eúQ)‚ É,á 2úV ÜÕjîÞÎrÅ~ôwÎV'ü#=.²ê˜<Öå âlÛ4ýÖçc‘ÿªg }œúÊ ðõòÊåu «í„ A"—"%¸úª2É"×!´”ÝÒî#ä ëÀë™â>îáM ì„™èWþZÜBáíÙUñB'l”%9îžÊâŒÞÌ•QéF+ ¥=crf C-o g Ô€þ²:LþA3Cê<çãÓÂàíâìWîíð (ùåêù`äïCæ°èËéøã,óÇåýøòƒö·Ó¶òbÀíÊáÔÄsûoä?ðµòté ï*ð¤êÞûÁñ·DøûÕÆ/ ÿ6t7FY9“îm ã÷Sé‡}7z*¼0•>. <04èì#Þðé{óëâÉ fúgáÔúTÔøoýþû:÷¹øˆéìŒëÓæ—òÎð[ëOø†Ó)êŽÊhÑÆÝ²Ð?øÔæÞGý-DùõýîöÄþM:ƧF~;H._B£ S'ðµ ÷íÐ ëŽ8m,»+m:qôûø~ü‰RøŽôÿ2ÿôú‡hû 2 txgàÒ¼ÂÞÓhν¿ñ›×öóHøÜßËî$ÖµÛ¬Ü Ô$ø}ߥû»‘tôXú¦õô ˆü` çîòzØõ´ ì²ñdõ¦ë* Cø£ Ûi ( ù6õ8öH4íëâ|ùðv Øc¨ Gì ý ÙÐÝ9åõÑŽ‘ë› Ä èÁ‚ÖÞ^ùñ× 1$Cu7Á4§=M!É1Z" C ¨ï‚ÏLþíà3&®ï ²é”ÿDÞ†á­åŽÛHNíHÛ%ÞïðµÉÕ+åÏÌSÜ)Ìäï@áiô¸öã§õ¼Ñ@ÝìÛ$Íùnßþ wü?5»"ÿÒþÍA."M„¯$Ô@!Z â$ óú¨qÿøAk Ã)„*Ì*-ËáË+ӡظö¶ÛNé1[ ÔÓ•í 'ß–ê”ÙÔLÞåÍ ñTÛP÷‡' šö¥þIô¬îçXòüº Þ&a"Ù&w*:!¹'6Ö-Nлt”n##Iö8 ÷Eòíýî5’'4—8iòw÷Ú±ìÿÛ/Ø„éÚŽïÛçqôxñÞóûˆê¦õëßÍäFÞ«Ò«öÕ×’ùøŽ1$y06¬²!‰ãóñfî¿Þ$µöP+ä!@â*b² ëõýh vØ Å ¸†EîUþÜæ)ésò:åéþg÷ãùò/â:ú¥Ñ‘ݽÒBÌÝΆæªÚdò#è,þùñ"ùé·ö­×%Úfô&Óv3ºùBG?c<<8î• 8æ¼æ@ÿÀì±´ë&,!$!™/(%'išÈf «]nÜ®Úéð_­Ü½êuÔ ÙCÜIØéæ[î'ñ»èèíŠáßá×ägØfýÁà¤eb%ÿô•Ô׿(ßÑMÅçf-žú2F9sÓ3ù÷Œµýlö° )<6²(œ/‹:Õb4]'&+R9ì¹Áåhæ³ÿ¨í7#¨íl%Ýæ #Ð\Óqٞǡí]Ö÷?êÎí+ð›à èeÙ‰Üîã¦Ö#ûõåE£þªž 2ì|ý¤ÙßáÎâWѨSç!Q"Ð(ˆ$Ò ø| ƒ “¤C*ñ.C$µ1:6) Ç-KÔ ú~½ÙÇnÙÜ¿ö¼èÁÒ :ÍésãÓ9ÜåìΞýYæ}åý6ïIÿ9àfì­Ü´Þ”ÞçÚâ™ßðóéLYþ•kˆWª ÷ sî È<(…5¤@¹Ö1VôŠ,æåóØðìd œü# W£+¡±%â+ÿ٬٫ôñÚxþï!µ#væ ø2á áRäÁߌç|ä­õxë— ’YáåΜ٘ìÕë_Òg÷°/Ý!á ä-BŽq‚Nô$#ô!ÿ'µ"?éšÞgèwê³á×ýïÁ 9Þ¢bõŠIäNí×äáÛ9õBàÉô âÚûÁ 6ÛiïZÁ˜ÊýÁȶNãÿ™âî¨/ )ü*G—ûu÷(uõÂ-î–01ªH,Üás*àƒßéõëåç gìTý ñS"ôSù•éSìÆö“é¹ ¶’ú³×BîPÉÊÎùÙÀÇoþÿá!pfV@ñóòÞ¯ã{ù5Úõ#ýg8f,ì*‡=«ÿH%ÌÝ?øâèUß1Âö`4„#½4/>d!L8$#§ÿFósüHðò6´üµ¡[&À@Øç¬ø‘å'åäìæèjõÓñ îØ÷\Õ­æ"ÑHÌÁè²Ëš§ì ó]ôý è´î³ó*ç®NôÀ(½ý(á*QË'›ôÄ IíNòÚúîˆNÿF&{h7¡,U:>9û$a4ÿûݧçíîCíÛãÂò³ ë £õÉ;Ñ~ëú±¿5Øs°û!êJëwªØ´Þ\îž×XyïðìöRÞså‡íÌå? dÿjÍ÷þßú´ó‘uq+š!l&î0ì¬!©Ok‚5°;Q?•8qÙLŽØˆÓÌÒâç,mëñV ]Ñ9ééʶÌFßËÓû×áÌ›þç¹Í(ö;è?í6åìâ–ïéæýéùImpq…ÿqxóøK Ìô’7¿¾TJš(\&âfQó¯ÝX$Õv&0+]óÝ óÈAíàØÖ·÷Úé‹ ×ùØ×›á§Ÿ‚#åÚð Ò§ÓÇÞÔÊÎÔã” Ç ñÐ":í$ͬÛEÞ¥ÅT ßà!1…–38› Ò-ÿç×þ+èÑâ7 jîÕÃ$YÐË K‘#öÚþEçQépîBåÒYøL œ áñr›äåêWßÿÅê>SÿŠÙøñĪɬì]ÁI&‰üšú(ìŒ ÙÙÔæmîÈßmú³(§T1Í,ï-•7\°0=ü+òAû¥ð&ïÍþ,û·ï1úþ‡Œëû¸æè?ùCê³ú†bƒ__ÝUúÏÁìË/Ø8¾è÷bã¡õ¨þƒ×âñNÎ Ô÷ò Õ¨¼ø)Ì"}(>!Ì Ë#Z »ˆtä ´&(n +žóÄüþVð 'ˆ ;‚2MS9=ä4 ÙÒãØœâÔÔWùÔè)ë × åïýÝcæ²Ó]ÌØÔ,›à‚Ì=ëcá‘ðíéòîùøìo ô€q6¾ N$ÿÉ$uá~ é ë'·g-I5±þÑ+ Ø'õ1êÜ(>þáCq6ð N=cûæÅâ ÷gÙ¯Þ?ÚÔ·ßDÕQïäXû>ø4ûTÿ®æüñ,ÑeÕ\ÕFÈõÒ×ÄF&c(}¤ã¡òCç4ÜkÝë(¶ «T—Ñ„ñ Á÷Ä+\1Ÿ2×3¼úøíŸþOö_ôŸhûù «Ì Cõ` `öÛ¸èóðœàößùáØ›úÿãRÍ«½SàpÀzÊNÝ\»ÏÂéý'¿û´%"õ”$å_îê·áaû1ë> àJ —ü =¡ ¸ £ * bb  l õxÍŽ÷¦×èÈñ°ä1áŽñâ)Ùò< ríŸô­îHÞ× ñèÖ4/[(4eìBÍ7ßÔ Í„÷ðÜ ^–%¸)eÊ#kõÑèú3òß »ý – ˆ ñ‚è‰"Iœ ßÖc÷ÍÐÿÏ"ô÷ÜñJþè éüšá_ݹî½Ù· ÉìP˜D÷Q 6Ú^ñrÕÈÙoðÐÚmøhn~Í…ïPæ€ë¤ûµêD˜/ &/q6›"Ä0j"ùúÆ 3îƒöKþÑðø&¬49ð r3ÉÎü?»ÃJáàÅ…—õi|Ém›ãú-۞ׇëÓºùMã×ñ‘ð~åAéwé!ä†ò êÓù ð€ûÃõ* Áü#:~/%¿ÏÎZ-üÊI3!CHâ";èü!rèÎøT÷êî ¥,a$A*w4»”'°ñõéFæü{åÛ¢ÔOÉýô ßì_ø)â1äƒ×&Ö£ÕpÌï·ÕC ®÷Ïèî1ÜgÜC÷/Ú…C¬'\'øQ)æüD `ÿïþ¿)>7t!ô3ž9y¿,ò; 'ðÜôšþÿöÞ ²(m€pwƒò‰2à¼éÜãÛ¹ô×åõXøCü#bàó9½ÈÑ_± ³¨Î~µV¿á»ûå v9òüzàÕäZô¨Û2}þ×v"÷™™ÝîôÀñÚè÷‹Ú‚ÿnç Î äSA  yþ Ðùö.@úœñ <ôš ù×ê‡Í‡ÓääÑòCð±,™’Z%âîuÌÙÙMî¤ÑF )íšm ŒŠ‘‘ñ¬ýæò%ï¶Ûö' ­%®#Ÿ"m+š!qÌŽ;¸ k 5-a#Ø Lïߟì·êÎÿ^ì>ë4Gxõ (ÕaäCâ.Îñæ$*jZè ñûÇêó†ñƒûžïþ­÷>ý"ö4 €þ,© °ìbܺæôæÚ6o.})Ë'¹9¬ÿw#µàþöLà×ÜqüJê%» ×ë¨ <Ó%îÈ€Õ^ÕÎË9ó2Üè!÷Š÷¤ýÌåîéIÞkûæþô)øtñy÷ªðöýæ÷.û¼èuølînè 1÷Ö2Õ:ß>»‹9iü)'äÍò/êëî9Ž “-ÆE©ý1)Ûyõ%å¼Ýuú’íšúûÁôýøÚùWøÏ½íÊýWÚ˜ÚáJÈÍþÛ !óû4ÿ^õð¥üþé°âö¥^‘ØÅý^úÿþÖ zýµ3VTN<2F¬Ovå<ˆý!§¬‹·õI…ºþs}úE 7ôwþÕí ðOøâéõýѸàÊ‹Þüó\ÈèËÔe¾›ðPÕÊ _ù«öU¯ÊþëâÊÕÂ1ígÏ\ œï4 ù ƒš=Ób Ôlá ¨QŸ"K±ÃñO ¡ñ›ùš¡ù³bê–ååîQôÝ(ì‡ÝÙë¯Ú² ëí¯ Jì „»ÙÓ¯³|«Ü²øä{þ£ñ^ýùîÎô­ø˜òä"ø!ú< Á¼À9ˆ» –ø ž Ï ï rШ )! ü¶ãñŒÿ#½ñšjå!ÖnÒü€ðlðÌñ*æaýËëù‚<ÿñóbùeæšèÚèôÛØúfàÇúá"鉋ôöý&ñBòŽûõçƒ Îª#î·-¯#¹õVÿ¤Íú4>,%ÿ3+Af$-¯ÜÇ÷èê7ã&ÿ-óÑ¥ÿö§ýUâRî€ÕžÝ¦Õ±Ð=äÐô;á­t$óˆIÚâç=ÜTÕjõüÞü«^4×%7|÷ˆD(ü'Á>E9B-,Nú0{â!ýïì­qùìýmðçvùÚéÐÓ6ÞÑÐÔœÊqÇο]ØË_å£Û³ôgì8û]ó£óðä¨ô‹)ŠJ3](ë &)Rä:þÏà‹Üè é‘(è#0- ó)‚&æ!}&L%¦&™(”''+ .ë$3 ¹,—çþú×ãÝ!ÙbèøßÄòèï%öÆú$ðÜ÷ç¾èjßh×÷è~ϘÉä¿Ñ ž Nã‰ú•â(Ó áÝE ž›±gøñˆÉ›-"m/x.™)f2¶¬, "´$  ¤ïé'«''‡.€í¦ Ì’âè×ɽôyÞ>:üð ÿ®Ö§æ‘ÐDʶãYÆ›©ß šleLèuü¢×…ãµÖŒÕ“ãœÕ‰óWãýdø¥Ú à í² M„g ×°$Å&å-ÿF0Ãë¤yÚtêmìWàOÂ*OçA£Ö€âÂߨÔNôÐÞݽó×þìþtõvúõâ ìêÕØ%Ö°ÍíÅÔ ôQ‹áÉÉí÷Öú×5òk"«€2K/ïÙ9hüÝúöû ‹ø"¤í&O+‡-÷ªdïãCîñÛú:ò/3þS ¡øk¬èaðüêÄàû ã ¬ó­F ± Êû¡«ìûî£çšàüßv$_\.L*§ ( Ýqû¿è'ο'Öòû;ß3Î%=# -úÕ}ù~ùÐýÌýnd‘¤ ƒí1ØëØRÔÃèhÓ¹çúy÷3 ÀÜfø9Ð/ÙæãNϬÿæ7 ?¨'ìè÷7ägÜä€Ül,”s=ö2+qAŠ´(ùðD ù€ö]Ä:j/ª-ðDÐ 1ë2 £àFêóàÔÞãè àNÿÎð÷{ 8éªÔXüb¾¤ÆÜ7ºÐQáÁi ÅÚ¡þjÊüÓÞÚvÇúÐ×j˜ù´!ØBÓ}0²6Ìo ÿ,x1Æ+î#G1!  $Öô=ñò,ùë …úÏ(•.5(4)W8eù’àúíéÞqÔàñ×Àô‡ýŸ —à±ý­ÊŒÜ8É—ÄâÆ…ËãŽÅô6ñâ˜äaï,ÖªDæMªþ–S‰å þT¶­ûzÿYýË3“Ž;h=¥·8¶êK ›îé3_÷Š&t@’.$«çb\Þ²åUè<Þ‰ûŒíbgåù† §Öéó„À»Ì.Ϲ¼#õëÐMüUrdëkí’ôâ_çQíÚÜáì¡› SŸ#4ù¶¿Ýí÷êîeânÖüÌ1ß'ƒ+=m®,ë%]ë¡êÈðÄX ÷ëñEÜ/ëÜàÞ)é—ã!ø«é°Àüb y ÞñùÎOÊ®òŠÈsµó™Jbü]^Ùd÷·Ï#Ó¯ëïÌY*óë63(Ä$7Jo%íÿAï†êå €ô‹¸+ZëhBæpïj÷¢òB.Þ ¿lÀ `Úëjæø¼æ¨ØÏrä×<Ç.s8ƒýÇÝí5ß\Ñ3˜Û%‘ $f&¡X²ëþ¤ì^æNí –ç"_š'KGÛê]¡äfðÂïníÆüø-— 5øÞå—¨ÚìéMÞ\°ñ{/Ë#Â2"ù)¯Ø é§â’ÐQÙäL™žèîì :Ýç2ò“ßoÏùO(ôi"‘#‘0ê ı÷¿¬è&ñäõÄè#XØ'Ðù#äÒû½Ï‘Ö-ë:ÙÊõÈF„÷5ÏÐ÷ŠÁõʼÍW½ëÌÍ¡¿ïFÒÝègùÕïÚeâìÌ6)âR€þ °ž0åþÚüÙýÔyh)î#8ù'\/“59 o(;êiíKæ1%ó˜)§*S9½þÕ,ZÑ ÷ðÌùÍ5þd×å% Hå$&ù8á ïëÙÆÙ{ÛÓ©æùÔãøÇåx KýU „ëÒü­îå; ËêN)›.²½.#Å£böô$S÷”Ç ì,Ü,^ ^8þô(NéÄøŽñ, VEž=ò%þFÅðÞ—ëµêåÜû~òžrO m jú"Ú¤ï!ТÍðë€Èõì¼ WùIãÙýÒÔÑ×¾÷Œ×H#/±&'*öúˆ"Òò6Ó^ÔñéVÛ~ó™ ž Ë‹ò9oërøÛô÷ô\†Ù!†aH¨û¯Ý­òƒÕÕ× ë2ÔrMïÏ)ôH*„ùä'èµëQûÍáh ø9< g&/3é‡ ÕÅ2ÓíÓP¿%ùvÙx ’þG n êJø$8î®ûÙëúìJþëøjÿ1ŽÏø2ÜÜ–øÝ!ã—õ¨çµš‰/ß)…1ë ´ähâê .çƒ.L f)7$M5Ïòâ©ì¹èöÉì]+Nµ9o1$-Õ8 Ë&€ìÄYæ©æÙøçâú¤–€Ý#eýÛ+ëÒèóºûwò Æ Ps&iü"ÖÀ4òº»îQÅEQöv ^ Qó'‰çCíËñÆåüzðÍ*u–Ä læöÎÔôÛ;ßÎÒÕ~ä²%v *$)½S,šöJvçíõHþtí“ ±*&*ô6,á® *ÈYÞýÖËâÿSâ¦Ð ¡c$Óøhˆåýò»âüß=ðHÝÿÝèV öüoúSíòŠäYäEëÛ´.çâÖ'  ܪé½ýpæ¾ß€0âw@‡G>TG®þe2»ãþýëñÃï|»N4) 0ÿ>&ý44ô Pæ‘ðÎñ¸åîoö\(;å)c*û¤$žø¹ òö¦ú«í"§ôùe QÝïká×ÎæŸÊhÉáô4Ƹ01`){5ü‹!2ÚþòÂìÒÜíþù7”ü!Wð‰Üœóßøäwø*ïhü `"Â"³¹+%Tüîðÿ¨êyë¥ûÓçuCÿýtaÿT±Ò•÷ýÇ¤È êîÇ+‰îK~ E`ä šÊêÓ‹Ô¿SðbÎÑ Cî ˆ <ö¾ ÎÝGòڵܸð!á‡ÖúUç®ø÷áò~Šñ)úø2óˆ Þù­'îý2`/Ñ î6ÔúYàlÊl(ê}9µ‰$á5Âõ" à"í<äAÙÿþÝ8ü(ºd!Š$¦ JôlÿÎë³çõÞá¯ûñ¬ x**rî Ùáxñðÿæ@gú^#&10àø/׎ñ®Û~ÒžgÜnà Çx!‘ôžêßÁðkéûÛUï˜1.1ø5%¸èÙöñì'áV÷zäÑ"óMlæ!È!s-+æã” cʚݎñ¸Ì;6Ue9I :^Æžùuµ™ÀÙ•·.+åP'yƒÅ/÷á- TÌßÊÚFÏæùßvÑþ˜9„é+ÂØ™ÜäZÑ[¹àìuM( %05ÍèÄúäÖÜx Mâž/@ô'Õ8NïÖ#-άè¯íÕÓ?|õ®9Z!ß6ã@ho8Yöúëjñ÷ çèÿWöȹ+Í Uø‘éÐó€í-áj ˜ë³#¿ä6$úú,Çá]êÙß‹ÎóÂÓÅÙöÀïôWCç*õŸñ_èWT÷­)*w.A1ýë- úz—ìpì¸üÁç´yw)´¹,'c ëÌ#æ´éØõñçœýþ>%@!=ù(<ä`ÜÏÅÒQáÍÃ@šä¬,€³W1Õá¢ý» Òaѹ¸]÷«Ùšÿ,ýíiþÝ|éÓàÜzï§áY×óú &…"}Þ&Îî3 ÚFçÖéNÚãó7(" /Ÿ á#ñ]NþÔèv."ÁG6±< H—¹2ŸØ‘÷-ÏÄTùË1-éã ã1=æ™\Ð!Ûêâ¿Ó·ü²ç«~údþý™"4eèã¸üÓÌÙ`ã¾Ëþî­6 %cî¯Nïçð Ìóc1s6ë7Å I/•á›úáÙÖ·²ßp6( Í%-3‘#Wõ ¼á3éŠóNÚÀÓ÷Ì'ÑÜ< )ÖƒówÑØÎdé ÒPê/CÕ¨¨ ÜîÝsÏËè\Ô1ÊNÒàpð¿,À®áfÔŒ»-ä·/ô#ïs-Xú]‚ëÃ÷vçÃê áÍãÐñ_â úØíê Mêí˜ÄëÎäÚ€æ0ZyâæßÛ-Ý,þ {? 6šìˆ5Þñß*ê¢ØAÿ0ìü Æì Œ©ðFTÛÕê5ßîÛpôããøÃ Ô ,ÅP÷Ç €ìôø>îí ò¿/ˆÛ-S:[7,óÖˆ÷+ãYÓÇí¥7 ¬;?ÊÁ7"ç¿3ìîÝP =ê[(ù ËàõÿhèîðAÜJâÞÞÒüùàÜ÷® š,áà¦Ë9ÔcäØÈ†‘æ`ôÏæ ÞõÿEÊSÛ¹Ü Ï•úéæ»(ïóTàòUélßµð]*ýAø&ý.Pï ùüñyñö¯ñ"çó‰0Ù$¼4YÚaÉÅÊêÆ¥ï57ýf«Ürù¼ÌšØ÷ãªÌ… 'îîžÕüÓãüÂÜLå,èâêý°ïœ‚Ö¶¤<üÔKürüû"ú\O Ñ#Ëö5"Ÿó^ ÁÜNê2å$Ö—4èU6hy+~8 K&$æÒýmè¦Ü4 ­èÉ,{'¦0$÷h#ÎÔnòÞsÝtõëêë·ýj _“¶#Dê «ð Þòæ’ós| êáÄâôâÜ5¶òô#î§òãéߤô†ÜÏ pòp<Ö ÀqçoÓfæ¦Ý4Ø@ø æb ÿQ5MÕÖûZNèò–ÿÆçË%é é5I2Qê8!í‹3ê èý8ç¹ÆüZ¾SÚlõnÑÃÏ%ô¢Ò ö`Ot÷þšð ômípï‚èHé´ê¶ãÄ÷0ì2bŽüm{àwþêÛá‹ö÷⨳ý29†&34‚@#Q1÷àyÿªí™!Å5J)^)Å<Ù!+˜Û$þOè±Úó`ðB5ß×#o*ãl^Ìw3'ôýøVÜ´ãvä#ÔƒN÷õþ}ÔØþ5Ãç×ÃÔsÄŽü°Þ£„ ‹˜ðƒ ûà\å¥úxÜpEþd'\&Àª(ËåÉ;è~èÝeêv"”Ì3`#%]/è äÐúÔCûéó±àñ0yùx¡ jV ãç;×ÏKØ.مƘëÙ þyöf÷š MÕÞùdÂÁÐÒ·¾n ¢á¬®ŸëÅÛê¼çOàI;ïHÝûKûÕÔû*úÉ.ûÑû„û@4‚?4(Á7§eôcú­øçèvgôº".£K.jüŒ åBûcÞQÞYü½Ý‹!{^$áû!á¹ôê2à§ÿÅìíÿ%ýúûí¿K QýÍ ïñ ùÆõiìç Ê÷†ç<°N ³óÕúxý8ìø Án1¡+ÜÔ3+áÏ ›ÕâÐØBá0&ùþéÓéüýæÊètñÕäÂJðâèdñ !1üä ‚î{óìâøSãé EøÖºÿ×ò öÇüÉê5òü,æ .“ÌöþÄŒ½­í*Íñ õ¶üWÝú Ú áhçFà…úZè@ðúr W b žõMŠëƒóeç2çNóMä ­÷6$•'7/-×òô:î¼3 NDú9d+ÒB§}"aówéùTñ^Êüºkd ÁñøuÈñ¼þEógóJò‡îâí‡é¬ìcéŽî³î^íÒóšåí®ä\áÞìÝá3ý}î¡ ~–äëÿ¢Î³ÚÝð Ì2,/x-ô6å*Zê’þ ìq.k5>º,N9Ó;¼!63 Lôý»ëúùýÒ÷Íü,ûÅÙà ´ÿ¡Þè¸û@ÞÝOðDײó úšßÞöAÀÍÕh¸W¶!Òë¸UôîÞëúpUÛàù\Ï8Ø¿ñªÕ×cöÒíËÿkûØ ô-ùjýUòÅúþ4$º£$Ñ)Ä!Ç+¯«(yP1„éç÷šø¶,”$ðm/Àâ# ÂÚžÝ(òÛªö·m àï.¥àªò&ÛàŸâ-ÙLí³ãœðMï‹ð¯ôŸòžõÒùŒ÷¿Âüy m«3 Ì´¿“Ÿç€ù'÷ÀãŽ%’»"- Ù ÿäŽúì߯çðW4!Ä’+Âñ] Ú÷ì¬Ú¢Ö6îÜCûÏðˆý²[õØdúoøßüüvù†ñ¢ë÷bç Aû‘üùÆáxñøïÃߢ±ùPr”ÄìÅådç·ü¦ßZ¬úlwÆ\°á'øCÜ(Þ€í“Þÿ«ðÓ>èüi xëxýuåpëŠëŒáƒöqâüýÄëÈ Qü²‹È% ûò®ãþêáàS.0u6æ9Ñ 8¾è`Kò5ê t÷ÖŸqÎ%ˆ"Íø'ÿuømý] öèë²ónçÂîMãëlÚŽá>áÚÖ/ü5æ^ 7ª/Qëaþ±ãQâýõ_Ûx¹òÎ4åV#ª:ðísáOð?úµìº#l :Í1š1xBs‹5 l˜}#¾[* ^Nþ¬ö šIgýÞ~ø¸Õ,× ñuÝ ¡óõ 8Í®ê>È•ÂQåÂá¹à(ß™êcÒÎ9åßÏÚËDù¸Õ{Ñ”e"m,Oõ-/ø ö™ ãù· [ Ì3!U94,8·m(Zd$þÚ ÏýÛˆ4 :þ»ûÚÂõúÐKІíÒÆåóßL vôç-ÕsëCÒF̹íÑIêîëð*þxЀåXÑ™ÎÄë•Ù‚õA› ×c G#)üQ)ö n¬ÿ¶Ü™þŸ/Hš,2JG,4ô¡ðë%õîßòT%-Ï/¦.C 4t÷áƒè_òèèôáÙô(ä?ýþó•õÜû‚éèõäOìkî_êrïðÔå¾ëšî—éÖãøZ @ ×ö.é ë’[çñz‰#ß'²à#añþ€ù,õ3šýs0;ˆ#„2ôÂËÕÎéXçºÑfë{¥ 1r Ù&ü;šæbñ°å Þ`ô®äœVøŸ¦ Ùó,ä[îŒãoÛWóß›*z *.z1÷Œ)´÷§´ûÀñCuúA„gÿ è$ÚOåŒõwàÓåÿ 9eæBòàù)ûÜóß÷ºùÞêaì¹äñßüó.âÜlú±ñdUÝ›ðÕ ÝˆëâÖ÷¹ôk2Ô]( 9nóuâñç³ çn)\ç)-âA**ñg:  0 C D H Ñ á ²òsþ Äömðžôßò*î–üôQþ‰ î´,ÖìnÑûÒJâDѸ÷Oæd6þ¹ìô5ÎiäÖ3ÉVXÚF*‡ a Ý*b =úƒÿà æý.t )¶å''¸"·$ö)4$æ2ŸQ\ø …-°-(ì.?Ì$ÑåOÿcÛšÛôÛœÅù‘ðs`ÚIñ˜Í)ÙØý˶ôAÚÞ^õTð½úlÒ¿ã\Ó­Ë'ðº×.Öô"Èÿ ûÓÿ›l5 Ž €½}£v:ÿ9>ö9ß$37àÝ5ìùo¤æw/Š ,3Ç6o„4ë쳂éòì&ùŠìüÖøšôtûuæcõÞXçèßÓßèáUíËèEç§ìå“éPë^î ð+öíCöëûë|øæêøÙþ?¡úaÓóbú60ôÈ(~ ¶=.-–2?>3(žà`õïtߺ@ö>èÌ9ºøÛWü~‘VÿÁþªû?÷b±ø¡ ª °ýÈææóñ‘âÄÜ`ö„á\å—g'¥édvéXæÄ Vìµ*p h$$W;|õšþÞòXïœðÅû¦ýs4ìŸþ$ô»óÙ4û8Íý,üü² ¼e'rü¸îwõ}ñôçÃíÒí &&j d(òá”ß× ÖþÓ¥ðÿ“-©þÓ kþ}þŽ~V·ÐÃhÉó%5ðMö¹õÌõ©òºõ3çCï5àâãëßèÈôhýõë2ßžïíâÛ½ žðÛ 4ì| Ë&çÂÂñÅ&ÛˆÃâþ¯áYöämVòZöœó'ú¯$=c&ʉà ÷¹=„F ”^ÉI”î´ §q@ç&‘RïCýþõ@ç ÷Ð ê¶ç° ÉËÑâÌÙÍYóÜUúõÜÿe qâµÎzâ,×ÏüDÚ3­þaˆ NšŸ KîC è: –) ·;Á.¨7:B[X559Ñý[ôºYö;3®o1t8I›'Sèa÷Êðòà }ñA  ÐðQ*×$ð‹Ê,Ùî×ëÍè#Üóä)ç£ÕeàEÒ`ÕQëVÜýÆóhö³øríéç•6æ(n * õ"§á‚Û†Û„¤ã`?#É5ÃEsË8°ž®úúý Vûø H.¿L€î9þùêðôð;îš÷ò‚ùÞöyø)û"ùÿøÿCñ^öwÞÜãïÔÍ8éÁÑž¦ó:hãåíØ=Ùd4ßr{½Hâø¼Êë9ùóûðGªýÌ'öh…)æ÷³œùOûRËÞ#Y}¢2ÿÑj ÷ Ý!|’ ûáo÷èãÖj Žìu%y~1-ÌêÏïÍï†ÜÏ ˆë ÙË ] ê‡Êã×ä„ôp㌠÷õ¿ ® Ñq5¬„Yb* VøúüiéìóCëü‰ÿwçW[Ó_á)ãÓxKñ2+\,ƒÿé)ߪóRÞ­ÙVøÜKÞûCvš÷‰»ÚpóÇãîØÇÜê ncÇ~ª«ÝöN.Ižÿϯ ŒAÁUötjâšëìçÃÙ™Eða¸ ÙècIÑŸéoØÃÓ€òýß}ÿ¨ùøú•áüÏúâ®ß›ÒGü¤ážÛýñíäGî×÷ï×+Ñ « ÏS 1:¾Hg:„<•Jl3–ÿÊ1(ñƒ "²”d,êøD,ì6øÃÿnñÈÿ $–ÊÐ÷ÒNùõÕý¶ò‡öOñsî®ê®êBíéò¢ëòôrøâôP ÿýì üYõ«ÎÚ‰éóðTØ•%Óý·2í&ݼ{ööû¥ñyJÌ$Õ¦Åÿ¬‰ñœö=ì”ðÐüêôä<¡$\#“4XÜŽÃ2ËÊàôÉã¶øˆ ˆ%,8pæ)õUÐiØpÙ³ÑùöAê¢VÉ|#¯úœeÅÞâO«÷²ÞÏ$²ß³ã#0¦è,¾.’²©Ý$ðJÔ½×ø§å1 jCþbÅð±ûžî-ðÙö‘ô{ÿøý“ß ÿ/"@M5ô"T8Ëô·mì1î¢fúÅ Â?ä`wÉ+ÖàØhÉÑåá&à0ì6àsÞÒâÆ,} %&ßÛg é¼MÉ%佯*4û Wù ÷ñ«ùóð ÈûÅ-©U^; gïXI‰\–6íïZZøTõÎv Z*U$% ì6‚Ñ“ÙÑïV×S¸ûÐu `Èmëš¿ª°&øüÏœå u òeýêUýÖ!KòV¥Æ•à"Þ]ɉö¢½\àƒüÇßãðµÄ1Ðw;N?Û2-;8'ÀürªôÒü®òü¾ö½ÿÓîPügè{ïžÞÎãÝë×¶èOÞ¶üvîT§óöbÞ+å¿ýãá$W# .ZÓ{°™¾‚Øx°þžâm÷Äû8ýöú¤ ¥{ñ=ðhtÖ²ã•òàáÂ%ü‰=ãKËÔÖ‘úHÝ? S q˜$ù€ &ˆýã ”bjîõÁó¼èë'þq E£!à!L îòÝü”*ý; ÁðùÇ•ëë5ú7ä<ÿ" Ì"Ã(«ý"¬Xû W¥õ$°øsöjb ®6"Aöa ˖߉ÛÃ{ ¿æv"¡!iŸ!!ë a4ÝèáÎ%áàùFã›$É) Ê!ÝÜ@ï…ðXÊ6rüJEU>| 3›ð„I”íO{ ²‰þ» û„íÇøAøé|ê7ðLàðñê= ÒýóÀ{áÄí3Üß…èçñÓúGì‹IÙ(ò3ÉDÕÃÅwÁþÙîÂnøŠà ÔÇ ªû×únõŒïø¿0 ”$9' ë"§ñÉOû!òe$ÒÂ)L,ë ›!(} ?¦ ~hEöCô¹ú·)#–§(­¾Îê ¶°¦äÕÀÈ÷÷Ì a¶ì< ±æçð—æçfðÂàZþ¨éÚ (úæê?§ÔyJþs„øüŠ@·Oø&¡ÙUYq8QTOq8B'Kyt6Ó°ùAùÿúòbšýzù ³q!û´ßÒ½îÃÅ]äó5ÆÀ :üÿÏ<ßWbÉ Ò.áýÊ ù:áæeô! ús©e$‘ã^%Í|Ô<ç’Ê¡ŽöHå"ý lêëú€õIë´HûÉ(0ò2¬ê¬+ÔñçÏÒÀÑ4ïHÙ› †û] ,ìw ¼ÙçHæŸÙ3âäà * F;%ãþ^ÐÜØDÓáÉìÊÖCû Å…åÅýCÜòÙø‰Úy~ý:+ã'oùú%¸Èhï¡ËÁË&ïAØ’ø%ü¿—ÿMå†c vüôÇ´òõ,þ !3£1û Q+ˆÛã÷Ôë"×Ä øs:‹(ê(Ù60 ¡ ¯ÿSŸýÂûR Œ÷Ê@ž4à l/¾8ì"'¢ØÊòÚãgÓgEíS$…÷ œ ºéøÓÙþêÜuÜMÚ?Ó¸æ‰ÓâøßéÞõBùŽqæ¹ï‰ïÞy <ìz&d 9%a)È ½jì‚ì%çôÃñ ,J†$ŽïA çé‹ìézðGƒª#9r!Z#é)Sû@ \ÏýôÂVÊcÖ#ÆBñùâ¸ê·øÕwë¾Ñ]×äSÖTù—ç—"û"LWJ­üAÚîô×ÖÒÆþÑÝá—¶×:ö3ÞØ Ùí^)çÜý"N҇ϯ• F¸ÓÀσ–' 'Ýò-®èîðý²ìöØì0¨$o(h4[y ¼ì:ù ýFíĨþÈ'U0XŠíŽŠäÖë èOá°æû—77jübý Àü ¿ ö C< $RU÷:€èjð‘ñôâG%Jý.;/$üö 7â„í_Sáý: „4½9—u#¼ß²øcä3ä‘è¤ë­à7àð§ÚÐ ”ø`á`îÖÍÖÜRàÆÍÌãòŒ ýü)áïðýNîö0ì‚ïÝäüÞæ×¹ùéûj¤ßAûÆ ÙãÞ¼Éá Hð1^ú(â™ùÀïºë‚ Dýpµ ÂÑÌûûæiùRÜä坿ÒÜlõ²ä´ ôQ7‚ÊŸç“ü¸ìQÛYìò*-¸9P'ñÜŸ¦ÈˆÚeÓÀÐÕãÛ…ñ«é\>ù – cîjÿüÞÆü´![á&J${!hæÞkîéê~ÿùg É÷ürc¸$+˜!I S€ûØ6ð4Kž8$!:?2ã¿ ‘×Ù€õöÚT¸üÈ]  ¬í‚ Ü/èyæËÙlõéâÎô¡úÿ  ïò¨Þç"ô~èdçùòÐè Ëø|[ uëk:ãrçñÙà ðÌ=E?¸;ôôàmóŸñ‚òF Åy¢û…*ÊêòÈÁóÅìÌe÷%HëþפôÖôFûÒíõGÜãçƒîFÛ•sž §%©Ð¡^¥lÄ+ȸ©Ç Ü剿9þ  øöüÿXøÊ ;ûñÀ : Ãúû¯zíWÿ*òuïd =ö($:)á&’š'ÜI)ûˆÎy÷¦“ÿ( ÔO*.¾ªë”ŽçÉ錇íÕ+²·8š094N÷*8åïçðŸÜâ ô‡4)#Š5*Ãÿ¿éø,ünþHöû°ô8ö:ñìýšöt Ûfý4àýïçWÕã <ê(åÉ%ˆéëäåãêÇõw콯ú™ ) u6ûù ìîþ èjë{Åæ0( e:7ðá5Híì zãÂås÷òäob÷—xÇøÏ>ä¤óæÏHÔæÔ‚ÃÍÙÅ"ý AO Bë€ÅÏNã®Ö÷Ï®ò7à„¹ýEýؘÕ[ÿâÇHÖ‘â´ÑI7î* >çà†3 Â:Jã»ì  4E œ@ ?Õ%JBSôNìÝ6é¤ã- 0¬(pñ&Ô6×ö2…öðüðEh+7"$â6FÜ· ÉÿÓÏãaÏrîüßLÞOöøÝìã€ò0ë ­ùínÿ" 5%U3 Q)úºïäÇí’ÿ’ç²2#j „ L"©ôÚ ÌñQóbï4*ëk;8'+Ì3Äì ñèdüOÞ}Þï^ÙѯñÍB ù! nãðújÖ—á:ÖÒ–à2ÒùògäÏÿÇÛSíøÅÛèßÙÚúúÚæµB¸ž$÷«PÕíIçœÏÑ.ðä*© B°"²ÜGý/ávßü#ë´ ­µ ` ¬€ðÌ Õ³å“ÓñÊy±ÝEìI :ðÈïæªðö˜êûú *„O4Ø0\'8Qý“#VÓsíÙÚpÉ2 õàÝ.og0ÃñÃâõô. ûS· "ªtÎŒPô’£áxì«â—ÛÌü`èÅ ¨Àå ¤îÉßäEç÷³áR«÷£4C!Î3mð6ÏÕ儿Ö  ï'7¼(Ì.™A-žÿ:´ô¢ø˜fë…ü±0GÖ( 0– ãó¶ãÜŒñîä½ü½ò¦ÿ—ÿÅûŸ%íÀüzÙdçÎÎïå5Ê +ïšùWAÏ»òg¹£Å7ç ÀÊûø5¤ë¤çPBà ëõêãâýáêQÙ#$m£¬*üÈRíFïšþ—ê9¹ö1¶#a&ß2#»ßÀýíÒ½Øâ†Òºù*çÅ%ûÛ •Úçø2ШÕâíÝÓ5 óp¸ùülßâüNÖ°åÝÌÛüòå+ÿTú9V’þ*ý&RK#3ä6£#[:„5£*€8ñ"HákôøöšáC)©éG5".?`ðNçèïÙÞ’ÿ3æ½ Pø¢ »Náì˜ ÒåxÕíÊëó½Ùf:þnX Óì,êeð“ó íþÿžó™ ÈþÝ÷ ¬]´=p÷ïûÞï5« @:<ÆÙ0vÛpøÔ–Õ»ôu×%iù@*| ¾ƒî ÞäÑæìBݽü(å0 üÊ6|çs—Å‚åäºÅjÝSÄ ´ñ«K÷t4ã¿ñZòñàúÈõf&P’!}'' f$ÖòÊ·çZôýò.ëû zùš!Ž1 Ë!Mç.ÀäÞ é~22*$5-8Fø^ï üççïíHå¡ €ò (]Q/*“ÚM ç»DÇ'ê¯À¼&xûšDÀ/ä6¹Iªn5ö† ÿšô3¶ÿF)lò¥#^l2î±ûZç'èríFã*ÿªíO›‡¢ãgH÷ðþAýÈïòp÷ä, ÄPÎïŽ ØÉÔä]ÃìÂäÌÛÁñ÷&<í¶(#ùÞäÔì¯Áß­5ƒ rAâ;"æ=ùŸ,ôùËùúõ9ùõé‚ù™$J©0G.; ˆ)9ÚôßÙÌÑXÞ&9xŠ)"è/ çÉ­ç%Ì…ÏÝæÕ¸ûNðÕ÷Ïÿ½â›÷rÑÎàÍÒdÑŽìÚã žùc8I5í ôÑó£ ¹ú+B= .¡0Ê:Uý)w÷ ðîHÃìØ1æñBF<´+‘BP#jåfø%ï·àÙ2ïÜ!3}•ïÒ(ú5Ä7ÒæØ³Î±ï‘ãínó&îòð+üûú^þ0|ôÚýâíæìs´íH? !ÜÏã?áÝ¡à9„æð/éÞC@1K8>êp*œ¹ Ó÷á¿ÿ³,ûY)@,ÌT#ßß3üÂÒaØËéÒè Üîgúï ö {å ðÝÜ?ÞÒÞ€Ølïå·ü7þ„ø( ”àžþ‡Ôâ×àŠÚ#þ‡çß NEÏð fàëT_åT0¢y*{4Hÿú Mã|÷—ð‹æ‚þxøÑý½úû.ù3m˜#OìÖ KÎHß(â@Ìö [êÕ#¤©È%øëCâøFÛ„Þâ.׊úœåÇd »=ä [È}Þçè#δ!ˆø§1'j×(J÷Ž|Íú™×; 4È f`þ§eè‚ûŽÞÇáÂî>ÜoÈõ˜#þ>P$óK×åÀäÐ êg “‘ à"Xß;鯕Ù2гğñüÑaŽöP+K–"j*Œ \eûùª8õ"49'P%‚3kg .ô".óìóÛõæð+ùó2ýáøÎ |Ëùq åÛôÎàEÜ.òêf!†Oûñ ÀïéK¶EºˆÚ¹¾†ú¡ä0úßúÁô¥ûò}ûbò÷öï$ðsüŠë‚#ºe:-¾,l;)9"Zð²ú¾ûðÒBý*&¿#b,¢ Ù(éöûñ ö\ÿï2ª@!íÆ  éè‘5Özæ”Þ܈è çÝç§íÝïì8Ñiâ@Óö×XÞ–ÖØñ§Ý®Ãñ  {³ ›íþh5újûiü­÷ÏF@$/ô4{Gša(¦Ýªï‡•Ú= ¢EÉ:)&x:Z Å[«BþoüCú6 •v˜ìq jÐó߇ÑxÈKçëÐÄïöä)êë¬êbìîô'òuùâúaù"õ?ì÷4LÂ/òÇÕ£ßÓ÷—Ö]+µÀ6)5ƒ’.>ó¥@û÷ôK:þ‰4*6r0+#30é$ ýîè)â2ë^âþÞðjæ?°ø8 × °ór{ΦêdÉÁÄÝëÓÇT¤ï‡ 0ñó‚(áËòSÞÍãRêëâ_þÑñKU´ +øKÌïúóc´îÀ+( r1+Rc)Êîf dòòí ûÿ|5 Ùàó â!pö’ ÕFç¡ËÅÕþNÑ([˜(cêC –ÔSååsÙ\âëXŠÇ¨“ Y3÷š‰ìTðaö¬äü $ó„› ó![#=Ò#oW¡‡ Y²#ÅÖ«ð•$ëwü¢äÏèvï@äÃÐóy îiíäIüdܞܹó^Üyüöª,±’,±âJ lɦÙàˆÐ)Áëwf ê3»¨” pÌ.¦Êÿû<‘‹G¹F¶a@FÞ¼êºåŽöÝŸ¢÷ÑæèîöOæóîÃæ­èÊéMä+õ½äUÿiøÀðó[ÑëÄ¿ÍMÚåÆÒýðé§ÈðHþîç+îèð?êü€ò3úøÅ´õ3M`6J>¢0ËóS/üó¦#7¤91 *?=ö~*Œâháæâ“ùƒçÝ ?þ!U #ø‡IûŠûzý½ûÿôö×é¢êíÐæØó0óOçw÷ÑåuÇIΫáÅÎþØë`ôþ½‚úý`ë÷Ù à%ãÉǃtuïß­ÙíÝ'ÚÉå(Ô”×7Þ_ÚÁêkçí¿ñ$ç_ì‹áÛà'çäØPˆãÚ?5ãïS LÎ2ßãßgÊTmîD.î±.Â5)´.°÷: øõö_%¼ ä7Ö,,<–Á0Âð&ß?Nì‹üvælè˜×ïº"Jä [å»ýNÏAÒFÝ×ÇŽðóÛ|õ’ëÉíüðšÜaçtØ›ØËèÈØ!ü阹þ`þÆíç`ù®Ù…Ýòæ.ÓÌ «ìÕ##: L+3x![ _ Ï (ó !½0¿'¤+¦6ö¥'WÍ›ór¿aÂ*á¦Å– ~ïâ¾sã‹úáÖFÚ£æçÑvþ‡è]ýÿ.ì`ûßÒé&Ý[ÝRÝIÛëâ¹ÞÔò?ìQ $‘¹á¾>›t&|x:ª,E3@>êê.Æñ” fç4òœóf틵þm%¶S1@#3ã1üoÜO܇ûCß {h#­üúäUòâÈßå§ßé§äõŠíêP5_­ù~ xà2çSïåÙ¹ûø.ò'M·)ýlÿ¯@&?Gæ( >éæ`éà/èdíªä#ó‚ Äð ñ…ãNéEæÌÛx÷ÙဠÒ÷Ë * ö1Íׂé)ÁYÈ–Ä^·®ê)Ç$|÷.Š#öŠ)ü! nü·ðÿ(úO/¡/g1z!)Þâý|çóâSéñæ™4 šýÅ Ÿ»ÉDï<ú8è-ç[ü—ï%÷öˆuÔëì&ÊØÊ~à™ËKˆæ¹C¼ êÁêÞqÝ8ý°Ú:*g8]0â!Š8ûúõ§ßôDî÷àn5ý¥6)*z3?«6² … Ìý„ ¹ðùNõôóÁZz›û'Éýüeé€õÖä­åëð_è.ö=öoë{ö—ÔäzÍ­É3ñãËýŒó, ¯ Ùô¤çèáíd÷æ)Døþ)t;), á'%ðÔÐî²í þ4ñäCO(æ_8V.¨8:L¦1?ùM¯äì ò­âK°ù §+öXVÄç¹/¶N勽àúñƒsôäîÿ²ÚáÙNó±ÛQÿþñèö•Ú„ÞõQçí ë } êöv>ý¥óAj Ÿ'[%%',-í$#rFìk6‡†AC¯©<)Ó¢÷câ~ØN¥ìô•ð ïkêg|Ïää`ËLËÎâÌUžåëÒðú±£[ôJþÑè ìeæâÄôéð1þ¤ì ÆûòÌö?÷yô„F LT1[ÊÞKñê8 <÷ë+'âúV/Jè¹2ËZçlÜ~×ýžíï ý-2Ì* ÄJE ßà/ìdÖÑbâuÏ)`æè#¡Ýh"è$éÇÕÓâ9ÀDè‚3?.¹4j –&Iæ'ü#î~âÝ ;öÛh#Ä’B „ð “ôÜû7ãéòå aýÛQ 3ò"û âwæí©Üà‘ð»o#ºËKñlÄeºû¨È)#¨ë "ÍèÊ ŽÛ ãö+äP¨ÿA+âó1ñ/b,©7^þ/ù½íÿö‡ñÃîÇYþ “  ü©—è%ø¶èæýŸís,àX¨ÁÕpðƒÃeŶÙ#Ähúòã€ðzþÐÓ°ê]Ö6Ò÷ÌÚGPû¨)®ñ™&“ . ì t Ó¢ !Û2!ù @&s,(¥@ÌóÍürñ0å Þ=>ÏV5 ÝgýâеÖÐåqÓüËìw+ÿ©³íãüÖÛqáøÒ´ÈíÕÐÁ3á%ÎÍíAä@ñ7ðÁòýìÉû¼í™ÏöfBo"öÜ%ò†"²úüŠJý‡,NM(¶7‰úÞ%4Òöò’ó¨ÛR-V ¼9þ6·c5ÇõCêà–ñ?ÙùÝÙËÒ]à‹ÕððŠæ¥þ»ûXö·þ1älëAÒÕgØÈ+ýxÜï![ 8"2(ˆújߘê{ê©ÙÚdò«$Øt!†ý^ŠõÇo :ýž*qV/è24Ä1c÷"Öîïü6ö…ôâ#üC ™  ~ÿu Wô:þæçúî²ßÝÂåNØ}þªç’®ð ×Íö¢¾‹Á⓼‡ÌîB)•"Q‚!žõ†å…î¾ì\á^þï® ê½ œN áK  í ·v e < aÿð æôØhç„îƒæcá>òõãî ô¯´.þpïëÅò¦ëáÜÜèÑ5 _"/4ìç –Ë¥àç×8Ë&ÿ‹äl  Ï&-+Vñ#ýò\þþ ó( üÿ¾{  G€JËQ$güE2Ó‡ïªÒÍyü㣊Eãx÷nÝúÔêõŸÛ Pò¢ Çô G×™í6ÙÑ×õIß±ü54ÄŸ—ë­þdç^éîþÅí± ˆ x/`*-Á4‚ ™.Ìh À÷G îiò·ùòÑ*¥0:">ðþ5[¿êÄê¾ÇãëÎ Rôë:Æü zãÕóaÛmÕ$ñ:ÒúSèçïçî åãçÉèYã¬öeêTøÕòXþ²ô:ë"QpÌ#Ë 0DL2ÿKM7@#Lu_7³öEêÀ÷ÖúcòÌ–Ã,Õ'½%æ2² ú"ìãÿþèáöÿ±èÖä<E8ø÷ VêòÖß|â4Ö“ÓÒÙBÌóŽÙuüh Äwé¤ûïÙèØ²ûˆ×ð$. Ü#D/f¬!Âü¡jñþƵ ø5Ê&I.97ê()ïzøð¯ôÿø tp÷_×!úAï}ÿ¤á«çµå®Üø‰çãû®ù"KÝýïf·Î¯“«dØè·ÞíËV îÿ‘åÿáµúËá.úX~'¨ï{ÁÝeðjóÜèÎ"áW Š\| ,`þûï|ûô– “þ dñ°ÐQæÛÎþÌ—êÄÖ„õÍ.Lò‚!ì³ü±Þ|Ø_ñnÕoÙð7áBLèí ûÖóúì¡ Pú—-Z%…$Ï 0*Î! »ÙV£< ,ñüâ0&Ö‘ìÓý#ì=è„Îì * Ž¬ïŒ£Õ,á,ç;ÏÛ‰îç(r[å ö $õýëþ˜òý¯÷­÷¯ %{/¬žçàWå°öê4î 0N0ˆ%38£ü¢™Ø7ðËå°ÙÿúðÿÂhêJ#ÐÎëôÈ<ÑáÙ#ÍÜõ'ßàŽù[óœÿfãç×ì9Ý5ûOé0ÿ•ò1÷yò©ø@ð ÿ~úmö Œè·ôýîçïkùô6ä%-9°@zJ9=ñ0ìŠîuCù5Ò$Ö(áAÛõÅ!ÆÛöî.é ßý:ðþ÷ôýóóaôýÔù'ýþìÛöòØÚ§åçÈÍAâü. ]ù.þõOì´þ=ë…:øL.SÚ=ü¿ ‘ý.þr†X6OÂ?ð>–N*É5<ûj Í ÿŽªw¶ A9Âú?ú܇òþî~íþþìÔ[+ ÓûåÙë¦È¼ÉµÒL¼=üïÖ» MíBÍ„å7ÎÇ3ò!ÑùôT æ GD·;! ¹øèG Š)§»!w´Öñ~ ƒôúï½ûëô 0åWÔëÛݧénÛ¢Öyñ>Ù½ Yô2 t ²èzÕ¸4Јºú¨‚äÀ»ÉGêqýžsî*ü*ðÊòôú°ô<i÷è‚üÕ FÎ÷öaY&z …¥ … ¼ô¹ ã!föN/ñqø‹Ýò Qµ ‰ÊÇvúÅpðÚîüòRçÇýkíåêú¨þ¥öñ'÷ÝåÓæBèÚëÒâOŒÿ nÛ­¨òüøñ8ðÐþ­÷Rõ ¼"/%,°2zÿSvû—ý ¡ÿ>…%Æ*,A^ø "àóóvìŒå³àôc±*ó!ûñßÉìcÔ°Ú=Ö»ÏÅê_Ò" çç-fË—¾í¦jÙ ãåßÖvøÉá»õþ="!3Ç%9þªíù›ÿe Èþ,ÿ;9>^$3I£öN(!â=øœômìV€ýýõgä½÷žÖ‰ã9Õ)Ý„Î#ÓâÌÅHΕÃòÙÆË>èFàqô!í¤þÈó@ò­ñÝ-4 f0*Ÿ P&zÝæúèúÚ1ßóF%Œú#,½V*n:&Í"¼&%&é&ã'A'è,«-Ò"_5&žæ€ØÙã_ÞÔØ^êeâ*óxñ™õ4ú ðHöúãòæÎÞcÒ\ìGÑÏ ié­t /Sãñðëìw×Ë &éÌ ± ¯qèÙ –@#Áó-&Ä/†/í%«2Î?)Ý ¾j´ ô‰ Ø^F*â(ò0RæÍ }ɰÜCØ ÇóûÄàBÿÞêëúñ×dákÓÌÊ@èSÈŽóä|7>¤ ¤åDùWÕêß,Ø™Ólæ!×Íöúæ:‹ûZ ñ !š ¥ /Ý ÆÓ\'µ c%R2¢.´çôÒ¼äX÷çÞÅ\ ‘#Ãé úÕÏã—ã Ô{÷2äËõ³ÿ­ÿðòú¥á‚é¾ÓŽÕÐÙ^Ì}òÛÚöë–¸¨VG÷Vùk˜ð#(%…1À2€:ÔõÃŒ÷òd‰ý¾!'€$±)cŽ+µý;¿î°þØïXñ¢ûÐó  _ÿ?  ôK¼æ ì$ìxß—þËäÙ ìõ>+$µøú¤éaîªèXÜY÷ã‘&r7Ê/Uj+WÖé÷÷…Ô—&FýÒ:(/"c>ßS&ùüLÎ÷ØÕúüVLÿrwC÷é'ƒÖèõØžÒ2î ×pÿÞì÷¹¨óv Ø^ó-ÒHÖFçEÑÜ™é1 {ïüœ^éüñûéwÞƒåû/š z;A8`$=¯#æíü”þÛòË# ¼=84§-|G¤.,éhoà¿é)âSßIìÖâçwô|› üj}ЉõKºÁåaµ ”ïûæ­Ù<öCÊRÖ/ßÈ}Þ‚Gÿ—"„+QÐBN Ô/ç¤18.!—0€Ç!Äö §öíúWÚýJ-Ó3"7ÍÂ4%õ6óÜnèŽàìά÷TÚRûLú %ݼû”É„ØAÎRʼnå‚ËSªçh½‰ðCûÍâUáYðÔV‰èpË+x Lp »:\ ¸üîý(ÿD7@ :@— ª7ä-õhæÔÖþÔ(Å 0%þq8ç;ÿ™ßƒåþê<àfþäð1œžõã7ÓqñÆ»rÇOÓ¸…ÿEÛ×âL¾_ëüBåÈäêð+ÞÒ dïK«Çx(˜òq~Ý©ñîñÛãÁ\3&.O#.9±œ%¶éX¿ïAè ²öò[nÙæcÅÜEæGáà(ìä ý°îªŽÖµ¡(Ú¦ðæÑüÇÕøMË&ûøØöwTÔ•ñ4ШιôËÏ<"[ýO2Ú(‚"y2nìQùšóët ]÷G MçT›èhî2ø@õü¨š L÷n ®ÒãÉãá–ó&éíÓ@…ê¹?ï ÷* 9Øòº±ØËâçMÒü—äß$ Ô 'Šôê!ùCïìå”ðCa Ç$:$¤¯'ýóŽêÛæˆð=óï9šýJô ZöÕ¶á{ÉÙ¦ç í1ßKgôx5#8ïu°ÜÙä/år×€æQ ™ ª]ëjìÚ×ã­øhÞÎa¢)$,#Z$±8æ È\õ<¥ëcîØýYíót $ù*%òtªÐóëÐAÔÜï½Ù= ˜ûn †Oòç®Ð0ñ4ÂmÊìÑ1¾ïšÓ ô˜Xãã÷øÒÑÓ»çèÍÜ´è;’* ãlp!(ýJ¹þ^,[:¯*È+ö6r=$þå•ûíþáAe÷æ( ç#u4ûù&³Í*ñ´ÕÍd3á'Ò™²%Øó iÞ+ë•Ø~×HÝ&ÒlçYØóüxèM Nñý …ìùÓðŒå®ìÒ+» Ú,ŽÖ2#óý’ôÇú¢ùóÚÇ*F,iª7;þ¸#'ë~ÿ]ô`4+’EF)8Cìê2‘Ú-ç¤ïŠâìýùèÀÄ W \Åöä {Õ}ë ÓMÈó-ÍþÐñÿ1GýFåÞ$óKÚIØýú)Üò%qèí*ó@ÓŒìÕ™Ôÿî>ÝñGøn ì Gòî}2ë¿õùsöÜÿY1,öý öÚçí×ÊÖ˜írÖŠžò-R éë*}ô@ æèoê»ÿNâÄ&ÝýÖ8P!fD)³â€Å,ÌÉÚåÀ6þàGÄ; \ k°öF^íïø ðjìßÿ£îÝ[étòÛ ?ÛúôDÛâ%ûæå!Ù É.‡.Gú-ªåAì•á ï.+žÅ&"Ä –öî.ûÀîàæ- ‘ïš1±³<Ø7H'J8¾¶ì_þ<é%çìü*éq„þ’<ë“#$ûÝJçh§é´îÀÿòõšË ¿ì&¥ïÌ(ÀæUÄ_¿'ð„ÍѪùD.[ïVþÄçìò1å|Êò€QZŠÿ„ –åÉòxÕŒÛDåùÓ¸ ë`'Kl+m+Ž -óãç)òç6íg'Y S'Ø-’:&UߢÖɽÜÌÛÀÌ“éºx-è#1ô Òà î€ã@ݼñžÞúÿÐê†äöZùÂø÷êÏðÐã‘á¿ï…ÜP „êb ðVbç=ûŽàðÚß@äÐ?Àë//?3ÿ)‘â~ý>ø6ïæ  4ž,/™?µ„4jíº 9äTénõéç~ ûM)®=$¹) Ì÷˜ÓñôhûXí §õ¨Ëy²í§úÍ£áüÑÃäþ9ÒØ*û&­0©óÐMÜ’êôÚàœZÿ¯uî "Mìí vØæï’á”äÙü©òL «!œ$`n)÷ÿÒ-ë ýöë$çøþ]ë­Æ!\ õ¨ÞˇêÙÍûÉkíEÐiñÚ51ÑßðøÊgÏ›Öz½Óö*Ñ øô— O ¤óôÏÙî±Þ-Û9óuæ@£ûÛþn ÷Ÿ3ñK×ð÷MúEô ~ùm0üŒ03: 9*eÙõµä\ÓÕNíI6%3þ/ýòpÜÿéiç´ÕŠñà~ üÈ*Çê/$››óû•îæýõ=äÒ“óÙ g €²wë¬ ÅÞèíÉñîâ)‰þR$!Óâ+§ô¼ÝÕ²íÞáÐû æ%… ©ìm ‘Þ!éªêÝÞ’ñ­:¬!Ë'Å8ñQIë³õíFâÿø æ õóù ¢ Á#‡*ÿ×8 RÉrÍùÿÐ&8¤³7ND÷4,Â8î]¼Í½-ßǽsëH'À&Ÿ-›Ü ƒÉVÚýÛrÌo0ä³w¦Àmèúü¨×ÜÄçVÒX å«ö‘(ü–š%Íþ¤wåˆòVè?Û‹ òèþ2IÙI84èˆÕRåõð~Ø#"îø_;û)³3ä?l?7 îå &ë~ç-ùvêeÿ¯ö,-Ë= \ÿ– _ô¹–ç¼ïÄîæß7Âí'·%ž&‘ô¤ /Þœä2áÂÌžù]Øâ Èý H6óüOæò™õç™.ü~+åý0l4X-Ùòãï”é}þÁë¬ÆA‚!Î=+aý‚°è·ü?åçùÞçåSÂ$& Æ'&Ü·ý²ÏƒÍëmÊì?íñ+RL1MØæݼÊpÕ™¹¶ÿùÞoþJÏæêû«Ýƒå¨áyÝÜòy〠÷dø#%$–.$}éiØÚàïiÚ€ù.*g ‘ ?20RQòàú] ïÍû: ÕäjñíåÞß7ëE.ÈU¥/áñöWÖÙîâá ÞÞ÷Hîoþ- (Å!v ]%ò¹!ðqü¦ùé.ú³ëçÈù=ãááõÞ‰Ïô— êŸ ž"ë ûmäLÞÊøkÝZ÷­Ïš'¼ãH‰Ó®áñáqØ'üûè‚ß:Tü6 Ì ç@øN wîìñyÓí +Ïæ0É7+2œë¿_èwå·&æûÎ7ÈÚûÓÑÔ6òÔÙÑÏ=÷XÛ½ øC¶öåüñÆóWí¬îéoé'éìâýŽì-H÷¿7â¼úß$ä³üèä÷"å:µ+ß1»@¬ ).:ôõþ£sëy&Ü82/.&ä>Aûæ%àHø¸ðõà§ ÷ú4!åÛ'Ì­Â ÿ T·ìúùÌ×§Ùšì´ÙûúýiiÔvüÞÅ)МÝÉŸý'ã"bžØãí¬6àÞâxüÞÚ@(«-"ü,Ì øèV\êËéÛ`ìÌ&Y W2ø&F!«.³ü»ø"›ü½òõRòµ¼üÄ ÷.zãúÓ7Õ’ÙÈ›ò~ÛôýÇþö XÒsø”·ÀÈ àúº°í(s,î, ¯Ú/ê¡íÁàU«ô–?û÷›ûüv Íø-üþ ûtÜL8 T;f7ó!‰3®éòåø¢ûâçnú¾#äg1 ø5ÃÞºò#åÛ.+å׫/Û!öÄóâîîyïÈáƒ.ïÉþùþþ€þ-©¨ þÑñùGù-ì|ÿý­o­ž)ýÑ®óbôÅðH#¢b;3¶ ô8ÙôúŒÞÁÒ7*ßV¸ìÈúRê%ú@æÃædô¦ãæñØÀªtX 7ù.îàïEîÿá8ûZæ• Rü¾º‡ øüa Wó£ô¬ù è9! þÅ,Š)Àû#pÏxñ½Ç­Ä ô«Ïß §ý¨õè žÞcöÓÙcáëë=ß(üì­ Øûs ½ºëôÙÿšé‰ñèêgæVõºçñ˜úþ&0!*#L8Ô) îòû »è¾;$BpAž&ò=(D ÏóòýmþˆóF¡ «~ Ï öú9ò#ý–óJóLòÉíéìµéÙìQémï'ð-ë¦ò5çáê´äÕá-ð­à.àò ‘K–Úvÿ¡ÏóÌÖûJÐ-. ,/­4Dýù&Âî¯÷e OòO.1ß@ò-z5é>DÒ1\cûõÿüò÷vûfù"ÿûN Íü:åÈöáÙ¡ôºÚS]öaúæGÙ–ôེ̙¹¹³VÖ@¼»ùMâÿóÇ"ÙGñŒÖ|×oõÿÙ½ù+c¬Œùë ½ð3õîˆðëÏŽ$RÙ#b*ê ,¨( Yÿ:§÷h$ ±,‰-á o.çÞªs×ÝØ€ùõÛ— AüÄ™ PðÁÊÞÏðµÜ/Ý¥äâÚmîèähñ ñöï õìóLõûïøãåüï VÎÔ õääLY·ãùLÿS^ø²kR&´ + p-8þè÷ÝSõ#ôtÝ þû,à ê&çëÉ ÚÙUæžÝ»×iïÜÈÿ¼ó~ú¡Höùýñûù¯ÆøUüýöVñ'êèý>èäö×®só 'ßpé‹õáßQóþko åý3è®üëê´åZÜå׉ýÝT/úÇ„߆ò®ÝñÜï{ßC·ò4º•úX½écúZå;éÂí°á ÷ãzíOÿ¬"  ú'Œõò Íâ‹æ†Üàñ=42F®Ö,!íî‘òïï´÷3 ‚ØZ%ôø…õœ¾Ëõ5Ñ7mó è òÙç¯í¾à;ëjÚ“ÞfåüتýqêQ /üétø³ãàôöóÙ| Ñö3%nE4Xòâ;âqòû;î(‘×8”4@1èBX4ø Ó±Ñ$Oädß°÷ š Oü= eܱò,×hÔ­ø âù TÉò̺ğå ȸ-ð¶Ä+èíõýÂæIýžÌøÞŒ×‘ÊJÿïÜè× 5$â@þó 8ù«ôƒ îú®$p4Ã#ø54;(U5ôæ$Ò ©„ü& _qÝ Ü"…ö‚óѾê>ÖŠÍÜí¾Ö‚Wóäç îmAÕ°åˆÓÜÊ.õ‚ÒùPöµìŽûƒÌ}âÏÝ˃õþÚ\<ý* ðì6ý ¶v'̹îôÿ~Ô1, 7¼ ž*íï èí(öõ¨õ˜'#+c1$ .¡õßHæ)ïÍêÅß,öšæÒýïôðôýTäŒô“æéÄí?ìíîhèãêRðhëS€ûzá ó‘çÑçõBåb&Sœœ+¹üŽï~+üóK},¡m/MìúÕ-áÞìÏÓ97ïïšÜ õ _Žø‹ÿ?éÂíåæÖßáø”åÍ:ý_¦(ó$£ßºëåqÔO å¾/Òq.¸2* ¤'àøj Ãô= gý¨ÒTDãJþáÚúã(÷zß5"¡E %ÏûµÂóÖù|÷¨ô÷ÙõçyìwæóÜë÷åçƒÿïü]ð];×Nî×ÌÖ“ï»Û¸ øæ7µ&ò78òÅ×éÆéè xìÐ+=~%à.‰Ä&BzB S ? ® / ¤ <nû _ül µô“þ‘ï£óÎòÿí¦ý€õJý(ßé<|ÒÁæ«ÏJÏÓåœÑuû"êÙüÕþè'ü”ΕàÊÚƒÈ8 'áÚ*f¯*tû>Ðú¿øã äBA,õõŸ'ý!»!(+b+]  +0h# *-h"“&.Ö•#3ÛÑ÷5ßSÕ2örá…ý·ùjïgÖïÎÔ4ÜMÍj÷½Ü°øÛéUüpÎ@ÚÖeɺòÜÛÀõ<ý×¼ü¢þva¸ = ©ê'$ÇR<ñ% >´;Ð!²7Žúroðôñîîœ,öß/Š6 Z0Ýê<ÌëTìßùÞí¥üú£ñ*üå¡ò»Üæ1à!Þvéâøêé$çCëÆåê„ìmïð©÷vëXôòë8ë ù¡ê° ˆJšõ ;øÛùÿ »ù”+r×=O2D*Œ=rûö ÏÝ­ï_ôaÞ·¨ý®˜}Z^ô’dþ`ÿã°[ýçûÿõö»ú[àÍ ÷ |úÔ.æ|ðxÝ#ØÌÿàþ'Ò3"í[ PìÝ鄾íh*ö’ã %^ñGüNõ$숊ó½l Ï™ömúí ûYö÷ûê†ýQ´ý‹ vžÁ LúÑ?íÍó“ð¤ä÷"î\t ã!…$8V#Þqú&ß&ÒÁÛe¶¸Xþúk ü +Ú {9s¼F˜ôk¨ðø§õ)õrðoö„ä^ìûàMäWìëZ÷à{æ4Ý+ç1ó\ÜÊ Úõi üæ·1ÉÙßµÇMÆwÞŽÇù4æ ÿC°ïÃoöwïû„ùê&ƒ8%綦 ¤Ÿ9h@ ÕTY89é5Æ*”î ùÐõéç;ôø«HÙá]ÏÒßÄÚϳøµÝ?øüÆü² à;ÈpÜmÝ«ÈkXãâþ‹ a2õû VÌ] !JL-D¾?H4j3‘D`/bþÖ ïý+ò7Òûb1nª)K5±!ÉåÈñmö®àC€÷x G eíþjÐ ëtÌôÓuÙ¾Ñè¿ÛIâ„æÓÝUÖPÕÂî·ßêþ|öÖð·÷EïËàVGê¨üˆ ‚%Ø›äÒÖF°òM9¢&I5C±R7TþñYü´ýûzûø"…„ï¥ü×fì˜ûxêÍïò ï÷WóAùb÷³ø4üËø-ÿƒùœþÍí‹õÝ‚Þ1ÔRÌáéeÒ6 –ø±þ°¾á¯÷ÂãµÜ#@櫹 1 #Áõ’ àííøfûïî Aÿ)&Ú‡'÷ úfûº#Ym ø›{š± üp'ÏÿS\Þ¡ñ8èÓyî§(×"¨)Òÿ‡ê»îôÜXäñ†;dÕêæ¤üýã7à]ùüãQ ;úI. ’ °Üú - •˜½ ãÆóþú~êôè*õmðÌû¹æ•™ÊÍÝ~ìñËê¬"ƒ)äÿ2ŒÜÔó:ãÕàý`ã@WþËí¬ï":ÙDêœç…Ù† 4íöÛ“ É,+}µ•kí”t žÿu­ }Ö>Œhï”râÌäÅëÍßÎþ¡ô‡ÿ¢ ´â¦ Ò•ã ÝÝÖ—óMãíüçö4 ïÞyþxÏ%ááÙÏÌ‚å…àúïÆý$ê¬ïÉù‹ò b á 0ù ß4z-N²>9 O2 * iÿÈõÚªŽ"#BŒ*t÷MˆëÜõe)òeÀd%›ÿì óHýTü1ò§ûêóãõHïàíÓë¿êâíÏèó*í¶óÎûzöO ?þ’Š f싦ڌßùþÜŽ'€3 %" Ùúžö.ô÷´ƒnR$— Î4þ‹ãïäôHíñSþ÷Ø$J Ý6*ë(*ËÛþäôʔìsÎ?ÍVG%œ²ßaïeЂÒ÷ÚˆÓ3ÿéï][{Hvòx :À!Ù_±V²^Õ–· ›ê\1¬ +{+U¢kÓ4éþÚVÓÈûñ] ìý…ùïû5ðBñï÷ ög×+< Ù7ù !?Ò=OŽ23ïL {éDì®Òû*çà£üÍÌÓmÛ¶Ì¥æÉä¢ÝFéRãxÝð &á†8>A¾1lÐÝø_ÅgÌ äìÑÚ 5ùš›Vÿ”¸óžùŸñ­ð´ 6ýl2â%\f@f^ñB$XÌË0¸ïCË6üö"ˆ-Ã"ý "GæTúÒdÙ"òúÓv vÆøØtÁ’×=ÈT·ŠøîÔú† ˜ÜIŸûð b‚çÿ˜ÄÒÔå Ìç"ÿñµ#ŠÐ1úÇÊ »0þ\×i+Øä?™<½+c=ûÒ½ú3 ¨òúÍõûýMôËpð9úäAînà5ßêÝÛTì°ÝFô€* Æx êüÿYãÿÛ•¤ì/+20¢3¶¿´ù ¹f±AÞ]½gù±ã4û û¿þýEù¦ †Ûêˆ2Ѕ߀ýˆÞî+Ã#Mµ=â MÑÂØi3ß4ð Ùý0 Á$ ûÞü¤FíbïsõéŒ÷þ‡%¹ è>?áþ)¿øú™¼ ™F{øççOæ§?âT Ü ¥ u"÷&lú­þ¡#üŒCÒöPþ›ûDùþý Œ¬'‰íD 8Ç:ÚúÚÁ»µÉíW,'’.¢ |c“ §«Tà¨ÿüÔÜ}Òê¹(^–ÿ³"æ×xÞ6úçÌç>BL…B T5}÷§ù¯ ø˜(ãîøú• •þU ”÷þ‚æ£è;ñÊÜN –ùUE‹…qï|(à5èß2á éFé]ó§ü{é®×µí´Æ¼Ñ¹ÇO¾¹Ý.ÆÚþ’å6 !…Mþ˜¥ù÷­ Üøk ýÞ"R'ö=$˜èMþ½êíæ)DK&+× W"ü2 eqI - føÜñôþo)“,üù+#p¶gᘱ'©$ôzÀîVú€‡ðdïãoñ êâ±ñOã¶)é÷-þÀ£â‡ ~˜ý#â£l2Ú"€—$òG—sÃ>éõNÍ?å=Ië¹3“ýÏ Qù²óOÿ…ôl –þãè®|¯ô²%ÍzäGËä¿JûåÊf(Ç¢ #gÖ|õÈ$Ê'ä­Ë4#äë•ùõ ÍÿXZÝ!\Þ‹ÿ$Í~ÎOðÌ϶Ôÿ¼ (õ=^ç­ðü ï³ÑÿCÓB!ëL–ç7`ÒßãxÔYÐïôÕÚµåhUè’×…ä)ë×s Të  ‡õÔÿ±à÷ÖαÕiÖ É¡ï°ÚT i3 .ûîsçI÷}àµÞŸýSÞê$;Æ"²*[ñÈ‘ÃÝçþÑÈ=ó[ß•¨ù8üÔýRç Ú "RÔþ¼ÿÖ ÒöÕ.‡·/÷3øþB#ÊÙî?ðÕ%,küE2û<ñôãWÞìqåþåçç„åÔßÜñeà•úk~ç¥ËÌÏÖæ Å) üëÞú Ùé%ü&ñáñvèØðYÝÖàíä%Ü€øçîù–×÷‘ÆÒ×ã:Ìücõ>ì îï<•äæóÎð`ï½üî˜ì’>úÈ ÂâõõDß“â æ^Ý{úÝãK ‹ù»¼!Ä"hŒ(ëýô­ôñâ5Õøl+¸"Nr#Øü¦ÈV×LÖZÑ~ä«Ý:ô'ëÂþh j{Ìþ‚Ð —ýÉ" b" %oþæ’ärù òæê¸Ãûv#ÄItE"]@øD ç ÿÜ>—ÓF(?kÊ8GÜ‚^×ÐÑöýóÝ¿ÓÑÚßœév”ßuçAçÍÛ¿÷™åöeø7ô>üï jñÔßæUóæäsøZ諆þ9ÿþýêRýÀã‘åõfáò {óÇ ªra¦ªñƒþ<ö=îà Çõíc pËñ«èÊ ë‰ÇÈó†Ñ]¬ÿ’sÏû‘Öòœý|ù»û¾ç)÷íÝàõônåt‘4ůúªz¹!ÓV±ì yãŠ!0îÒü‡ …öÿûêžôþ ‘þ6Ê d þø¨ sî>üôð䢸H&9Ê'E(Ö›(€ÿîwû|ýõ ŸúK '·!iF)•îÎéÃþ~ê»èÞ ‹ï32¤¡5 56Ð0Üñáçß åâûèÝô!ýÂ.a Â&_ý -ú™úºþzõãù õéò ðÑñõD ¯ Rù[ $áeí)èÜ×EãîF" G*éÒgæeêùîØþ? ´ «Òøˆ ßêâú í£èa !îÿ/y0857(81êmå—ã·ù´åk*únÛåþÍ*ôÜCâ$í”ÍWÒ ÝjÄÓžã±H ²âÛlÐÒÛØFÓqõ‹ßñ Œ:õ)KÓõ‹Ë.بåïÕžYññ óÀj *d —¤ ?7'É@ü@ •>í¬0çær óëe,®ò-Ý(Ãÿ$0Eö‚ü÷ö«ð¥Fð¨)© 3º+q…-TÝ;MʫԳìBÒ§öìõH×Û¨òÇßNâÀõî%‰ú’ Vkf(Z5)2öj ë›í&ÿë_ À 7$> üàò¾µî³îà “îS0x :¥*ž&Ä1ë—æeø}áñÜÆòÌݽÀõ•ó õ) ùàZ÷œÔ2Þ§ÖÏÊâ¼ÔPöè°qKþëçjØtåB䢨pþ¦í‚Z  ‹`ìðYÕËá˜ì3Ó¥õ(*á!cþ5pÞ¬õæâKþ}îp {ŠüsÇ8 Óíw Ó˄߽ÜÙÈ%héŒçË|‚ì¡óè¹íúíqþO,Îv7@4É ù9÷óDÑ æ’ÜïÉ€åæ>( ù 'ÿòã ÷PóIUüeJ@ÿÍ¥¦ïÿ9á¸çèãÞƒü-îé Çÿ`Þì…ú¨æVçø¥áw!—úK5z&t]1–èÒ÷Òdß^í@×oö.&×Ç%é-E*û6õòFÐì.Pü 6ƒ#m"5õù´|âÓðëä­Þó¼årýùôÿ °úa(ìü¿ÓläéÐAÈê²ÐØäó‚ù1 żí!Ã'ÀÄï4Îä<ýÚ)ûÏå4ýÐàéOìCâ¥ÿ»ë§ûª$ß!úÓ%0ûnêí¤ï¯½ê^"ÈY0>%M$^2ˆý( ËÛéõ¼Ò2ÕÔänÓlþ{ë{¸7õš gÚ·ñÖx×áð–ØŒÛ÷b Ô ö]µÝcûuÓÿáÀá¡Ú)öíéÙü~kÿèt–«*&˜¬6ù$q<í6#w9×û ²ÜíðoøMÝe98'AD>›†4Âò‘ æãç‚òƒÞzÍéQ ^ûBÔ ‘yCçëWмÜäØŽÊ¤÷rß‘ h£ûê ÏìýÍë'ðSô îÅËó­ ¯“¨­ŠuÏtïÄö Äíš4‰33J5éë'¦ÕWôÛѽùŽßØ ü‚"œlúìAÿ_ãÒä1ïpÛ}Åêj ðþ\‰‡,†ä±vÀaáÞ¾ ¿Bå…Ë]’÷Ï ­îbËá ç¼øÑä+‹ù:%•2™'Yz!+ñŒ 8èÁòþócêwiûþ w6 µ2âöû‰ädÛf^ì.-62(b1íöööî®ú¯çíëï³æúðóY2iRÚ0¸Ïƒù"ÅÇYï8Ê’- ÿAÚ7³/‚D© 60ÙòÆù°ñ, ür*ÓÓT$OüÛ ìåö€çoæ•ðAå]ˆðÿ¹¢TÚ÷üÎþßîWãøÍê¿ èZøÉìàëÆ7Ɖì÷Ïj‹û %t’Ø'dðî ç#äñä•>A×GÓFÃø<þøÆ“ôWúø ôûûÔôÊ Ãûp)VÝ-ˆ0ë£%Ñ›ìyݦÂÏÉè''G&›áE ÈËdâÒ¿ÑêMÚXýrótõ=ß­ô¢ÐfÝÝÓáШðºÜƒ`BŠËñ\ÊõÉõ7áûÐ.äì7óééòÿ÷ òüqoüñ* ’ æçTnÎéÚ¦é|ÌýëóV%N,&8ñ5öà4ñÙÛÞ׿ÚþòêÑóÏC Ý£ÉáØøîÝÎë,0ÿ»/û1 "R÷Â]lûÖx „ €Wú™œæøÝÑÝPõòÜ~|üË&¹„ !âêëÿ‡ÜÌáíéµ×RÙîä±W% 2Ûšÿ{ÆÊÔJÓ ÄÍöâÓ™fþÎ,H#† ä'RHüÂþ»g÷Ž$< 4H)¨Â2d ÀòÿXôeñùö4ò ÷üòI‡ùTñõäÝ|ñéä9Ý9®ñ\À£óȰ¸èá{ºt³râÅŽý²êMýÿ[ñ„ý ô_ùQñ×÷ðµíÍî§'· Â<Ž2 %¤:õÿ¸í÷YþÞìÙD%û@!g*~ –%öB cóªô)vñ7ãn!ô…C Ïã;KÖÄáß:Ü/ìøçXæ¸ð ÛÀêÓÓèáïÓ¢ØÜâ ×(óŸá´‹ó‹ ÏÀ˜;ÿôpõ·ø½_õÝ!ž ¬>@/º.0Ctøeqä§è¢ â™B.jA?` U5‰OÄÿ §kÕûÃüXjú*Í L Ûvé¿ ÎoÛxÖšÈ'êÕ ð[ç)è¾ëíë©ö¨õ7ú)ùcüc÷ ‹û“Œˆ×ùë%Á×—ÛGÙn3‡2Y9 ñ)Ií¨sÿî!3 T5×.å/l}ì_ªâ„çä'á‡ðçSªû«çèïb ½Ì¨æ{Ë¢ÃDô•ͦ¥ö¡ È Þñ™ß^ðÞÞÚáeí¾ã?ôÐ MüÛó¿Ÿõ>ò€ ¢õŽ,éX.¶,› o$Öí'·õdòÕ½wb½Çcø ´”ôŸÏ³ã>×éÄ+ß"%«Ž¹%Ëâ'C×áßœèËÜ#¿ìµð]Ts ›ô/éîMîÀ÷2çŸÔõ¬5] ³"›"7Ñ(q“ Ù#4CÖý`%ëÇù7æ€èMó¼æÅí÷.woýâ­õãܳۼõdÚ7ýý)j!ÐY$EßÛbÌÍØèPÔ ÛñÌ -Yä3«!É»ÂÃÙäýwF¾#z;§IÅ0ã?ÿìèç¨fùœ® 1õEèÝõûæõí æéìAâ÷péýäøDîþ:ÍïçHÅÝÉÿàÅÍTï¶ÿqÌí}ûèìóÐêŽ*ôþýûâˇ3Á%p-39É v)FðfjÞñ’'€h9Ã2E)?Gÿ1'íà#üµà«á¸ü4è ˆð§ùá6üÑûIýÚûÇñ±ôNêèç`íVéhõÄóærùóÈ âÀËÀÉžåÕ üíE”ÿýú³úkÃ÷^"¾@0$ßBóA4"‡=¥ü¸_ëÉ÷Çÿ?î˜*\ `7(3ðu3îJ¯üõMýïmþ!ýZû.æûqj5ÇCÓáóâÂüÈ¾× Ã´ôQáîgö·ÜÜç[ÖþØâhÔ™æ}&C›¿ûÌ µñÍùñþ™õLÙ ¾‰xÜñ «í¨îÀùæî›’ý!%Üg-ûÿäêÂþ æàç™ë“â¨ùŽëåýVüb÷åLþ ÁÝm½Ä¸ݳ½Ï¯íªd è)°Þ%ã ûÞãû¡X Öü§Múó!¹ |DõE` ºõ.–<+è6 ‚'jðõüîlÙ ´ê J¯ð ÷à©ï~Ü}ÞaéæÛÍëöA_ŠâÖûPÑgÖ9æ±ÍA ìÈk\· C­v ¨ùöü­ù¿ò† ùlà,)7%Nj&f žýþw Sþì"© Ö)-±èßýïäÁÚ âë‡ 'èÈü-ÆDÙVÁ§ÁDÒOÅ·å½Õ–ù è( °ýöÏ5ó äÓ¯áŽÕáÆÅ÷ùØ ³êÝSÑùZ$õœøZìõoA7ˆ)\><@º]=*ä™ êÿÞÒ §ôéеú…‚ÕÏõ>ÖÛå¾àXê èçiè”íJç/õdóì(ó”Øvß»ÕHÏÏó†Û¦ ßúÑsù» ¨ç¼óñóqã—Éøö="T<>Ee326ú±a9ú [4z"Ý>`=H2ñDtó2|ÿn˜òö¹ürïÊ ” ‚Óù‰¨é­ò‘ãýâÝäüÞ‚ï´æQÿ_õ` “þøVæ‡òp×åØçìSÑE`ø1‘Ú¿õ0ÚtÕg{á’:<n?K;R.Š9Ê,½6: ?Vý UÈ"š0|+ôýê äÈó£ßuÞŽï¼àû >øÆ½ ³èìþ ÒêÜxϺÈqãLÐKöë…ñõúÝßï™ÐÁÖä÷ÏEýAç óÿ ¢<³ ^&ú îÿf „" Ø%»Ê >ãûû”ýóøý' ³&¥,$<(ðÔÿöÀÚ²Îqwè– ê$úd“èRùOÝýâ¿Ù ÓÓâÓЙ÷ÜâÐvþÌÿŸqñpùõÓèÅ õô!‰'h% %±)r %>ʸ ý&Ý í* Èb"ð¯zýâî(C?93P4méž¤Ûæ~ßÚHë?ßwðÝêÐô[õòòúõç‘óÉÚ!ÞüàÍòÿ‡ßj ÌÐ3&¬"o0»û{å¨ì“ù¾áœ 3‚&Ž(é$Ê‹þ­ý*—þf~6b ¿ªû< ¶ëqø°êçFöÉërüÆõ¢ÔØ×ò)ÎÌÒ™ÕÒËwßñÑŒéYßú÷mîD³ýxûs¼áåîYÚ:Ò‰ ºÛ8>}ó@ÅGÒ `3MÞ=ù~ìGß'@õ#•J&.&7-D&!Îëù~Q7µ5{-"žv¾épû*ØSäŸÕå׳àÕÜÚê°é(í¶ðæšëÎß¡ÞYêŽ×ç6"] èÞ"—ãpgÑ›Øcé«Ô¿ÿóg1í¿(ã7c ô&ÛöâHGôY,^6T3+G:Nk0¬†!ö§éÍù³ì)èH ÷Ç!ýßöHÞzôâϵÍ߯Èiô]Þ‹öGñ×é¢î5ÝåGÛÆÙóê0Ú¨þXì¥-´û70åwö ×cÙ•ëyÐTøõ´&²¢„+OF 6 ˆØ * i0 ±#Àr23+(‡8§ûú!;Çzìu»:»¨éjÅOìü¼°ð1Zá·÷Ù´Ö¤ëQÖÔþdì½ûqÿè,ùEßäæ&ÜªÜ ÞÚiä•áö îö ³—æÕ ˆ)ZŠ;‰1K.>å /*kíµ—åMíð÷GíÏ #ü!î?-LûIá—õéàÜp•ã]!¬ Å2&aø}ªá™îálÜèqàéøæü«ïàæó‹Œõ€Þ â"÷Ü€"A80 ,;*ÝÙ # 0—Ç ®%qÀ™(ðý^+æþâ`çKï£åX;öd œ >TËîÐÿµß§äIé{ÙéúßåF Uú·”^òOÔ›ä À\ÄÔǃ·¤ðÌÊÂ#Tþ÷.>,»Æ%Øú®(ýGò_Äû¾/{(10>"\à›÷öì¬áϲø8ÛÓ NûÍm$Iþ{ˆï±õììÑê´ü&ó(G)ðª7Ò˜å|ËóÉãÌ—©ë"Ò• æç4þ»ßùÛ(ëßK+ k4'0Ó5¦ó°;߃ìÖòLã! .Ì;ó2®/Eñ¦0Ë óåúÇ ò@ø ÷%öÎ W¿¼l%%úöæ§ðçIäñë¶÷öôèköLÑ’ßâÕYÉ¿õgÔ‹Ûö* Š —ð“ÿ’êBêöùyè‡ ù5/ÌA%-1Ù– Ôïï'ðëî¡jò_kƒ*Õ:\07§:A/Ùò× åæó3ã [üÝ ¥óëåÏÅÖÞ}¾¸ôíoÃÃûqQUà§ú×ÙÙÓ‚ü”Ü+ý+ú8ä”ï:ßâ•õkëžb>á ù÷)úèÕøÛ w,K)D!Ê.“ ¿ÿ ¹@$À4#E™,qדö¯å¢Ù{iòAĈ ™çÓËíßóÍSǹé8Ï®Uìÿ)ïÂÚ1 =ó8üðç´êZè€ãrögë a6 ÷§ù° ¥òÜñÖùÙDK*ßI‰Sê™D;èt„~íþ)í 1ºÞ;RɳÜÜâsÛ¹½ð¿ÿïå1£ W¿¾ûÔàé4ÔaϘéºÏ‡ î²(”$(oÙ ÚÊ ÊFëÉÎXïÅ4©(ð4‘¦ÓåÚö5ñ„á#éúÈW' !°\³ \x‚ïeúŽçbækö»ë($ÿgº JìÁøöä3á«íéàBï)õ…ÅËðâuˈÁ,þ@Ñ%%¾ Ç I!æ/-ßÀâúwæðâî,|a2„2²*î8-+Rý{²ï;õIô“òþÏ dOù0 ç¨õ„èÒãk®ïÕTæ…úÓéÒë»ÅþÅ:âêÈßøÜëèë üžÑùæ?Ø”Ï5ÿ€Þü=ì+ü$a*¢ =ü” B µ בþ ì(_ç$MTÜìªöá ¥î¹6u9±< «0ÏÙÙù?ÖÔµéÖÙÕüõî@ùüd çêÀùÚÁÝrÑ­ÅÖ¶Àôä4ÒôíPçVñ:î’ô¥í ýÑî% døÖ„œÞÑï%"ñ¾õÿëüž¸o-%%7¾ð’ZÚžësý åQ1C„9Ç:$2ó—êÞ#ïhÙ1ÚƒÙëÒGäj×ô@ë“ü¾ûøõôûà±éÓëÐKÜhËÿ7à)Ý.=ó‹ ÜŽã¡ì‘Ú§ $õZ‘'èü{‚÷ûÿ»¤þ¾.µf,‘5·‚/óòʇîøútõeüþÄ †ÝPÿ <ò`ý”çë…ßwݪæx×/WëÀDŽ4—ϵñT¼¿»Ëî˜À ¦ú!$Ñ(°-ðòæÎé(î@ãiƒðJ  Ñ aS ª^ \  à ®¯ Z ÚÁDì (ó2îåyìLçßñõùåHøZ àý ©èîöåÜ^õ|1™yI,ÿâ`†ÍËØÜÚÏÙ¡å%)€7!¶/T++òõü7ÿ;ò~Ú ü WO"äõ%ÃóÒ_ÐíåHÓÌÜŒæÒûÿDãÒîÏãlÚÄöºÞ` ~ôê}ïÎÕ@ê>ÛçÕûåàÜYN¦ÿ°êØûÉé.é¶ ò"ܬ.…+˜+Á3äe- Ï¥öUëªñ«àð·7zš-CCÀõ¹$çÄ!èóìÁ.í9Òt ~ý’iúhžÜ5í>ß&Ð óÚÖcü9ê[îbñC䏿ìåäõìÈøÛòïÿ…õ^e."Óâ„!  ¡ÿ©<̘Muî‡J×må—ðoßþÄøø³úžônøoûû‘ü¯Œç ôíÚŽÔIê²Ì[ÖæuŒFu Êø‡úÝóéëq¡éZWýN çÙüZ Hþaý±£:î APB¯;]Pn +0®ú@ß “`çèÏ ì2ü£ ¦øÅØð’ú>ï[ìéíSˆë¡+øx_ÑÒ可VÁŠÛiÁ_ûàŽ&…è®ËÉÞ-ÔÉÆ8÷lÖI\ùÊ Óïí ¨¦, ®ø‹ ª•-n!ÉþqÈðÉ=ö§øm¬ýÛ“  = þHç1ÿëÚäàF×6ôIÞ ÿö[ Û wß­ù¢¶ˆÈh¾I§íqÁ-DôéùxÌìÌúŒïoñhý|óÈCù™ˆûî >ìïÏc¼ “B þ  ¬ š Qð 2J"Eöõ»óùËQõ±ë ‹KÒ N™øgÿïìôƒæ\ï‚ ýþVóî õTå¦ãqícÛ›ƒçL6bª,m'þ5 «ñù%ñWïå,ø=HŸý)ùO-ÿ·òýfü¸!;H;,×#3>³ðCßfìºðü柹÷C°ŠîdúÏÝ0èSÔ˜Ù)ÙîÏGï€Õí "ì²( h äÅé}ýX×µßÞàÚÒòþ™åÝØ´ ± æ$Œü41ü&þæÁ.³œ<ƒBj÷H¼ðÓPàHóa÷ŒêÈ k‹öàîâòÂ×å Ò<Û0ÐÑÝÊ ÆRÑÁ¿ÚÐkê‘àü÷-ñBü}óâ¹ï&÷{/Ù“,,jµàjó”í¥ßIBùÎ%qu!à+¡)FN%r#Á&ö$©&`)„'<*·/²Î1<Ž!'ãJþˆÙòàÇßìÙ~ë2ägô_ó*öûû8íóôEã¯áOߩҌï{ÒªNïl¯ýž®âì³ðåÕü~ïM Îø¶ÿƒçËþSx À#ó9.Œ&á,00$D0X(x …í …L €$·À(.~S-öÝ( ƫԞà•É0úîèßû-þ éføËÔ¢ÝçÕÇ?ìÅËW fè¡æ û# šßÙñÇÔÝtÙ™Ó§èÙè÷Iê_ ý( } ù õ  éñÒu&l%v%\2Õ ú,*á•\ÜŒãûê4F!"8ã¹Ø^ÞŸåÖ8ú]å,²ùNþÕÿcðøíÝWåcÕ˜Ó÷ÛÏøÒÝlýÖ’öJ÷üí cùyôH†òT-µ5<‡;6%÷š ˜ûo÷Ì|ÿE#ÿ "+¬ )’ú‰í¼û…ïìîXÿcõ  YÀëòÓÿQçvêZï/à¦•ç· ªøƒñÐ" 9öñýŒêÈé"æ,ݼ:áÍ2 P)¢2¤û­vÞ{ênû‘Øæ-²5î4ÏY986$ û ø»Zú¥ûÛ—IÔ ÊþÞ ßåÈþ–×øâyÜÃÔxï"Ù’xðy'Ìñ¤OÔ8òöÐ(Ïhï³ÐXò÷ IØú­NéÍïaìmÝ,ê;2ÊI:V:? Õ<›ü½€î-ü·ÿvóÈ*6>; $îCuÿH&wçhÿKàèðá©Þßïã¬ôù”¶¬ú6fÂîì3¿®²Êë-Ãt ˆð¤ù™ ÷ÓÂóÚÎEÐbãØÍÛUà ò~ Áé ü ÒÎ #Ðþ/§ F.Æ.€ -­ü(ô#ùhøEk.0ÿ 4Ã;%·2eì÷éÛá—â Ñí÷vÞéCýôe HÚMôHÉIÖ~ΪÂììÆÍ.UîK~¦ìüÍÞÚ}øYÕ‘r댹$¡ iËþ@ úü—ý }ý§A2%¿3UH.X-Öãõû,öòæwÚ$ %’ª+ŸûRäÈúZà âî“áKô¯· ó³ WÉéé:ÀN›Ú=Á"´Þ, ÄmGþôìé=ùùäâóüÝþò Ø6([ï1¤Üïñû“æu!O æ0í-N!ä8 ý!.ê¶úšïRéD öCìÐü@Ûã5ûÏÛ æûâ à$îuå‹ð¯yC9þˆ^ÑÜèKÔó¾O$Òæ!íu> $ðèaÔëS× ÏÅúð× %ð!2¾,11þÅöèÌ÷Ëókå#}û8¨þ/&çrÿêZïØúŒöæ’ ;ó ÇuûF ß/ë…ëéÓÐ#\íþD|(Aƒ8ñ¤ Gܯãé0ÓŒé1%õGÄ&`ýN–çóòtã‚ ªô Ì"¤$Ì$rú«$ê[ýÆè•ðlõýð…9¾óäÅßÛüØÖä®ñDÞ Ÿüü/g) Z-€î› ÐÚ âåëU׫î–\ Ä Ýá†Ý_Ü=þ°ãš!eé) !q 1#Üš¼çòKÿ:ëÄëãîh o´-ºë¨ÚÆêÑפÏóônáå Çþ ñì£ÍLêNÄÔÆ„ÕÀÆóiÖ¯ dúýª 8ÞáðíÓ!ÒêÏi ;ë})ô ÑÐËýA CþIX­.ÆV<-I*ÿ8âú©Æå®ñôQçDÁül+t" 6Óò€ËuëùÙøÈÚÍç¶(Oüz#í¤9Û«äMÚ‚Ö ÞÒÓÝëxÚ>ÿüì5 \Êú“êê8ô9ôÌä…î1,&ÐK :ýy‰÷›üiFüq!0g(ó/–…4Áù!ÞæSûÞˆò':kG[IOCá‡Çà_ã’ðÄéEþø‘Š»  VNñ& ¶Ò§æ!ÑÃ!üðÍÊþ†s(ü‰=ÝUñ ßEÖßÉãå%‚¾+"ì8 Ñ´åõ×PÓäòúà Oý± Àý…&ïuísöpüÿ÷7g $7u-ò ØëVÕÒÃô×ÌÕüû*„!#ö'Qñ(äíÞæÊè¢*æ¶:‘&Éß(fÜ¡ùAÁÇ¥Þ+¿ØLç¨ú Š yÿE˜ôPBî¾ö÷ðìaGð 1 ]í¸ ½ÖîWà5ßB$íg%B¶*¯/¥÷'GèÉþ2ñQäò óü-Ǧ"t#}-ê¬÷éíábô”4ž9ÿ61$î4í7zëÂùÚë8çjë[i™à[&bö›4æ#ýUë}ïÙ_÷4O÷L&éãþ½Ã߈ʽyøsÕ%–ÿ«qêãûèÏå”ö×çz æõTáÌ Šü·(â“ï4×.ÙléÖ@®ï,k(/„Q*Îí, ©é:îÀ ¯ñ§%Mg#7+S4#ËÙþ<ËÏÖâàNÏÕ áì»#?U 8&æîemáëåïܧôáßm…í®òö;÷¡ø‘éuî¦ã,àHòªÛî!Å ’ZÖvòãþóíŒÜèøïç>µ"s)?1÷P!äƒ÷“üµñš#ü U8©4ú+“Có-<ìÔæTêæù¥éætæ(v+"o)¦ˆ9õ¦pòDòrýÓìz Ì÷£5 Þ¨bçÉü Ñ"ݶׯ周 .÷ÐÙ0#í¾ÀÙÆã®ûëÞ5® ›î"{¨ÕçNÛpíòåoçê0ö7œ!õ%¼¤)Vûäyë{ø>ëU憈é,òZü ÞðÏUÍaè+њɞôsÔ¼§÷ÎQ ÇÁØ%ó<ÈñÆÛú½Iû½ÖraùÝ6 ÜÛÞê9áÔÜ2öcéaIþuþ¥õ`ƒñ\þ]ñw÷Ÿú2ò²2þÜ-ÁU)14™ûJ$w×îƒð¬Ô˜$ø 6/*€È0!ì… DÜáãoèÔÓ› Dän"eû'6ˆ6"yñfòqøKîÐä<ù4åokø9 •ûNå=àÌæ%øéG+#S"H÷*yîÉîÖèçÜåÓÐŒçê#'+ :!ëõÚ™å5ò$Ü¡õúµ4³%¬#è2…þƒ‹ëåñˆî"â^üç jùB½ f%b*Ù4, ÖDý Ï»Òò©Ø‘âœ_ÕˆÝñù”ÚÝ%·ÿ‚@è.Ù0)GÁ.æîèiíÒê®ùtë¦ÿ”øhY¼d Eÿñ ÆòN¢ã[ëãóßÜÝÇô'à=%ððU߻ߖåžÏ$ü­ÝôµYïå ׿ÀîšöÄæ\²þ 1¤#f+5µ?&²ó®ðPéêµî„M ¦U#]‘+Qùvßåq÷ çUäÙü,ëž+ #•'ZÿÅ!ŽÛ¨ò|Óá΂îzÌè)ô=)<%¶ |,ÎÔ¨·Ž»¤âŸ¶ûUíÔüåŽçú¯ÜXåØä„Üðõ0ç„ åù!!&'Þ ‡#¢äû¾ÖKÝ(ó}ÚN…‰(¨%ý~-‡–“ó©øj ûïk6º'G{>v3õEýW(bÈá:Ó µ íØY1@Á”.¨ÜÃÝÔ ×ìZÚÉý°í­ýùƒ Òýa· õþ»×äô¦ÈðÍžòßÎŒ)ÿ¢¹Õ 1íY¡÷õû9Æ^-Ò?(ö#LØfææBÑn è?!>(Ç%tE*¶aCí5ãlàøßG#ü:'ÿ'qòÑ;ÇÏà˜Ø=Ç,ò‘Ù‰Ÿñ! ¶x ßZæsÁÉôÞ‘à°ÄôO-ÏÚmô·âÎvâó»l%sö*µ+ež%éöü Íé+õãqéZã‹á<öëæx¡e#+"–ÿLéèžõÂðûâÖÝìŽ8(9!;SY4¼ß˜IÚºÓï?ß8$GÒ"@,ÚO&uå~í(ú9èô)ÿ~*¾Ë!-Ôø‰Ô‡ïÅÎÑЧòîÖ>ÙòÁø{îüûëGó¨îƒì:ÿ†ì{ëþª'Ò÷ºîÕ±ðBã’Ç%äîêæ ˆ ÿø% ÍÞvôýÕÒ×,êEÐçêí&!¥\2ÙÁð"ËkÑÝÍÉÿšé IG±ìË 4ã+ëºûqëtû¬×OÜ!9%çP9Þ å@}æ*::4äG±f/Ø„ô>äaÒ—ä0" T¾€Øç<öâúÝkðÏÞ;×òfÆê”«çeÿyØ©áÜãWÚü)é yþä Ë”Îôä†ë…õìó%ë5›ú¶22$*j>&îà"ÜÈ0áÎò¿Ï#åþë;÷+Õ/ @Ãæ'ÛêÙ÷ôoá×”ò°(’Ki2 »ð±ûKä®ë ÛlÝDäÓ);æ…˜ þŸ Úøó—Ò*ÎŽí¦ÏÀSïqW ÄùxÏ(õ–ÉFÐÅç­Óªö„E¾Z"þî, Ùßvì©ô_ßT‹ýª)?#aY$‹òÝ Åì¼òaô?ñ&ù(óm`ù!*Ä)*,[û(1ÓOó¨ÍÕÄúÊÑ$ñê+(Mñ°@Ô6îÚÐÑÆïŠÔc õù™ ’õµ ÃàPôGÞã$í£ä1Tôâ ?„ŠdúûÃþú  ÿO—ÂG!± ë:ì"’ÙÿãìŸÑW%Õõß=Y0"†8[úL‚ãCð«óòÝ•'ôÖ,O "0ié*œÖrèãÇÞñü*ðñ# ˆü´C£%ñƒúüíé:ùþfÆüÞÊâ¥öÊá¬Û)ûßrüª­Å$ûè÷³èÝÅû‡â6àù!¯ÿ˜IàâûôÐoÞ:åÂÖüæíÙ³«}ÜL ‰ Eö¡ËîÛî© Ãðš.¼Û0é;°1Üå“üÂëMà–cêᜇ ôÀ¬Ö³ë`Ý(Ñ¢û3ßO  û CLô£ûñ_ñ‰íïàæ­çîfã–ýuò åôÜ LߤöÑãâ_yéÂ'Àœ=32?-×CðE&FñnøP³ëô+ 3*3*æ8}÷ûÚÞoòùMà%5ÿK6h#M*wþ§úHÕµì`òܘÝRí‚Ýæýÿò-LÑòGÆάáOÈáÿéu ïJFçÁJâ’ܶƒá•#< m2(2ý$”æœûñíŒçª ¯ïí* ¨4‚,9Ð.˳Køýýbñ8eó 6ÿ[XŸÓÿøÞõàÐ#ÏަǴò‚á×Béðì Ëàî,ÃfÆå Æ¿ò¸ ú+èFˆÜ]æTñ"â„øº‹‡úÈ”úS¶ûüUú÷¼ñûŒT 8z y8@6ã2ÿ_oóüóZûlç®ôûy'¨%g¾0Ëóº‚áñÜçÜä@éë3!`!ÙðŠ’àîèdôNà¯ô¸ÿÿ'ùœ}ý-™A JùlìòôPüØïµÛÿ¢3‰ åýµ Úî±ó°¤é'.:Â/K6·W+KÛ¶øÔá`ÒÆ våÌO ±üÈ÷‡6ç ö!ç äÚödä‰ zõl ø9|^Ì÷ÄÈíýíRïÙá`ý+è‹[²” 7sø•«ïíôÕêò%²$fú¤Ê‘èÿÑ_ÄhùÙø #ù𨠞ڲð{Ühß±í}߉jîQ \ÿS ë?ÿZ&ó¸ücêñð4ênå/ú_èS’+Š"$ Aòú $Hñ<ðFËò›<4»?»@ñ:†côâüºÐòU‰r;ô·üó¬ kð¡øõÛñ±ðƒí›íæè»í…ëlî8ðÑëcòåé›çÌßçðäx§òã!ýu¡Ú†ô™ÒgÒw-×~1€"—4ûš•ñ^÷ð ó¢4$J?{2â5?Îz1©û´ýJûö÷rücùËÿ›üãñüs½ù1èâùòáSÔHúµÛ2þœõÛrÕ“îm»ôÉ ¿~´JޣÀø éÁðEÿúÕ¼íIÙûÓ¨üÞëMÿÆÅ; Š öèô%õOäôÎ?é#ç$*bh,|&v ¶™ûZi õ¥%´ ,-c.=½+Ú,ûrá Ù£úmä ÈüE0 Hì ß@íKÜ ÜçØÙ½ðè?ñ´ó&ñfõõrö¡ü”ù ÖýT s0 à£~ÛþÊåøˆûñø (á""¦-ÇöcuäùïuúKä#³´+C%Ê ß#ês‹Ö¶äùßÓôõ”àDÿ…ø+ü‰ö®þâýÌøù'úÆôÃñ7é—éV²Oá!Ûï]fÜcænùÝ×öÕ»#¥ûµé™ûìPä26èDÈ.w„õA§Ú;íÈß5Ù5ôãyºöÝgøñléÎ÷ñæŸè×ïá·÷7å,îG1"-Ë&¡í îß¿ÝåªæJ6º +cJ$Xóz#Õ êˆ×”ÎAõâÚWúè —ê®Ð~ÞcÙÈk÷Ë×dø›çcÿhÈÂÛÙÍËõã3ñü Ì jðî""¨•&–ÀCþ}úf>¡4m)~&ó4»T'ºîT ‡ò:ôT fû)‚´)J3Î ),$òG «æ~ëë6ßêùŒç¡þùŸñü ç†òÇç¹êvîì‡ìîõèðé»óÞíÙZýE(ëTý–érÞÌ êíÍ%Æ(s+·üLÁóîýÊÿà÷‚t%0@&¿q/çèK †Ï˜ÜÖò Ïpùµž #W 5ñ÷Îýíæ ëyêûÞßú¤éêÿiS DîÿØßXå°è³×¨è91wÊ%é1­ âaùâuóÆÑ3ZN«‡ÛGý¡ÖÒÙ%Õ㇠ ¶}üwÔòRøMúdõEó'ö€æ“èUçÃÜàû!éå6¯ë ÛÖôéé׉×ùFÝè Bb28)ªç2yíÎ ¾î¦å{§òÞ.“þ#3D #ª=VÅ å Û O ¤ ç eRùÔ üަó¯ýôîhñÈôhíÈþdøqým$æUÿ¾Ï4ã…Õÿεè‘× ûìyüqâ‰ù,ÏvÙûÞRÊfåã2bÉi-´ùƒ FüÂûöjåT^! è&%: r)ï¦ ™Ö#$†0å#!a0ðúhßòÚáÐÙ§÷ªãuýàüèétÿÕ¦éÆÌ{Ó¸ÞÉ$ÿã¶ ÿôæ öõÍ-ÛåÙ¶Ëb÷¥ß\ûøFý…þ?þúÿþ^¥T Ô —ÿh'w C9+r8Ò>ó0—û9ÔñFñWÕñ/X3+ž74›,ÒæiÍîmèÆû[ñ·ûPû>ðËû¡âñÞ0äüávßêæâêê&êJæfêTç“êíñðïñø]ê°õžêQç|ÿ±íÕ ×™÷{ óøŸøïyû—.4‚?U4&õ?/ñú6ÞQå¥÷zß”áráfüÎØøþ!Óýû3þö¡üá jm b A÷»ßùè”å#ØØäèc€¼ ±ée9ô;èô°+Qp1#ìçHñvøøóÇêˆ æôÁá{ ®7ö_ £îHû#ø!÷•ûl ýwøýÍ š…  õzì÷ísôãæëkòsöxk&}þvïÛ-ódâÍÎ ¯à¥ ‚ 'møè ’ø×& õQ°Ð¤ò ñòt÷fôö;ðzôdáBí àŒß¨ñï ô«üæ†ÿ|ß$é‰õçÞ¾aúV\âKÿuÇ]ݨÇÂ|åÊAìôñ ëhýôüâïöÔÿ1&e•$ ± Ý º¦ cÉØ3­ÂÝŸ¬"!à&Òý*ííõúûöéE ;ÿùÿþÿÜ>þ¬ÍiÙOàäÎeú£áÜ –ÿnù(!ØNù@ÍÙXá‚Ï»QåHø7\(rÌjgá æ$1õ?þ61KCŒ[*ýþKo­ôELÿÉ3Ø##\6iúùáâ¢ìÕøøÜ¥ üŸø_çâ1ÐÉèðÌÉÓJÝ:Ò\æ7ß”à2äÊÐ>ݺØ-Ò¤ôäçüFúsòßó~ìãA yêœ Îýç„Ý•ösë°Ý9ä÷Hú0JRFî3~F–&Uþâ uöW´ "´"Š«,{ðÐí–ðIbõ"Ç…€)`û=Ÿöìû°úmóÁú”òëójîqí¸ëéõî¨é^òqì´õsý óSvÒ cëšúíÜËáiÿáú+ e*Ù$3 6‹ùUô½ ’úpåÁ$ X*ùèýÖîJó¶îñÌ]ù¿& 3*À P&ÀÓ6óÐÆæÆ‡òìѶ  ¼Ë+/ú†`ÜSêÏ„Ñ!â ×ÌyöÂz›?6댷¹¾Òå±E¬³Þ±¼·×òO93%k$h0—õ ƒÖˆæýÝäÙÿtô‹Hû- QîÔøÖñpï‰økùiiÿÚ9 9‰'Þ=#:í/Rí¾wñ¯î¬ÞýÃlÚ·õƒÏϪÜÓϦêæ`ÚÏíÓà®ÖTmç‘.¼ !(Ñ<÷–Æ¢ÊÒïLØL G!`ü6ð‡ôNóañ³Iÿò; !ì]XFc&]t6‹Sáf%ñSÔÊúÒ#d'_%M¼·ÙšûàÏË“¯ÚˆË 5öDÚÂØþÎ`¹ƒ»ßðä1d÷Â$+"Ä#‡á‰Ë»éÑ7í§Éõ%zµå*ÕÙèÑúÄJR܃3¨°9K?M, 7ù GXø2¾ñ{úúô^ý˜õ\ìì+ø)æûëPÝ1ßâ/Ùí#㱚ó•1z ,*ë,ú…Ü’Ýw äé;-…'žü~*BÅ)ëW½²·Œâ8Á\úéúÁúÿþÀÑ ŠÞ²üyÕÕé7í,Ù ›˜7ˆÙe¸ÞgÕYìü8aqœû/ÅqÁ ˜üiäçzíØü&韢Ö"±É ¬“ Šý´Ôÿ[r > ôÇð®ÿ´êÞáú'ç²%x Sß$Íz`ýÿkãü-—Uó ú…¯úX_Ú&ýቼ)ȾëIÁŒ®øªrµõ@ ­4üÖ]Ø*ûæÕ4ÔÄÃñ®%s(;ý‹ÊÓSߟàËîF¤û9CF »$ÐùXüÐùF|r úg þêçèñ„ýnénâõsäŠ öúá'Èö\ïÞüºÝðèà&ÞìŽí8ómÿàèa±ÒzénÆq̈Éb¿îämÊBÿTíÙ Ù"TuúOÁþEõn †ý¤'&ð!k0Òû×aî°÷òÙôè(( "¦*ˆy˜ ÚÉ.súËükíù/©.YîÀ"¬.Ò¶Âa©ËóHÒÜ=_S™ëGçî<éÁã1ôâÁë²S‹‚† hÿ”þt ¡Ü©š"!£l&æsz"#³@Ô %S«BØ8Ne*dý0cúÕô<öb Uc\àí,ñ® Æ,â%ËÌ´^ ªÓÈ*ï‹Òéõ1ÌUÊÌêeÏ£þQé+Aù° ™R þ— 7Û°÷bÈ1ÊHörÍC'? \¿+mõìäë—õ‚ýwïê³r»åü³ÿj+äšÿ~Î:ß Ø?ͺú•á.V°gã2bÝyàïúÜ­ jí="”qJ!”ümÞÛ¶óuÍMϰ״Ǫ÷‹àÚ ã‚Ùúaã‘óæ–Ûaxål* ù²2™å. ÂÅÝGÒÉdü?àiG&ûBÐ Û¤˜;þºü‘ ø 1ËE0u4°õ%ÆÑàcþ¥Ñn/) ;È05!3›EËÿvFEû1 Øú%¢ .9Ö+ò%ð<ÍðŠåÌ\áÍî³Ï";üR…"Þæå?ðÚïçÌÚ£ÙõÜaÑÊêºØPÿÛï%š Èð—›äÎã‰õÇÞ€Ìóí(yÁ&|»?êÞùùEå!Ÿ )T"± „!åÿsìªäÔ 9÷ê C#S« ã%Ë(ãí­¯Ê\çàÄÏÆfâC˦õ¾ðHâèùrÏòábÔ ÔZëpÙÿ²íõ Iµ& øñ :Õ”æõâBÑ3 9èw €Åô5ÀÉÝåì.Õ*«ûg'f&¤Y  F¨ó$Û#OK؉ .^úÙëDëˆëVVóö"b/²(t„/Áø `ðýòâ…ïO#‰K(•Õ,¹üŠòèÌøä[çaïá¬Xîx»'þ¹TlýVfnìıt$ Vóœ³èçëð°è )”ïI-OñKKß&ãˆ:â¾IB(x$"BÊò!¾Ü$ï,åä*èyæÁä{ßQøâêœ ÏÉÞ.ÄÌuÍgêNÉfð4d¸ôÚ >ïcùzîúôçéìÕÝà—è^Ý¿ûÙôøECÑòöAÂÈGòÊO^€éfñ,?ãFóŠùŽïGÀ3‚…qõ½|â•óìÜ àÒêNܦü…èô/ül ÔA7e ê7ñýüìã"*Ø*¹%óþd#êÐyó9ÊiÒ)Ø6Óé\ßpöLïFÛ$ ~ë=·ÿÅt`ý'Ž“'4ûÞ`õ?õRäƒïG Ó܉5v§ …ÿúý â^ÕD4ôG¢FO ¨7׆ówÙѽSáí # ßil¸¹ç_ÿàªãhêåÜîø§çZû?† ¿ü Qî9CæMî ê¹åKùIìÒ•ÿkû¿ùèQø„åä²øŒâþèöSc 4ü¿þ¿AñzûE÷î|ÒøOĪ?ìõÆDãØÎ{Å"ùÉØ2¾ž6÷õ@ öÔúçóÃû‡èñEáüâú+騊 ûÞ½¿ðˆ©T±YÞɲ íïú!ÁÌ îñ÷‘òù ù"“øz ýþ Åø<÷æ íùªö<ïH$ú +Ëþ%ô-‡#“ÿˆãü°ýü Ðû[ ùš%¯"d1(ÛüÍ¡æAúìóäRÝóM9Ôg6d9-BïíÄåðæ‡žáj&Ò£*¹#Ÿ"0úÁ =üÔöôÿ&ø¹öŠóQö|ïq,û½ D 4õâßÞç]ñÚ]÷Ü 2ò½Gæ}û›çËè}üøî¸ Ê” ŽÞhöƒ›ë¬÷\ðàèƒÑñ¤4ºF5š9— &.¸ãüé‘Þ#ý|ꬲûÚ+Ïü_`óþ?Ýê!ÐÎüá ȱ éÖç»[ß<ü Ëþ×ÞíÎìújé·òð áÐ9òé϶ÕËêäÚ$õC L €Z XD KÛÛ i¦ tŽ:‘);ÀAz;7ßì´ îê(æ\.ðÂ.ñ+å)ìÁ#zÿÎ Lô ùÅøeîlôc(w /¥*j )¨×ðû9Ï"ÒãòÖlü"ð¸Ù%íéßQãjû†íü¬þ‚ #j $Š  $±òo Tì†éP9ðº!%w `&n[Ïîkÿ3ôgîäíòØ1‹‚7ó,$!›.YÿÚšä¼óíáPÚÙö5ß' «ú iñF ÃÜMòÕæÚ=Ø ÑæÞÖOøŠì:>üþ\wæøü’Ùá0äÆÙjÅí/Æ— ¹mëãÏÕžá>õxÖÕ!ºþÑ!‘"’øs>Ý{ðâéÀáN¾ñDqI'éæNÒoÛ8áÐÅôíkí<Ôê]eéìRüNìwëå1*!3ç6*^4¸î×…ÑLàgçßÍ‘àðõ&k!†ú$ðožùòÿ”#ªü_–*ÿÛ ãí]ú2âaçùépßøâòM ¾ «þg Ìèßùåµàþßã½$õ5Ç')ú/ä°Ø¾Ý¡ñ{Ü¥Tú³'Ÿ]#Ö/Ê Ô&‹ùì ‡ò車é~%Êb1è%W|-ö¸/â`ìçYßõAè6ý÷·ÿtPúuýæµøæÒÓÝ…ÏuÉïËÐ9üøCíÕÆã²ÇÂ.÷¥Ò;#2ÑÅ÷æâúCá¬åÓîLâ9ðï™ ]"± Õv#|öïñÏìŒÚî&¿ „1Â*k ö2<øDéÓXîÓGМé~Öÿûïâ°ñà³××í8ÙÔA÷ØÜ…ùüÔ ãï?­ØÕóÕ“ß/ã.ÜøêëjþÜÿkþþµÒÁÇMž(Á9()„9g9\"N6±ðÉìÝ›çØä^5U©<í9Ù0xî™è7å!ô©ßžËëÈ )ýåvÞÿ¤BäüQϘÛÝVÌüáä¨Y˜ù‚ßëuû(ì¡îöîÞôô2oCTR÷ý¸ á÷Æô“ö¼5¤z-*7MùV¸ÕìÜÎÑÞÿà}ÂLP¡´èŸú„å‡áÞð´Þìì­ Í¼´ÿj5ÜLÿοÛjºX½³î¨ÉÓ\ ä 2ïš?åêüæIƒþc$…éb'œ¤íö׿dí…ùLëŽIÂ"t Má0õ¬ïÞzõ",žSm'çéôœÂïµøáã«ì”ï»áPûÎ(í$Í8$xÎ#öÃÇvÃMýàÑ8.ë Cs8Æ*óGýg(óòäÿ$dñM'¨\($­Õ }úW ë”ô è•æ&ò®åËDóNx×£þ—ÀôOö £ì^®ý JÞY`ãûÿSÇbÛnÍÆEðSÖÿ`ÿ¹%Þ$ØË%\í=·ßßõãZBÕ0:¾B!É3÷Ìô¼ø'øªô¬üuô… ãþû."á/:œ÷d 2Ï=ÝÈá«Êv›ëK%˜øü"à.ÐÊÔá.ÔÝÎQïoÞ8ý$÷áóõ·ÛòêÌbغ×}ÐùöÏá§D|ƬýáZï†ÿ2úûôöeÿù2—=³5é(!:ýu ñGý³óÓé~:ô¾9;ç=Y X8søÔæ}îGùãRwø‰%~¹õÐëÁzéÑȼÉ5áÔÔnðÙéïIó¶ñ0õý¸þíü¦zîþùÏð æÇôùçïýI°ÙÕ÷»ãéÚ7qðÆ6á„@Y43.Q8ÿ îΞ _÷ð!1N!Y%Z1ÔøRØ×Ìí×ÄÓ*ôîÙW cöü ¢ Ð „ñ4ãõê‡ÛÛUâUØÏôì‡Úôµ ãÙoöHØ“ÝÀèƒÞQ ïGféE”ß”ã Cç×; &úÔ;=ò¿¢â¹îõ¶é˜ùîÿ1üo Sý38 áßßcÿ?ÎõÒî|χÔø&X%d }#òðG $àºïØÜüÜêXÛŽïcŸ£eÔÆÞÊÿÎU/©. -¼!;÷ƒ² ­ü.< d + Z6Ÿyõ÷Hzã·óqܾÛÄøÝ ³C ¢ ; ÒUéDþ^ÛÞåñUÙMßö9 Ì+ÿ’ “Ôø'ÅËÍq×Âíþ\Û2!º+‚#  &l™Aübþ ö *ù–1-Ì‘1óú+9ðèö±õËñDövñ©ùwôßÊûÕL´ð )ÝÕë èkÞ@)ôZ"9ç6µÕιú²+êÈÆ."ñÿ÷%þ$ôûìòäú¢ð^õ›ðQì}ŸïI,t¼?–6–!A< ô+-ñÚï:žòïŒ&™‘"+›ð nôßôwòá¹ñ|·å"ʾÝ6üÜÔÞšã]Þéèäê{åýíÞÙ)ëDÒß…ÖØã×÷ ãn¿öŸ Š ê.µüÃùùQ‘øž(ÌÕ<|5m'&>µðÿäÙÞÿHæ”FsØA³A¯Ø6®:ïU 6ò¯þîú þâ,9¿ßãšýÌûÖìØFÅïî[ÚFîAìÖé"êÐìîN÷ÿô ú°ø2ý|÷„ ðýÅqE!Bã‰ÿ?Ú×Ô6Ý¿;oÐ2èB ÿ®"|óúË÷N!¾ 6½!†0å/-2-Ÿþ ®ë#û1à¯æÑäÞ/öòëÑÿ,Q{éìËßÓ0Åœø°ÓûaûPÓ :î¸æÝ¥ínßðߺðcä¶ø<| E ´ô°ÿ÷¡ñ£øÖ-8©,-p#êúûæúÊñ¦ %Ãú” ½unì¶´Ð'ÝRÜÈÆæl%pË ú"dÝ´ÿsÔ÷Ùìð•Û}…ôÿ€Ä<¥Oôoï¨ì’ûÎçD¿ù)!€®²%l/ À0z8"Ø“!Ô³­û£ ÁéA÷=ç2çðö#è× -ü7j¿ùîíÙ›ïá”Õ]ýãD‹k(~!Ý­!ܾûÛ΃×ãî3ÖN Áøø×‡N,u"¶Ã„=$E/B?+~5ƒE$ÿ/*âðö‡ñ$ê6 ¾ü|´ ð™åç»ñædîèçÙíB䄸¹ëCü“úÌèÒû<Êýâ»Æ†ÆöçÑ÷Höÿ9¯éuû çè öWìIÿ3ö÷ šûÓ Ô Ñ4Í(Ò)¹:qð"Õðôûžòô/^™9²;"…;9úA fÝ­÷¿æÈáIÿÐí 5‚iú¾ÿÅýøüÂûMûò#óßæ¤çâï1çYõ¸÷«àèõœÌ¶ÝúÎÈÍžéf× ÿ×ñ fGý˜ÐùûÅZõw)g EŸ,u?ßB]‹:rù=ï(õ ùóR-s5K6W%/-0Hûw üüQw?ûKøú ‹û” мïdÄ©ÄFàpÈ–ó”ç¼íõVÙzçÙÕÅÓDç9×–Ýê4†Ÿ ˆú> ôýù…÷‘œBg#ÖðéÿÍëæìOþêì·IÖ"´R)}üEhêŽúOæçî*ãÃù—îvÇþ%û ¢ß·ú¯Áx×Oº¦·LéµÀ4)ú •¯éµãá´ãŸéç—=× ìù÷åóɺ <ÿûñýß µþ9.‘ß'Ÿ7GÇuõà÷ÍKóN× C8 Ÿ¾óíõŸÝ‹êÞ>Ý@íªÝl`ð‡ Ë3Þ;÷xÒÑîímÑj]ò©“Lß@ ‡ÿ õùÑùpü”ô ÷û€"½º$2'q{! » 3þÉùî÷”"Žä$<*¥üw wÝ ñìíÈÞ þò¶vâWùRÄìÓ§ÄëÁ^ՓǺçÙäü³è:J“ AÎïöiÒ¤àÑÚÁÈèýÌß¾ªr[‚KE÷çêõöÔ÷Ò!é y?.’:1FÜv4ÜêUíFæ»Ï÷&Q9ògåÔ«ïà×YÚƒèSâŽé+êEè‹æ í¨ê2õ¶ñjê5òÿÕÄܶÝçÏjõ8äª ¹çÆñóÔ'ê>ð¸ñ’ã„"!ùbF 0£3iD´õ*óûóÈjúÚO 6'š=^?†0±Cô‡0äûI !óJòNýð NðÀ)÷›”éˆð äâ¹å<à+ò”çí7ù»éCøÞó·Õ¤Ï÷Öåÿ”~¹Ù/ô“ßÔýê[:µI>Ñ:Á+l9v˜)i龓üä¡ð#Ȇ>)óûܹâbòaàYÜõOã$zý:R&i$â¶ømÎÔàÒ ÉûäEÔ¸õ.ìòîªúGÙëíÔRÔ¿æ¶Óép  •þªÿÑþùÇÏ Í JÔ$ÓVðÊ5ÿžþ[üD æX&:Ú' 6©ùâ"’Ò¨ê£ÜÈÒÇ EêIõö!Vùç“÷(Ü@à+ÚÑ æ]Ò…ùJæ±Ç2þ… ïRôÁú!êÐåû#Ù;('v$t)4ª#f{þ í¦ ½íº$Óô¼îÙï¢-—a/®4• k+ÿå›@Û†ánâ,Û[ëŽà3òXìßôN÷]ò€ûjäÁð-ÝòÙÈåûÒBãÕ%@ Ï0t)õ,Uô9*ä¡ãž½ã5$M É)a,AÔ$5Ð @iþÒ+ÇÌýqÀKIøqåë¸õ‹èžæ¿úþꨳLï½ØSîgÏxÔŒÖûËqá(Ô{ëÈáDù¡ðó¶þû÷”¶Ü“èfÝëÍûØâJ"í:\M®))‹æ¦ô9îmæß ?öq ÇD$(†,ù [ 3 ÿ˜k Ü QÞýŽ¡æLø@ØÂá×7ÙSâæÝ…ìëëSìMñÖä•é¸à‡ÜOêñ× >ê€"I² ŸÃäÐþ^ÓÔÚGïêÕ¦hüZ/R%C)‚6['µó’üÌ ýôˆ/Ôi85Ê',;%u-˜ÿýåçšöð˜æÌý!ºÍóCÖFïOÌ$ÃæçËgóãeôîžèzîNÛ¥âØÝ„ØCî’Ü4øïõ‘¿ùýñßìð²×ÕÒìïˆÖmôø‰%V!îÞ)Ki. ÁÒ Š K ­&É1x- *Š9ó¬ ¥»ðÝŸÅÔ¹FîÑM˜ü‡,ú¬ØàüðºØÖvðßÕçòÜø“^ç°öÌÛîãPÝ`ÚkÞÖÛIçÔâˆù`òÑFRÔ †ï|,=Ù>‡4Ñ*DAÌ$wêqÿè0îûCñb̼"í$aí*Dø`ÛÜ òã€Öaèš#“ú?(ãï´ ÊßÐæœãoÝ3æ£á6ìçsÿ¾òÄË ÒnÂð>³Ú·ÞèûÇ×,,. %,4ë-"J! 3 >#ö :å%‡ùPãËø ã$åÁñƒæ¢ù¤Ò»Œ ëZûãÑâñê‹Ü¹ü§çÊ OýÁ® ðíšþøÐÏß'¼(ÁTÊß³ßüEÑÎ'ñ ª-¦*î#ùº¡¼òП1ëT#ß3Ö÷„®ßêðíÏàn<ú•6ÙþGûýéª0ßûwØíôyîÛéU ÷e ]ìvåËà.ÍAÄuêxÑ §ñ‡f©ä›ö&ç+Ü8 uæç.®‘3M5=­2Ÿð–aÙ çø±Ýb*v a8n6’-?>{í/¹½äù_ùð÷Bù©ö:5ê w"ö%Lõ ã›ìYçHãäóíôX÷ åÞñ=ÑQÜFٶȽü ÙŒ„ýü9 jí’ý”èžæ“ÿ0çñÈ`,Ri!Ò,8‹Ûî²þó´îŠZõ¨l-¥3<-3h4“;²û)¼ìŠßã ã ùˆçt$]Åå¿'Õõĵ>ó¢Ê[®ÊÖð’áφùÑãüöôõãð¬à¹á1ýï, v Ç žõêø„…÷p[,+û*¨"b.´¸S¹æ½;"(„.ÌA˜õ¾$ÈÖí?ìÂÛ#-÷pîÃñÞnüÂÉëÙßÏüǤìpÒÒïZo – Yœ ñØøŒæ‰è«è‚âùæì£ ¢N“³Q½øùíùõî€JÓ2é@V¸  9üã‹ýß gèÑ2 Ö±35Ø—¿ÆZÝææ¿ØžÔóû ›ÿ\^Ù ß%CöèdÛŠáÕ5Í£éƒÑ`”ñ(Ù²¤ ƒÛaøzͨÌbñ3Ëk$ød2l$ê%ð2¦úr¿á¡ìÇøâ+w ¥€"MÝ"  Tw™î,õàççGúäìмÿtÄê¥õbà£Þzó)Þ zø 7ñKÉß_Ó ÀçæÛ#$Þs‰ à-ú±àÐÝùþeëD ëL/º$æ0D3 (6[Æ'ªûQ Ôî«ô9öóÄÞ?€l tôñ Eâ&îììoã½™ô0NLÒ'ó‚ ÐÏ>ä­ÈÏÄUçöËÏú‚òæü‘ÎFà_Ú}ÏF~à7&» ô'L#üž%€P€þ? ¤W5 USy¦"+#6']ß'¹ú‹ó9ôé Úõ‰7…2¼=•Å&pÚfò)×ÔíËÚºÿ¬óN€û‰ ëæö,×ß×UÒ1Ä/ÙÄ“åuÕïþçnñdîzõ%í‡þˆïÖ ?ú{ ”!svð'Ê ^ DŠýô,¯$Âù3cëuºÚÂåû¦è6X9 =£µ0§éF`Þ éù×ÚîÛôÑæ܆õ‘ìý¬ý¢ñÚù]ß;åqÏóÐùÞ\Æ `él,M=Z+î¬ =Þñà"óAß– Tû?7÷ ` =ø¥ÖùöüYN4×Ã+5; +dò ;ï£øLû¿õÑ.E 9 ù3Hüý/òlú‡ä ëüÝŽØìêÞØ •ñžÞLþ‘[Ëõæ3ȉ¼¾ó¡ËI \ÿ>" #ú‚/ï\ã]ç]ñìà'öú p z lF ¸º [ Ù ¤ % )  Žžÿº ð°ýBäèãçÙÝ+øÖçþúÛþÆøi™ëØêôùÃßy"xúØ0j%ö)ËÞØ˜ÇûÒyáÌË ñŒ(ú¦”-/ÿä{ô.úAaõ šÄ   áÐSY_'sð¾ÎÄ‚à³ÙǼ Aðz…`û¥iáî‰å­×)ü“â­ Dø- ÿã鯦ÑzâàœÒäúçÑ··ûùÛéÑ÷šíjê+ ·õ %°d/.’*#4#k, =œðà‘îÙí[ùN1Ž"4(3<Ìì3‹ÁøÝð˵’ïðØ 9Ⱦó žÚ°æÝÞFÏ­ö:×vù“ì¤ìÈîîå`æuîæ?öEîÌø×ñØ0÷-˜Ï'|„ '¶ z)Ÿ;¹ÉK?!6šF?O,¬ò  ÷ìïóå(öc"½Ò.0/¿ ¥2•"ævó¢íÆÞH5ðç) ²‚¨õ™Aèçî•ÜÞóÖÛÐ+ÛÌWÿÓß» ”[¢âöïÌàzÖ³[æþ%ªEð)— •ú² [þŸ(\±:60a'¥:§û×Oë»ûWõóV'ý^Ç €ƒ¾ zôýúêÊøáßÏâêÞÛšþEîkŸÒò9þ‡Ñdæ³KÃh»ž®(çÍÇ0 Üøö)ÓNèJôæ3Ü¢æÍ'¢‡B-äe ñá×é^ÿ‰òYÎ# >º;Юñ*ö‚ü@Bðˆ¯$±~ærÿsÎÝ×ÚÏ€öÇß© íÿS)Õ!”¶¾ãÕïß_Ъû××Zîý@IWìù¸ ïîîõ:úÙðuþ‰×I%~'Ž)æø Z biÿ?€Ô Ì?2&xûȵì9÷oòVê¯Ýò]V çÿ ,çXûÁÏÖéòåËE%:þå&L! »ôæûLöÉíôýô‹ý÷v½ú¹%rÜ´úí6ÞBòTááš~ðï%6›.µ4ã&1Œð³Cݶçïî^á¹+úÑûÝ ·â=úÉÆâîÉËŒã8Дýÿèà»ýcï"ùãåcåþñOáÙýÄìæû*ô~ö>ï™üòPÿÿ¢ó4åAñÒöQçý" ö70¼1>ì ú-lí[çôþî[æ ? 3=âEúß xÜßß#ï6ã<þuõÄ÷Œüôô¯÷þýŽù–ÇäõïÚ‚ÏûïNÍ8 Åí[Ü 'Ý »õêö÷KêSî9Ñ®ŽQûâÑým“B‘"‰RfK¯1«N €&×ù´I…IÑæ†\Ô…û Ý÷rÿîWøÙïTéOýï,$ –] Êï[ mÓßÊ‚Ä6àóËç™\Gä•ÇGÚØœÂEþ™ÛŠÐÿXA_ Á Kp0 ìéW€àY"-û›zð´7÷~÷Q þ<> ê>úÒÔæõúVÛ1ã¤â¡Ö¥ù"â+¨ûtï âÖ“ø“°o¼sƦ¾óçËyÌ÷¢øAÈì ø2ôò&ýøõ©ø3þ $%»7–Æ ˜  Ô % }¿|é ˜ôã ÷­öú ÃøØø C<–Žêõ¼û±îêfõWæÉCñÅ|þAû²þDîò1æãTðÖÛQ XëMÆ„Y’Çûº÷îVõ ôNïžýœ×!++þª2üf) .=û5Òç=Ûé˜ÜÙ<æèõ?ä¸wü–ý}½í¦öëÛYç£ÔK׊Û÷Ïóó×—ûð\/ ë¬ãåø&×ߨüåøÖôéA' 0²"$ 6 ÛüM Óü'þ>fW4t%Í;‘GäFóä[SáˆéËü²îVù0öÌuàÊò×eâ’ÓUÛñÍ ÏªÌ˜ÄÐ|à ߾Ñ*ísç(ùðÖô ñ _ù›/¶;&ˆ*úƒÞÀëôfàØèÿÂ(‡#° /pa& Á%/#ü&>&A&G)X)s)E0¹*1“ûçFà_ùÙŽÝŒá~ÙríÞæ;ö†öáôTû¹ìÌòÛá¾ßâÅÑ´ô×fAôh5‘ö. pâÖå¨ò0Õ³_ó1#c ÃÉ#V _&œW-’'W-Ì0¢!%0…®%úç ( „¯ ‚$—%(µ-f*eÚÿý̵Ó[ã*Ï ýIìÔù éä/ôñÔ}ÚÔ(Å=ò>ËvòB ù¹ÞSñ-ÕèںܓԂê¹Ûdú©ìuK_ Þ ¥ µ× 0 J ‘ŸT( '- š2T&¿à)ÁÞPâS¦î|‘“ 8ÞJºÕ×:ë»Õ ümëÃ-úƒüpíõÝãÕ•Òæà;ÐNûLâ4ß“Y0€ýçgõ6òìdó /zR+ 8 l.èõ= ÿSöZ„#‹‹!~,Ôk)öÊë³õÙñ¯ï;6ö èðû3èÜèìñ/à»ê(ÔúÕMÕ`qõûååêçŽì°ÚbëR.) %7/¶óÓ©àšâƒèÝJ2Ü 5;#9–øÛ ‰ø†ýÞû!ý&lÇg [ùµ ääù¦Õ³àÒÞ±ÑoóÝžºò+Õ òê MѾèÖRÐáðm×`óDµ8÷áèÑëäî5Ýâ®ìp9¼Ì7sAÙ¢8Íø·{íÃù èöõ,Â=9—;ßH@ãúàýå—û~ßãäHäßíí›äü ýùêaví’Å"ã¿Ã9º%ñ ÆôïørðƉÓÏìJÎãÎé=ÌxÿæIþf"Ê8ðP± èHM%Ýd/,#ˆ,€.D-.¬¬ô„÷ýöRÌ6r'.¹<© .,Àê,¾ÛSÞ æÇѽúä+lñÊ›ÔØðÈRÏÝЊÂeî˜Ïµ óÖ¨é>ô`åÒÚ úñÙ¬ñ¥sYôï ¹ëùg •ý÷Å"*ö=,œ0oBkûŒ)[å õÔþÄëÞ§¤$[%£¸,ýö)ÖáYöà½ÞQð^áŠ%øb' `êÄMÊãÂÚÂUà?Ãkå<hGƒ>ú*WæHôüä\Þø»ßg¼ø¤Z £&4ê ãHìÖÿì7'Y%0º4ù;5°û>ãÊõéô{â¨vÿ­‹Ùú×ôáFù¢Ü(ä0ä~à8ð[æf…ò½ø8WPô¸[Í ÝÖPÀS4ÔŽ Á Lš]íï þÒÛçüÙ5̬fÜQ)H ù1r0$‹09ô^ièhï(úvêÙC]õùëËæFûÊì ðÐüÅø6k† „‚TºRùö LÛ%é|óVÒ!+4ù:é*¸*/ ëôÛÜ`ïåÕ€í}+…«;,êôâ;èïí‚ô¢å× ß÷Å ¬- N%äµ!î÷|èçû%éCîø½ñCÎÀÄï³Ý—ùþÜòâvú‰ãc!¨í.R*©H+ªè(ÜÜ„Þ5ëë×–—î5_)ÍáõùNß*ÞìÒæë"t Œ'#!ÆE!u VÀï¶úÒëüé2»ð‹&»F25ãÛÍUæSÙzÔ~øíâ‚ ˆ+eè ½É²äÜÂpÁãÚ ÀFûpÞ- äþŠûUÜOîïÖßÏŠðbÔ ÁL.ƒäþ>!ý@ (þêü2v•:91ßµ4Ãú’îåØòøçæèÿ(ƒ&gY4 çAlÅ;Ü@á{ÈëÓðk+­ &#uêìÿìÛ¼âËÙÖWßvÓ[î¶Ûp ñÍ+èùOæ@ñžöwÞ46ôX/$©(¶"̾ãú1 »ù«úq «ÿH",ò)–1Ðå6xóÔèx÷… ›õBC!’?9Md ¸60äÉàŠå/ó êùþKúR³Ô  §ìÔʬÛÜÚFÂûýíÖÅIýATÇô CÞØë‰àÄÖï ùåŠ)6Žj- åú Ê•ÞRÜÔÐ÷¤åǃ" ¤"ûDÅí+Iî-õïÿZùÏ"·Ø Íc蕽Ôdâ­ÚÃÒ²ùÒÜ@ [D'½$È ³!ñJtíæ( 3çÿ/Ø :Ü)ƒã&ƒÍ×ò‰Ã¨½cçÇÆ¢Ðì¥×‰¦ýQ ¬óÖPí³ôšñHëwËïC˜ D© éuמí‘ãà/2ñ—%ªê%W.©#¿äOöœö’âíø$0ó@!Ü&ùþyêkò[ö6æf»ú6§ Õ6^8}m1òþfyè?ö®í²äï,íÄT Ï#­óÊOæZú]ðð'RüWèu})÷Þ¥ ¼Ý×ÝÊȹ«Ø/— LüÏ —ëf÷cé„èµøÜçy 1ùFˆ ì£óøïŒÞ ìíÔßÔuïhÖê½÷X.^‘&H09;&°í‚ï{ï” îö'(§PÓ-Cûº¿ÖùìÅßÑÙæ‹Ëm»ø7p 1  êìê¬àLçZèWÝXõ âÍtîŠcøŠõø÷æÄì±ã]ÝÑõÞÜÓÚñ9#vsùÑüä ýãàðëñûÛ-#÷c>ß*š$X>#îg¸â|îløñl(JÛ6«5î&3?‚(æêlféãéùüfìò!‚*~ ­)—(ò+±òYî@óí»ú|à ´ÿš ƒãÍùÚÎ×éݸÅÁ iß45eÂ9pá( ãÝ?Ú(ÿ·ç”1 á*$ðÿçóÆÚ@íÙéPçÉúž¯Ì!¾(S¥(ööÔ™çóSðýä]•ðþÀ AXé IÌá–ÕjÉ)ùØîÊû¦ïÞú­¾ÓBêðÈ[ÅJàw¿*ÿéÜ` 0ý¢. rêhŸÚÂå–ä­ÝÄøìU<’ü ô®wð§ýêò¥õêþÄôƒ!Ä/X!^"ó4ôGÀÔ"çÀ÷vÑY/ 3è2‹þ+¥åþ4ÜäÞIïÚÖ< Yê$N p&3 o ÿnÌð.ôMïãÍù)æ¥ ”û8 -äú’õå Þâ6é ý>ë8G þ ´$4)©çzÂÓ¹ÞjëéÎóLò— Ú}Q!”å–áªã|ø•âÿ"SL4´*Žü/²úÅÁéÈítñ‚àBýkêsûàhÙ‹)%¡#ÂÒù×?Ï%Ðäö;¶$%%àEöãß9»qÛÀݵ¯ðãÂÚ%5”$ê2qùã$ÓÔ’öÇÎïÔbèÓ= Rïve HˆßnóØIÓñsÔHàïm&°~'­#ÈÄ"I÷Ÿ Ûå~ì óyÞ·ðõ31X)\ Y3SÜ7fÔÕØ«GØY0ó p<Ì4æ*@•*îYÇïÏêÚúZíOÅúÿ‹Ò8 !þT‹îæðçßõ’àyö÷ç$xà > ãîÞݪè@Í}$ä¦ Dë. .æpç?ÿë}Q_/î$°(¹3 Ý!ZòQþÍòé- ò8˜ ØÖ(þ+xô Ôä7ó1ébäõîæYž'åûõ«ÖîÖ‰Èßõ‰ÒÖˆø01¥+Ö¼3Äðï¿û»#ã×Â'/ìú9Èä«õ&ßŸãƒæ6ÞÖø¶è5ýe$d$¤(ç ßý“ÚÐÙqû\↘U(™&^/-¨ûÖGõ ôÙæñÙ;"ÝKGE¡)@H¾òþÙË„Ü7ÙF½×Tßd-ËS‡'úÙwûרÔañLÝüþ½ñXTúÈ`[k ÄùÔ ÎÓ3í:ÔgΰôÏØw?ãl#¦þðô’óJî–"Óúî?m-2$z=zòD’×ä+ì…Ó,åî6"˜&»&OÛ(A©Zê…øDäèÞöèà)/Þ ˆØ-šêõ”ÎßAÚqËÇõ&ÜÞ¹ôI ÄNe‘u$Üö™Æ•Ï(í;ÇàXÿàû&ØÓœ O¾LÇ.ò/ÇF$Áœ)½*ø %òô°ŠéSô{âoçôâ¶áŠüÅèãU2!hx5èMö%õàr0ô‹8ç;%=er6Ø÷‡Þ(Ðä éåº(½Qr.­¶!.íæ^éãì üë6°«+ƒ"žÅ+CöÕVÊéGÒÆ*ûlá®`»÷UJî“úÀìeòñWìw/î´"ˆ£(k%ýI¦Òïå3å7Ël/vñ‡GÖ9Û^>ðÚìà©ëÆô°ã|ûà ’÷"F&$%{úª\æ©ótóòçƒëø&'5í ?*„þ CÞj{ÏØÛ¦ètÒ<)ñ\ @¼õÝ “ÛÞïcÖßÔ;ò ÒG(ó±h{þ´ÊÔòíä̪Ìsã¢Ñmñíu¤2öåäæ´é¬ýEîssÅöRK!-ÁÞàÍoàsÝ »ë.?}#›6»J<ó7-hÖ€çîÛÕ³ŽìÕ". =>CýMýåšñDã¬Ümòà ”õ’÷ z8\åÔùoØfáèÝÛCÿ»ìÚ 4¹ Ü ÓkÝñhaë¿ñ{öpëÒ¡ýê<â)â¥Bå§ÖCßIõgÚµ(X÷;…3(È<Š!+åKóûù$Ûãüü)N-kÌüÇ wïøÅâsê9Ü]Û+é™Õ6²ëÉóÅ÷ÔsÖEíóÑ+Ê‹ñ¤ÏÅO÷ @Ðòg…Ñ4ðÖËÑøîºÖ¥¢þ_ËîçÄ:ßyãŸú÷áa"å+°%Æ È#"óùäî—óbôò£ú3óà }úõ%~î&v.}Ò'ÇÛêÆÍ¾´ ƒÕv ¤ díòí—щç*×ÏÐþóeÚÂÅþC LŸð; ÆÞ^ðºÞ¹áfðÎå<$ø+X V 2Òmúc¬úOÉô ˆÇ'"ú=åI7Ö[Ù†øêÖ&ëþ;-‰×7Õô–÷ç"í+õ–ปö@-$º /ãT ÎÎ&á«êœÜèýýõv! Üý†~$$ÿ/óðõMÿéêD­º¸õœá5îrå ÝÿRâxžÿe¡r&³èŸòoé=Üt䳨ÿ£ šýaW×Û÷EÓÍØÐêdÛ+´ñ›8 c<W ¼–$ô¨·íKíçîÁ6¶!Ü*øB>n'¦ãVûâîõß• Œï “ü‹í²ÕgãâªÑþþ¹âÔ qÿþåÚò÷pðÌñãë|íïè¬æŒï0æ. õ'R ÷ð; ^ÝÏò›äüßw¡é5-"&>8G)‚@´"áôiô*ò&+Ìâ3[3Ø8:„ðqùߥî…ùþÝ2kÍ5;1ÛÞ!{v$¶þµ[þÑèWñÜÝLÚeóÍâÿ«ïÚ–É—ìçÈ(Æç„̶ iî Uÿ5çãø(è$Þ  çH%Ü@„)á÷c—åÄ÷í¨ä,¡ð@1Ä/5.P*eÿ#Pø5ú\ÿÉñžÞô´è7Ò2ñúý–Ú†í·ÎãÉà/Èuøç<ýªëk2ÉÄéêÅñÂ/î Ì[úf¢|ß©ÝIÝ(ùºäî§ü ,rùÿ¯ú"{û/ >ùŸÖŽû"7ª;½$C5C9ÄŒ.¶ûމïÜíóûç‘Ã!×%¡µ+ßïÚÀàmëIìºÜÿ —í…$;¿&Ðè·ùáâ£ôMãÅ!ôîýðÜø®YŽCã øñ”ò´ÿïb—ö]T ~ö†Áóïs ›ò,þK,t5Øýþ$[Ú+ïpçÜÒ!Êê»’ ˜ô# ºäëñµè´â"ú–æ¦ wøå™ "ågzõÞÿ¬ì»ê3ð^á³þN馗ÝÁÍ÷Õ.ó¶î·©í#U «½&5ò*€Åšà;Ö"¿%<à±A 7îhÖëî'ÞÜIòêâšPñå ™ÿؾ:þÄððÓüèè írìÎåOø¨èJT[-A/Ѷ:­û-ôáó[tõ >=#>9ð?šì5û{aôûöÇô¼O „ ” ¹òç¥ò¯øôònñýðì×ì¬é9î|ëŽîÛñRêwñtåpè·å¡Þ€õkãŽú§ àúŸ\ÖŠðÚlÏÚ Sá‡1‚||5èóåôÞðöêõË8PA.9;.z=CC*× fûüûøTü…øïýÕS` ÍôÆ—Þöé¸ä9Ñ;ûÎázÎýáð-Ð'è%¼‘Å¿Á.¶ZäÃÆMúñìÙþõÒˆéÙ‚ÑSkÝç ‚‚J5>õ Öõ4óá÷Þ ²$J!œ!ñ*ç ò*þy'Eœ]ýþy /øÁ)Û%:0Àú_!Ü5ô:ãÄÙ^ÿ¤æ/ uý ŠêYjÛê¦ÝCØÕèK݇ð¢éñqóeñ¨õšõœö›ý úæ=þÈ aD‹ …¶Î £ü Sù0úßQù%%âø)Ëò¥Ïã»êåæ&®`+x(\ù"£Þcû ØX݆áYÖöÑâÿ4úÜùúË÷™ýþý«ø0•ùrõ¾ôàñ#ä.Rì{Ž ¯ô"Cçj –ààà(ç@= žç=÷£4èïô‡ïäÈRé!-‚À$ìÅÛ€çáíÚö_䀡ù¢Ÿöš5ç‘õ‚è"æ ðfâ•øä}oñ\Qš& Ù3&Áêé´ëaààðW8$ $æ=¾û¬ ìëÝûäùîèýþ^#ïÉ7)ÉýŬöý’²÷á PòüÏïUûÓéÆðKæ¾ï5Þ¹ç^ÚRÛŽê€Ùá¼òd )@õÊ [æðËèTÝsâÏ%œ.+œ/Âæu ˜æ×ç”›öÞ2¬!;ô<µ) B4t*â -Þg #õ ¤êd{K ‚Œ÷ —ÐÅé ÜDË­lî-péåVÊÄXزÓW»ŸöQÐ,šîUþMpÛÇ÷ÉAÓ‘àôÈtéo$\eµ% ûFóôOûÿ°öÃ.")¹ã5:);3ê5#3 C†ÿ®]Yú¨k+„Kà ,íV `ÔçâAÝÕÏþöJß ³ü{`ÀåŸû¤Í®ÙmØÆõþzÚ¹ðü{áTöµÌ ÙÜüÐùšæüPÿ¥ A z³›' #?*[ýs¶ú„¿ <31'Î!}5Î!lï7tóÓó"`ü7,,q'À5j ·(@ì±VåüåoîÚßÜù%ëVüÞø8ðãú…æôðEé+ëvîãìÙìoî‰å¾éeøì ŠAµ’îú÷ë"åŸjï’%As¢' ýŸ|ðøü’:ôê Ñ k0i'‰¿0.Ú·JÕÉÒ¤ú±Ø;)ü*,Å !Æ1gn4ôUû åÄèêÜáÿ†ìaÿy ðìÚüÒߺãð§Ø7úñ‚/#1¨åùûÕáôHËéz÷S(ÛÃôÛàHÞÿvês ôP¶ù4 ®ô@÷:ûÀõßñX÷îâMä­ê€Û—ü–îEo¢èö€Õ‡å%ß2دüZã,'"Ù01ê-êSˆì=àÝóï2é(±W0Ç Ü ò?æ[ ð 1> W ü w ]×d xúS´ò¼úïðõ*î /ùrù¡‘á’ùœÒäßpÖëÐ(íFØ–ýlò;ùÑÿàáâøƵկåáÂö òd,ž"(Ü'÷øÕ uÿêùÒÙ_k’8"k^(?'¾¥*- ~Ö@Ÿ‡?(qý,ë&&k,,÷—ÝÝíuæ%Ù’úöèFüÿlèÇÿßÏÑåþÍ^ÌÆäΚþÚå$,ü÷á¿óÿÏàÖ÷ß…Ïbùxä=ÉúQüRþ&ÿ ½ O_© ÀÎþa.' >u.d6ò:r=.ÿ÷À ÍõÃïóºôò6Ž9(µ@¿úˆ%æ(øÌîé«ýäðsúý†íÑùñâ1ïçÝáã/ä1߬ê\åÞê(ê æëvæFêËð®ñXð³úXêòâï«éþþŒòÀ Ú'óYöþÙü?ø7Éý|6EK?ô=Ú¿=íBÜä·þ–âÕ(¤™Ûý”øÐDA¸ `ýëùÿröK ÈüUÔ 0E nô þõápèjè÷Ùï¾î“&RoƒæÝþê÷ã›ù+êR"™ü¯íï¼ôçùÝìK Zú&>ž×ôB Úï"úVú|÷8Êü±ýx™ü\ À•? §öÞììî³÷ÂæÝ Î÷Î6šÅ'&öSØ™èiæÞ̈.åÉ$"¡)œøZ ¿üs{½–ÇöSþ Öòéñ¥õ ø³öáì£öçß­ç.ãjã¨ðFñ–ôðŠäþIã¯äübä8ÿ*ü´ÛŽüÆÖÊÿÁŽéJËìðóø& ýûað²üAÿíò¤^,&¶#ÓpI q毯« YÏë "›×Ð#³1%ïú15ï`òüÿëpyû"ÚØáù ̽էäÑË~âè¸+õm |×3öÏIÖ è~Ò¨‹êIãÚT}ôÿ] ú”v öÓ 4IO=%;Ù*>ôÂ$Mü=H~ñ ù‘5Y'á!è8\ì’äâwßÛÿãä­*6 UãiÏDãßÐÉÔ^Þ·Ó:ê áÜsçDÐ×äÛ2Õ÷öæ-ûeîõóôÁá‹öôÖHªøsqÚ`ðËóÜ2%Ô]?b4+,CGæ·+úà Óÿ´ùã|Å óŠgø% ºëà÷åìGï%ôVð–ø>ô=ùÃøÜø£ýù«÷ÍüCé$ïÌØ“ØÛÂÌ©õŒÞÉÚuõª 6߉î/èbÙ síCÎñdÁîô­¡ñN$ Ò Ô Ë !)ôÙîáú¯®˜ÛÍG#jêb+Åñƒùß“äzôpÛB?þç# %+"#¹ø6×åžä(ýýÚ‰|þêßÉýh¯áNócé³Þz"ëBgk„nê ¡­Ahêö{ê'å úÆôpö'­Ý%öÊÔØülÝ+",/û*Yð 7Øã„êÝÓ¯äêk tçÛEä‹ò.Þÿ¤øƒ¦ød¶=Èçµ+ T=Ný S&’#'Kì?ù¬äRã¾ñWäE¼ýÍ÷ºxÙ+úÑžÚxâ ÖŽû êŽøÛïòÚØpõ¿Ó/Ú€í Ö÷Žð—ý¤ëúåé¹ëz<õæ 3 " u ° £ ”šþ>;Ð)P6Ù90­'?ìÿöÈîŒ úʸÖò…Ê?Éäå‚Ì6èEð§Ú6çÈî$Ûä©ñ^-ÞµÿçùË–ë'Ì:ÊÍó^ܤ¤¬ lûuîíô¸öÝð­ý?Ñ%é`’J[ô\F1«L…þP ,óWûõ$Н-R(ÀyãØQíP×7ÒáÜ· ­ê ÙÏÇÕ„¼ó ­ç¦‰œý“) ß÷{#… . eÕw`Æ Ë¢ú Ø~ »»^ͤâ`ØSÀ% ßçü7t$rç÷8å–äoócãÌÖðd§ ] /Jý® yÈüò0!†»ùÔÀ $Ð w~ î»ÿöêôä" ”ïÇ+ýË 3oáú åäøÑƒ$3ó¸@**Þ!ó6ûì ßZêççæ…åDäåÝÖýSâ0& ÉÉÚDùIÉAËÂó—É0úô0iöî:úÏî°ó×äTëïÝ©Þ}è0Ý?Û÷ýó}2ÊâêsËüɱñ5Ötx ²vìü‡è¶ò”ù×ñ¾w/§† ÿÏñ¬ÝeíAà_ßì¿Ýër",Ì;!Þû ‚ê±î’ý˜âX-Ýr'Ã/§óq;ηìºÉÉÑÛ¥ÓSë1â:øµñ`žç Èeˆ÷þ iÿÓ(Ú›µ*Zï/[â8îíüÍìÔ¤Ç KßE˜ ÷ô¯;ÿ˜$Rü÷N–(ì>HÉS,MÒ!ñªá2ÐÞ íêÜ¢ "ÙßûP æ¦øîß]âìÛ`ü§ê NÿeÎ ùÛ ‹î‡ýìå%îUìýäSüðáÈÅùåÕõlæW߯üÃä¥ûtü[ òò÷îûâðé\üAdö B }æ?ˆÂùßÎ-ÀÎÿÙæ© k÷¡ 9ô·üGóFù‚åsïäá ÿcípåŒò ³}㲬‘ª¿äĸ- öfƒ!yí8ûïø/ú;÷ j™ ó‡õv [ë»öùTì2!F)m0$-* ·"$þ ñüûÞýB#¦7%­%£j(pøƒÉã:ôïBå’L÷ñ7#b.Ë5€“&ïê7éŽáûç‘)6…,a([ #`ù\Ùü6÷Àþòõè÷íóB÷Ññ¡‹ýÌå 3ñ‚­Þäãø]Ù¿Êÿ ‹"÷ü\Þ“öì˜ä›ÿ«óËþ7 d ׬ô…èêgôÒó´èxÄô9=#n1òA×þ&#ã\ô~èÉßüÿ§êLÿI„xýêkðêüÚæ)Ï™É|ç•ÉXðïq$‡âþíÏ×BòòÐÖ;àÌÕ4þ2ë÷g lëI'Ïôì;Ñ.Óañ¼Ü´ûó ¦ ‡ˆí aÏsÒª%Rã:.8DA4éåÅî*åàßò5”)ª0:`ýd §õ_ø‡û>ï|L÷,^h+-ç¬%éÎÍô;ÓñÉÛùUÝl…WïÕ¿ìOæõáâü®òh±ýƒ s© ð$d# Q"tï.WêÈå’åïã']¥Z&,ª†ïÙÿú÷UíŽý÷3JË5{..„-Dú máî”ãê×~úüáš ÿk<MîÆ1ÝYïãÔ]ÚœÚùÐèßÙ¹û*ðÏWþááÿùÕÔØ=ì-Ûµ_öפ3jåãÿõÚÇÝüÖÛ¾'ÈX'=ò÷Ûãëgëþà  ºôæ ¶V¬h!Žã©üëѨ×ÂèÖÒÍ .ö*>ÀüÿJç(ú[ëªè¡ãïv?ƒ1±#˜0 70­éH ×ÐQÛXïëÍû&ù&a÷"@çÆý}tíÐ0µQÿ ž'þH Åê²øüá¿ã1íWáä¨õP ¶dù„ ´ç¡òéç²âðPæ’))Õ/N+ ‘(eáÞÿÌÚ­Ú%÷Dßÿ¬)Ô!¢"Ò1e ÿ$©õ[óöãí„ ŽïM'¾Á/Ì(Z*Dòj ®áé êpß@ö§êiÿù¢ÿ0 ÷ý'åö¯ÐÛTÖOÉåó=Ù¾Vü<çðRŸÛkÍÅÀdþ€ØJ"F c%ððÚ÷á¿õ¬â¶å¥ò ãçSô€w Ø z#è | €óÓ ò)é… ðïµ)èÕ2J.Dy1‘ðY×Õ é”×ÓoìPÚXWóÚ& í'íÕêÇØDÑuþÞëKõJîò “Øól×ÞÉçß+ùòî'ÿŒZ dÕÈÍ+JS;,H8°:Ü;2³ïÄ Hä†è ;êS:mÈ6Ó;ê >*cêhþ°èÈà®öá6lîÌíu ÿ,ýtßý÷ÄÑ%×}âaÐ$ÿéé“»Íö«ÊêºøËìÎí»ù í²%ø.‚DzÝlý. kùkóµ›úŒ8 !·)+8žð[×ÏïßãÇÍýïçfC¤Ðü“Îè§ötæDá½ôAàbUðŶ ÈýˆÌÙ¤ý’µ˜ÓqÅAºˆõƒÖ²á¦ Dë±Üè‰çÇé±~~%Õk)3ÿ†qê³ÿéûìÎüæìÀ›XÕIýâ ñ)õ‹ÞJbü¹+í`‹'WýK4õ†ÿKì»ø¸æé…ø¹çq»¤'K$eûI ‘ÊélÎYËÂÖé7 #A^A›' D‘ýS$sñ‡ù‡ ÆòÞ% ÿ$["~qø âé%ò‡é’äô èW(ô ± ¼ Ò‡üÖ ÷öéÑï•Óÿ7&<ßû^ÅÚÖPÏðâø\ÙÔ™ ¼'Q( z$1ç¥ÿpë‰Þë]ïT@ƒ"²5ÏAì .›ö ŠõÀö?ú·õ$ùcôFýé2¶"&÷3<õÛNϘÞíÖËÑù' ŸVÝØÂýËõÚ9Ö>Ð;ó¼ßÐ&ýïEÙ\í¹ÏÞתÜHÓyúÀæ 9GúG»ïÃý8ú!óU8Êà;g7­$i7WCðìùõø¼é/õúÔ:l"˜;ö?¼µ6õéLä%íöøÇß·">ýóP!Ší,êÆ2ç˜ÊÌËhåQ×ðuì=îÑò,óŠõ2+KúíÏóRô—æ\$ùlˆõÆeÛ#ïí‰ßwºö›;T>Ó8Y*¦4v ·2=ýø …ô% ð.t$}E,ö«ÁÕ¤êÜÒ0ùÛߦ NùÖ" Ž Dïü±ßHçLÜ_Ølä^Üð÷0ï·þ…BîBÍÛoò¸Ö)Þ»ï½ÜÒ ³÷ 2Õܨý¾æŸ×s€ö 6ë%ëõ3µí¯ÔæŒëÉ÷ îÏÿôùÔþJûo Iü3„µîÙÝùøÊWÏ.øWÐïÜÔŸ"‘vƒí°°ÞþëÞØÚÈìÞ^ûðjßü˜ 4Î)ôÆÎ§ÎÄ—ÙÏ2%y/àœû×ÿm ÂþWø ¬ ê hئŸ ™÷ìlßíñ/àcØþ å·"U è/dÒåàõ´ÜDÝSóoØÊoûë£$Îö…Ïð’Æ Ë+ÝbÆòÁà•"¤ (<#Ë×#Ç/ùýÚøµ ù.Rµ643ç0¸ù.Êñ´øÔôñlöññûVôÎþ/#÷î2 Õ‡èeï|×ûÿ\„#á· §®½Ð‡Ä¯²£í Ñ\üò°øèü(óÈûÄòyúºïÈóîðõê ôð7 >¬þ3&øCðƒòÏò¦™ õ$…öŒ+'‡ ò_èôáî óH¦ »!û®‹Ü•õÙ‚ß‘âÍà´ê–êŸâ°ï“Ø&è·ÑTß)ÖÚÕ ç:ØŒùæŒúå k ºT yúþú:ö ŠüÁ*NB¤9·˜Aµé9 bæ¥ÛPfèFPo&™8”E‘S-ÉÂ”Í ×ÿ×/ÿ±ù áGÑ'Úƒù^É<ÊÕÝNÉóí"ÜïÏé‹éœìîîHøBöõù£øÑüŽöäÌþó½ ´à¨û|Ô~ÐðÕà:æ$G&e8ÿ+ñ=û3 =õ¢$£ 26†"N0h2ˆ,õûtæöá²âcæÅáeøí檃wlåŸÈ;Ú˜ØúÁHþ±Ú®H;Zç\ÿ³ÜŠèÇáPà¥óIè¯Þú0o îüÛ ¡ó6û<ú$òöšù(6y&¹3û'Òé¼øêûió6 ÞØ‰ð%ý lX*æþbΑÔÝá@ÈÁBì,,k"¨þ•'%ØÉòêÚÛ®ðýàa Oô H -2ò ýï|êøüˆæÙ þö! ¬×$ã "×)ï@!]v ì'@ô÷ù Lè ôçÍåùOèuñÿzU ôì ùÞì†â½Ù ¿äo!Ï Ó#ì!Mã«Ò·õfÒ Ð"ó6ÛÎÏûלœ²›ÖË˜ØØ ¯¬)½ D‡3c-¸CYø±#ÊÛÇï"øeæ? 6’) }ðmþJæ`ó\æŠë®è+çÌî‹äóù¥ítü½üãüÆÚÛ‚È~Ä[ì0Ô¶ ¹ú~úõ {éuõë+ê©õ<î_†õx qý!%"´5F-<(Ÿ;òûn îçñ‘õ*0ž8¢9` :ôŠßRô÷è¸âÑ„ð<áwÿ$äøöÿ£ýKûqýûOíÁñ=éýå¾ð¾ë:ñ¾ö;Þ›òÈÊmÚbÓ†Íí)Ü™LôGÓ‡ú‰úø7 ³øI,ª .Ef.¬9C93øaÐî$óЇôÎ0457­N.HúM °õy H£Uþžÿþùœ ³=ù™òú¤ËzæÆãÁåøÉi÷ïŽènõcØ%â•ÖOÕIë¾×Xÿï ×E „NøKÚõ½÷%+øÐyލ­Æþ° í'ú³ïRìjòçfÐ!v"ÝÄ&ºùö‡èÚö`ç:åwîLãñýPð{kÜ÷[õÚ¤öK½éÐŒÄg¸¬ïVËeIÿg* åõþ1åPâÐƒéØ¢ Ϧúý ™ñÌ[ï¥ûÿB›¼.>$e"Û5AýçúôŸñ£ õ …§U!ƒÿFáê_ýóÞ¸éŽß-Ý™ñ¾ßû Ÿô<ÐýíÜÙØð]МÌÑôGÑó|ýÍ>tË ÿþ¿ù úÁý¯óü‡ÿd#È9%Ä(à ìþ”Éýäú\ˆ'ßÎ*lù:bàƒðòñ8à© ?øþŒÜÊóóÀòÎwÆ.¿~× ËIìÈÚÒ÷î'ì} _éÐÿ«Ó–Úà/Ìrå…E -bígõú¢õQó¶ Ûø(à6<Ê1ñ3æA Ô-èýaô²æ†¾þePênwÑåæÅÚuÚéãçëàéçÖè:ð¾é°ó:ô¸åíí/×ÝÙ|Þ¶Ð üâç` P gE1ò¿KäíéLüØã9&ôÖ@P/æ/ä@B j%ýÊJ Iü€"‰ ¹8º*¡>·BÞ+¤CK™)Ò÷v³òáïáúò Db1Bõľèî1ã¬ágç´ß¸ó…êACúv 7 ‡ø>;ÞÝéÛåÒáøUÜíóŠ÷¿ ØlëÑå^Ô“¢ï£>Á%Ì;ò®ŒÐþ €H4 5 ÖOYö—äæfñ…ì äûðSÿ-`ígzÖ¦êâÐEÒÌØMÍ…â]Ö.ìKã½ýöñqÙ÷õ»ÿ±Ú‘åñßÏó®é¯D-q07Dÿ»!Dä0òpôæpðüS"ؘ%¯+¤Ç+¥ ÊR Þ •Ù} yë ¥úÖäSõ\ÖÊßÇØ ØaäUàÄíî™ì8òäèóàuÛâð$Ú ®ñu@,Ý×ø®ÖâÔJõÔÛ˜ïÿ96¬,#1:¿­­ö­üá4ø2ùô4ý6œ%>8”Y+ó0m÷T »ç‹ð¾ñþçóíÿu'?$êùw¾ÔgäYÓàÈOå9ÏöŽãUòIðqæ+ìéÚpáÞœ×ñ+ÝÈóFÍXõÞ/߀íÚÔc÷ ÚÓO¡#w"õ$(Ë Ù ¥ £ü E(R 6é0`8ÉéKsÁÛÊÈè½Ê÷ùÕ¸Å`øOÚ1ìÜòÏóÌÚEö‚÷ªäôLÝœâ‹Ý%ÛžßÝäè‘äþªô=” –C )c°4ü=D<‚$z;0ÁVé}þmê"ídˆôÏ÷ ™"#'"û*lñÒÛ¿èpåvÕ9 4íÏ&^7%Aï Æáòæ”ã±Þ»æ6â¥înçEi÷Mq ôPSæyÜ­Òi×ã*ãÆ+n.¼ë!ž#Wqîx%# z'žõË9áüô.ãxáöé—ý¤ î„þV áê/øãïáyî¥ÜóÿHì¶ ¨ÿ  ê°üIɧ؜½Æ»¦Ñ˜¹¯jÙá+ Ç&’,P ¡?û)ÿ®Ôò#Dº0&¯!72ó Øôè­öÜànÂ5 Ëîþ[ƒ+âù•ÿ{ìòÔîéú#ù˜åñÿ±ËBÚφÆrðeÔAKøx¢?ÿâšò_ê䨅‹ë52ˆý3~8;±3²æº :ßã Õè×)ë¢8=7…)Ž=!¦+Ö¼Ð÷ûÏï¯õ˜û¿õ'@ !d%Åâ!OðHAåÖé"ê æ‚ó{î¬ôk÷²ßIñ”ЄխܦɅ#ÛÎZ®,‚êöwíeçš@ì ã`,u"‘+ÑþÇî%ûÄôïíÛö,Ô ô1O$Õ;‰5t1S9 ¢%7ìƒü´èŠå‚ù¥êó \àïðà0ûd»êÒ/ÁN­ø¥ÌÃ=Ûû@¶ÚXíïáíÔ£ýä ÷Kø"áÏêå¿â Ló÷ Ë þì¥ödõðzû²$ç++Ù/{%)D c$‚r%» Ì=ï1 '¾?îUdÐñãœô„ضù?.ÿ„%ÜhøÃÉDÖjÕIÉæðŽ×ÉÖóæ¯ £ <¿þs iîéõååöé"ã‚ý¨ð Î _“…õÆÄþŸñ.%þÈM>7B=\,ø¯5˜åàë%òˆ-X Î Ä-)Ò‚Î>ØíKáéõô ÿ0Â4 Ž"Ùò}þìÕ¢ÝKÖ•É÷òÆÖ¦“ùÛ#Vt‘ŸÕðáÒ“È|øâÑc#­ýÎ7e+\w5$óPTâê¶ù‘ã1ô³"¥-Mæ& ˜þó!ë òé_å®üïÄ PCüú îæÒîÕåäÞÊô»ã¥ «ù… b¬é&•ÇcÙùÖ¾§eá!%ÃHHÛ¶óFáØÜCEíI!Äè.{%04%45K$ô÷¸Sï8òi÷½ôŽ 5$¸ò–×åúîSïÒå‡ vøü É–þî êÈØÝ„Ì/¿vìÒÆûö\åpüúʙ޾â/Î# Ìé$6 t(]#éÅ%áh‚àŠ 0—ôÕÏq#y)óç!%ú• Üô"ó¦ú,8D#ú,¶<±øà }Ö·éùÙyÒJðyÞkÏ÷ÂÓ øÏ]åmñûÖ„Õ ÓÄbÛ#ÆŠç%ÙZïªé™ñ@îöí•ÿ^ïà üÔŽw ` & û·ÒΘþ [ 30D)¼†5ºä² ÜÙžàR öéDÂ#é0dC¿˜&–ë‘ûÚÅçMÚ:×ÈÛPÔèèÜ÷:ñ²ü³ýÄñDúÑØáГÉCå·ÍÄ&î +0´`(ºë{¹á9á%õááYFÿ‡È— Z7õi`öU÷,äÔ2(“$6 Ú&žï+ ÌñCöýbøŸ ÝÛ ë •ã jüÍŒî.ù8ã•å(ß>Ù$îÛm|öÙ@öôÈìß*Ìñ¹þFÓ„"?Â!†$ -èäø©åäGó£äóøh J ¤# D× Q X Á Q ¥ @ Qcaý~ {íýù`ã€æÈêÆÞJú‹ë§7üƒ¡êô(þƒêeå ÿ\áÕ&®þ#5,âH+ÓóèÌÐæ¤Òà°ô»' þ–)Ðüôòô9÷,£öÑ ~ ¶ îùMÆ-&¸å ½ËÈÙ*â\Ñ ³õ–I˜ó“ eáøæŒç”Ùðý-â¤Lý†£ œä†üÓ3âýâ ÖÉëê Ø GJù1 /çÒôÙðwèý qúƒ(1¾01R'W4$‡(¼™ÒðÎþ•ó§ïu/þÒ4v)O<äÔ%½•֤ͽùøàaY ý x¼ï{“Úƒâä¢Ð¶öÛm÷%íê`í&ç å¾îÛçî÷æíÔøFô ÷ö; ¥#Û!ŽÊ  ¨ÐA•#˜HC£0kB Š(Ÿí¡í¯ïooøš&Ò<.µ0—Õ/lûþêç9ïñuáÄ íóèÕ ÉÏÐòÒÿ æ­ë>ÜSÜÍҭμâ!Í…èÑéýñ ŒÝ=ë[è¾Ô3îïú%¦‹+‹5Eüp ö½,•¬:€316@úÌúìuûÛöIôüÞþS, ¨ó sŸûÅ ÷äDôà›Ý¹íßïþüð:uFîïûËó໲#¾d¿ê¯|í‹ÌXÂSË$ÿ–ã¸ðçSÙ‘ ËìÜ’™$`ã¯üäéF°÷Ó½± 3x 6O…\!Õ’Bø"üËÿáôë"ÇÚá÷WωÛùÖZÏŽÿ±á6$ ±(ä"€+ôÚbç°ãðÍFÿøß/iŠ…þ aZöÿ‰ï4óuüJò•2ù!ô$Ì)b–'2 mmVGºxFg¶X#¡ø îéìò/÷Îèg ÷¹õ åÎÜÓôùÖ‰Ñú<Ø,#8v$’ ¥Êœô„÷É÷Êînþ$õˆûy÷¡LùЧ9 ù¡ÁÜ©ò@çFàûø&Iÿ.P5Ð0 ëÕ’ܳäÝðàtÅý ø¯MÜöùäÊèÞeͨÍ&ç»ÒmÿaíeþÙý}ìÍõõåÝâîô’á®þ'ðñúóhöøïûvóýÿ ÿÍî¹ÿèç1ìjþtí,$’ Ç;.3þ)2@ç û$½è¬Só$èB+3C9|?ùò9XãX èÛ"áßó`äüÈøáõ-úöw÷éÿþÊöt|àÄékÚÌ2òωîñx %ð(÷âõX÷Äê^WKŒ÷wù7ZÆúá"ëOET,NÅIâ-ÝH‚N"Ýþ"<à™(ñ ³G¤Jû• ÿöÞÐî¾õ¯ï\éƒ gñ•ëò 8¶íŒîÍ>ÛmÍÑ¿ŽäŒÊv…éåd ÙãüÄ—Ì4ÞÆs®àŒ@ÓÞl¦4 ÷5: â©P¯ "ŠøC„íXÿðúqö ’v ‹<÷ï [ã ÷Ü0ßtärØ]û]âOf§„ÌÒë€°í·¬È%¨PùЄíüåõ©úîøìó ò©ÿÐõ[ù¹Ïý? Ç ]gåþ q±  9  }OîðûóØÐ÷øóªù£!÷$^l :óÄ÷ûîÊèÈ÷üçúîóé.þ.úWý ërð„æßUò*Ý… ôìÁ | ¬í8 ŠAù“´ð;õtõ;ñèçÿÜÝ×å*[*ÿ÷¤ìõV/«ˆ?K;…G<ïá| â!ä±÷Oë:åûÚüèÿÔéJõ}ÚIäZÔ[ÕgÝZÐ1öÔ×Ѭô’Š‹êÒáaôæ×ïÙ†êÝ×r²ï“9ˆ# ôù² ÿ#û+¡9*h;8Kø eB’ç%ðæÿíTüRñ VÚð/•߇îeÖŽâŒÓÙ¡ÍUÎ1ËèÀhÔ¡Å€ßÀÕHðÅævù,óáÿ0òK—òN¢ûÚ3‘Àú-ôjÛfçö(Þ™"˜q&F*+†›'Nñ%)$E&6&}&ï)!*À(ò0Âî0ö:ÝòJÙèÙƒã Û|ïCéMö:÷—ó¤úWë]ïãáMÞõá¥Ð¨ûáØäŸü¸àò¶ ÙRݤýMÓ˜rý—Ÿa ÛŸÔw Û÷>§'Æ£.F*,1!I0qÚ#Nö‡ jf &S‚"8.7$(Ö‡øUÏîÎðé«Ówþ³ñëù[¤áoó¯ÐYÓmÚ6IJöÞÒÕBôÓƒ ô8ŸÝÂì6Ö•ÚõÝJÔªí©Ý!üBït› «› ßI  A) { uß&~(ÒÍ1~ûü"Þ¼ù–á…â¾»ï/ë{ú5&šÙôòÚ±×QìÿØéþÃëx¤ýÚú!ýÀëêóŠÛàLÕ;Ñeã…ÐT¶å½<0o uQúÔYümò”Ãú40ÏÉ'T:@Ä( ö¨_uö˾%~!„ª/¼ Ž$[ôÿ ì¼õrôîïMùF ‚›ÚíùéßåØóàÔìå$ÿ»W qå©ò÷÷>èæÑð˜Ýéï30RfÈ-³íeÝùÚ,ÅÛû7¾12>ª6ûýŒ'úâøuÿœüºü<zÇT h÷> ”ß}ö%Ö&ÜàOÒøËÝžùn¯ ¡è½0ÒÄæ¨ÙáÏö‘Û ø¯l³õšänè,ó¡Ù¶ÎôM;ÿ$7DA-÷5îö=Õòúœ $û 2|„6 @î<ö=ÐãÏõåà?ã1ã¯ÞßõÄçP z7 °7éå vÁ›ÛvÌÚ¸÷ûÎ@ Œþ¶ëK ÇÍêåÒIË‘ìâδÕëÉ îÇŸ~^›s æ° !'LP1È%*80D%+LþMýïÑÿ7þRöhd;4Q(<+7;ºD'‘è±ýÝ,Üjè™Ò¼þÚçÖqÍì¼ÓÑëÆ2Ì‘Õ×Áíõ‰ÖµçöÒÿeêçõðè©ØÿnÝ@&õС ¾^ÕõÆûæ_ûÉ$øu?E0ð%vAe÷öãçýò»;í!—e# *˜7+ëðå¡ßØï±âmÞó æ /û ‹ æ²€ÆÝÆ>À¯å=Ç ¢éúz È{ö ¢å¤ðÕçUÞÏûšã µýŠ&”$7æê/ä3çWpïi*/n2Ç6$ó7îó.èò…øMê‚Ñý7…öèµàäõÀÝoâšæfâtïËæJ—ó–î ÂÕ6ñ½ dÌ¹Ú á.Á@ Aà¬õ ¹ 1ÝåêiÒNàãÛË ñß14Ôz/‡7ê+‚õG ‘êgð„þ]ìÎ^&önåŽ÷ðïèý=üœe  „OäíñZ€à|âöüÚX/ŽJ:¨0TE,Rè2ÿÁÙHÚ›ò¼Ò›1õ˜(rx²&Šô/ (é¤ìù³çEïü’!ÿXS&0 ö 2ô{ Èæ°÷Ìé6íëúqóæÿ\ íI DÝgöàhâ‰3æ'* u,^.` 9(Kâ–ýéÚ­Ö ôfÚ-(÷5Åþ»ßÐôætÞF åìÀ${&m"<B w~È­ 5íùìgèu õÙ á €*%ácŒÌ=âÄß Ôü­éô óÓmsáÓÃÚBÆý¾ÛÞéùü:âŒÜ õ÷>ÛUèÚÏ!õo×è õZ¾¾Dóþ€%üF NY ‡ ;4ã&6‹1Nn1Fô‹¡èíæûzë(áD/ó-57%ൠÆöÙÖæ¿ÊJÛöb$b!üÖûè8üBÛáÅÙ:ÕÉáÃÓ°ï?Þ¨ó¡ º Kóå ê£ìþ’å#5øJ0«›$§#q ¹ù°†úqùŠ#Ö'1“(8ÐU2Ìð÷ê@õßû„@E+û7,I¤¿/Áàšútä„ä’õëìñüÊg¨È4Næ*ÿÐÁÙÄÜBÅQÛ -ùL1ò ó×]æíæÉÑ‘Cï.À’˜-×Ý,ЛÛLà'Ö3ù*éa ÑŸ`]ø Wí¬ý­î÷ô€ùíÐ & Ïaêç`ÿïÔáÞÞíÒÍÅá(" ½%€&J™ßë:úµðáò ëJ6¢ »7¬-jíâÏ_è«ÉÂðêœËo …ñ ¶ ‰êYü íòÐ0ëòèô*èº Áõð´ BÎä=qÙé©èã÷ ô¥+†/ f2¯úá’ðø¹Ý‰%5ý.å !]ýð ëéð…úçuh71&,58#=0€øÊ俣ðÒï¨ã÷ ñ:¢ @MƒN"¯ñA‚çiùEñ¨ïcÖþOÁä'§×ø W°õÊoظÙãcDü6 Eèšõ´ìæ û2ëcûúôÇ ã 2¹óÁئåÈ׎Óó=ÚœTû-,_ ?!’-­"W꯶òüë¢ûP)q”/jõYšÍÎîŸÌæÍFî ÕÇüB ÅüëUþ&áDæéܰøaã ñðßÿIùGòÎöæ©é°äaÝûpÞÎø§ ¿¿ Cõ? XäáèŽôËÞ*ø]E·5!_DŒèäæ}ðL÷,b/4M9!K;ÿÿ­"—èü ê`çk6í- ü,%´Ç)KÿâòÛüâô™îõÿï"?ü1 ¯úH –àâòÅÌ+ÖóÜX¿€íãÀ4­) /%â,ßNÞŒléöçTš {ý;LáÊþÍÛìè¶ëTè =ýÊ4Ÿ ß)Ï V&ŸôŸ žé³ñÿñóå óhG ôÏãH ÇÚøÙ=Å—,Þ¯¥ÇÅõÖÐ,å¤Í®Â©ä(ÅúMàŠ誃 ‹çPüÐ×aã½å[Û—þð§¯}ûlÒóoƒð_üUóÁôL²ôóú1#² \8çNÕlÙàjÖ42¸B3j3¥(*åPüuß!ßÇñMØ’ î%¥‹%!-â„ýb •ìïöï°àpý£éøCÿ# Q~÷;ƒã ü…æ‚çÇúîôª -"u(ûÙ)‰âÀæÒIÙeïæÐÑv÷Sdÿ’åbýjàÒáK&ã&* ÷4Ê,±µ1àòç péóä[ò#âþ1ìÑ7ÿÊ÷¥B*E÷×Ðñ†Û#Ìgˆêõ@%3ô¯EYÙ¹`´%ÒóÅÒ²úªÍ¢"Óü—.•óî‚КïAÒbÑìO×2ðð¢qŸùˆ­ÚÜé%Û(Ó?õ¹×àQóÀ& ×#Á#úþØóˆâQç,ùÜv"‰ýW2Z-Hœ5 Òœÿ¸ÜSÓŸÓäæ/îÒíX%4 y%6"ºù¢Òã”õ#Î 2ôÌ@H9yy9úè= E䣿ãöçaRü#Å "Q(ˆæ!ƒöÓ Þéýò°õêS9ü3&Ÿ£§(hüýgÖMú…Ð7Ôîì^Õæ Vöˆ —˜ðj«Ú ëÊÛdÔVøÿ×c£øÌ Gõ³ xÒSæ=Ë0ËáçºÐô ÷?zz&çÔªéÿé¹Éñkƾ€;¸z"Xû¶àÞ¨÷OßýÛ¨àí’Eš,.'JBñ%2ÛèÄð»× ÿïš!6±ˆ©ù áá¢ì<äGÙâ÷Yã¼ "û§Eýð "âgö‘ۆߨëþÞtcït † ©5þ;%ñTcêDðñù€ëc"¡4Ë/ãY76ä'ÖܰÝÆ+ È<û3[&lAéõ@äûä¯þàw"3þÍ&6ý¡}û_eî÷Tá)é6ۑرî¨Õ" òýw¥ôIÍšåWØëÅ™÷˜Ø¥éùÙ ³íÓ  Î¤éÇÐ=Ѓô“Û#¥K#$±…å´þAàhãôÿìã %C È$7&uÁRò(-ð|óô2ò¯ýäò ý*M,{6"ñc"XÊßÓØËÁÿ¹ÚK#e . l xê`¹ÐúåaØãÌ¡ü&ÞI´ÆTìÇÝMì¥á„áôé†û_ Q jÆÿ:âúÌ13úœ#z¤%11·ãÓøÝÐÛuýïÛ(-J=52c’1ñÜóäUçwüâÞ• :ÿ2ó(N X0¯ÜyÂסàGì0ãíþÀö~–žÌ :]Ê#üørì·îv†è … Sì2ô% kàqíêªÛèk·ÍoÚú*3æ$ìoì ÚÐ^çU ÄëõG•ÙòºÖÒÚ%î Þ&uõ1ý =ýdƒ\íhÿ5ñ3ågù5º#j(×=áùa#”ç«óÑóåÙ Ýññ²÷ — Àêg ÎÐßÔãyÊÏèS £=ýdó/ø~ïñÉë¾ì:é×å5ò²çkã÷¬ ëë~ Ûƒì×é²Þä ¿ðJ1B¹;Ú9!”ïàBõ^.è(K5«ö0!ŒÔå¸îDαLôÁÂà qÇð[æ6ïìäöüVéÏ Æûá )Îòó&ýÐëèXñ›ß—®íàm ÙnTõöì½ òt&‹(,´äK†ÃÚÑYÛOÀÝåä| ¿ ­ç< ÚvéÙáìßWóûãHÕóÈ qoÚû”ãï}ù½çìºì‹ãDÞí$š ã*¬.LÏ7Œ÷Fzúñz!¸ûLAY)Õ6—Cå1˜ù½èòô<Òôå æ šóUXò…÷ºóÛð¾ïÍë<íyéNî)ìTïÜò4é0òjä2åªé×ßµöwçK ¦üy tÓó° £Ôlëäݧ̾ç­3P%Óž3aí\œòà눖øÙ9Ôö<ë6,=;÷Ã&Ä ¸ú%üüûK÷ ü¨úÙ›ü\ ¸ Ì«ëñubßèœæìÓOÿå,°iì¦ÿÆÌ¦ä¥¸sÀ|Çá´ ç'Íþ¡ôåê4ÍŽä¤âzЄè> ^²©{õtö«óY ø cÍ#ö#ä",g~+ #5à þÅüLxûx)i† ð/ó–åÙì|çûØ>ê™ Mù tæ·ûdÜ÷æÎÞ.ÚAê“Þ˜ðÉëðð»óºñÙõ¬ö®öàþû“£þÙþ%¥n FÖú‡ „üù ÎýšVß&é'¡‹)Kï£ â\ç €äˆ3rI$¯.@ÿÆOádù×Ý«æÁÖìö9èÿæúªø$x÷ïúêIù«^ûHöäð0ñHæXGî¯qÖ8 ßçGqåhâEÜêÉÜ 6!©õDså=ótð,ß îìé€Í +×ërZÚ æ®å8ÛQøüè!û)`#ò ç—ò6è1åóàÞúçyóÓ !"‚ >×èâû ð}Þ1·øÄ8¶+÷ ˜>góW·ê´òðŽ€O#¡™(1üU¼ùÅüž_ùz ­ný#öîÁøkè ñÙæóíxÝè¬Ú¬Ù|ñJÜóEúÎ6ôõOäÄì•ìßÚŽaç°)p1Ü2é'/àÅþ¸ìé}èúq5l#7É?Q%}<(b'0 Æ g³ Y JU¿ÿx ì l ‹ïë'ÎÔ«à%ãÜÕhòJµTÜÊþ{ÆŒÏÀ×¾›ûìÑÎ Øö÷÷8Ø%ñ•ÉRÓgæ]Ê……ðq>ä ï ½ù4 õŸùU£ööC_+—e7+O2Ä8HŽ0r ôû¯®®ûŒ˜™[° x`è^ÑæÝ¾ß<Ìþmåâ Û>³ áúÒÎÊÕáÊþËáþü ü^Ü©òCÍsÕûßÓ}ýÜé!÷¦ ?õ¢#W'}h#yH sBü›%U Ë/&,‚1þ¿ë ìöñðµĉ0à#À$²7ÍÉ#qì‹þòæ+æyï á•ûhí9ú]ú©îùåðiê=é»ðßî~èÐî¥é©çKù{ñœ F= Wê¤ö^ógã‡÷Ê%8A0)Wö,tóHøô÷§'H Ò.´-·ˆ)»ÜšüþÙ…ÕÿMÜë`µa‹ =Qƒ3 dï7úsãÙá8îVßÓÿKï.Ðøû3 Wê«÷ŒáñáófÙ€÷0n$×0Sºqõ`õ†ó¢æ×  ôƒ‚ÚÂñâåµÝ ~ðSî (Ñô‰+÷´ó¬ú#ùäïéòKävä¨êµÝíÿéï9úOgå¯ùWÖßäß'ÖS$æÛ'–|4S2 :1{Þòùžø^Þ‚Tÿº-$)$/‚ˆÓŸ” Ú ü Ä Ý Z¨¾ sù¡ñ‰ùŸïMïË÷°ï—þ)ü)ö»ñÞéö+ЮÛÛ·ÏÀí9ÜL‡ó\úzœÖVó«ÍßΛí±Í)6öÅ*>$} è#ú2ˆû{rW~—¥#‡T'9(pp(ß … øG(b÷,©(õõ+Mòj¦ÛÏèÒç‹Õ¥ÿ±ìJú­“âFúŸÐpâ›ÐIÎìè±Ð$ÆêðúûªÝoïÏšÒ‹äßÏ'ü‡é8üûTˆþÂÿ ¿ µ·(±/0@?10Ä;Pe'—ótoóÑçz1÷o7~*"¸:Ä÷!¦åƒöóÄèdüêôEø!ûCì{ùnàµìÖÞLâ&äßJì-å§êÎìyä&êõèë ïÂóMïá÷Æêñïð>阃õô3 ØÿŸ óYDýôì:7| Õ<š;iÌ7èû ä•âˆèétR ã ½¹ú­ gùÆdyHûHùUõµ DÑà% ‚ïžû'â_ã`ë=Ü Pñ_‹¡÷.=å·ôˆü$ä0"ðûë+ü-[ ÏúGfñiô\ûíãý]kÒ9gðVuï†öZþ£÷ÄÌý;¶ün ÿ{ nŸ ×ò‚ýëînë8ùEéÚöøM$¯±¾+<ójÔÜãtêãÊ(ì¡Ìöú{üÞü*” {¯³¤ú€ €ð›ûôõÏôàöaìBóAàuèÈänäˆòõñ6±âûù"äFál1å…0iûê]ÖJ÷äÁñÐÁÎÀÿðyÓ”ùò®Ùùüµðöøä$ô,ˆ'/0!€ @öz‚ÙgL Òˆ. þ¨ò+¢#(Ë$zõ ¨ðíÑí ºù>'ÑâóÑÇÐôèöÓXØê'jÖî¥<Õwï ÐåÔ¿ìÄѽ ªð ÐϋŔ, •+¾ õä ˜5]"ë;U­r%îæ dßþÞÙü›ÞÿöÌ%·'x $bïôýDêpßùþáŸÿ~šö|¼ãöíUì_á½çìx¨ ø$É¥ Ó ƒxóù»ì+ò£ìé¹ú#÷óÎ2ÙãïP×Ö9ߘ+À2ŠçøÉÚ*à‹ë÷Õ í¥ü JSžäD1Ü»ßùaàs‰þK[nÐ5² !^¿. ” òŽ ˆUd º|þýUé=÷£â®Þ‚÷ôä~º™ô€ ñ×Úö"Ñ-Ù™è%Ø ü*ðÿêfíŠaÔ”ð²×6ÖzðFÙMÒò¹ü±äšö1ð˜ê*OûÆ H£ /#gB×*óDŒGƒ(Ÿ?r&MõÎêú ‹í ê)öç'ÖìZõÙïXhþ„{ à!œúÚ ’÷@ù/ý½òæù.óÈð¥ìVìxë–é–ð0êbô)ò¢ôJ¢øðQ~œ:ãþôMã–ÚwëÞ2 ‘$.%wýG «øÖí­÷åt Òã ´ÊöÞû=í´òŸô¹ñ‚ œÿú1ü¹3(3àõ, ¿ÅnÜÔÎNÃIá$#´:¥%©òÅp×NãœÓ€Ð-ëŸßö ùÿW Á€|Û-ø_®¿ÃŽ½Ùª¦ñqÎî#©:4ß)ã&ÊìÈ<Ó©àåè{ÛÓð„Åkõ¸9íó-ô<ñdûûÝ °$ßù<ž, 3¥;ØZ!Õìú÷ÜörñŽ ¹ –øvÓÍêè‹Ò`Ç ã¦×å­ê"ÞCæàó÷Û "Ûø *$ ø÷?ºÁjâTÒ»Âäýgé B ÷>;ñ}ñûŠöé ?F#,ïbN¤X«^S(ðG0úÀ(ï¶ø¹þ)›$_$æ5½Õ„êÌßÑxSë0$ cá¬ÿ¼LÃaá躓 Iöö.ãüš P ›öy&®ûcÄÓCöRͳ̰Êߤß«÷îùÇMÚAÙ”¼qï{:„5$8h?‚#Û0_þ–÷­þÌñü´öˆýòœÿwë4ôŽãïèDÜ•Û)ä¶ÙãòtçåRü!÷ & jãºð|ìÞ_JÏ,zíθ/ÚjÅ®¬˜ó-Îûþù ú“út[¿ò IéÛúòûáÅÜAdûÑ)Ë1öú,yÓÆëÂæUÔÈöNV"bþÿÖýÑþ¹Ò,egöïü\í¼ê®=òNJ¥$ ¨;¤t¶ûßAÅ@b7ïqöèðÈãØÅð $)« 8E %þ”ü àýTýbSñË÷¬ rþ'K, 3"‹×)ø£ÍÌÆéöÐÒW‘#8ì nUfÒÕüþ6ÐèîÑá~ؘÿ‚÷%¦ì†à‹ÒháhFÉ(ô0l@Ñü­%öÑë kÒ`E\û‘µþ&C¥îËôËë½áüÜè[!–âüÏ>èåõ-Ûiâ‘ãâà$ïpó%ñJ9â«ú³Ïûà3Æ¢ÈOÐÀíÓØ›ö5rè ˆùúÉ'ö^«î&Ôì‡* ù"¨ò<÷«TúS,# ¶±*”3© ƒ ]!Û³òjõ˜þ«Gñ%ÿ° -'ØÁ±JÁÁÏã²ìFà0®Í÷ú-èš÷æŒêì£á-ùœäêÃòMzNŠZ æ~Œ*ôüfý^È%ê¯#óÃ_+‰Hm+-K%Gx,ÆAõ O âúcµüYóÖzøÄ€ã­ÁŒã¢ÿ‘ÃYÒêÛ×»|Îãñ?7õÐÍ äŸÖ6ÉñØBÿì;-ÿZh$Fõ —ÑØæýÖɰ'áŠÌÿ #fï¨~îÃïÁ¥ò¦Ø €ÞÇ ‰öùêÜõíÏFÙ‚â…ÒÛíqYÌùú¸ÞÃôýÞoÜ”ößÝlžöC$îé"ßðò ”Õ²æÐdÍkàÏ´þÁëÓ„ •eò± aÝ1çŸéÕ±µít/¬š<1¢×:±ÄÕ¹àDÏCý ì²þVý-¶‘ ®‚¢g¹úsõIÐún6š%ê¨33êq0ÝîÜ8 xäÝ4·­3g5–+ãû’ýét ÷öLþ$+i7G2£4¨äKœØÛáÿ¶Ý¢"¦ ÖÉ!.øARßc÷sÛSà\ÙÁÖ{àTÐ ñYßäÕøˆ9 ëëÍû¬éÑá(þÖã-äþ„(¿ d÷%]ù JåÌîXæ´)q €"P&Lý7~çõ÷ÿë–ýã )J! ( Û&} d(ÝÝg íÂe׆ÊBËé?Õò¸ô%Ýbô£ÐBÞ\Û¹Ôtñߟ©ó"«þjêÏ‹çâÐ\Üíí‡ÒU÷œLç2èÔ!àøràØ 3.$C!^ Òp ù ¡ˆù Žs 4m­¢¾°ûÇÞéÐùò½êµ3ûá(k0/]-|î”ó“ì ÂôÅ%H#Gɱ6ö¿ ØæÌòùäïäõô¾à° ­õG­„ '©ûˆ )(üŸû£Mä® Ú ý˜ Úï»ú|åý嚃êÅ6” '0íåˆÿeî“ÞZ',ø¯? /*2pév×ßèUéEè¯âÜåaçˆÙaêh’ Œ¡ÉÓò.ÔûǺ÷ÕîÄü{¶\ó1=îÇø¤ïÿò&ãdë¶ØäÚÆðíÞpþöý‰í̾èÍôÉýÜ»ê …Ø–è`}çåì5NõU˜R`@1î£Þïì£á­Ýïàõ¶ì'¤'ÖÔ*õÌ ¨äÙä’ ´â=+×à!*âï™ü˼è«ÍMщÝÄÖ&ììãúñòÂÁä±ç <ÿØþȰþu*ƒ'Æ'Pï0ÁåVï `ïï‘ D×/Ù´!!s j· œüj&æòH¬(~9œDoøô#„Ö‘çUæÖ> Pí šMCúqààlöàÈÛVï@Þlý=íˆ3Pú EøL rë~û.çÐêüìç=ÿåï O˜ôéÜâ*ïHèÈßøÿXæzIþú…Ê Æ‘ú* Êñòö:ý°ï™ÿÕ”Ú AèÛÁ¼ ÓÙPÂGÚåÒ•R­ìõüæôdü3òFøâ±îŸâ»Üùcð¬©8ì0O®*ÞV°¨£ó»ÁÒ3û:ñ÷θûløP©øö¾ßƒYñ‰íÅóþ5ñò)¹üK*¡ çuü^ 3×ù˜Êþô$çg$ú(áü%žôC÷ãò·÷ˆæÂ7a$Å+S6y"ËêfûPç©á} ýã\1g~%-/÷÷÷§®üTö«ýžõàöóòÇøó?ÿÍîZêäþ3ß’ÜJúµÜ$°ù5'ÝóOúãéñ)îÒé~ô¨ YÃl …þpªó«èGó*ô4äw =øÀ;â)Á,¤;#ý!áiñ¼îýß ð"ÿü]üž³ílûÉ×â{ÍÈ2ì(Ê‘Løf` úÚØñ¹ÏÔ«çøÖCþNò€Ï æ\ ÊÈåÙÕùϯõgâ þx x ¹÷ þÿ?½u…=(G6?!3†2C ‚.Xá«ü“ï€Ûm"{ú¯1ì!_%*í Oû¿uõö³þÏî¤ûŽ/Í`,F4[õà /ÉåæÓË­ýZÞLkçX&Úóç[长Èýšóþù`ÕþÍ&ü$0 l#ÓäüòñáG¥ùT#áVì$6ü©öðlú±úÙîbù–6NP4Ø1ï9+†óò ûÞõè¤åt׌þvæ| ½Sþ 였ÚÈëtÕq×ÖÛXÑãé„ÛÿÃó ° Í÷…pàñòÚßÜ~í“Ý Êù!±<ÐÄß+ý£ÚõÕ7éÞÔ)jú/'ËèÙÃä”ò€ã~ù¢> ÉšågþKÏßÁõ0Ð[Õeì#ÐWçý }#µøŒPç·øí4ê<Àñ!Ù Á1' /7†—.=áBÑÓTóóÐ-(y-&/¨ú:_ðçøŠ8õºÌ*œÇš Èçú“ÈèÂõ%ßjàñÉßxlýE v^ø °ådñLíßठüì),š .X/ä©$¾Ý[÷”Úˆ×'üLàÂÀ\*n'Š01ÄöáMøýìý´ñ)/ *.á)ìåÝázãXëþß±ù±ìîþûÒþlOôxåá”ñ»ÐŒØHÙ–ÈûßÎUíãŒÂºÖ×LÎÆ¹@áàn½ wžð= +à óæ ä¹ôæJ ÷åB+!J& Õ^ð´ü™óçªÕó - .Ì/ÈM,íÓÔTåãÙ½ÑâðÖÝåS÷É@ „èmÇÍÑáÖàÏÔ.æÙÝ]P¼èQÙ)îBؾޠêxßwûÂòYaÿ0Žÿ~ ÿD /¼?:8/Q3~8™L.Œéÿé³ãUðµ@­ú5EC^’%çVò®éµàùæá2òð…wv =ø ½ÛóûÐzÒ–çÑBðÔëó~„è õ:ïÑìVúúï][ùG‰ú- (Jû(²ù)óú$ù9@„*’»?ŠèI „ЂßåIÑ> ’ê­6 lO6ùï¸æˆñvèÜßõKáEXò'! = öß5ÒÎô?»€ÐÆÊh¿>üdÜ3q ³Z è ÿ¥éDä“WêCʦ&¦"©(²üèµé½ýQìîëÒcñ  '!õ™xâ6êÕ÷9ß„&ŒW0Þ*d²%*ýÇTóžë˜ôné-éÙüÐéçQÒ#E'¹ómµàkÒúœ ¡àq= XA7DHÜA ü,ãö7ùìö 'm_"9#* ï4õ”éáîöèä±÷‰çU ÅùQ¸  8\û0áøíóÄœò·•4”¾ýùÙW÷ ÃÒÏaÓ Ãÿ6à"^ó!ó(Ý6èôúïï/ßM!úõA,+Ô1cAËÔ*Õóbøêõ±÷özõ_¥0É"þî1Jê ðÓP׋ó‘Ñ’¾ÿøÈ&wú…Ö•øiÇdÖ;Ü`Ï[÷læ$ýLýêì™þ´×¸êxЛֿà;Ô‡ÿ0ì'V ê¡\õÝ|î«ö~ÛõeŒç8§!D8Ý8†™2æ“Xð¼öxû¦é± ¢þ`?*Ç7¹Bn3ì¢çfådæÀw¾F‘æC 5ƼàWÏ͈é©ÚkððpìÀñ÷ö‰úú„í„ô‹÷¼èÍÆþÌóqñ Ûìò†Þ¼dý¢={#°?b:?$u5>V· ûrmùd& œ-Y&çÎ*ÈîÕ ™ÔnäüÞÒ¬þ¾á<Ôÿô  Šþy ìú«à1åvÜÆÙé0Þùýô£ýa%ìú;׈íÔÛܨïRàZ÷ˆ ÿý âìö>íBár ÞúË3‰+L¾-´êLJéèèˆûxñZþáû«^ùY zŒœÿhÇÔ›ò ÖáÍÿüCÛ/ ¯çÆ$ËØóêŒçÝ¿è³ÝÙ˜ï%Ý. ¶øÄu÷îÓÎéñÕšÍi ãE1í4 -gý,Zý}ûu \‹   ŽÉ(Uñ^¿à ëÝäóÛÿaèË!  ùÿfß%õóÛ,Õ^ýñÛ£0b"ÝñtÌÍÕêÎÉ¡É5ãØÉD ½æ&(ã&Eð!­× ¯ú÷Éðø¹1î .U2Q]*}øÕ âò÷ñôsñøÀñ£úïöâsýÌÍ NçÁ"ÚiáÂò€àÑDö!(Õ€¤´ÏÈ‘ÊݸÃñ¶ÕÙüIö8÷‚ü—ò2üó_ùBðÖó€òÞêÇJõ¾4ÕM7Œ9&o0%ôëQõ–ð+œöK ¹&é$ÂÝ+¿þðñ•þÍ÷àî Z÷?!)_"2÷5*Û¬òõÙÄÝCæöâ²éžìØâïÕPèþÑÜÛÙ×0êÎÙQüÖé-¬ü1 f ÎÛ ãû§ÿùwøü »ùŠ4ªJ>,@ :ûâ‚üìÎ×B+DôEH3-{4?}½*¬µš; {Oèý úCÁüΩÙpðiÍöÎ ß­Êâï›Þ¿ìóêêëÚðxïiùøøø„põàN"VhÇœØjòëäDÑÅKðæ8ã%&"Ç7Œú1ÏõŒ÷¨ ¬øæ*‰ ü7å(É,N2)Q÷EGç ôáÞâºêJâöùøñ& ûçݼûQÈ%ÓIÚ™Âü1Ý *þ¢çÐú×ÝèäJá`ö[ê*þŠ AýçÀð¬û€ûRí·eýÅ3)"($Á.ÐùÊìO÷~Š÷ö+„m’a K]'¶ä•ù=É.Óæ;Ã~!!÷‚!ˆ'’ûpÒÙ€óœÛÚø÷Kãì LùäE jP©þ[çï5ú¦ï¹åŒªê%‘¨"Yº% ŠÓ¾UôK#lGä3 Ùþó6”äõîòèðãÿý¡ëÀ­¹ «ñ „Ügèžé.Ùf¶ë‰&8 ß#ô(™øµåÐ7íúÐüÎXûÂ܈¢Jò êjºb Ž4þ•+K06£)™JBçôRâ¯çù/î Kв àì"ýÌç_ñoæîëÚèWæðÞã:ýð-úT àú÷·٥ËÃ1ô¯ÚñòþÁøýûéñôIìê2ø”ï³`ö£Iÿó(ìÖ:D3ŒÆ<úæ—òcõ7Uù5¨ 2p<æ13KïÏ«Ýþì½îãF€ôÑ Õäû°¾ø~ü£þü¬ù¡ù¬í†ï9êçnòíôîø^Ú{îÙȸׯԧÊúóŠßÿûôàÉûœÿoüKùúØùü1ÿ±Bÿ2²6!A!1÷ò4 ¡ñPïç­ø;° ê2y># P'þºˆúþ•ý }úœ…›º=íS>ÃÝjÊQ»˜éÔÒ÷ÿî#è-õ~ÕßàpÛUÓèî€Ýé åñöã õÝ4÷€õ´ö0ø’ œ)zõ&ýLî¢ù±ð4ìÖ9ôW ø!…#‡'lõx©çcòÚæQâËñ­äÖýóqòG¸ÖQï½HÎVÈ­¶'úÓš<^ÿÜ–ý¿äYØÏyïêFØq eý¸I ;mß0´ýcü|%Ë2S++8ù4ÎóÑîמõÞ!gÚ ÇýSIéBüÕÝ:ç@áGÜÙõ¯áu )ù™–HølÇÍÑç×õÆzøÑÙ4ýÒ«šó¾ê3ý¸Zùf÷Êþ-ôÇÉc&Ń#z)÷ b‰ú²‰ ùë)´$|)òh•â’êäõíãP¸úô¹ >ÓÔí}¿ÆÉÇpÁ„ÛYÌïîÏÝâ'òmŠÎµÔäû“ÑÂÔüäÌÐtëV²v§ûþ̯ôùýIù¶ô] °ü*,=h5í,“AÌŒ%ÚãøøåámƒC#½åö|ÏIæþÛÙHë¯äèÖê°é®æöðíóCôRâ:ìXÕ¾Õ áÇÐöþýëCªË‡ï$ÿàéë<è‚,™ >Õ4¡)á< ×-üâÿŒ Žûª&k†<û0 Æã Aþ" ¶!¥#Eá$‹òS ¡ÜNç_ãí×"ýÞé>V…Í úDÜ’îþÏ>Ò5×~ËÎëÄÚpö ôoèúÔxâöÔ]ÎðGÖuô w Èé ÿnþÔä"±"I#+¹¨ü~+ÿû;(ÒP0í”üÓ4ã¯ë|ÖÏúâç© HFò¡ váºíYÚèÙÜÛvÏì×Ö(ÿ›ïX¨ùŠ‹ò}ðiUî¥Ä%J')\#(åí O ‚Î Ð]ôØW"ñü‹òÏõ‚¶ö2Qû+>9Äý˜%¨ÙHòP݃Úúä Ýëí5ã¿óóðµócø©îýøþàÉénÝ“Õ×ë2Ô|ëêš-Þm1.1‰7(^êV—èußÊ ÎîÓ&n4 Þ)¬ žOîiXþ ¹ô —GFÖTó[*éÍïªí„æ­üMò†üþ é«ÿBÔ€æHÑfÏaÚJÎaâM×[ïæä÷ý›öNÜ!ñ®ýnÙxà¸ëãÑ"oõØDÁ1•(QCÅöŒä=íPõ©å1°ÿÌ$‘D#[+Ú×)É +8  nÊ4î H÷Ä záÈð^ÕNÝ Øg×âæìáyî±ïkê'ðWãÔåÚâÊÚNôˆÜœ&öþÔÉíƒØró±Ô…Ïÿ!Û²%º *2ˆ.}z5¦ýëú6ùVþY3Ð2Ø8š#~5*Š Åàö‡Œâ,ï÷„æƒA„÷"žöEÅÓFâúÔžÇêþÒòô¼æŒòÛïºãÖì¼Ø8ÞÈàîÕºôüàO†ö ›ñ Ý€èÞúÓùû×Þ\!î"&™ˆ&ô ÄÝ A  å]+éû0Ì2SØ1|ân @¿7ÓàÐ$¿üºÝÕN z²Éñ® }ØQåÜøÐ¤÷—ÜIùµóßÿ4ãñ§ÝXáRÝŸÛ}ßÅÜCìiæ<ÿÜ÷ ( úŒ $ ¸Ó 3ÿº9e9Ôþ8éûìè‘ú ìüì†DöN¡§", â)îB ÍÙÞææëâ×sõr!© û/ì â§ä¤ãÒÞ¦èÜâ"ígéKqö¾û¤•çnö0â‚Ú— Ôç÷- û#º-‘ çòe3 !+§#«$i&Pï†àËï¹æfãÛø#ìÒž û3û§ Õçœólä©ß ï;Ý®Ëí4¨+ø èäÙöɺը¿­»}ØÙ¼· oâ+®£$€+ǵOù øZ óÊ#` 5.))#3ðë“Øàjè•øÚ瀿 $Aÿp—þ¹ÖU‘÷Ó¡è´íÍóéç×<ÿ€Ò "âgü Ê2Ö<ÖrǽóÛUXúódmøÒ åßTì íØ¥¯î@8@"É-#:I ã*pçtaâÍã íÎ-E´6Á9Ê&¶;¨)^ Ìó ÿ$ñnó¤þMú¦û™%¡á0ð>Zärê ëOå-öæñ*ñä÷HßwîÌŽÔ"à/ÄÛ ·ãŒ6»þ¨ aëÅöÛîræÇ:ï7"w €,Ñ$ c,ùežíËõ.öÿìÏ ‘ù 7k2&j;E6’*D7ô¬ké=ùê[â?ÿï> ‚-àÑÖÕû¢´šÄÐ ²ìÛw nöÇØhæ¤éWÖxþêÌóCøQÝèèàkúÕÞ 9ýZFõÕô\þÕ"øW*˜+n*ðÒ¢ ù;&ÜÚEÚ3p!CG"Ýf>×9ÜÁ÷‰àÕQ›ÞKøàÙ`òeÊÔIØ_ÉŸõ¯Úª Žø´z èújÓìsòÀåµå îähÿÉõA¹ nüô: øïüÀû7ô´.–+WÚE¥2™]}ø&pç&ñƒõ,¢%oþþ'ƒÒá÷ÑÿÙÝñþâ½*ùœ ´þ–Æm|M B%ìûkØòØ}ÚÎw÷Ú+”ÿµ{IûqÑ ê:Ò:Â-ÃÔß,® Z4=/i1.ð´cåãæÆÞéº ÷"VaR‚÷ ªý79èñÊèfâ-ó;Ö ¹ú+çŠï>æuÝüçî ýÿÁ ²×áYpÀµÌ1áÕ¼š3ïü%wPú²Ú/ïýé™ÞÕÚô~#=0¸(Œ/]5³";4q !vô=&íî¤ûkö ­ BG¿¤ï鑿xëDôFè† ôü×ÀÔ rêçM4ÈæÖYÌ Àò¤ÔKû ûiÝ÷Ò­Ù„ç†Ô^‡ì|'í£$â$p$!siÞ;XÞG©åL$É>(]Òœø6ßöØò^Âü:=,ç&#@Šñ)BÏÞáeÜÎÎäõµâþqûEö‹bãÎíñÕMÒuÔÃÍܽÈêÛ’ðïìñXï ø$ìlVò VÿâT¿ Ñ!õW%¤ zAþöãAû\!WÆ.ß.1j2jÙbâÝ·Ù>ó„:R*--œ:úÄ$Vçþ…Ü.åÏØ‹ÖæÝÔêß-û£óýQ.íôôêØßÞeÒÇ˧ì¼ÐGY÷¼'ò¡ %éæý~ã Ý0ú˜ål(äÝ<:ô _ú¹5 Å1Á(M +6@D!¤ðØÉñ[öüÿøm à… à Ðá åø3£í"ö×á/äÖá¶ØóšßüÀú4µìÖ ÑÅäÖ͸·<Öø,ÞáP'µÿ¦ÃéÔø³æŸã•÷nçߦü˜ Ñð ž !( “ ( +p  é =ySWû£ëäökå>åŒì@á©ü í±ëÿZ“¬ôsû|æ×ãgoÝi2K y+0«‰ÓÔÃòλΡï×H>ý})v!:+=ø£ ’÷{ô¡Ûø¢è1 AA”Å V Ô"ââ9®ÌFפêuÔvÄÿÈø7ðÝÝ,áì½ÔåÂéwþb 9á0úßÔÞ¢éoÙ@ð] ü J*õ ræñ·ñáèÃiýK,Ï ².•2&à1QŽ&ý¡%ïUû.øöî@²9º-GBBWÖ ò»ÑÆÝÓfÄÛü¢ãå< ¼òìnÛÐà«çjÏùià\õ_î7éHì—åå“ò#æ±øìñôùnóÝGúÀ‘ ’"Õ Þ© ½ß# FD‰((JAGS*ÑAÙëg˜ððñ uüp(D¨*1©\+”÷µøæ4êºõ?âo Íø²zþ'õî{úå®èêÙÚœÖÎÚæ(ÒS íàü ÷KËÛ¯åËêÜÒžñô'6$²i)%½7û Æmÿ.aÜ5#3GÐ2·öªíí¼ø7ùfõ{Þì€Öñ Ÿ§÷¦Wæ ñšá#ßÝðZáΊôÆÿá&ëùÉÅ¡ÝZ¯h¸Ĥ­ÉôµÔµôG˜Ö÷2 ŽåPëèí¬ÝŽãòùÿB!Òà£ý°æ<è îø!E'?t © ’¸Þµ}È“÷9ùÙ0÷Û Ð 1þð õÜ õ£ÌÀÔçÜÑåMçì+/ \$Ì%Qú¥ ’ßèãñæ)ÒäBã…‹Çã[ ƒ‰óCîÛï=òÆ#Ç#s)O¦%¤ õn2ÉÇ Ì Ñ ‡ýã#oôb Óé|ïå÷Cçwõùîýn õÝâïÚ ÒØiÝ &È † † îŽóò—ú‹ïwýÆ÷µþqö®Yþõ²òoŸß[í'êƒäd uû+°#+j6lm-CãåcÛéÚø|åœ0ýôB •ÚRø5ÉèÚÞÑùÌ×ê¢Ö¿cðµüHFèIòÙç à öäüðEú¹ôöÐïRþbõ·ûî6î_ûqèªë…$î³+îÎ:o8Î*(?Cþß"Íæ°öNýhîþ'£~5P;d 5LàÒá®àµöBèÀûyûÜóZø£ø‚÷ÿOöîþ6ÞdçOÜuÊšùÈÔô %ø6 ·ÿu'õrò3ú;ê·¸ñj¸]xMúY©ý&ç 9Hõ/¢IKL²%BåêŸàÿýR ,Åó ªGüð€ú²Àö·ÎìéóaöOé¬ „ø- tÇå1ü ÏÈÕ¦Éc¿6ì½È Ñö{üí !ÙBöÍÇbÐÍâ‘ǧ-ç6ê¹ Â¼ ästPÁ¶ ½!“õÙøð>þ³ü¸øÙ ”Ïæ&¢ó â\óXÙ9ݨæ'Õeè»>æüâ ‰Èqç³²ý²aÓÀ®{ùôØŒrýwó‹-îFõöSóxîõ_ú`cÿá î Ý©³Éó Ÿj Å ó Þ †âfÖþJðÊ$û²ñÜ^ýÊ"ïÇñjú »ò õ\ð«èâønéã@õGÓÿûörûLêéízä Þ®õKÛDó®î\¸P÷7@ðó‰øRòŸñp²½0ù &høØrŠù4+M9Y=å 43èäkõãìæjú^ì¿›þÐù×þ•ç(ó]Øüá¯ÓÚÓ÷ß5Ï—ýòÜ…Íú~žû0 TÞ°îÛcØ í#Û ò-a2é%TLó÷T¼tûM l902”I' Ÿ9Lå ¬êÑë äõðl«î$ÿÛ„ëÝÖ/ßmÑ=ÙËÍžÊÍðòÔÇ~ãfØ€ïwéü“ñWþ5ôç ôî¯"÷ÿ€2ºó.Æè?bÜ&ß.]å¹– &Œ',X&< Ø%,$&%q&Ì*_)Û(&3!x/Añ¿Ú§îhÚ™Ùçå&Ý9ðîëšõÃ÷yóvù é.î àÚ…ä™Ï‰üHÜKh‚të*BäÕÛíþfÝ&þxÃŒ+âÏT ’ÒBã(G·/…+e*”2Î-pU!Ëø  ©“Ÿ'J  C1Õ÷‰CÒð͘ʱî™Òù5úÉó" ß}ìwÓ(ÓUÝÆ]üw׋Rú˜ Z qðãkÚŒé¹ÕX×ÄßÕÔîð{ßÂþÈóÙþ Y ÔÓ ‚Æ Ï!ÌÕ'”,Ó0šù¡“Õõã×ú!sø(ƒÙWóëÚûÔiñKÜlý…ï¿ü³ø%þ[éâðÀØÍÝùÔšÎÊçÓÔÙêìç yK †¾ù®ýTð¬|ÿ?0Å%¨%Ÿ:À–&Çò–üÆPôW ¨$ã"u-ƒ!Eò¤híiô\õ)ñ¬¶ù[ ¾õü¡këõâæKâN÷êߦlïÝÚÿf V 2Àxðqö…æ‚â*öÌÝbCõ„6ê\\6QáiçäÒNIèË9Ä.û>Ih1ÛÿÙwøR#ùïüPýÈýb¦ 4ôîÛ˜òAÔVØeå\Òøø¬ãÉúþÑ ·â³ÿãÑÖàúÜìϳù¥Þ ü°6 7ð;ü‰æ2ä ÷®Þ’ 0ùÅ;*O/©?4/£óDÚóäôgsÿë4‚#¡8DDiw=ïÃÂâoñ|àËáªæÅßæø¤ë‹ª¨Gâ7½ÕôÏÚ²P8Ö–œÅæb Ëã’Әȼó0Ó]hò÷ œÉ™^9 [g ’)ìb1*(Ž)À1Æ])ªûÔÃóÎÿ„3ùÏ!¶ £3ˆ-ê%9”!¬ä“ø¿ÛyÖœìÓV>îçœ éðlÎæÊùÉìÙƒÆüøúÙ_ ÍûDújcæÁëèçÖ=Ý»>û¥Í äSÛ ÏG_ûÎÒBûf*Gª>È4XKARïé_막ñÊ&¾E"@. Y(«ïJ 4à#î½ä/ßözèÏWþåþÁ éáÍüÁٔƤº]ð}Éñó O‰ \óâ æ³ì%êôÞöþ€æìQK.!Ëè"ÎáÉLä.å¤ð1 »+9ÓS/„òÛËè–îøþOí$‹¸èðç“ÞÕïyÞñáºæ~áèõ¤é— Mø^÷ ·¸÷轈ÏEÕ¤å—ÃÁæ13¢4âi­Ë_ÙHâšÈàtè^/D,î20(6ñ>Ïí¤í3·ï¿uŽfBð´ Íåªó’ðð ˜ü ‚Q«v. ³)îû-àVÝÈÜû4Ÿ õ95ª ­+ÙÛvô¼Þ{Ó°øhÚ¾hù¥'†ñ $ãñÍÎêxê üØéwèÿ¨#âS-(ÖTrð4ÿå4õËìÚí ýÿöT¨}ýŠAêµ ÀÚ÷ñdã*à9Õè.'.ð5oÿ¬#áÛòoÞ|Ù¼ö)ÝóÃûý8bù‹Ýþð ê°Û`%ò±&O'™#º› Æ*àýù Rì›õ,ó5ê¯Úûk çŸ)ºÚVßÍÞIá€Ót…ì²cjÿwòÝ‘NÄUØcÉP¾SäžÈßþ¼ç„íÒðê²×Zâ­Ü¯Ìðù¤ÜR|ù°;R¸ýdûýeˆ#× 61!z44¼….»ðM ùä èxÙé«"+æ)3/¬ÿ0ÝÚk–ËÖÔñÓR¹þÆ!#¯^Ìå ÷ÏÙ+Þ ÛÇÓârÕjô\à±^ø3T Ìóäoëpì<cæÀ#¿ý/ý"w#d QÉö÷‰ü1ö M(R"á%¿6 ö.(ï.8ñÆõæøDØ3{0JµýN' Üüô6æêßÛú¼ñ:üþ Y#Üx]Gâ“û’ÏæÓÌäèÆöIâ•C ÍÅë?§×OàWé1Õó7)¬#åJ&ªÝ–þTѲÛäÚÖœýâíD §Ž6ö8 êóùéñô})ý‘áqì @ä†úAÖ3Þ‰áÔô)äN'‹l#X+àûëè<õ"óXàçð×5oŠ.û*kû9…Ëá¢ÎÀñ Ò÷ Ñöÿ ëºïYûy oð4ÿÞí—ðé÷-ìU Ê÷{>£þÓÌã‹ÿØVè4íEáy{úî,” ~!¢4hñæ»âˆéŠöäÍ#iZ,®¾J øÛçëàì»þûèn#tÐ;C,ú2Ë;”ö* õ ÂæWîßó^åã 5õ÷:Ôª w´ îö :çÔôcõêðÃ?%.E‰) Ïdþè¼îÉFÜîÁ¡‹ç ^ ^÷¥wéLòÊìxæ‰þ%ëEMÿ’¿a u:ðäütÙªäjÛ˜Ó ûÑßi°*-B"á/®þ+ éèý)ô“êþþü_-æ%3.!ïk|Ïóë<ЭΪõxÙy>Œñ!ÛÕ]ç¼úBá'â^ëäÜÄûuåØgóþŸùñwõôäØçVéãÜNþ ãõú×!ÓxkUõŽKÝûæ»ûàÖP5¿{<š8Š•7‚ç”÷êNî#)ýÔ,”ª4t:iç;Ðú݆æ<öèë™äœñX#& ß) &p&xþOiòTû±öyîãñ[Hª “ Jûm ™Ú·òúÉZËHêÒÅØAða1s%? c-ÍÞÃüZèÝÞs kñ hµ€$Òø.LßPû+Ú{å(ò1ê' ¬ &*W ý!oòF …é¶íG÷jçk "÷ˆ!2í8#ó× ÉkÒ­ÞÉ…âp%€ð:ï›'МßåÎlÁGê“Æ{†ç÷ÆØ Êà€úAÙ…Þ'ëÜߊý’óÏÈúAyó%Kðìú/öô2äö³"5~-\8å$ ”Ö6Û qÚÜ4åÉ(¦2ë3â4ú¹à0Úd÷ïÚIòx(:ä$¼%H-îùL-ïgíÜñQã/ÿì >z6õAáòøíç‰ä ¢ðÝ™ «+Y$hÞyb×¢Ö‚øLØZdÿ·5Eú€fá\õVäuàÚ¬äì/² Þ5P8¡*-òÒ“êCç?ôÁâAÂî‡Û …:*mïÊÈúä:ågÉ'ð÷šCŸ7.«E=Ò) GºPÌ Í¹8ÔV&#(Í0TìPÚÎêÎÐëÍoò%×k^úoö§”Úè­ÞRÒ¼úïÛ«øX'àn#ó$Æ âqîž^ãGãøúÞO*O9.Ù3/ü|*IØg÷ á…Ø‡Áçõ49#:Š!|=úÐêðóùóuæ¬ýÆñqxþÖà <ú ìUù‘è]ärEä (š °#Ÿã ôƒÜOÒøîŽÏÔî-°£ýØèè*þí_è öñÊ"î¡1Ò+~"5ÄÿrPíBñ:öcçµ [ù}²ë*ig#(ð^ šäî~ï­ä‡ º÷<#òŠ+äë{BÎÜ8ÝQÅcÜF%÷—"/ïáŽÂ·à«Çˆº@ñ+ÍäûñŸàjí;Þ{ßÅëNߌŸî¾6%>ø&ªù³Û¢ïæâÖØüZë;$'(..|B*MôúCî¥zûïB¿%ABÀG”Â9ç…þÆzÏþê!¼p&õ±(/-øcÍõëæÜuÏÞø[ããö3û% Lž % ì~аáéÖÛÉüXàí$wÆ€'Dø™írûj@ï* 5604Ïå ßÛµØjö“Ù@Úø+'å%«+³E&íúNæ,ðxì¡ÝÐôì„)P6¿$¨ß \ÌÄÔ/äúÊËüñåüM  IHCú\ÕöÒÏ2΂úÝ×2!= Ÿÿ®'ïÃ8ðÏǤº®ûÕ?6¹¼!†5þœ¹ñ:þ¦å¿ð1ã(älêóãÈ÷ñ‰¤„ <ö¿ äãIêëýcÞ'Úþk?Ó(„/@ø=$›×ÿéŸêÕ˜ïóñ%Õ…Ñ+ü˜‚ç3ûaêçsŠí²$Ÿ k*(¡Ì(®æy wÌéÝ£ÞvГüêŠ Xôpí øîêøï`ôvêè iñT&q >ð"ð˜ íÖLÝþßÒB;´ "Êðjÿ[ï!ò=÷¯ñüËõœþÇ+$S_1'î›(ÉÜDàÓÀé2å1–† ÃâîÍvÜ]ÜǪ̈ äþ µÇÈé½3Þ’êëãçâB÷ÁêJ Oþ¾ø8 ¡þ 6ù!ÿ~Ó÷°“²!%šL"«ü#–à2ôŠàâØøàô0} ,5‹6J1WìÍ^å'â5ÿLá'{*_+¶X'ÝmÿÇÙ®àæïæ{ÿÙø‹Àï xŒÜ)óôT kò.ì1.ð• ^ &Ñî8µâvéëÝO ¢è}Ø ƒ“8÷Å öáç¢î¾ØØÖëc¬¸§ñr ½ÖªíÂÙ9ÙàñÎàÖóøõ5“‹ž!Îÿ­¢ïÌúõ;ë­Óüº4* ù9Ö÷µ+å²ð6øºá&ø·•g _"Ïßc°Ò^×ë´Ò$zìe ûF¹ògöïÝð“ë…ëwçÒå_ôšåȨüºÿéÇÛ ìBî)àååõ’2vª8~:Ñ-:UùÏ´ú³î)6øÈ2:L2Â= \4·æj «ä†å( Dè™29ž)û)Ú _¶É# Bþeû™þÞšæ¥áfÓÎú¨ì2| áäþ ¼Å³ßïÑÑÆ&ðQ×§÷¶8÷/ MãAð»ë!Ø+Äíµ*–#Ò Ç)™ï' äåð øç‹öû,0Õ*G.Ð$ÝûÀ ¶ùIö,ñÎ òöšË^mðï‹ ï×Ôâ=ÔyÊŸèJÐüòÙúÛöäEž½ÞÜÍÉSµåW×€Ž ‰ý WÝü÷áâYßÌü,ì;þþjþÓFú‚3üåãùþßùÿj …ûs,\ 8>î-%--8ª½$(÷Aõ ë Þ麗Å+¶h)¸çDÌÞàõÝ£åøí!%H^ôåLüfè9ã#ûJè ãùçúÄÿûI ^ ™ôö®ñÐí¤ŠóµÀ R죸oôîýªù©îrÆø•7 "[>SçW×ÇÛ_ñ\Ô!°ökâ÷jðP·åóíïíáâåÿLëoÁþN<&ºèÿªIñ¢ø‹ìEæÂôaâá_òšQ k»»Aö0üZóØël¡ïÜ+W£.á²›À†ÑTâ¥Â7/íJ ¶ç‹ÿÐØŒé–ä}ÝXö/çõ =O ‘ú íWø éèé`ïæ3²ð›G>(h2WÞ3òåAýAê+êB§2Ö3ƒC Ù-üõ¥†÷Þóo ´ù·`“þüιò:óòmöÙóð)ïxëì~èxïVííUó_éÂîåËå°êSß·úÅê `Ú ”nìô Îôß…åßÊLwñ´9¦)^6 íQéý{î¢ i<ð …;H;G' 8â'#´ýAìû%ùPûùÊü2ùaA¸ ± ìþ.î"Óß·âŸë Ö˜yê7ÑôæFÿBÈkÝè¸F½ˆÇt´-ðgÑTü%û™ãü(ÔáæpÕ] 7ëz•š–;‰Oñeþ|ùõïóÏû[!Ì÷$x&u!+8[*!÷ìéþ”ûÕ<üT-g[Â1žîFÔ~çóë}Ó}·ðøµøúåä}ú9ܦãnáÝÚaëà}ñjíððôòõõ÷Ð÷œNû A› o؈ša˜ûz ýù  þf!†Å%¤)æp(¸äìãä߈‘ëµ+ó!F(äú7-Þ'ó ÛêÛ/ç·Ø¹ûðéÐýp÷ñù<ûÖ—øEù óŸð©õÔåg õý¢ÖÆã=úêßo EðžÒ A$Øí® ¤çuì4öàâ÷ñ~"ÒènÿxÜ„ã*çÜoü›êûvÿh ,ðÄ ä³ïÐê¯ã¥ó1ânüþèç;÷z{ %A¡ç»õ¼ïëÛx)NüÍ@Å$ŽÎ&c)œŸ%… ³x #…ù+oÁ*€)[q,Ëé Ø“ÜzîQÙºýPñø!Pà/ù ÏÎÝ+ÕÎÙëaÔ°Ôí)÷ÿ½×Áé]ÐÊÎþæÒ1tì¼Iîú«þdýŒ¾ )'.嘇d4Ýß>4”0˜< &hï¡ü6ûèêW.ÿš5ò)-¬9„ó|?éïóNôñêÝýö’ö4ý{ê‰÷òßûëvÝà*ç‹ßÓëlçšébëíåóê®éÚìNð(õäí¶÷ ëóî;òeéÔÂö ‹úDõœþV(÷4ç:¤$˜6=µ•0ôä8oçcß3 "ïíæ7 Ø÷7 2ú7ÿ¢ $ÿ†ÿ&þ²÷êø) r` ÷\Kï½øfÝeâIðÝÖ.Cûzf!¬÷IñäÚô‡ åt%YŒ%»”“J÷ûÍñÒï¸þeïÊÏÿ£e°Wî¹ ò*÷Ùý ù#ÅüÊîý3 >ÿ …ýÿOjòTû¹ìŽéÛýÙçyW#¸?ñ(\ëì}ØôÝPô#Ò™ÄóäVyþûYÿ¦ ×þ× §- ËÀø³•ð¼ûôóúômô÷®éºñïàèæyæJæ’õ÷ïºÞvôôç Þ×m饒÷Ž%ÒsðiÆ@ÎíÒÄgö1×ß w÷þ ïÝõ æòï#= [&6!– ŸTê. £3 æ 9õ¨'% ’"‹ôkíßéÀøìí A Ùï 3ÔwíXÒ²ÒfíSÔ˜ñµÈ î•÷Í2íäÑͦõŠÖ~ ‘õ+ ²‘7 lŸL Ó5+¶ ~" ·9x&é: A÷"_:i9yù4ø‹ ÿòR)o@1É/~á-Gë6ðë¶áv1쑦"ûv &Ú€ø?ÌÜ[Ó×ÐøãyÖhèõ乨bãsÒ¯×Äã Ù«û¯í/øûoíòè²ûCäá‹þ_"Òæ÷ØÖUßÚ*á 0G¥;p;«!Ã@Ó!´úï,Nù?I ÀñÅXðá.é òÜîîÚõ‰ñËø”õüø!úYø&þÀùÿÍóÒú‘ãàçKÖtÓØÜvËæ§åå c“êȵÞâãóNÞ˜ùùØ£ýc³ï[’òÿðJˆõ«$åØÍ%õÿucñ’ë ·üÄb2‹d &"y$šéÝÍÙK ÝR' j+ ƒMð:ü'ëäކ床«©ûñ“ á4éÃï߸ñ[G² k Iž8ê]–pýÃëÉîÜï±ê›ú+ú®ó<mÐçîØäËñé…&´Ê)¤å•‹ÛaÛó6Ú… Ïò*´àýœÕßù—ÝúÜÚûƒáÐ8 ?*7"y¥Â  ÿ ‡zþ.ÿôô  å'ùÆ <äUîyåÝ6÷êC…„ï! MÕ2ðŸÖ±ØÇé6Üÿåñ†ýïé1÷ÒÎíÕ<Ó'ù¦×¼ú4÷4ªéJô5ñúî-íûa 0 Õ _'o€E‹.ƒE5MñO‰3ž.:×+â[òûý î> áí~ ÎѰâ÷ÒßÊÇådÚeâñë^Þåâàö'Ü.,ŠûÆ(ö+®ä aç×áÒÉîÿ è°<Y(°ø"þíîƒózþ?õÝ E FKø/fŒSRV†^×Cò` ø÷÷ê@*þË& ÷¶ 6Öråßgц°ì£âÖÏò±½ »âTÂëø½ 2£ýîמú.#Ô}/Ì´ë‚Ò,Ê îåô$k¡êòÁÊeèÃó™ý†<%/f5êB†/)u…óÌýêôïúÏôÁôònü3é{ôá“ã\ÞàÜøã+Úuù¦êw_8ïÈùè áàæ|óÞâÜÑ%÷2$ÞÁ£±-Ã!Ϧ²ðñÂÔîüKõÅúþ.ä#xûH ZÕKîç1×~Š¿&¯8R÷¤$ ËÊè8î[ÐÞ\„mS¶üO* 1Ó’4ò^øAîéO—óí#Ÿ†a#%w8ØkÿšùÞ -xmKe2éçó2ö4Þ…vùk$ç…!–ø –øÓ‘ñúbÏõŸù( ›¥ŸÐXŒÓâï”Êt—.Ò+!ö8 !ܰ vrߪó{vÑ„ç{ìÞèëκ(¦ãÌú+ãŒË+#cé×V2d%ùGr÷w ¿ƒò©¦Ú,õiû{™­üü‘¿ìƒò!é¶ÝÊEêZ)}º™ù± Íäæñ‚ÞÙá»ã¦ädñ€ôïy‡ßÜöÿÌ{ÝÅœÄ%ÒAÀ‡ò×' ¨ý ?¶!ùü°ô 9o$ „á(—ð£›õNíjl,è%ï9)Ïý|m e± &÷:ú³ ºó*´}-LÐãÿ`©§¸ëÖ9¬Ü hð^ Üø³ Ëæ?útæöåÒî^ã2ùNå» õi* #žKu eþáí8%¬Ý'+M0='Kå1‹G0H(¤>EÈú–û‹û ò¤àø L Ï{õ‰ÊÛþø«ÆàËgã4À<BëæÓÔë ?ÊDÛ‹ÕfÆúKÙ†8ô[o,`ân#Ãð4ÎOàöÝGÈ îép!…,ø%ÖèÎÿ€ó0ë… YøÛ J  s™óò ÏÙñãЃ֠æpÓÃsò PgöÕ ÙúðGàÁ×ßþá‹›ýh"7 À ©ìš+Ô˜âdÑOËÎäÆÑÎ<ð tR9~îÄßùæ÷ì-ÙNßò‘)J ‘U)DÑ…ÿÉÇÙÎçüÓ”ÿ ð²ÿ(ùs Ñâ º¨ÛÍ÷· j§6.(Ag1„äÞÍà²ÙI”ç;õ!Ð.ì7îV%ÎÿAÿiý3ù½Ê&/Yž3}4Bõ0ÎÝàþoÜwÖâîáî(M»Å*Œï0ªßœòkÙÊá÷ÛgÓ&âbÔ@ôqá„ýºýã¹êë÷Áç–ÞR´â œL(n"ýK%HòÄ Xíìc Øî–)R‰˜&¼÷ä8è-òúÎìÎ? ¤u#y ɶ)ã”"ýÜþ€Õ9ÑxÄ…ìNÜïPöžÙÎñ{ÐjÚGÞÔÒô/âÞã÷õê Í™¤JTã¨üœÖÄÚ©ó-×6¿þb ƒÍáëÒòÛ7kÞ¸(¤ê#r¬ 9$™ "Ø‘ *@ÙØ<°Dö—àèõò8õ¾ìÒFý‰/Är)z1 5$=ðú1÷­ìªãø~&…ê!%µ JôíÚâ8ðdç¸ßƒù¥åðé÷ÉgµÕ)þãÏîý›Ëâù. ý‡ —  ?! âæ2úÐî á0ã÷Ê+!› ¹'öâ úwøbÞ>/ݺ>è3’ ‘1Wãý±âëæšçÐè<ç ãJèŠÞpCìë¤ø¤ÚÓêw× Ç2ÿvÚ¾­­üï5_ïÌò®î0ôyàÛçiàyÛ£ð!æ¦þ¤ýç¹OÉòàÔÜÉí8ápÿÓåíûLèèîÚ1ô )ÊåEêy§ß½è_âIßÎñŽßñô!%q+k!“ñzÄëCæ¼ íê†,w²Ô)Aép·Ë9äFÐôÐÛß9ÙWîòäÚümöÄc Ñ I ¢ýaðÖ&9(%NërèèÙìéVò!R »Š¬óo! ¯þ ˆÝ-ëwIö0C.4AGñ@-Ó*àîæÔîõõ!TåÑ óÇ ùá³ðOâ°Þò}ß7þ<ñUMØÓ öEDìÅù,äÄé°ð<ä]Vö¸Åô[|ã¼î”ìèß°Üêï­XR0 nŒ÷,òó—4ñE¼ f·|ØòüôÂPÓ@ßÊÇÐ ÷ì†J„¥ó£Ãøú²ï+ü^ßÖè4é›Þ…÷—Î࢑­ÀÑ[¾­Aø¡Ì. óx_a°÷b¢û_ö‚ wùWžý ¤¨ÿ7øñ%4ïÑóÊpò[#5)õ!/*4ãûB1Ïø­]&ÍU!(Û Õ";ñd öævï©û é,%Ï’7n+R(V5½ôä߯ójí«Úœ0îÝ.*4#y)é¢ùi¯ý´ö~ýëõ+ô¼òüjñˆ j¢»‘é¿úYÝsÜ<~Þ–"A óÓ"ÀóúÒãñ™ñ½é˜ó÷þ ’cþï´'é.î¥û%è.!²«;“*g$r:÷°âãgí&ñ¿áã‹òö·”‰€ùÀì÷ÉÓÙÞ/Ò¨ÅðöµÓåÿÅó6ýÔËè¸ÓóÔÓç/ØEPõ)߈IÊ¹áƒØ ÒWùLå¤ öo X š› 9tQ&Mù+ž­?~5Ù3þDný‰)hâgò8ú!ä !}g1 "Ò!m)¥ Øù†¹öÙô€ýÄî2zû#5# ×#M2ŽòBkÉÑåEÜ‚Ë|ªç ÿüdåãûÇÚºæOíßç:mö¾þ« !Ç#ãP#5)—ÃÚëÐ÷ôIè-»ûü"[õE"€ûXœícøLþÇê¿!bþ*:à \1ê2 q'»ðœpà’å†ê€ÚÂÿ ë' ²‚ýNéêÿQÙEé$ÕºÔLÝ…Ðoî@ßÊ·ø¢ Éö ÜÝ›ñ¦ÜøÙôóã’ aþ˜…Íü(,Ûãò>ÝÍÔV ½â/gŸ9(çДßúå[ôZç„ øú‰ ¸ ÷{@þÂ×ìò¤ÏˆÉÝõ´Ø’1§õ’Vç¯ô)òëu võç#4*û,ó:{ m)JÛZúyÎ&ÏšþÂÒ'× Ýá)ükð™ù‘õ±ø f‘?è †ù´5ä—ðHã-ßÍò æª öü£P…óUÕç3î÷ì²á¹?íS/Ú,?1\ÿ:"EÓŠï6àÓ]#èž. m(ÿ'5>.‰0-õ¢üDû¦ì„xó-0Fv-1Äm%tè®ýðàâ¯íáYúïêþDý8ý ¬óyܤï_ÐãÑÅÛâÉVþ_â uyÖº“»Ë<ÜÂð Çé¡c“ëíì‰MáØð˜æ(ãv÷Çæ»Lù‘!EcZ(=ÿ&î’úˆøJç üù´,zh,;/ ú*JçžÒÓEàGÛÐГó‚ߤ+ýPÿó ãâý;ÔàùâÕÓ÷ðèu» ®ÿþáæ;GÕ9ë…ÚšÚ{í¬âˆþö]°JþÔŒ¶ _y³ Ì/Ö;‡/i/q;« ‹'Óæä*Þ¿aïàF°0‘+x>ÿ”æióÀëpÞÉû¥ä¼–ó¿R. RÁó” }פì†ÑÏ2êÝÒ–¿ó§= ñ[ÉêÍóÄï¶íæû=ðÝÅúvM ø=~ oÜó$Hû+éJ("˜9Ï,Ì3rãP 5Ó`ÙDí¼Ö{ üïÔÌ ¢ WÔõi ¦äÈî è‡Üúíâh‡öcÔ Jyýñ‚ ŽÍñêº5ÊdÒíÁÂ#ãÆ:àý´Rã#ö*íÌà ï!u è#Æ"bZ&îø§ êžúìXë‰ñÕJÝ#!Fñ õØwãéÜp)Ý ù&v&ö!kúvò¬þìé<ó6êˆçxìÒ'ß >#>1ûçK÷À-ÔåÖÀ‡[å>´$ÿ8*Cˆƒ:Ì÷¶9úIô‰+û.(¸I!-%{$¹ð°ÿçUëËë”ãcùë… tûÞ«|»úkKùò Ñò3¨dC<÷«sÔãïŽÂRÎøÚþÅSéh Õ™ É&'ÿWÿåêðàô[à‚&*ù¯HÀ3o(}J*ÿ&ô¸ÿ‡öÆõïùîó9ø¼RÇ/£' ¤.aáfŒÒƒÍºüôÓ;"† ˜(ò¾ÓÐVðÍÕ4ß1ÔËøé.ûÛþ/éNü+Õ„æ%ÑÜÔ âJÔíQï¾!Ð|:õõ jðùèêõ}#ÿ 8Ž%6‡8Õ+0QüŠ<îdññþÏè'£Cn/33RD¥ ›*êí¸Gé$çy×çV ã Ë‘àþ†ÀYÚ°ÔVÈØì«áÄï]ñ£î]ó”öLø“_÷Xî>ð_üëðxk«iêX ¥Ùå‡õJà·)=ÈA,å9©:ð v.0´ýùa†ûi(þ®+i(´(Hç «Ð¯ÛÎäpÏÁ8è¾® ’ ‚úNßê@öKÞAã$ÞÊØbêŸàüYø ü¢ êèØ]Õ¤ëmÚÔפùã- ÿ±Âûº`àîðDöãz%ì3ë/Ïl-6æ üˆë¦è;üHò4¬üeüÄ—]ô÷IÙÓÇë<ÙuÌzàñ!7{1&]þj”åüÉÛ>ä¬àÙöóôáÀ Àü°1ÏîÈͰéKÚXÍRðç 4b"ê’,‡û'^úŠúr:q¢ O×`þC Îï ÝZè¡èŒÙJ üí·$ú#ÅõXßxëñß®ØçÿÍà®) U"ëGËää6ËÇ’çrËnZëh(eÎ'1)Ð tý³Í—ö9œÿS1ÚZ*Õ2V ¡%ãöH xò!õ÷¦ñ\õ©ò¸ýâô.ÄþîçgüzÛ¨âü_ã} ó ÓÍaû¯³´Àèцº’õnÜžÿÂøló'Ûò_ùˆóÏù›îâñ/ù€ëAýû8"{2ú;)ÃñbôÕìû÷ï!S£%Ý%—+–ü×òüïúð09ûF!ÌJÊ"äð#ÙÎì¡Ú0Ý–éâ{êÄï]ßpî°Ö™å ÒÞÜÜÖ1ìÝ þ]ëñ O 1 8 ¯öLÿŸûcóˆñò4fl=@W ‘4~äPû÷þÜF0¢üÌG3ñ-J=¾&¾'K6Ôüé…÷_OSJÛùã Ö¼ìnÐ9ÌãÌÎ ðXáŒë ìêsêòªñ«ù@ù›û+øtû÷üŽ¥¼áü¡kÚ¿ë·éÓŸ!ø8y.•¡4aô1ßóeð¼tû:,ž¢7](¢(Ã2HÉ#Eõ¨ FåRððâuâæéåãgÿYò™ ð XþyøÙKø+ąΛãÆÂ gç˜äü fåÌøÂÝ:æ¦æpáQùû솮øù1òUöO\ñ v3Y#H’,>öMrî·õ$Lú®á öíŒñz Lœ EÛ-öBÍOË—òíÎ’CÿQn$ óZ†Ø³ë2à—ÛWú6åæÀüeÄ ™óú¼ ðöõ ò)è>öì9Ê 4 Â#¸~ùDG‚f#.†!4ñw æ]îµìGåîÿ£ï›Yžj§ìdÍܱä,é:ØPíU(l¶õ'zó¡èÌ;è{ÚtÐäþÐäÓ·iH?‹Sõ $Î Cüi9ûjGRBMZ@ í†âÉë!þØîW lWü yì9ûLçUð°æ ìaé°ä,óØäZüóùòþúÙ3õÄÀÓÔÞÇ öTázè-õõ6é¶ñíÓé{ûªðYúTmÿh-à4 5œa4¼öÅÈ÷Çñb¶4á%¤3~=³h3Hëç ÙÜòêXðýà ß÷bR 4ü›úžý3þúûÜ÷-ùdì¡ì¦ë9è¿òvîíðLú¤Ô×î(ÆϬÛÎvõDäp€ú‹;búþžÿ]øL¶ü6MüD}752‹Cˆ ¬*îíâÊñ©ì‹)üª7ª'“+|8 «!LþS¼û2ÿA^ý×ÿñù©Œ v· cãÅ ÅýÖDË׿Uí9ÓÐô`ó)ãöï˜ØxÞàÚÕêôtÝ¿ ˆøtoü©ñN»ø8ôk ‡ûy× cìr‘"ú. ³í‡õô9ígjö ó!g) J%ò— …æèîµè|ã<ô6æ´ÿ#÷ÁÝñ¶vϼíí¼ÇĞ̺¹ïþzÕq ¯úr!þÛðîÞr áôÝb \ ·ÿh2 Íx(ü)ÿNõ!A T2¥1 ½3ô€+úíHÏýQ.G8ú•!çøæÝBåŸâ³ÛOø>ãÄ"ýR:4ñ SÔßã$Ù…Ì„þ:ÜLî©$& ý3pøöâ±óø­ž(iág* OTþD×CüSz–$v#_;%ï36ÜjäÁþà‹žÌøí£Ñ6è4ÀlÈSË„ÁôÞÁÏðñÝà‘#õÏ® ±põßÈôSÑeÐhç_ÌÒ ôðHƒó ¥þû OõSýûòôFvþ´.³<ç6Ë-¯C[÷Q!¤ßÌæ"¯çà»  ö`áIÒnáÀàkݵé9æ(é¦é]ê'çøò‡îýòøõáêÄÑïÓç,ѾÔô` 3îîÐìŸþ ì¶æ™ûìÊ0—ëAù9¶#Y?úŠ™ü¨úfþ~*í´<(3 9D›#Ò;W ` õK‰÷àîÔŠù  ¹Í ˜îãû¦åPç±ã¸ßáêÅâùWï,J½ ñÙýNÛãºàÑþ æƒ5êb=Ӻ݈ð4Õ,Kþ¢A<1.5«<«$¤1»¡"Ì ©›·Ÿ {þwóè#O& ê&šë"ß¤ã¥æPݲ=ìå ‡¢õp tÙèéÏfÏZÚÖÊoï¸ß‚÷ÚøZçaøêÑláœÚ…ÎÑôÞÜAv÷y ` Nqÿ ‹(ÿ½¤«"%%Î5!¦ 0ÍT æüI‚ûè²  'P$Z¸.eäà ÏÓøÙlò Ù÷«6Ù""Þæî%³áòë9ÚÁØŽÞZÐï&ÚÒfôäÛ÷ÿ†ðóímEí„v }&ë >'N)‰!¼'ÞÒã7½ú 7©Õß%ù«aòyò3“÷n<¹#d#=Sõ˜#ߪð¨ÛêÜÿç±Ûkî1çàóñ9ô;úúìû÷õßèkÛNÒóSÔ˜õ_/g6.Ã0‹ z"kê©úíKá¹Þó(²L)B^¼þ\ÕüÔ N&P VÖ¥Šñé'íëð5è÷ý’õ¼ü rä³ÿÒ¶ß^Ò~̹۟Îïå¦Úñ$èäÿIøHpòìù¾ØݳòfÑ’+NƒEï8¾$ZCOì’½â-à2ý%ëÍ7%¿h!H-èm&o &‚K( 8ŒHdA ó2þÝíÌÔ$ÛúÛ;Ùëç'åâívïêHïæá§äYäŠØø ßJ@ùu áJ÷±ôÑHçUÜ•ÐSæäŒ(í ï/ó27P0üT/üW÷Ùzª4j&ü0U9Ö!Ç5°\&¹ dñ)çÐë`þxí!í `Z"Ëî- QÒÛl×ÝÇhí§Ô·÷Lëåïsñäá›éÚÞã¶×—÷áãÙÚùx~&ïlÿÌÙÆäÅàÍзã½Pr%û*£&Ç î– Ó ¾, Áö†,ù!ö/Æ4Aé.ÚM»ðËÀÔ¸ºëéå{Ï+}“íY¨Øâ³â±Ò%ù«âÓÿÏú”ð¼þ[âÜí(Ý‚àtÝOÚÂàKÞQí›è[~ûb˱ð < l© 6)$M6Ä;¼[4Í÷3×åÑõî^ë­ ×ù> ` !å+Çñ%TéRÝ/ãñÜ<¦úÉ„ ÍFõè<ü2àøàÍæÉÝøæûåÆòïé– ý»ˆp('ãò»èÎØü¯ðü.\é!I/nù—@ÇKt"kø!c&• ¡"‡íŽ áLîªè äXû4î¸ ² *Ãøè :ä?ð2ãÍÙ×ó[ß¾còŽ ¨ý”nàKòQÆÑ”Á*ºÍÝì¿GpèP/mµ‘,j¢÷GöY Yóe)²ª.F+s¶+<éB Dã@ååýuì¦n Fû çJá¨ôü/ë[íÛó»ì†’ÿ)ü ÀÜ<ôQËÓJ×ÈßúÞoÜÌ`kôý ÎÚçRô¿Ö2ù4C$*I7'ßãmüÑæÈâ Æñ+4ÙP5¡?¡!Ü8 k$îÿkƒô ý òìôJzüÀòñ%«GKëçûžå›æ8ìÐçeö6ñ,òbùhØJëÎÏãÎ{ç9Ì¢;èH{Íùöeê—ñíñçÀ sðâ'é ô+z*PZ*þõÛ¨ì˜ó¡ù3îÙ qýÙ!v4'(:]7ý&é5 ÀçºòÑêáCðaê [úoÝÓUð@º•ÄÖж‡ ”ãK_ïJ ¼ÖÃà ï Ô>ÂñIï÷QÝoåè!ä’ 9ú+ Úù˜¥û'ö÷+ (ô{&Ë/.#¢FþîþA4Z@¨>±¼;ìác ¥Ø|Þíþ)ã•& ¼?õ¤AÔûí/É×ÎÜvÉÔúËßWˆý–ÿS~;ù3 ëÑðÎæ¸äÅîw檥÷§ÄËiå9ð`úzFïº4äPãF.ŸS\ñz€ñtí_J§'C)"öâ#µÌÆîZÕ8×iõ çÝ Ýú@ <3„X“LAëõwÕbØXޙ˧ý>áµò"Ü$•ðßâÇïÙ`ÚªÂ}NÜ-% š1^1í!+ îÉÂçgå#!í?мq"«Šk Õ XsøI2çì;ïœæ÷ûÑ[õ¡ûåñèé‹ß‡ûcç}#ƒaúÙ ÷éÃxË å. óÄ÷ 1óà¡Ý¹ëHìùàÁ ‚÷Ï%„0t*º.õ6Ïs2goòLýiðqð]üýùQ  š0á/íÍþèå>éôõDç,—.U½³™â_ý|Ä\Ò¼Ô&ÁíóêÝÚôú¶ÛròÇÐüÖ îwÔåµóà)æã$°(/Š"÷•üÕ¹ó‚)ÖG³%äí&Š úÁñ,òùŠë™#]w?µ2£"j=öæ[-Ó”Ü[áEÔÂõW本ü"¾ ãòàà€éBÔÿλÓ'ÁòßÁÉßëüà­ðlíõògîûø”íªoócW‹ŠÆ™"í$ä&qþDñHýK#Åá01+Ð/ÒÛØü»égàEÔûpùéh3ê ¾ñ Ø 1 ó ¯t ` hÆäùõ1ê’ôÚäãcïáZþ¶ð¬ öJ^IíôøgëØÞS ´ææ,ß …(U,"÷›IÒ÷èfÒ·Ïò Ùu&&(*¹R'}ôÐŽö˜ñ {ú› ? Ü‘4[!8Ñ!dÜÍùÍ)ÔLî'ÕÆO 6$%é'þ“à×Þ3í„Ú(ÜéR ´ û’XÞyõjÕiÛÇíþÙ\ úôòÇ– sñíEå`îrøê¼–+Ò"è-2D$»0 ú$¹ýÈîÐùnö%ì{%òÏ:J<î 8‹Ôuù¼yË ÜÅ×"î~ ¯Ãåý'ÜFÙê²Ñ¤ú<á}ö~ò¸äsëeèÊã³ò@é-øûð·ûUôŒ Åûg2øñ µBz* lL»3EÆHX%³<`ÿ‹ëÃüö ñÿøú(aÅ*2íî)#ôÌ¿ååæ¿ö“áòMýÕ]þ( ,ïÑø²â9ç]ÙÍ×¹×dÍóëTÕ Oò™§yï¯7ØÛÍòqÔ4’ý3)A$5—)à ]éÚ32¼2Ã7€4.žò—¤ì¬ôþsôd ´ÍIÕPZŸõ§ä¹íNä„Þ”óxäE÷wvræÔøÈÀH×@¯â³­ÉÞ²Uþ™Ûê›[ê÷ÄâGéyôÛתüy¿üg"¯×{ø2îyßVx¥Å ‘ ~ àw¼ôÿ| ÛøZ÷ ß÷Õ5ø‘ Qرì8ÌÔ®áHÑ ªíÕ(Y, Löª‰ÞOß¶ëžÒ9é›n ñlð¿þrñ—ïö³{ ð#Ñ ¾!)°+#> /V×3ñí ¡ Ò! ó"àîLdë¾êcý£ì,6ýD< ¤÷ƒ Ûþç>ßmÒrðà -ÉEõ%€úþwóñúñïTÿÎõ½þeùò#ÿ® ©‚î2 8Ýé>ïeãõò1)N,²:kt'ªâõútá?àøZéù™hðD =×^òsÉ7ÙFÒ'ËöñGØ‹e÷Fúfè}ð èAàVùÝäDÿeòù¹ò…øHðÿË÷Ôú" ë`ùécèq9ñî4}X<ê? !;€þ²Ÿé9øSóð.~Ñ/¢=Ÿ.9ÛOùrå®Û½ùòî½ûVüíôù/÷eøÿÿýÿBñ×ýŽÝ²à½á?Ì$üýÙË;üK º þ¡yô…ñ¨ùXè@öó©Õ (H$à9ûrÊ dþÉ,ñxJþ4ZHiNƒG@ý½V-ÿ’á ÊOÄ-ŠýÅÎúôô<Ôî±ñÄ÷&êsü3¢—sá ù6ƘËÑ ½&ïþÑ©rö[ù* ™ÓPð­Ì¦ÌSéÍ& 9ìZ> 0 ÙäV°E Z+Ö erQ‹ôãðô7Zò¢ü³ÿ«ù fÄŒ'4Zñ™ ”ÝÎíˆÜñÙyêHÙXë¨VIó–LÄãݾµ°tÚ9²Ãþ¡áÝHõñùÿ¯ëûógùñéfø 3úµ hÿ ƒ X7Jií ° ½ ô p ¶ ü"Œü¨ñùýŠòT¥ ¸~þ»ÿS‹ñ9óøð†çxû¹êØølacõhûuçê-èÈÜÏù àŤör$¶âÌ2¤õ¼ðjòˆøDòB©$ #®N.# {$CøŒu?ût5\¯2<•-«á›üKèåýðv’ÝöžLäïU×vß§ÕÓiå§ÑŸášóþ’@÷¾hÜëÛÖäñÕÛVÂ÷ "·€‹&Ï®¼ù9øûý4"ÛÙ:4x-ŸK”B2N㑤ëéßÛöæn ­ééùóÛãéœÔàÒÒsÕÖÌÁË¥Î,Â×Ë,äpÚkô•ë_úkôšµðw ÄòŽ%X85Ì#,+Ðè‚<ãU⫳êú!Y&$“);¨*>Ð%—!ã%¼#&ø&%›*ì+½$ç1Ú î*Ëì] 0Û9ê¸Ü›Ú*ç3ßæñîËöùúñúDè®ê‚ßàךå~Ï›ðà=þ’ Ëtëÿü'æÊÚ?‘á:gÃÿ‚[˜ù˜½r+o Ö-^-õ(œ0£“,y Ð]` Á ¢"?'¡%»K0/ôntǸæXÔþÅó4ܳþýø<ó™þ¼ÛZêáÓ}ΧáÆÇt´ÛlÿQø¨ëÒþפäkÖ0ÖÚâµÕÒòDãéÿ+ö¸¨ ¾I  K › ±"÷Ü'å,Ý:3î!„Øsí¦ëÎá\þ(µ©ð9ÙéêÁÞŽÖ óëÝH²ò)ÿ6øýåNîâ×Ù ×ÃÏZì^Õ·ñí ǶÀùèûÎÿgòÃôÿ87T, A€üÅCö üÕöøÊË ,$Z&7Å+FÖð“Úì@òaø.ñKÀüÚ Ë%ûÖúèÁò ê âŠù ãÏôñ\Œ @ §ýy¥îñÎæ/ãzôÛî".÷ê4â+Yp+1äCÿ\èØ÷lí•7Û&Ï'';P +.!ýJZù«±øýÿ`ý ÈŒ, Nð ºÚeíÍØvØæ/ÕÚü™åu›úy úßýýÚÍrÛjáÌÀqåØ :èïœù(çãŽþ¹à“%S²:Ô-¤-@£ Â+òýßô5õ€Sÿy=©+1úFX ÷3úî.âŠïúà5á+éAà?üäïŸè ÔöÖ²†º Ç^ص6ÀÜÒ$à‘ÕÌXÛöÙúËyõ ×ÈöH õÀ·œ6‘7§ +@ü06*œ$™/ìý%nù­ƒô²ü:ûi%…5o1˜"-;iüˆYß‹îÝÛÓÁîÈÕNëðƒýh 6äÿÏuâ4ÉÙÈBàîÅÅüÑá‹ Ñý©øçàßïçíþУ[ä)ýŸÈ Ñ# vý6iû¥ ‡ú½2ié>Œ>ç‹=çèûÃçèMòÕ$#.°+ù($ì¯ à êpçRßÕø:ì^£ÿÝ .ØîúÛ¾¹ÎÞÌ/½1ñЕõ!WG;ñÌäíé\ëVÝðç²þH2#HþX!ÉÜ›ýÌë7â7Ðú/-n!!+’78 ¼-”ï’ùé´íŸ¦ìûí  Âõë¥Üõìkß8à;é¬â°÷†ëÛ *ûX¦w¸àýÀΟÌ"ì%Ħí©!%ÿÜúÖÑ×Áè0ÏEâî¨0DÁ&3n ”!þîT§ë¶é ^ïëõ– ‘»í öäòWôoñÁ ÿb×– ÁÞ¯×éLû®ß7Ú ÜCA2w<•CáœñôÞìÕ~ÿÆÛ10²$i! Ù!cíæêòåMÿTë\\·#¾Û'tð‚î¾9çýóŒîæîÝþ†øœc ßùã–ç3Ùìî3â¹Û;ë(/;$1~þL²ÝÚð˜âÖaûrãòbþS%÷ðOÞÚñçÈìºÜåÚõÅ*­¢#Ž$$¥Ç bÏúœ\ë óûöTée·š$«Ç)$Òúÿ”ÌÜÑéŒ×peòÖ Ø »ú™+Ø}úüÄÎÒ˜Ì`¿‚ç¾Ë¬2ìE ìªýfÓmÜüÜ3Ë0Þ÷9ýØ Íd9åÿHþ!ý°v&,{7>#>5'7jÀ.Oè»êœæJŽð¿$ÓÚ&Ð2§7*©× qÌÄГú Õ"–¶%küŒ­ÝHïsÚ‹Ù„ÛnÔå%Ö@÷yäø{úÃw \ïÆý$íäè!áçF(¤Û/*ò®$ÛˆÉötÁþâø[ÆÂ'²&n!é4Ñn+€éQ $÷žð1%äÙHï9ú/.M§íŠ ŽÜØé)êÆä›ø%ò Ýþ ó‚Å…ýÊYÝlõ£ÍHÏ&è˜Ã4béÔƒH'çBÿóØ•ÝóÙ®„ü%¢$ôÿL"cØB÷Óó×ÖæÆØ;öðñ b Årƒóo ìçùAô_õH àþPûÙÿ‡pà…öÓbÚå(ÑÊ uéD*›B!ù*œûåêÖïùßãÁàô˜8ì]'‘-pñ®;ÇèÙ˜Îó»mùÔ¬ Lˆ Sø7 AðËû¶íèïWûìäû«*Lû—(Ý~û4ÚSã#ñ,ä@™þ /E%j×0pðiâç<ê$‡è·'c-)3!º]AôŸóèóç )æÎ*L:Ð/ƒ.[8<m'Žñ·ãèuìÐö$ç[ 4ø­Î"+ «ëdðåòÇøuð¥¡!ÿ !¯ËZ÷1¿£ÅûåŒÆJ ð ¥ 4ó0çaí\ï©ä†³îþ8Ëb÷È Þë…ùÓØåà—ÞgÔ“ÿŸâõ",~'F.+øÎÍäJ÷û+êÅä<(Ú$ð<*Ÿè WÍäaÔHÎlû Ý[d -‰';ûGbävô<áFà6îØÜ²üè³ñóIüaùï|óšäæ1ê¯ÛÉ»äÈa"u˜ Þë7Ïã¨áÓâ=4ß à8ç9“ 2mæu¥ï™ïóÿ"2Ü$3ü>|w9óôˆìäòéï*æW tõ#-¡'ø%Ce$Tû•Çñ&øøí¿áñ.´’ j'óåÈØŸêIÐ3ÍŽðË^#ÉùÌ*’* ù%ÖÛËõ'ëTÛ÷+÷ýC$}òQ×Üö5ß"ç(õÖí;-¥I¶ø)Õ€…ðœèkì™÷ìäºÇúõr“k¤Ø÷úïɥѲåyË… ™éYô êà¡èwÌزÑ#¾zíãÉ ÜìLF ŒùÚ iáõ®ÛÌßêíâ«þjöäÅ"ù òb#ñŸúô,óu ²õ%I0¤,Z 1òÞ÷à×vQå¿4ê¯%Æ3ˆú‹àgòàrØjüÇÙ*+ù (;É!5#‹ ’úøÓ.î‘ë;ôÉâüKðH 阆0ïY¢Ý3ð+ìSä! 2ög!BWs+âü Ý ø"ÛˆÕ®ý›ÛÌ|éž Q÷ŠÞió¬ãMÚŒ %ê­/<P/!2 È%¸ïÿì€å›öÕãEóñfÜ%ùŒw/ç>lÊmá›èpÌï.¤ýq9&>+¯8¯Ð·!ÈKÖü¶”fßõ)qM4êß ÷ÊÙÝØ Ï3÷ÞÔ™û'ý-ðœEÚ}ã2â¨Òþ£ÞâüÃ)ù³ '‘‚ëöücäçà£â'*' &e1÷w$¡ÔeðéIÖÚ¤ïP9¨î:Á@/={öÙ ëIòÞôlè£þ<ó?øÿg¦Úï Vù èƒöÿéÅßwüç‚%Ÿ g ƒ&®üEgãÛí áÝÒÁñÑÔ óu hÖøèÝø¤îÔç½ [ò(oY1è1ûÚ/ñüîïíÀðHú»èÿçþ$MÇ5+åÅ!¢ì_àä)êUñ°å¯Kûô&/>Q+&è!TÓËÚ-àäÈ¿ æà´'cŠì-›è£ö¸&Ö`Ï.³ú Ú$þ¸ðºßtíµàÌÝ?ï‹âS«ñU; ' Ì(Kôdß×FêCãÁÖ§ ï|'‘í".é ¯#8õrqþxî]&G%DÍ.â>ŠF `8òÙ²ÿÛÈ‹Â;ómÃP*\ü\(û."îxªÒöã°âÒÕ¼øaæ| ÷#PüÕ  ŸÆèÛ­ÅBÛbßÐÅï |ëÃÔ²:#fó_ñõ›Õôa-n @8¥6 24#ázýÙÛ*Ö¬üŽÚ@ò'Ÿ`#¢*Z#äøH §ãíì_ó5Û1ö;+Ó (‚Ôú¸ÎÄÈ…èKÑ©ÿèð þw¬Ê ÉÜôBÒVðÝÐáÉ57Ûn"XË÷›"m¼cç{ǹ·úÙí.*9p,q»8í…ýùæmíÀá^ä»í²ãÇ ùô1"bf'Sî0jê.ç3Cåå+EU>æ/&Ü;ÐòÖ]ãòøÔTiü 'å#¡ª,÷¸å—ögïCæO ñól$@f)k(Ï N')áüžÌ§Ú¡â Ð#§ð‘ yfÿo _ñ†í;öÝìUïúöRë¼@õo&/^#ˆæ?fÙþÓ0¿×µBØËA.KŽÿû0­á°ö'éäþqìÍèy"äCS&pÜñÁ/é„í•ýôéH0Ç(#ÌÛ)5ïÊÑÑlí^ØÓ¥ö—ÞT ¨þrè~±ØÀâAÞЄÜ)‡iãç¼ÍܨÒH˦ó•Û » £b÷hÇãÜö_ð™ç% "ùåèÀÐìÅ óæªÝaðð&à'R(=–:¶r> äŠ Ú Ú°ý^Ø ³ý¼!çö/·ð»‹áOåýééÛ#ýIêÑ ÈŒ æCô' µÜOí;ÝØÛ8ó—áž÷YÇrËYúþ ƒîü=î9î_}ðD*v2Ì5 v5ÔÒÿû„Ü Êncé¬5Œ‹9¾;½!7Mñ¬ ì›æ» Mç.& ÓÜ §.÷Ð0ê_ò/Þä¯Ü`ÕÃö˜ÚE0ýù ;è;ÑxÛoáŠÊÇÿ§âôF”sYá¬ÆUÛÙçÍTæDæì²$Áø{ªãá÷¸çeà,)îh'‚4b'ÑþLëäøó4ð­ökó#õÖÑ)ê ¥ 0€ä‰ :ÊÓÀåð•ê!}>7ÞíüÍ:ÚªáôÍC0ê;k ÷ÿÚ è;ÿœÝèdæ“â¸ú[í„ y ¥ï–ü6 rûVýV@ûh! ýÂ!5ørŠÞð€àÏÕ€ VáŠ7t1ù; Ì*oçDÎéjà/¾çt&x «&Ñ+ü##vÚýößÛöß×óWçgBýƒ%~ÆA"ªk!œöÉ%óŸìw ãòêªü/.ìDà?å¾ïžÚÈ !î$!àÅFñú¤åçãósÝ ¢ïÔÞ R >„ìuÔñé=ÚÖÿ÷˜â¼ ó®Xÿ+ˆþûîø¿ûxë !a05Å//É:Ùï~NçÉéæù|âvµúÌã{àâü=Ö'ØÂïÁÕ|•ñQôUùøÿÑñzôDðìð4éôëºéã”÷<ë£lÿ™þ~éåòÆÞßçµóÐã¯ÏùÙ7ÿ!á5˜?36ö Eù6é÷û‰4ö&×)ü9 „-åãÔÉééá'XïO4jÐ&T,†2ÿkþ ÷øú#àÙå=âìØg#ïcÿñäÞ—¥ÇéÛIÓaÆ$ù²Ú% ÆþôɈÜ6è$öçפúi$ &øë/ hèFì³üê0ÿ`3ƒ +'a1  8ù¸“ù¶óûPñSÖùO@‘;‚ ò5ìúDÓÌÜp؃Ǽé…Õëþ\ôþ–ÙàÁßÓ‘ÒŸÀ·¨Üòh Zõ*Ýéðïæ„àÙÿ¾îa÷ü®ÉùMû•dûˆuú.ÿ»'üÔ.x ;é.*f6€ !(öiþÉõÚêi Wï]$<¨2Mÿ|"uæ—þ§áNáƒù™ßšSÿbZÂÿò)ã÷-ëSàuþçë NûÕú\€ù#'7±þ,  ó–ûCöƒï.æönW‹ÏN¡Tò þ<÷2éµ.úb4+«n5±è·|×IÛyú†×Gcÿjî-þ\ì"öåeêMïzâ Éìºð´ Œþýù iðö?íZåZöjãËõ™©^EÌÏð–÷‹ø»æäø)óëF(-Ø™0ȤËéXËáEòzý uáÍû´ÙÌä•ågÞ7û…çdÒù ª=> X÷síéõPècéˆòøäÿõ-"ÿA+8ƒ º4Zðu ë¿.oDš5R,ÿA- å%°öM øóÌÚûÞM‹• úîcðìÿrô*óÚòÕïî,ê‹íÈéãíJîúígò3çEî¥åÂâPìNáõú#ë<êxeDçl9ÎÓÝuè ̨%r÷±.Ž. *Lð]Šï (n2<Î'´:€:l$\9r vˆü~húÑøþû$øVý£úAµÇ ®c®ë ÿ~Þ'ÞïÜÔsuðuÿk©â‰ûÀع/¹BйqðnÙGøfú9à¹øÞÒbÜRíñÕŘñq!ahqiûÉ[òðù\ûRò…;þk!™$˜&x Á+C( -ÿ™ LYöSŸö0„#ÚF4æã_®ÙŸßQðBÜú±ò¥ˆóê–ãsõÙÛ”âõáÙ­íJâñÊïÕðõ{òüõ0ùÑ÷zûû{ ʺ;Á˜9ºkMEùÈÿ ÷s—·$ŠŠ%·,ÄŒ$]çþbìõãìØðå-€ü;)öUÜÖðÙ ×ÀìxÙý?ðÒþqËö\³ù£ù±ù’þ+øLó îj÷Kæ?Hù:òþF”Þ³õcègÚECó1:;ï”@ç—ë°úžâ^å÷¼–E[ã>ûÛiÞéê3Ýhÿ²î‘³áþE öí‘þ æîØë:ãàô9ãüæé/ 3ù¤ôZ!A%*ÿ—Ùíëêû$Ø",‡ 5ò5ëK6ví´ŒñðŽ—öKõ 6 %â‚$ø üCø— °úÜ ê‘÷o3ëõMè}ï¡ã±ìrÛ!ãiá!Ø[ø(åÖ SÉ\ì~ááóòÚHÝï´.H0%_3÷ùÅ!Iä&ù@÷çì‡028µ-v3PB£<8X‰]$¦"` ôµ£ ,›ÌB µ âùµä¼ùÌÓ²Û í}Ö™zpüi&Πí4Æ»ÄwáÀ¡ÕÜhðü%ïhýNÓÇé°ÏÌöËÓÑ…œ!Ò®Ôò×÷!õŸù‘"0¸á5Ã/-,:6Y^)ztÿ—mûhÚ ,c"œ²9ÞëùÑÕ~é:Ѱ¬î¾Ãøè³×î{ÓÓͰéêΦëÌùÞàÍ2ëÏiÍŽêØrhóÕ =qÿ !s· ª& =üX§GúÆ-† /3T/òô"/íEù5©ôÚ ¯,'*|2CûKÖé3örçâŸóxâýÿ+ó÷ªý‚êôöçå8íìëÿî9î;éSìpí©êÑÿëõ” ¶ ØöÜ 9çêÔûEâd¢N'&ì &õóóuøÆ÷†$ü@,]Ù"00„ú”Ô+ð1àýÌÚìäñ²` ¨5¿ýnÚëÏóÏæPá˜ókãûaöWɹöÉQåÏðàXÚ¢ýGÜ)¬&.Ÿ-Ô)[ýŽ /ûüõ ùÌ÷ ï dÜêî µÔTæ9ñ¼×¤«ýãâHàò±ÿ%ø|ó;ø7øºë ï±äsà¬óµáŸ´ù©õŽß‘ôÐÒòÜ»è(ÖåFñå.¥G'!4Æû_èËðCBè/'C *+ô,ÜÖ+Ê?³vá ¾ 2 F ˜ É Y c f(ÿz T÷0ðöòîÏú£òrÃùñÿîÕÞïÖÐßÓÉà'Ò!õ<ãéÿ9ú&îŠ{Ô,ç0ÓXÍtý1ÔG+Ÿ".Æ5y÷4ÿ sû¾Æ Ì£¹%F 3$¶(ç$‚I“ècô -T=,õ.% ¸( èì0ÝCàøî²ÛcÿåóPóSæÝ¯óÖΑÜbÕzË ó5ÖW}õ„ô'þkÔlçÐ Í„í%Õ @ñXÿþ¼üUÿŸ¥ƒ8B }Ü©9”@>;{'8ßEäðHü0Íë%ç2p.,|6ùîáé¯îÍ÷‹ìeý¼÷(ö²ý–çŸö+ßëè/àá]çéàì7è€è‹ë€æfêÇê£î:ñjõîqù¯èÄìæöäæ«–ý]â[üwõžþeÚö¥"2 ;‘'æ4?tš,,á…ø%ê^Þ¹ñCF2ÈxøN+ü†ÊóXKþÊü ø­Wø° j 6R ˆèùõDàJÛLôߟ£þ³µŽñÚùê5ï§Dë±&„È$J!¹ ·˜ö9Žðlïˆ^ít+!Åþ-ï”4ó#÷;×ù¯Ký[›ýc ÷ÿk ã¿ý èí@öüï„çÅÿºëw¨¨"ý$^%‡èÑiÚKÚüZÔ®žü!†þÔ†ùòüäiýM¯gÍ–{öÐlò°ùVôßöôôõ*èò®Þ ä‚ëWæ¦õ]þüí Êßróéíݽ ¥ìÔ < õð (ÑUì[ÄËtÙ¼ÃDúAÞ<VÀ ¶SîÞ €óžðá ß÷…"Û 5'Gø!¯gé«ßÂUüh ëòh*' † œðîGô¿é¢Òô$ á ìŸ %Ðþç¾×ŽÏSðMÙÔ9óK& ýä‡ÜÎåÖ%Ð,÷æÙð Þ÷m ðZDN Ûu÷ ˜h—Ö.&•ê<–+;Cý48'¶þ®ø…÷ó,q.3 R(Uè_ýöì5Þ" Æíî ¢óA KØ ó~ËïÚ×oÐ5åÂÚüå1äi×±â˜Ò?ÕëjÛ©ûÀó4ù@ùëBê>ü?à Y þ‘ J.Á¼J:‹?ÆJ436<› ¾ÞóBÆt )º$,túÆóì¸úæú\ï\Ÿ«N(˜¼9÷þ(ù´õîûÖòð÷ãðÆïqìsë©íˆéñ[ëÀõõ'ó¹ 4ûJ ñ÷ä¾Ý ìŽëàÚfP÷¹0tZÕ ü±þÈCóè³ Ë¡"i^¿Ë…òßõCì°ñSù(ô‚ï(3Î "/-oèÝ Å*Ô—ÝÈÆô&!³%Ãolçü÷kÐWØؽÐ,õéwn ¨"âþb:Ì"èk¯c¸ËÊQ±ìäÜÌ0â…2»1êõÿÞ¬ó\ÕRÚÅòæšö9õñ†kïéðEöÔôüý–¢S,âA4)U:…÷b‰ë ñäþg÷W£KéØZÐÜSÖÌ’è°ÜYâ7ñÎØ=ßw:×c.%  ²%:æÞ eÁá×Vޚʂ‹ó¬@ ²ÄBôtûdðñë÷Ç)}:R}8©f‹U…Kð\Ñ&8+õ«÷úø <­'[!è. öðÖ ÉËÚôë7ÎÏEüE "0Ò€ï®ÀG½<î¬É÷² —þwÿÊ€ý}!k ü ^Ä$èRÏ ÀœŒìxA#,æiWÌ3ÏöéòÊv&9Æ8874Ø;I™)ÿ ‚ôŸüIòÉûÌ÷Ãÿ¡ï)ýêŽð°ßõäkßëÙ¹çÃÞsúµëì‰jtúìÙ’çô.Û7-­3„5w؃à¸"ÇàÑu¶?öÚ¡úô÷0ûÚü «ÂHñG _ÔŒâëݱ!º g'–9£êžŠÕÇßÅõSÝ1Oö^,ý?…ÿý *HêC÷µñ†ä» »ûÈ)" ËôXÙÿÉÿ\þP ¢Ròbû… Jê:ë¬ôáÕ?úÆ$Þ! eýkkYû1âö]ÿ5úþö¿"ëýƒ"Åñä Öж<Ïâø ŒÃ†ÁÓ RHL=ÉëD Â΀àýøhß§#)ãŠ%CÚÍöá|Å«3sïGF’: °5"üß r[ô<! ¬çÎþ/fúØæ•úZgéyê?îxàZÂïÅ  @ôöXänñÅܯß&ègæ3ñ÷økï‰uÜìõÌÉßÖ:ÅØÂØôÁœõØÞO Âþà €ý¸¨û5÷Á_ùÓ)ê'9 ()Vî8Þöþð²ÜF*¦%.(&ï2{ĺ¾Ó|ø5ôÀ x÷Š/;¬B5к¬ôï²­áD¾\‡ñ% ³Pñ UèòóÔæ¾çPïLá‹û!çk €öVÀÙUÊký§¾ý eëûÎ%Ô Í½~‡2awOÇ3„F^MWÜ:]_1ù“ùý„ó<GûËÕ _ TÝ!Ö!õÏÂëÃ±ï ¿c!€ú¼·!¸ã‡ yÅ#Õß]È]øÉߨƒó tSž|©$‰ê¹ ]Ê€ÛÐÞÃçïjv)„ÿ³Zëêýkógî/rø¬ÜÏÊ °­îE ¤ÕôëХѸêŸÕÐ qøÙà£î($Üeê¾åÝÛ¢zäô!Ê~S YáçâFÑÝÜYÑÉ+êÓ›ù“d¥‡Bêhá´á‚óÞ6à÷$+Ù&¿ü0(ÐÌîóÆCÌÿëéÑGÁø×ûDçýŸà) Z ‹Ùÿ9ñ’õ & <6@+I“2ü×^ûç¼Ï‡õ<Ä%-}7$Þ„ ˜ÿPþê:ù˳ 3{0¸8¥º,›Ö—øFØÌ›ãåf%®Ãö0ð ëÛ”ð÷ÛFÞ³ÚqÔWäµÓÄöiåcÎûá (æmñ ì¡Üúèò$[ Š#9%ô ˜Éñ6²íÊéÁñ),îø$½ðýÒätëS‹ì™@I"Ëá!="e‘(5þìŒ×{úÜÂwÑ+ÕÈÄ”ñÐáNì'ûqÖèì»Îs×¹á1Ô¨øå¼²úÒ} ø˜þzÝ÷÷ÙoÕÁ÷mÜ[<É ¿zÏgüGÚ–ÑwëÞ%ÙýB I us /Þq„ þ°³N\…¡ñ ,YóÇ æoñ3û\ëâ•Ï,…Þ&æ/áj ,ðýÛù®í÷•ú˜(%VèúåqîÐ7äçìvèÞáTý©æ(ý}b^K<üôÄ õüUŒ !÷ã¾P²æä Üù; $êëó¦óŒå±ý( '2b ƒßxôeüAÙß=î ©8¢?6)%U߃úÁàPä ê™æ6ä+ä¶íœÝذò¨ªð<ЊàÕÙÅ0+ÝÄÿ Äüì½ï"þ8ïÐöªêyðráÊåÍàÿÛ_õ¢éRýÝBã°‰ÁbÚcÛÏÃÂnï ÿ4©âýûîæëHMûr¾ 0Üþ_iéÊû ÝÜç™äàÛ¡óâw žòé] ¯”ô ïsÿðâãŒýñ¦,r¶;*à²ÊÜÑÑäîÙ@ñ’é“þ7ù] u àþˆ\ 8pýýˆ$Õ!ì "bå8=èðæ˜ôˆ ‘K¤1ŠNê6 #ÐÓþ·3? ªL¿7 &ãCçè ÒwÚ3ñÕL\úf½d ›^ñG à8íåeÝóKâƒó—kS äôÌÇçDõ!ç_çòÕç.=÷yœðAyäÁëmï€à'ßíY_óH Tô‡ŒñHñÖ‡ñ.x<ë]þ&áÑ÷ÆÍÅåôÌ‚ÓñÖ0YýÍ•ôÑü€õÌüêîšöáµèÌë>â,¿üC ¤¿×g ªðȤÅxªÑÄÖÚ!Å ë 9ü5Ù÷„üCþLøüõúíÑ ‡îýÔï÷ð~ñÂó##K '*t'E¿)ßyûú÷Q3ù}ØJ%2{(2 Cíïçºë`gé*” >Ë2¸ 7ÿùî¦å7ñHñߺ•ò4-ÿ&YŠ£ùü!k÷4úIöŸõ ðŒûºõø ³eÄ òä\öqæÛ­SèB"½ £":î/ ¥ä î¦ó7êÝîùI è ¦û¡ !ï.ÿ©ë¥íp‚ê['Nú9”/ 8Íï÷bãÜåÄôä2õ°­ÿÛvùv®æøôdÓµØ×}Çü¯Ø)lk‰ïý „Ï7æaÓ Î-ñ@ÝeXüÃþRZ܉ÛË Þ¦ÞÕvüêú ¦š ¹M ,ÿ±1ÙEM y0SA@:¸'i@ûšVåQñÉæ+&Å0%v5(ˆ¯øÑôñ~)ï .¦1 :0ŒéͲߊâfÏt”íÿû)ƒán÷ïÙ-åð.ç\úe0—ªÚ"²•x%&•éÉó%ûšèE÷Õ#ú’$»õ! ›îÈòÏíí˜&¥8ƒ#q,`1? ½"î¡àªâ¯ígÚ¨Ÿï ƒùûLþãiýj×»ãfÖÔÓà Ó’ñã2„û›&ñ£NÝÓëÞ«Ú_÷MãÊý´~úÁ~Øñ¢âNÓ&ë(w E!4ä^ââãnø{êK Žý» ˆ•Y–gôI×§é…ÖPÐ×øöݶÌ\ 5ýòÁ œçÜò(ô¤ê k÷˜(h¦51L)­8Ùž$'ØVôéÙîÎOÖÞ^&#Ç¿)ƒöF6óöÐ ïöÊ ¸)ïÄmõôüæºî¬ãqà÷øqè> éÎ\/ñOJäªéeò»ßØòø5×É'â4ìôN Ù®êåØlìr"Ÿº'ß*\-¹žóEú*üêè§0÷F0ÝÞ)Ê.‡à êæïùùãáðàãáúQñ ÿ?þïý ÎïÄT܉êýËÙÏ‘ßÂÇ~é;üOEÕáô!ÁÍêá>Æ/4ñ¾eŒ…édÿß°ìè$âpütè“|ÿb!ú<:&nÁøð“õ×ü릋ýy0ËÕ(¡1 (Ïᤞϡ×Kà™ÐPøråü#ÿü áCú£Ô£ÜÎéTÕ¥ïÜ éüãáñÿQÔ­åßÛÛ]ñúãmþvø H˜þÕáÃGºÁ ´4Ý_9m3U0b9Ð!)¨Ü¼÷ØñŸÝ”$þå@¦-s';…ùÖ†èmî|î&à–ýýåñ öࢠʵPðîÔÚèÓpÍ-ñ¾×"uùR` ©ðö¡êÆòÐñYí5þ¹ñèúü_U PijC÷wüâÑñ")žî8k/«´/Ùà@@ÕÚØ¡ñØÖý9öÓ,ä Éð0äËè’ëÞfü•å+ ¿ú[à ÁWë" “Ë¡é?¸žÉ›Ö½ÃÇë®Î¢úõ;äÐô{ðiã¼=ò"€M!~#Øb$óõ çrökð’é öc$ñÿv$éO.åÅá|%è¥(+ =%ö&I ~­ùû ´òøü¿è›ówèä7 /ëp)è*àãË ÏÀÉÒ†åMÃÁ’ó?'D8ßF«³6;÷ìúÌòw$úÆ,u'"%…QðOý è¢êVí¢äßü¤ìè uÿn{Ãû†öª8û€î^¨õ¹þ õTóž \Ð3ëáǔ˻àŸÌ2 ˜í^% ’K(bþ±àøî1÷öÖ€6~)CÄ;$Æ=ÂÿXDóþLøHõú…ôF.ùÁ!¶ ·4 .¿„1ÖÛàø«Ò²Ê˜×·'"—‘(1îýÆÐ:î¨Í|ÓEå"Öxúcîgú$³æûVÒ˜ãQбÐ_êv×í÷ä¡_sò¼ô¦ö©†ùj' ;|*Ñ3s;ÅÇ,Íö" vîÃíE@ëè, Ä>U4¢-å=×é&é“ý=ïiã( îà$D ` ¼$Ó6ÂåÎ>×Îdî‘â³ðÐñäî‘ôfùwù`ÿö¼ôVþìtìí¡ëi¾ J Uä8äמà¿ôák+Í Ø?Ï+8Ý9~M+M âçøÇ¡ý~+®`-œ,6 Ž)·á ÿdÒ|Ú ç£Ó@|êzà. B ùd\ètóØÝ§àˆÞGØðìrâ¥ýÎûýh œâ2Öhä¦àŠÜ†û[æÝÕ¦6÷‡ ¼Ý‘ï.û(ÞÐ2§ ©.n9 «#1âúìì4æûþÑó½ÿ3ý\*ûg¸9ï*¬Ñºã„ÛË· ½ã +%e?)OúåLúÜÀâtãëÙø6åÒ ƒû ”FéiòÃ\àMä’Ç‹"šöÄ6ú%Ð,šö Îfùõü:c ük Ò<7þÔ ·ììýíÛ©ä®èdØvjñ$aß: öþ cÜê(æ¢ÖOˆèU t$äƒ [Èeݙ̃ÄîîúÍCsó-+9Ù"€(}[þ­hö¡É1Ü"q(ð1óR$¡óŠvôÀñrõ·ñZøÎòèý3øl+ûu“ã%÷?ß¿áOÌæÙ"±§„!KÅØïǰɼG׸÷ýaã^ûDý-õÖûNóõû›ñÓ÷‚ï…ðû?ëVõ©:'2_?ÔÖ&¼í ÷ûøïîûl#V"(†¨&¸úÌñ"ùNýwïþ"ÄÿÂ"%êC ÈÓçLÞöÚzè=çè“í‰ßÕí÷Ó£ä{Ô¥ÚAÝP×]ïPÝÐÿÈî1 " |[ÿŠ jùfüÍüqöüž7×&•4d>IÍ,/ážòXÜp6ûÆI‘7 )·?(¡D× k’óý?ý|û™„+eó ¿Ò‚æÑêÈèDÐÁï‹æ ëïêÖé—ì]ó¦ñ¤ú”ø~ú;øNòø„O iôMØ¡âîÔz)­üÚ>M7ÛQ5#ó¶žùëô¦§ý·0Dh3&,ã%/Q C"úñ€ùã+îåàIßpî³äøÿ&øà  ù>Õð¼Ë<ÌfèÕÉ× ±ë™ ‡ ÷`Wã©õåÜ*ä èîàoþ¬ï¨fÖ |øñÁòöwÙñ~#¤;1=%Lg+ðð iñ>ñµ Oÿ4mœ ã„ê È pø ¬Ù‚ì™Ñ½Ë€ùÝÓ]"iÁã%*î×ÓaæÏãV×–ëûÕÿM õ lÖùX1ïôéô?ç–ðâq *!!Fô"Kf¬ð×4 #÷’pÅ5 ïÊÿVæ3ì—îS啵ò*S Cç¤åËÿMÙòÙ‘ðÚÑ<ô)MK†'ðë6 ÏRäßnÑÈê½mŒ>d+!u¨E…ý÷7HAÅ>qK;Îçõ ìçõéQóÚ y[ø!Óê÷ûåAð çÃéÙêÇäeó&ç ýôúò‹þ Ö^ï·ÃíÏeÙÄȉú°ç„à\ò`äÎì}ñsèêû’󱑸[{-Lû2ƒ6¯”0åôƒ Õù[ñø>9ž.‡.J>; †-iä>ßåúõkæûÅúˆdþúzü"ýÿü–ü¨÷ñö.éÉìëëä…÷éñWêûúeÓè7ÊþÑ£Ý ÐÞø9礪üˆÿiÅùÁý¶ÿÅöƒûü‘:ÈFY=-Bðä%­îSÿ²ùdïÔ„ 7œ*Ú%§6ÿi³ýŽóú¿³¡ûºûÖöøîÌ €üà »ÂïÒÔëÀîÛóŠòáwï:ÕOÚ¥ÞÔÇõÒà¹!û7(œFVôÿVú­õl =ýâ Š! Ã÷¸Dë“óñôê Cú$ ¶g$(?,".ð’æŒí©ê÷âHõRédÝ÷¬JëºÌÎVçN·Ä°Ñ´n âŽöÞ÷GÀÞðòøßÛõùQ-þ< AÐþ_zu ÕûÉ^ú»"Ò _1‡1¥0/’ö-¼ýñ¹W÷`† Løe ýãUõWÜáå¡Ûýý‚æ0hë NíIëѯÞKà>̯”ãal8WòúúôþúõÊ…ößK”%Ã!¥p%t¥8ûÑþ=ü d ø(²"3y*Aâ­Šã\ßíÿ€èM¢yô Í>ãŠÂ&Æ:ÎÄÕàQÒ2ôL⦠Cø³Á@þtàÚúð–ÑÆÌ0ðÒ ­ø‰—† ¶$üú RôAú\þ¹ô’oÌ3ÀËBd@Å!0AöË/å íÝMêŸnIÞZþ{ÓAàÞâqݪêÈèTèCè@êæçôó«î‰ò:õ³ÛçØæÐýêÚØ3÷Á ô²ûÏ~í2ø ëPè¥ <êß:ÞÖ>êCi‰7ÿ4’þƒû(‰f-ÇÔ;Ð5)7ÏCðî97Ê–ôgûïöîK)úŸ Àý¶ 8îø†åòæ"ä-à ì¡ãeü†ñPho¹ì—þºÔ<ÛwçÉͪ ¼ðÒ Ã qçÓÕRÜþ;Úd. î?è1%3?<%!/& € Q^åÿf °ýÛ Ï+)–ëNÿÛÝAâ‰ë—ÝøñÊ$ SJð‚»ÕHã˜ÎÀÊÂÜ…ÍJñjâŒõøláØô$ÔÜÞ¾Ñ5÷@ß` †û¯ [ ÝQ "ý5kü‰Y z! ˜$”  …} ½ý+ÿ/½ý- 7+È*—"0PàÏMÐ+ÖPø–Ù[R«z"lÄSíQõß éÚ’ÖŠà/ÐÓð}Ýû®öf }ñ[þôãé‡Òò>¥ ‡'Ì!Ä%¬)öê%]OtÇ DWDr $4ôÎ-ôï@ ”þà3Ã*ƒ3BóÀ ÜaìŸßÌÛäç(Þ0ð’çfô·óÎôâú…êøÞlâ­ß‘ÓžõÙ *ùž.b-*‹-ÝŠ—åÅòòònßÂúV+›!5•-»Ø¹þ; $<a t `ÿ™ÛðBýoè¿ìŸòÆåAúMù= Šá§ùˆÐÔÝ„ÓDÍÈÝ-УçÝÏóÏ굕ù³ÍNæ õÚv×Éô·Óñ4=ìK0G¬>Üë¾ïçNè\þ©ì< ‚#Ä!« ò+Û¬% õ¾­9ۋ̤ºª_Õï°.ݹétÖ©ÛÈÝåÚôé(çDî6ñlè>î§â§âäÙøü ß”íûïHô*"ÕÓç-ß ÒÄ íé(£ž/1¨y1ÜöÀ Áó¼!̵9v*Ø.+=˜P2áõ$+Xïâ·è‚êŒð“G¥Û!Aè"eÌÁÒkÝÝÃPð=Üö—ëcïnðà%é‰ÛËÛéæ°ÙíùfçGüÂÙ¦ê‰ýôØßúá9Ð5Sç$©– O*~G"W ëB 3 H NÚÉ-°%”0ù4… ‰0{Î+ü¼p¿‡Ü‘à Œë *6‡êþôØcàæìÑžýÛç*þ‹þJï(ýàÙìÝJÜ ÞÏÛÅâ‡ßÛñ½ë ýþ«qr%˜ D!ã'97'7N@:á2ñ2 )æ®òOñPím ýx! O©+ªB 6æm¶Ü Þ|øùÝÓh_!š#~ÿIcâ|ó»âeÞÕå¯àqé½ä”öºíÖXÐEåÿñ¶ßjíŽê#Õ?†õ#0N)I°+,ÿ¼GGQ"Oµ3%ñ0 nêXYáéêeë˜ä—ýtð< Sš ŸÔòqräÙê”æ&Ý]õ½áRßôÿ ÷ù  Ü¹íÖ͟ÀK¶ÒåSÂ^üôÇ1¥"'®,½ý‡ýôú˜*k.¥.Ñ Ã(ç;ãäÓìá\å¹%ýå|Khêóúbë~ì+ù•ïNù@ Åפð^É Ì‡Ü¾ÉæÿäéÔ¶`îS‹áªã4úåÝe"TþD6À)>$¾7Œ>!/Þ÷ø°çµÜ$©öz6\*Q4‚>¬ 7è "™ÿB ó{üVôÚôß’ÿ6ëc'‚Übéø{ãäàï«ç˜öõí`ö‚ØôæÒsΞí¥Î” !ï“ É *÷Lç¬îXô[äV|õ>)}÷)O*c{'ôä Þïdòbü4ñe;%Ç6S+c:n9q#O4rù•ðáÝë`ðïà/†÷Y ™ ÷. ¾ÌPêÆ½.¾FÝ ½Š©é´„@äKTÙáÖ ñwÚð©îK÷XÜæ#òæ5¡6C Ñø\üô¾?{'#Œ)ª.%á ð[é1ÍÀ;•;j7ݬ|ßþÜsQé¯Ò Ôï‰ 5ÏÑæÞÉÌ«à1Ëiþ¾ãl^S‹ÐH ÷´/êxîÈæ2äjñçæïûä%ÓÐnýË ö•øD —öý:©ÒKÝL"+Kì"öˆè¬'@$R3Œï}¹ÅÛé ×½ÓÆýêà.þIèÿô¡ b‰Ñ ±ä(ð× ÔݱÍãÿà')B¡&&Ÿì ¡Ì±Û™Þ¤Ä;yã|-/m0B o)ÙçÊþŠëæàù áòô žÂ' ð ¼Ì vö®üè ì9ñ„ç³ûžÇ <ôÝþ¿áaçêºÚƒÉíLM&m¬ÖÓó"ÈjÇÝñëÊX"ýÖò åì« ãÜ€åžð™â>üúR+91É.Ã,6ì/wòüzñùð·ÿtüÌ i5Ì)ÿlÉèPúöåwãíú‚ë«×:'ܯܥõÇÊÎÙkÄÞ÷ßãÃñ6ýA×óí<Ñ¡Óò¿Ôl{úÚ)Ãâ &Mn Më ¸´Ñ. _óúŦ B$Ûì)86!òüü(ð)3?<$5€u67æNÏÔÝÈã÷ÔÊùSê¾»ÿÕ² yðÀ#܉ãÓAÊÖÒÁ¸à+ÎçìóáùðÔíªó3î¶úóíÀ‡õ ¨:Ú¡ E%…‘$hà$ý×ë (þè'O”',2iÿÉ&_ÚV÷ëðÁß3&x<£2#" ;a÷êýá)ñlÙäß Ú³Ó9áìÖ6ïŠåcü0ø…÷MþÓçNîÔÓ4ÙÖ)Èœøv×Y!)‹+öþ ¹ÝVî›éMÚ%nïÝ b°pÿ+Hö$ÁûÜ(]Š3 3Ž·5š÷Õëí¸ýMõ€ô1‚û• ö  ‹ ¹† _öïÿãéÑñ‘ß|ß´ã»Öùcä ”Dî˜Ûþþ“ÇÞt¿”éU&Røq!bøh Üåñûé@àpýôëÛ èÈ XÜ ˜ ëÊ ¹ö ! . W zÿÉ‘öKPè\ñ å'áõð‘መóž WŸÿwíîÛóïùàļê¹/Ý´Ö,âðìÈÌïãuÓZÉúáÞà ‚ 3)”*á ¬%èôR-û¬óñ dýt Ï  € ytÜê#U÷iÓZ÷CÈÂÆsûöÚ°æ‘ËédýŒßõÜróŽÛõ­ïJ »Y÷VØlð×ëÖ6ó]ÜvÉùEàÄÉî%¨èMíGüèíñ+e,Ï%=-Í2I!É/¹Ü!-ú¶ aéÚò…Èî€&ôÏ3:6Ô2ÈÌÝ÷ÄDÇßâÌŒð?Y°~ä|÷ª×QÕjî Ïïû¥æºñðƒæŸé¶éÞäÈó³ê,÷1ñ»ýæó6 ¾þÉ"Îá&uñqKx-½ýH‘2B€GàÒ8îûÓ‰ëÝúQø¤ñú2^+N$Ÿ(Ë3 Å&Ì슔åFáºýÄ娗̒ü íBö<áåÂØ9ÕõÖ&Î-ïÙÓÊSù+ <áìÚþéÙÑÜ·õœØ– ~"A'<w"6þëÅÿ°þèVñ4#q3I8Ð Ú.ì6ßïóbþ8øö ¢I½ƒsòÆ|âÂê-äÜ$÷så/.û¹üsñà¤òê¼´ÒZ²á²¦Ô¹D½å A 2çïåOäMófÝ•¢üÁ ï+„ð +Ý+ñ‡ñ¡êÄ%Àb þ — xûÿñùÚñ ýPr õ áÓúéÅÐýЕçœ×»>ñë*^ ÷ðR}ÜËÚRî`ÑðîÙHX}A?ðÍý6óÉïž_øYµ $º!ä ¤)?q!ÆîJIm Þ»ÓŸ#úÕ.ð 6íçìÁþ—ì8þÆ é¡õvcÓÂå½ßûÉGÙç+âq×Þú;¸ò!ð^üsð|ý÷Fÿ1ø: G_¾ïp*évqÚ~ã°òÜä~¬:.-œ$—5bµ Xà¥öñäñÞÑû†î’@ÜíO.Ò¾ïÇÓß×ÚÊôÓÝ’·÷˜÷Ñþ¯æ\íòë<àú0èþ…ò½÷¢ñ5ùüïAÿÉùƒúÔçù™éæ+Èôo2Î!Ž7!;›_8Íö¸ÑíÏò›ˆ÷4hÂ.çFö9'…Ø}éaå!ÞKüÐí6ûžü£óqùªú½ø=ý ýíˆùÛúÛ«äGÊŒîÞç¡k lüøoÿôôì¶ýñê¥÷Ýo @ÿ£ %ýAÿz Ä¢1Á(PI<ØBvQ 8wøs+ý¬áâÓ¨üüúÃyòeþîïšùËé{¬þ —Šþ“lÜî§ÊÍ.ÓË¿.õoÖ”˜üäò÷ŒÏïê«Í‘Ç®ïQÏ9 §ò+ + r^ ÓßäI ìîÑ\ Ks^óª Îòäúå…ù;“j–ò|"îzIÞ?ìdÝ`ÙLï$ÛÞøð†¨t웼ÁÔ¸Õ¨Ý߸ògèÉÊÔí(þ‹ðTò"ùÚôRºö¾áû3 ]¯N×`'#:  H —Ë Èe m 7úÐÕòý¬ôîíÞµ(Íü5ð›ðMñ™æcýzëŽ|úæÿ¸DóÜ÷èéé•ÜXþüâ#û’‡™<]ópýÅïpîÍüçõXŠ ÃK#1. “YûíCý-:$Ë,?þÿ&ÔܧöˆéÛÝÈoô}ÛÃôiûÌâ±îZÖ ÝvבÒÚèÆÒ®¯åWîMƒðï†ÙHä%ÝßÔ`ö­ßÿüw †¯$»IAú’Y™ý¤(ÒJ:þ9·+¢KÎøà/ Ú(õ˜ó|çó÷ü€ý?LéGúÙîç}ÖŒÞ ÐBÕ°ÍQÈ]ÎÊÃîØŒÊ†ç^ßcôîOÿQôïó—åò=)7 S.%o t%›ä#þmç›ß| „ð%§#I-©I)¥m$Y!q&¹$ž%*'ü&ò)U,ñ! 2æ&²é½ñÙ…æÞ]Ù±èáQóðËö³û"ñ øØå)è4àóÕaë_Ò½æ¡[ „ÅÔç:õEèþØ" {âw#z @ÍÆ– –cñŸ;!R‰*M#.-b&^1Ô)¿ Ä ¡ Œ\ð*t&[\1¿é Èͦá‘×ôË[õíÞÅþ4üüí®ü¹ÚåMÒ-ÍOäÞÄžà@œ¾xè-ü×ëâùØ[Õïä{Øúô6å0?ù“a þ …©  ‘¢x¥¿% !&00|.:íØùÙhìòíã0Tò 4ì8·ÕWåXâàÒ­õkâ´õÅóIûkäÒê·Ö½Ø·ÚoÏðÙÙ÷ "ôPà5ï;.Kõ’û߆ìê& q11{_7éûáøàùë ÃûÀk$«'8ˆ,¸ÿ‡¬îÞÿ¦íðVúyòVþ›rë÷TÖéGðºìõáüËäj HôNñ 9 XýàƒêRðÓå?Ý/ÿÑày#¶‚0 («‚'á[õ‹ò½Ø§"]÷9T,/#…=à Ê(-ü‹øûëú–ü ÿ‡ÿVÀ¶ wí9pÚŠë ÙëÕ1ì×hýê oø?Ê׿÷5ÑtÖÞä Ñœgç_ Õæþ=žíõ0éáGÑâè+'v<ê5(è>À'9íGúòÉT%9/æ,gCôv/ÿìE uá íêâà´èšâ7Îî_§GîÔÀù¼™Ç¶Ý ¹+Èå|ýO PÞ°ùâÌpÚäܔɸû›Û±ëú®#ÔïûX*¢€†? r,žf/Ì*y"/m ‘#¡ø< íóNü îø5,ÝC6¯9`7S÷æ~ßì¿ßÓ˜óÚXö5ûRÿà˜ýnË<ÜùÌ Çáüdž#ãü êEò¼ããðíÖû æûÿÑ  ‡d ¾îŒxøýfú6œZ>¯=¹“;…æ YòâèÕßû $à-û êéÆûÞÑæ˜è"Þ ýùîB ”5øù J×Èò_ÁÉÎÙÑ·¾Ûø˜ÖüíÅžíïíÿ¾ãæêí}ÜUµì?± e©%™ö…¡á@÷Øð¼çs4þ˜0(% 8 )ÔèUbê£åV ¸ñ•ìšáJêÉíÜëê,á¯à‰ë äƒùšíO˜ü ›§ ÐÜÉõÊǹóÄÊäö’QûZ,ÛôöÓ<Ô ðVÑ<÷31$p&Û4"ÍWèJù«ðDèµ Kõbq||aë ¼è¿ñâö?ôðùIÄA Ê(/2,âhù á×Ó*äu:p£,w3!ýd’ÝuëæÕÔ“âù#læ$ ).:×éèø¦ìKåíîø­4#í #U%!ŽCí7çíñ£ñ‡ïjÏú   ø‘Fã lÙ˜êéë,ßèVô¥.“å½0ö­=ßê ãEØøâHéa– î‡4ÙøæôjÝèžü(À"’"&¿_ >Ôö~ˆì¤ðW÷Xê”ào#õ.žôl%YÒö_Ñï×{ë]Ùý÷ ¡lö>jÔyõmÃùÍ7ÏÖ¼Wí/Ðe_ôµoåçýûUÔ5ÙMåLÎ":å7"ýn †Ëa ±ý=»ý¶ˆø( 4<­(½-8 T%NêÓèìŠæh •õí#*|&>1lýœ*ªÏ*õ­ÐÇÊ3ÿbÛù,;yS*ÿô…¥àSìËÙÚ8ÝñÓçâ×+û€çBgþï- Ûëýdìõãº3èZ+˜=.àe"óH˜÷`ÿ‹àúYâ £)Ž(‰82#'RéTyõHñ0q “JtC ÒFZð«ïÝ:í²ëÛäÇûõvc¢ _ Äœæû)‘Õ³ð«Ï‹ÅîÏÊ Dí¨}?—ÍåÜ÷ÛÔÝöøÚ9"Z‘!G)kúCJÒÖñíÑwÏÌíïÛùñöm  %”^ò´ì»ø×÷ö² 8AìÓ½ùxúÙîðÔªÖ™êrÕjï&*Î ÿ'‚úJëï¥ü?âô"ôúó7VG'-¾çÏ ¾ Ë›ØÎ¼9üÞ.œÿG 3ºüTø2ñî’ú’îSîÍý;ìÏáþµõÊÛß÷ŸÚóárøç×BŸ*§&µ -ßë+ bëæ¹ ¢ì+p û)$à Ï»îÕýtíÚæÅ½îf,²É9â1<)ò6v î!Ðî´é éQúßçp„û‘Z²$2ý?®è9êŒñ?ýtõ¦F k„$ˆõz>ǹíu¿mÂ)íöÅ+4ùºæñðÂÿêç)íòðæñ÷àfðà KèŠöÌÖÈÝôà€Ò]çu)Ó†-,³‰-·õÿêÃõñþhïÑ‚ y&=(¨ M'zäMÉá½ÕSÈx3⬠J î$<÷óúâÕñ2äÒÞÏï³ß%þétÂõ¿úˆùíEò:ãÔã+ì.Û—6ç; l¤ ¨½ìôý?æñà óæV8½£3Á;CI.‚àœýÒôìêÛn4V*‡0¿>ôß4 ò2çð@óÄèõ\ød&oG%(—ð!¨÷‰ eðô(úìŠ ‡ôå›û ð³µÔÎåÒÓWɤ÷‹Ðõ)µé)ý2Šõì!xÚƒê—îÝ,zúµ b® s"~ðÖ „Ü%õ—á‡æùðð8Ó › ÿ lÜ*xÑçéÒý(êïæÜü&éÇoÿ+0ÜúúÔeòÎÍÁÎûé3Ïí©Ä “ áûüÿÇÛÑAÔ껑ômÏ 2òÆ ô7öåsÞžð}ÞrÞÑð%åê^ùÅÿAÝ÷ä{ñž¨ðørø¥óå ú"(°Œ,^0ׯ*NÛfü¿ãÖÒ:ê%7s"µ û2ºñžÕÚ3êÊåÓÕÞÿŠàzOü'ù" êHõ-þçî‹è·óã ñ'B ‹$èí êßyñFðå“¥û2 `øk)¦÷‚ØðSÞ<Ñëß*ì MH&­îôÞËëëŒßηï’1)2f&òìèù^í¿âs÷§å3"òA" !#š)Óá.†Ì?Úå÷ÔÏ/­ Ë5Û>¢þ½5dÈèõm¹ÍÁÐØq¸iÓä5-©&D 1àÙÕÌ'ßÛ{ÏÇý/âÓsÈ Eöêd˜×!ÞÚäMÐ& âæ1)ŸÓ%%Èlé¬÷$é™à& ç5.£Ï—4Kî+ùÐÈèÚêGÒS"¡õ>;S'û4?*Á6µóvíð@öêÿ6õ'ÿSóaC Øõ>¬çóðÇê/àÉ ÞéZ%´5$ú-âOëŒáÐL÷þ× òù` Íùôd®ãó1ó°ä]}ù3)Û¸.b0 u,ù ãïîJü…ëµpµÐ‘Ñ,3e?èIþx䝿ö`çÞ)!¼  q$×ãDrÑ$ÔOèHÊÕ Õçr/Lø^7„Öu:»?Ê?ѹ¿újÚªjEëkþ à éèá9ߢð ã‰ôóK$ Â&Í"ï 'õëo •Õ2ã°ì ÙT÷†&Ky ð-Âúó«þÀcîv*HTJ‡4Ò;œLžŒ.•×ÈólʄÓüÉ,Ê,ÍéX±Ò¦àtæ"ÖÜú_êÇ¿÷&–ýÉ‘h˃ßlýÚÎðÕ·ãíϸ à튛>!¬ôl ûí6ö Çî9534‚>yÓ,õÙ°÷OàšÒ?Óà’õ%ß"H)‰"[òùñä{è–ñ-Û €õ«-Ï(Ôý!½ÔÍïhÑ­ÎëPÒƒðëR ‹Ì§t :ÊîNMÊxå¤×Åë AæG&ô ï*#ÙºÞÝÚt¼)é•-ï ¹,dû 7íZú@åIì@á(ãjî[â\úA‡w ÅŒî §ê‚ä 5è§, !@Ô0x?Ùè¦iÖãÚ_ôÖÖŽ"Lq&Þ*:U)ÄôÆ¿ç%ô.òŽèüö(´ë$v*uÞ#â×?ÿ ÊKÐìÑѲ Kø™ Àaý½ eð½þÞìÝô;îÅîí÷þëLûõ?)Ÿs@$æâNÿúÐÿÎÈÖ¤I#0¿EÂ#ÛáTø‘êøáÒšð9l ¹#• Vø&ƒåSì=âê¥è·=ïu ø/'b$(ýêYAѺéÛ4ÑVüã IUæxýÚÓkÝCãlÎ> tã‡!q ‰ì°ã#ÿ¿Ë6×IÙ0Ì.õ¿á0à‚ <Oñ±&ã¦ïÐñïèn;ûSEÂ7 ^ Òoî)1Ûé•ùáô,y NBÄ@>CË×ÒýØá Õ"áðÐý° lŠÛí‘ýãáíâKëŒÜnì3Y Œgð IÙ?ê´ßMÚúöÎäeúl 0 Í5Rø, &ì^øñ íRKòÀ1¨‹4B©ùž-ÙÕÎðZãýÕRÆì:¨"¥1v=´/×ë² îßÚÌë'nøƒ #º@ôöü2è§ðÝŽáuá„ÕOú¬ß›=âóý|ÐUÖDãßÈÒåö¯ Î'lÞÙþÈÈ,ÜMÞÏá‹íN¼™ "±óàƒðwì¼ÞµKóë/=»S+ŒùÈïøÊñìñRøƒòÍ£ö *ß)º&½'.kÛ‰UÅ,Ç¢ó À‰Tø}¾þXÙqø2Ò8ÖÑæ­ÓeïŸi§û_YåâúòÜUæËçâQÿIð}‹¬ÝÉ–ƒü°vüšüÍ QülÔ > ‘Ž| ŒóšMÙ6龿`ÑÂfëÒ9-é:ÆM$[êMùðë1ãÀ ­è+q¼ Û-’÷PlÓ"ò·Þׂùví¤hÿ ¯\±D!> Þó€Üö^ë¸öõôð:„è ýÀàÙá;óèÜ/lò¦’ n}ð„æ ã:öÂÝÆ ´ô7b C øªæ¥§ÏHá§à×ÖÃúèß þùcÆy ñ¥û ñìCõ&ÿÇé—)S g4D8`·7SêÝ ôå³ä¡ æÿK·ÿÑÜËõˆÚsÕÞóÜÙ( õâÓö@ý-ò…ó>î"ðêÝéaëÒåú7í#_Iú¨ DäÌýÙÞã冸òⲿÿ5:(y6—BnU3$óÒþ íÀ ç3‰')'Ñ;¢'½ã/ý(ê!áOCï.9ö!y_-ÜfðÚÿŸ±þôïùíÝ\âÀé(Ûb\ö;ýËÆØ®þ³Å Óp٬Ƕþ„ßJQH > ðˆŒãçúÞ ¨ÿÔ!å$¼*"¼êßëçŒê¦ªè÷$Z4q'8$//ùCbüCó¿Kóû_ù £”¹ˆ<çÎÿÒÃ×k×ÓÃ8ñIÙæþkü±öT <ØrúíÁ£Ðܠ¼ çµÊï¬Ù»éÚìËßÛõ EÒûõ¿ù*¾üÔù¹Eþýª\ÿ13çÓ:w3n%£4wºñ;ûøåvdö"Xf-þûú2ä(ù»å¢ßý äSQz#=ø¨¤â ð€ëÔß•^ì¸ÿ¿øý÷ý~Ó æýÚÅò9úCøàí@ Îú"H{+yýØzñ£õwþ3í¦Ÿw3E* 3Éã0tݩ؄ÿÝÕÐ#©iþRêýüåƒæÚò¼â·öðr ÄMh Ûûwrïóàí¤ã›ø¶ä( røŠó Y=ýÏ bó‹õ!ûÔêä›ü©&,¥#BÓøʘƓñÏ Dû ûÒ ²Û‚úAØàë}Þ ûGì¬ùr 톙œö\*ë¶óëiçÌñXç' Ÿô¦'Ѓ&Á;q ›,Ñïð’yì’4Jè>9J)À<êY#Óôi^ü/ó)ôþ豦 †#øõOUòBýaóZô¦òãí›îëdíöé“ïçïlìëò$èÊìXä’â8î9ßÑðî Ë ÈÝâpÌÑØˆõ¦Óô&î,10è[&÷ïÄüÞ ð·,¹C7/ï6?iI3ž+ü£kûùÉûPøúþTû幪 ¶jÿùoæ‹úÁßߨòØœ"óýØÜföÕªÒX¼lºÕA¼Qõ¦à¿ôOýôÜÝô’Ñ5Ù«ñbÔúƒùnÇDöûj Kò‡ø&ŒóyBî"Ð"n(ø _*<*Á ö ÿ54C÷’nã-Ô'± Ä-”å ýÛâàóô£ÝÀŽøü ÉòI;àWôªÛÞÁäÎÙ£î¡äÝññ)ñyõŠóFöŒúŒøÖ¡ü` Ìá(ÂÓw!Þ÷žÿ~]ö-ÈŽ$-„Ü*ñ×Qåàù1òKâ’;øz-uÿ *4îô gØUæòÛ×.î}Û}þ¯ñÊüÂ’÷Þú8úìøüù‡ñÏé¹û>ç¸þÏýöNÛMìXóqݯ¡üáQj{‘ëÙêÎè½üþä±ù¤Ëýßeó—ÛÝôízÞ…~ñoª¼üFìÒüç¾ë+íqãõ%ãñþMë\ çü$c:b*÷Þ¯åCé/ßãk/ Ü1f:Á Á/ïñ‡ð 1õÀ !Š€&ø!ŸôVæÿèõ« »þòÖNöë+ôÉèRð™áÖëµÛá€áÇׂü‡åÈ rþ&ëWû^åYã<ö’ÜZNõ‰.È4l2Âô¨â±ófý@ëæ$!_;|43ÀC®5+ QÖ€!ö Rˆ™eæ¼âÉ h3àÜúýÏnÑù¢Û (2ø×nÍuì#ÈÁëŸÄÏ4äðûØéÎIâ†Ó˜Éúùó×·"¶U'˵„õñÙø¿ö² ú® W Í1t 05ò1*b5Þ'ÿY ·ÿ„úš OþA©ü¼)ÚàòþÖjÓWëÖ®…ð7* tòªÖeéIÍÆÉWò©Ì0kô^ïYþÒ—åÔÑѹîXÛ¦÷! ¤’"û Û*Ï'+þ»ýÁ=„/‡5,4 Y+ûô3Cî<ùSôÞ#HS+.¸T1<÷aåOîšéXßÛõ³åýåókö\üûèrõùæNìí ì³î*îKçÕìuîèëfú; œö ´æ·ërÿ¡äA" V#'ä^WòXú‚óÈ€G+_Ÿ%„1ŒðÑ:ÎXà ê­Ï3*í,8 üùM} [nrú|îé„ðæoß ÷Kãò“û—+ õRááéíIåÙ•yã2(‘ Ï+-cû&©ú¬`þVõ Ãúžé ¸KI㤮ÙõãÎóµàS×þN£„TôIüèøÙôn÷]øXè,íFå§ÜèôÈäsûëó@ÙØðÒØ>ÚŽínÜQ°ôª1½!\ 4 ÷”7å5ìv Òä³-ª}&2°§'À}¶» Œ L î â  l Qã øù ºýK õïÿâï€ôÝðíùüógÿŸëÂ*×xê7Ò1Õˆã}ÒùéçÜü¨ü_íÝý®Í4æPÓ§Ã(àØ9,ZÏ!B+Ìþ/íøåü… ,þX ­÷Œ+{&v!’"Z*µ'!¯õ ¹ “X!þ ’,£o%÷+#õ"ä¸ýŸß®ÜróÈßþÞ÷@òõÓØòÍ1ÖMÚ@Êlõ1ÛøõïüÑÒðá‹ÖÎsï[Ú•%óáýVþÛüØþé  8Êé‘ÿN!°#9#5<~7\#x6½Möòs÷nLî]+ 2’6Ø ¦4öë¬ öçë¼ùçêþWúEòiü\çÖóŸÞ~è.á×ßÉè£â‹ë“èèÓë·äDêðì¢íNòžùìíö2ì£ëÜ÷Þì6fþ¸µ¼ø¹ 9øŸûgBùð'” °>Š/À.L@§þ!%ŠÙÔòªîÊÚ¢»ø’a?m ø êüá~Ù;þ¹üµýþöù_ÛÆ  •üüçç¤ñháëÜ0úÏáe7Ô ®8î.,ìfêƒ`ìq)ŒÔ"ø!?Îeñ1ý·óœì¦Õòz’eû‚%ï¾þ&õ;÷°yú‘þÞ9üê :ƒ *ü^³îöåðþ欚îË#"‹ ç«%÷àôüçÜpÔhÿ·Ö"·ÿDÓ ù¶ áÿ#ûþ=GòÓ^Ô‰ö¥˜ðù¡öëô·òùñä/îVàûâMê¢éïöÄý*ë ƒßÒí`ðÁßZ ÖòÌ Ž cêm®ÌŒäÉŠȬÙOÄȟ᳄«¼Œò!yõ­óIù§#Fj$m A*¦ Ï—‡ãÄÃ# Ÿ©¿ß&jÛ!ñ¾ýqö§é2 öøÐ_„æÝÍãT×”ËÀøÛàú`þä FãÚÍúá˜ÛÖÏeû„ß– ðúa!Õ;þ Ïa–Î úÞþ(Áh=Ç/•3ü@³£0ÊN þuôûŸ-ðG0 6)Ø'³ÝîxôæÛ• 5õ³ Ñ éð• úÓ:î[ÏÅØYתÒoèòÚ ãÊçðÓBÞµÔzÔªêXÝo<õÅôVü,ìðä`èN‚ I }šáÆ´ãÞ î$8– T7`Dþ«9ýAú3ý Kúo7 "ÉÓ_#ïeþëMñ>ñ4ïB÷¥òùÙöúøxûcø©ÿÓùØþ¯ð‹ö‚Þâ×Ï¿èÙÓ/ôª Øåvû~à¥Þpÿá0ö/ Mö¶0ìËùÛù¥îìWþ]#x$$‰ùIûüûAÜÁ€±Í¡.<%—&! ãCôçøÙ< Âë‚$(¨'ÞJêºòðLÙstís!‡ qðçxxâAâ®÷{ãI søü• Í øAË e(p¸ö}€õÄÿæ,ç­ô;ìûPé<ÿïÒ$äIè5ÔNdù™"»$W "iÞö´àKÕUú˜ß*×ü¸ESô—Û»ïþå´ÛÜ•ë† LÂw-Qb«õÆñxZ ]ý¯ö áÛS½vóZƒåOêðé¨à£þÏñÙÿÀ a厫ѳåÊØ,Õ•ó@à!jûùä $â¯ÿoÒâä/áÌÓÍý£ä0yýÀòÐþŠçµð¥øBîà j& ± ùô |0nH08m; JÌŸ.\éK÷åoÄ!¬ ª+]ùÜQåÛõºÈëëé$,׎öÑ ú¢ôžûó÷öï.î‡ìì°ì—è!ôÃêøóbúõ ÂýSOêò<ÒÞÜæÇôß“"÷ýY/2#‡ú øaßô°·,)¤ 02ÿ­7ñ÷îÚð¼ý¯öï| f3%Š+ÅÞ,Ž¿[Èéç¡Ç˜Qg"1&ŒSâóÔÖ.ÛWÖDúTì‡5lF’ø®eÅáȬ߲¢Ï¨®å’ç1ÖŠ+T+þûDÛÝîÐÛÛŸöØì %þ©¶ð"û«ðFñéõ"õ‘ÿ 8 ]2K‹=Â6Œ I3’öÂÖíðLýÊæ­Ê:×ÛJƹíÒçÝúîUã„Ýfðä\-g a&ÑÛŠÇJÒ â¦Ð Á÷©q2ö 7ò¾ù®ï™ïL1û^-§îVo;a|XPEjVjÅ37òÏLÿEû0 0Ò!ì&Èæöÿ ÒŒÙuíjÕûů xÍAä”ÇI½ÈölѰë 5×èGû(ƒ)Sÿï`ÔÀ?ÜÜã0ÊÜbùÝ{ÐÞ»üÝ̰ÆR÷DÓn(/ ,?Ÿ<©.ü=W@ uû¢ úñ¡ùNõ2ýõoÿ9ñCû«æüïÝá¢á%ÞtÜ|ênÜÈÿòùTÂ9eíœUÝÞO`ç#‚²,Ѻþò»®À–ÙË»½ùàaøcü-üÛøZŠ  ñ„ÒÑäUôdà­%Œl66tè9b×?Ýÿ’áø¬3Tñù? ºùŠNeþ÷ƒðýð[ô)íüc"¨,*I›þtb6ù : ÷ðKüa ØåíéþáEîqægþÅÕ¨þ†¸øRÿËô\÷!M"Ü+ÁñáʤݓÙ!ÃÐ Dæõ= ÆôŸñ  €áãÑ8ÚDúºåÔ.[o Õ(Ù¾êžö›Í|1ÆÿDõ712Vúc:ÝöAråöi ¸þGÌ…žù]ý»ê íÖïQàØYõJ« Nò:?âåêÖÝTà&èçõeüýë:)ÚOðÉ*ÕÄÉ6ÂÿÛ—Çú¥âV ¦âWwÿGV÷L÷½{õ˜!g$(½ ¨$fîà«þÚñ|!^ i&Ž&e Ð"‹† ²F̞׫íÓñ²¾ôQ/l(³ý (Âbçg¶‘´¨è”Á” `ûˆo¾ñ2¹åÄòéÓäðŠâôÿàçD ýþÌ‚¬©îq¦ž!º 89!ÿ&$~ÈœÑ !<ôôP\=Ò?÷I›”4ë¹ãùZøÊþ7ôQ ŠýÔ(±Rú%]Í{é"ÆÁº”ó¥Æ_(.ýWA!àÝD^ÍÓâØÍ"ûyâ©kõ‹ º!C,S%Gâk tÅÐÌXì3É\çûnZ!ñü‡têùfú¦ïíCý)îÏ‘ÇêSÜÒýä:Ò¯ÑÁðsضNþ‚Æ î’¦Û'êéGÙÑéƒò3!u<ÍÛà7úèÎV×!ÖÉWí¬Ú† üsilþHoëAûdÞÙáïúŽÚ#åâ(x,qûÂ%i¿Üí`Ì·Ä"ó1Û0ù!þ&ÿij o˜?–žõ¯/”‚8Ï7üÿí+pÚð®êøÖƒ#Òö;ö*ž%4ê ‰ r `ÿYüÿµø¾Ky4ø"Z3é:eým*ùÒtí²çÖÒ¢Èð›$bp © dëP½ÝKîúÚ&Ý]ÛÓpåöÓ=þ.êbÍùp~æùðxîiÜïèíÎ!-A$™"’ë´í ý¥ñé6ô¯1<µç+Dëûnê^ëVòŒúA"÷£!{#k“(ûgЙöØÁþÇ%ÜVÇÉó!êÈìoú|ÓpìÒCÖæ&×zú-éïýóXø¨ ù'Üó5ÕbÒ–@ØÖ!Lÿ5?Ù.ô…ÝÛô ˜ìÜ&^eêê }D æa7Þºª°yªÑþ‚ ï[«ìZï·ý»ñ ÆË.9&‰!¡.—äÁê/ùZþ éÜÃß(Ëyý±yí‹ÿøã¶êåì&âžþê ßþc¯ÇýfH üû!V Âî ¶ü h÷osè•îûoç Q.S* ø¥$£Ùäê :ÞB<äÖ5{9™þG$ßÅô$åæäçØç’ãáÔñSÝágù‘5|îQ Æ?ÛàÂ¿Ô nç _þ™ ¤îÜÿ{ï£ôÊézðäßXãá ܲúTê¹þ®ßÖýïÅQÒÞàËðï‚ ö«!è)øÉïªð™ súËyñýô·ãiù.Þ•âµå~ÝÒøä› Î÷¬ž ŸªE ‡îÉùMð„ãfŒò§/·#9 ®(YÙñýTƯØÕ8Ьååܯò¥ë.‰û Ò “–9ÿÉ_  ýŠ 5 w(*$?M&Žà”ú”ï7è–„ú7![ œ àe ™Wîp n ¢øø¤=|kLwCI m=ºÝN €ÔAÔíúôÛ÷1ú¤Àìí|àyêÔåLÜsö÷â¿Ìöûðeý‡ }ò<èjôŠçŸæõõÜéÑðúç+ì‡ÿ¹ãçæ¨òSàà ÿð/« –*WYóö'ñRúõ€Nü ö&­Ï_ñêÀÌÈÁï!Êtô²)ýó#æô­ù8ì¦õåàÌåÐðÑäê_!)Ë­ÿ-§^¼©Çš« èÛô$=µ¾Qÿ Îö»þàÿçõŒ ¤ýÖ Þ Èpû“íwþCð¡î[ õð$)­(í&Ì(Ï Gý-Clûä»ã%Õ…«((“3ê äÙæ:êq0Ùû4=2Åh/öŸåhë’øöà‘IøÒ/:¤J)ƒýÊ6úûSþàõû"õEô¶ñfþ€ö»±­ûº ãŽñ\邨œ5ît" K"„ç™Eã}寸Ÿìã¦ýß ºfnAùt ‹íVü í^ësì.ý b;a8¤•5éªÊà/ã/øÏâø¯è¹ÿöû§ã¼ðZÐiÔŽÚ ÆhíÝÇ"z ê›$JãŒAÐŒÝj×_Ó|ò¤ßq(~÷4 [ÙúéË>Û¦ãÕ ?ïB ûp›÷õ §§É•@ x2‘"Ï<{:w!¥=ôSDç ìÈ éH, Š/Õ*¶&- •öNþÕöñ$®ñ<"-9/A$n*-<à™ Ì$ÖÓé Ñ€ÂôtýDvÙf÷8Ü™àíõ„ë@èú }ÿÓÒò#ˆêJ&Nú€ùç®ï¥ýZæX 9!å%æ % ô Nðíñ2 êïÜ((K7ž%f(ƒ0\ýfé]ü™ß"Þ¥ïÛ¿:óÉ 3 ‘öP oã›øYÖ0â´×xÒâDÕèó;æ:QÿéÕ«îfú×¢æ-á Õ€ü¢ê&°{ÅòÂYÚéæév×ôñ¹&áÖŸÚàÊù1áƒá'þê7LœwÚR« Ìñk ¼Ô³åXÛVÏñþãäRù Œ3Ûíˆçkî¼ö“꽘ûê*'¥371#M6¶ûÏuÖ4îÐÝ“ÌÏ ­åA'¼`*®ïÉ ÛòÂëÆ¹ú™¶)Š]Žàò+ãXëˆæÌßXûØëà ,LŸ1îýHä?çñôÊà#¢÷Ê1¶È­.—ðùRØ<æºéêØú ôñ[$öŽ'¤-Fû,çúR©óñó“ŽëÉõûŸ0]g#.0Vå`õxårà°ñÇå{üó$ñOû:Êîµý4×cç'Ð0ÍÔæåÎÞ4î øyOÎüîSÃÇÆéÇÊÊŽ÷ þhÇå¿þ­ßpêëøáßþVìÜx^!ø—$2ý ºîcñP¹éöy¦1Ø"ñ'—4.ä#ÝoùãÓÈØ,ã7ÔuúÂè*\–÷? ¼Ý³õ*ÔmÙ§íÔzaõ6xÈøºß#ýÕ„äNà8܉òóçsÿÚùÈÿð,ÿí|:VÐc#'z4#"ð<™5T'“9áýÆÏâÕô6÷¨âË+tb>Š3J-6Lõ=1çÐécð/ßÇÿ†ç¿ =ú~ N­Ÿë¬!Ô>ãôØVÏ9ô8Ýn ýiýI ”î²þcêAñÛóŸì~óü Pÿ°-R{¿Šö(ûó€òŽ.I-5Ê2pc,f×c÷ŠØŒÑ\÷Ü/úŠPàºçïÅåä\èî+ÞÓýhé¾ üì t^ëY ZÂâçú¸°½+â“ÆwôX#öµå¸ï ö`å7ÐöŸ#X¡ ÷%‚ )$wò Úæ#ògñ¯éY¯ø) yà —vé@èFáÉ”ì?*ý/"U'xÆÒøZñ ýPæÖîâî0å– ‡ôw)~ä *»ÚÇÑͰê É+'Åù,@U2ª4âE5úî>XîtÚD(ë?Š"žWî~ú0èªèªïzå£ý‹îÅü 8Ÿjçö@ý{üOïÓ˜÷žL Ê ¹íðÄÍ2æMÇmÈÈç–ÍÊLö&‘œD+@ógãQæ´ã4Š T@Ÿ:p:·ü½“ôdü¢ùõ:ø öxEöL(­¢0‘2ˆ @(™ÕßôµÚ@ÉÝ ão!y w#oçø Î|æUÐTÓ™ç!×:ÿkñþø—¨á<÷]Ñãß}Õ¿Ò½í†ÜH Ÿû!Þÿrqòéó*ö² Çø€,ñ>90²0g;B˜(ÿõ²xñÍìß ð"/É?€6³'ø>[UOé9úÀëžàOªëß%ÓÍCÒ(ùÄžÒ§ÛÏWï|æïõñÓïVôµüéúÈþbÿñýPîâéê—î_ç  Gá~ÿ/âBá{êz0Ð 4@i1€38Õ8(±g `lõjÃÿ1.äÒ'-¤¤!ÒÞßûxÕÂ×íí¾×Ôrïõ®ñ. üöˆæñOÜg܂ߵØ3ðáåÀÿ'Sö£ aãÄûìÕÎåä&Ú`‚ìÚï¥#Ãë¿CÜLâvÅäM2h<-6?üt!äµôˆñê¯þDöþþ‰üIú /0+íW Ç<Þ5ã Æ5€îN#-K#$íöËãìõÞÜÏàxæËÚTùTètµ{ q Üf ÈïØ=çäÍ5&¨÷y1Z*Ì 1%iûãýïÃ Ø óƒkêþ±°èýðÚÌß_óœÛ‡úç#Q;Ë ðÆœÞ‡æŸæ×î êÒ"J 3$ÈÜv*ÅÀÖ³ÑNÄsôdÓ1²ù§(ÄÂ!ù%r *#ýæÿ»Ÿ÷¯ Ú7'#î8:õòÒÿ»ó°òö[ñÑø—ó+ú7³6úAÝ©õµá Ú3 ío"ÚnþoÞ¹ë¶iµàÂ¥ù®çÜüûôŠýæóû4ñY÷;ðEïòûˆë$üA3E(Ç=>ïûûßíŒÂÿ"íÂ!‚(Œ 3&¶÷f§ñõ%ïª$P— «"6æÑŸØªåÞÿÞêgæbçóï5Ýëë³Ô»ãœÓîÙàJÖPñµßŒ/ò* ýÕ aÿw +øoú¤¡÷wšÖ=ž))/«DDý4#+àðêD{Û_GH ÀF CÏ ­8Ñ °3 ‘’ùý ýñ­û_? 3iìØ ÍµÜ£ÕüÄkéÕÙñÂæðê í5ëÑìxõó1úùsú÷| &ùwóHÏ Âðã jËmÜ^øÛÍ>3Ý ‡28*ïñ`LýEòK0jr5P-º ƒ0×ákîT â±çAãÉàDðÝæ™TúûÞ ó@ rÑŽëÎSÇüï5Îä }ò‰ ' ¯ó·ßþïXÞKâìiã“þ5óô¦™û ‘öÓõô±Eô1+à Œ1.#Ë'Éë2Üï¸ð³˜ÿ%é °þn ÖSÈôÌÔ£å©ÕÙÈLÿ¼ØÕ*Á¿!.=áI“ÖußÁæúÜÜ&ë÷„fáÒ¿|÷Mïñ'ösæ* 7ò!2"Q#æè#öÁ‚hü–w"µ6`aØVíKü£æëê¥ïÆä4VôÏÒ z-åÛøÇ܂ތô¾Û{úå%A— $&㨠:ÎçÚå»ÓhOîÅÁ ½>÷„—jG ’.ìV=á ,÷`ïÔÿ?ÑTê}ÓËÒÛ:ÊFòëŸ÷ %îVþËévì»ðìûýòf›úmC¢0ç W1„8…/ÉíUÿWí@%Î §:t0N*Ø>Ã'šå¡Íáç[úýç' @ÿ©a…úÙ+û+üäûóÛ÷Ìè®èÎíèçIóNó‘èT÷žÐxåÜÌÛÏäâúÓ˜úøê:’þˆýZ^ùwûhöù”>°ØAÑ>"&ßgV: ñêÓé÷ð—Abü-?Ø÷TÕfÜàãßáíæè–çÐêŽë;çõñhì*óªÜiâBØ^Ò=ðoÛã¶þg LCüçdöžî£àRøó¯:³Ð;e?×4îþËFüx-¹0›½=¡9‚4¿EÂ5qÿ…ñ“öbúåí"‡þ™ F ¿ûäìËõ´ä å‡ämß„î}å üaóé «rj ñæ†öï×—Ù é™ÓE MòXqÐáÂûÆÚÊÙ\ß›4 å>Ç6˜0y:ß-ƒ.‘© Ã2þ2Z¹X-l&ˆ‹~çÎø®ßÃàòëÁÞ¼ ·ó÷# ÉùêËÒ ßƒÐoˉàÐòáæòBøDßQñ¹Ò8ÙÙášÐ®úñã² Lþ, —à" øþÄ¢ rÿ° y ’…{#….Z }+ ú#ÿMÊùç9)Ä-+ °*ËØåýXØ)Ô¡ÿ|ã´ J¦ §þÙåêmýÉÞ×åÐÙžÔý߯ÏÌöYßËÝýÄ>Nô³úBõ³ìö îô Ïr& #R%õ(éØ$žbz 4) O³¼ Ý"¹óÑ“ýEòÉ!9Ø3Í,vŠ2Çí …Ûé—ßgÙxê4ß‘ð9êõËôò†ûééEó¤ÝÙáÚáîÑGüÞVÿ3."4%%2sÿ½Šã!îDòûÜ_ ¼þ4)L(*l&LŒeÿçÀŒþ@ C) ‡ ¯­þ¹ [ìšûØç¾åÂõ.éjÝû+÷¯ Þ÷ Ñ„ÚmÕ¾Í1ßTÒœç«ÞT÷Âë€ðýöþœzæ°ó”Ó‚ÔoãÔ$x1l0ë?S',êïg §æ»ðö÷îÏ#ü Ôªë+:þ´â~ù8ÝfÛçúQÞŠ#®õ3(úûÎäñ³áÕßJæMàêå¥ùïþXw1¦÷QúÙøáó2ÖR4ÿ·1(£Á+z‰ØÑ9 n$๠(£‰çJàÙçí*ã-ôB 8 ƒ(þñç‘ã·éfè(ÜkøÑäê÷¨ ÷ ÍÖô芾¿ÄˆÅ‚¶]ëAÉîZø’-Z&ÏÂ%_ÿ° üõyFù:-Ü5+Ê.G8(ÁÜýeè ÛÿTõ ¾XXÄüG $7ÿ9ñ ø=ë&ëœüð‚ ² „ô' îÒIè¡È´ÊáÊÛ›éG È ªŒë÷á·ß{¶ßæ&_~6ƒ-~#a9ºôÎÏÛ”íïzॶþ_7…*Ü0?3 ËèüM eñÖùõâó… *ÙÅ™'›ûɶæ,òjçÚå…ïÎê¿öiô—éáö Õ-áÇÔÈÌþð”ÐÆyó 6Áï Héäê<øàçPUøk*&%Û*T œ"`ò€äïNðêþßñž_é'B9=/b9ˆ9Y[1]õŠ :ç&ëûðÀå~\øø ¾ðoéÊ æà¶¨¸Ôꂺ\ÿ ú*äþ_Ø—ÙÇôLÙúþ²ôXègò”à9äæô+êW æ0^ ±öAûxü¨õÄ,µ(Æ"J.lm¡ Céò6ð Ó5>Z}1hÖTûwâNÕÊ ðéñª ¸Íé>SÎcãêÍy˹åˆÏhoèëz%ˆf2CõaþJèìÉåçá€ôÝè¢ ŒâµŒ ƒ9û© Çöíôi4û€>Ô!‡NQЮLÔäÆ ý®ç†)ûB$ô0©ä§‹ÉÞâá!Úìû2îõ •ü>'¹Ý Gæòÿ èáíÖÑêͧåõÍv ë¼#pI‰"JåOÎïÔØæÝÈp|ê3³ô+<6Åh#$ã”ö„ê5ß/õƒ¬ä Õ!Á›ð ÂòÞúªçéhóÓè. „ýy¶ Ôì‚úYäiâ³ìßßõ)ïbf ]úõ ÆÓÞë÷ÉúÄîû¡ÍG#×và"Âèâ׿àš÷¤á›¤0*§1!/*V6é,,ªÜëðcøóëñuTþ1\J * ûfÃèe÷EèEæóþö튰  úñþ“¶Õ›ðóǼÇ+ߘLJú[é-ô¤ÿ‰Ï>íÕ›Îâü\Üþúÿ*ž¹(à yÜ úÿ[ ¹1óß E(5ò%âmÄñÕû•ÍñÌ-³7Î6íd1þßEþrÖØŸæe×´üžìèdÿ òìÆû1ÜÑàÀÓ­É›×Ã[ã{ÑísäpñIî*ôðíÚûîyžö.àŠÒ»c%Eï"$ÉR“U+5¦#D6¾ö0Ù·ðlótÞÙ15>ª@º4£õžß}òªÚ¥ÛîÚËÕæâ»×’òéüú’øzý¾âºí Ò¤ÒñØäÈ™ýݤ'| d |+>ùê âäë`ëøÝÕ ò­õ[»dþüÃò8íö¯/NÖ-â4«µ/Åõ*ï‰ú…ø•öî;ý6 ÿǹ¹ bô•èîàŸÝTäÍ×Yè‡8 ZqàØöšÃ‰Ä2è±Âš´ó‡%uõ"cò€PäÀéÕì"âSÿÁî‚ ˜º ¿â § Ù w“ Ì ` - Íá  ô±å6î†æ^ßLôƒåúö¹9\ýF‘ì¼ïúòÚߪtï34¢8Á28æ &ËîØé×Í£Ìãa#RC"Q*Yàxôÿƒóy C› _ Am¯’ $%áù£ÐõêÕ.Îû6äÈjj)eæ1õœã¥Ü:ôÝ 8ò1 i^ñÆuÖÛì*Ø‹Öørß˳þ¼a¯íhì¢þê¸êT¶ðÙ @/ˆ)§+ê4¶J-m ¨êö/ï?ó-ô:,z€.Y:¡ý *?ÈÖîÃèÁÒè ÏÎ £ù±ŸXþáßèñWÞCÔ–ðSÕLúßçþïÏïëæ0èYëLæ1õ"ë"ù&óü´ô÷Øþ4$ BR#u Ñç§u3Ý2I‡8)<„Eù€4œöªìêSöüèñ¬ ›.:)h&4!/í…üYê:ã5ê,Bß ItúÌ;ë®óºá,ã­ÕêÔÎÙGÊ3ö¾Ûþ þ¶ öÕç©ü÷Þ¯Ø(ÿBáO§ Ð#–(y |"aýS zlRèœ;(9,`:Ñ´%íî?ñZôØÒù? (æU¾2œšîÑÿCßáãKç+܃ù¸éê5üùù$>ÚîRº>ÌĶ~².ÚÀ½€ÖìsI Aßî¹ßÚÞMþJÞ9s PV%áð0Þ¹ðøÕìaC ÛÜ h¤ õ3Ñ 9œû©ïøLô” >ü¢ ùã팰՜åÐrÒïh×Srùq,¥!Äã¨ûLÜ)ÑÐô ÖÓ ô{ËùáþÉ1ð§úö\ðJ 0ûc¾-%%Ä*Ç“dG w~h ¸K‰ç#˯ì¢þlðdéñ´R˜ê—ŠÓUÛÎè`Ð|%ï]*¡4Áø||õ|ðÛü òýøÙÿÑöMýâw‰ä”ßAâºúìOæ‰/ý,÷©6çùPfß·ñQæÕÝ_±ðÐ Úæ’jÐeé­É´Ò-ÛkÍ?øîá%ú(ôÏûdæ%êkîà6üÛêÓý‰óš÷ñ—÷ðOúG÷Ôç•óúòQéMý”6­#o4@D1Ðõ€çWîý¼ôr<Ú/¿&*Cjó" “ØÅìnê­Ý^ü¾ò§øCûèôFøÎü¬úƒüëÃê—öÅÙW×däfÉ¡òá`sT² ˆúüõVîë$±ûr eÖý- ÿú¤ü‡ÒÿL9hqO@q?¢NS4¯ûw V <%·Éذ¦ü‹ ñùi´òýTëíéþKé¼ÒTC»ü»oجìÚÊžÇÙ”ÃÐ÷JÛv ì• ¦ÊÁàÄÐúÂ¥ô"ÓWx÷¹âm “Æ õçïêü (”FŽ M*¯ï; ô9ö–,ü_Y nÞÿùêpÙÝBèÅß‘ÙùðîÜ; }óDéwåM ¶ÀËâ¶»¦ºé»'Ýï­ûeÂðâûñïô·û©óYKøñ\ûÜ àöÏ)ëÄ  J F $ ù 7áÒŸ.zøªìòzúOôßf0÷|äˆøm§îRíbósæ²þ©îÆRûçþÿáðãö'ç›å²ë•Ü ’伯þÇ${ÿÏ zòjúñ–ðYÿƒ÷y |%”-dþÄ7ýßûlp<¿(ù-íC±ï£"XÜÇë³ï}å$=öo:Tñƒû«à%ë¯ÕŽÛ¡ØÑïê¶ÓÑ ”çd4 W âËìÂÙ×Uâ’àÕßû7äpí^ÄD?#Òý û¢þÕ nÿ†*ÕÔA6@×!ªM´ñç"Îâ<ö_òì–û±ù“å:õ^Ù$ç~ÔñÜÕÐïÓO˥ƺЄÁsÚ¯ÏØéò߸öð-ýFó Ññ­ ö-ø +v*¨| ášøæcÛ–0ñ©'ó!'!\,×à'ÕÖ%é!Ö$z$ñ%˜''~)L-íå15Ä$ìä¡9ÙâwÞØ%ëõâXô‡òoõíúõî5õ>å+åÁßPÕ3ïØÑ‚ Èí= ’¨²ÞóðçëSÌ rì¤xQ¬é $¡: H!§y,$Í,„.à%d0eP)"Ž& Êq Q!‰™&Ÿ)[+NåN ÌñÛaݹÌAø2å þþ¬ë–üÎÕðàYÓ”ÆÞé­É±#æz†£ÿ ¿äñö’×ÒàaÙOÕ_çÙØªöè)Mû5 ¶ 4 ä ï ý<¥$À!Ù#/ L,çó JÜéLö¼å~ú Q â'Ná6[ÖjÞrãÞÔÜøãÞvøÕýIþ²òûø1á&èYÖHÖeÛ!Ϙô7Ûéœù„×"Òþk døuö—óô&Å ¼/ü2|4Èù|úeøž$ýè!o¬#Ï*Ic+­û€÷ìÙüQïÕïýjô· ¿Ž³ô„Èè‹ìEî&áïý®å’ ¼öOµëV ŽøIÿ¶ê“ì’éðÞNÿã@(‘'**ÿ¼ ÏÜÛí>ø÷Ó«+Çz73À ÎíGÖ"Ëî­xŽóþ àØôUÒ²Õ)ê4ÓQ²ì® ôôüÀãéÅòÈèhÜ å_2væ=:K"÷>‹ÿÌÐðœhþ¼ö„$W °8Ð4d'¤A¢L*érÁá7é<à³ß7í:៌ö¹¸±üèÍ-órÂQÁæÁ'»ìlú¬ ×»õƒÍ3Ó¶ÞÕÊËÒ݆\¡ ÷oì }Z´ Š-D.Ç,‡L-1¢!™óþ >õöþ`-ý&3O7Îe3óŽßç¯á0ÓTöåÜTû7÷3 ²Ý ùlɶÙÌd“éËF¥ëü»ÈðOü äyàdô)س Üê ÆžŸ P­ G>þ™ Wü©ýZþ÷9{(5à@ëJ0¨èÏôæéÚuÿì#ç …î,Uþ”gã—üßOáìuàßþjòõ¶GôäÑ‘ívÂâÉc×ÁµüÞÚZùÖÿ)SêEû=ä ã'ò“Þ} @ñé‘+$õò—¤ßLò÷ûæ-§S2;+/%p;Hÿp$ßèÐühîé )õJ) v›çËêÜ|èãÚàìê\årýÌìäȳ“-ÔCðÓ{Ãû Ïl7üDõ'ó×åî[ÕvÒ™ô`Ò$‹ýê5F1ùb3:ÿr"ëæùóéã ùù3¿8 éa‡êÀïÈøûö… ˜« ^òÿâ*ð„ìØ$³ì˜<œã#»4öˆÜ²çôäÑÑ ·ãö'k!;(‰åÕé­÷7ðtåÉßòFÄ "ð!ÒÐ$ÎüÂMëqÿâæÑðgóyîÄÑýÃÒÕô#EâÿÞÛþè.ð•à‚úg.Î"šã.ñz˜ÙÄâçÕ‰ËêÈÜ ¸tèÛ{Þ8ã:ú=ã5‹e'1Ö!Í!ålb o öœ\æ í(ÿöéä¶ò)óâ×ÏóâÕýÔ ñ(ß Ýúë €rñóÏtîÎÁÚÅHÓå½ ñ×Ôõ‰öÂÿ/äËô ××MèËÐgíè¼–C U>0ƒüµUþ•'-•­7¸*W*™4;£!‹é}ù÷ñ[èý=ùþ*:#"{8&øC#ÊËíVÑÚÇù Êà<'D‰4#¦ó‚ ÀÞwê9Ú6Ù6ތӯéBÚýYé C!ý× é(öWòää€Çíµ,ø i+!¬ ÷‚ýÌ?ût})¢.Çê7–ý$§åãÿçˆñÆ3ÑöAoB§³?“êºàéÃîöçÄý,÷˜> æ ]áô ¼ÖéÒÆÉJöVÍ»áö‹l8è¤ÞßôÞ½Örþ)ß|& o$„0ÜíËÄУçtØÏÔ>ïKàVùu vªÿD¢ðë9íê÷ùãõ\m#œÝõ¯ -Úí×2ÖJòËØÔK÷¨(Ï'Hô\ ßìé¯ÿÌäþ%6ü @¦$Þ.¼ßNÿgÆôÌùÚÃÂß×áè  @ )n™÷3í7ù°î˜ë# îϬðšÚƒòçàqã¯ûžëD"2 µ(I-ó ÿ(ûçú×êóá0ÂìC/"Æ$$u µï=üðçN óº/„‹8·3'06Çã ëßü’éäæþ7é[hWwÍS$Ùù†èégÁêòèŽõ[£I$Bñ‹º¦æ®Ä ´ ÷—ÑÎÆýìc fîSXé™éõËè<¾óû*´] ¬ {äãòÈÔâØ›ä0ÔÐ ì‚)$n(Ÿ+)•òuÝëò,Ïñb#÷*&|*“"(žÜ×ÈןÝ2ÍN;êw Ed#$jóý ä!ï’ä«Þóèßæÿì÷³ùöù±ê0ðÜâ|âhðÛ% Hí]Ž¡‚pðè’ö£êØàOémBüv.ÿDPõv#³áã÷Yøï½ø Û4-/ +È=’ /ðïr ½æ:íg÷øè¸Uü)[-&Ã+4 T õnvòþò2ü în Ëö13|˜ëÎþXÓdâÆÑÜÅXWÏ”0-$•5°ò á×"é÷[ÞawQú‚ \ë JÜð ä+çéü ó“ò ]#E&+/ýaWìûìmèÜÏêÝ7ÝNõÑhÏÕìÎ É ñÀÑ% ök·¯’¼ÜøÚÊÕ̾ÚÊÀuöŸÔF õ Í zòî©ÜLîÝÝÊÛ'ö­å—Òÿ+ÿ¦©ö)Éñãÿ¶ñ¡÷û³ó=þŒ*O…,3þÏ)ñÕÞïZéÑ"9ñ€<#(Ö`4Éî> Qߨè¼çcØjãƒP'~ˆ";¦÷òìú³ìgãø×ã&röñ 7 Ž·ïé‰xâ‰ífõûèkÝÿ6#^v=,ÊòyÎÕ$ì¸Þ`ÍÐ âãÛü³ žŒïá Þë<ñ7Þ]ý÷ë2žè'4(ïëEòœí‹á@ûÆæ ÷ w G!ƒ$r'‹ÝVZÏEÖÚ×í7- 1éEÊö·.?»E깈·šå½Ä„ñ'b%à”+×ÛǰÎÖÙYàÄÒ΀åZ5OÎåÌúC׈ÙÖèßÑy(çœ!$h'£+g#tÿ*Àå.ô5í^Üm'í1O" ý8mß9“ÑuܱöÝØ8!1ýŠÖÓÍÄï×]üïM Š/›ì â ³è- YËßáÌÙ¹Çh%ë£#ƒäÆÀ?×cà‚Àé ò.+–%pÑ)pöÒ0ìó/ä£ëõáâ¦öäæžöÿuc"nì$ûƒï.å ê^6'>};z;HᤠjÔ_Õ·ÿçÚ‹!Õ ì!û(@ R%6ò] ÎæÏð÷¯çášûŒ*M'Z.ƒüŽ")Ôáò`εѾíl×s ºúZ ?ãúÔ ð÷üaí ô1ì)îLüìèõûÄ*¨’ #Ø·ùïߦË$`è BC'r,x@C÷üäâ^ðëíwä‰>ò …$ú#vh&Éÿ!ÐìœûÔìÜꚘñ-"R#»$ò -&_ã³ @ЧáÁÞùкåwz lû\á÷î×0Û`éºÒ Úè”!À ëݹÝúõIËLÔ»Ù=Ê<ýCåqê %þîÿ däºï÷ë®ÿ$¨ÞÁ ÖÀë|=ÚèæVúøÝØ<Èà9×IÕ2…Þ?úÒâ”Ø’â};VlÓé¤ùÐàÞþíªÜ×ñ/á8Gë~ÉÚLæ«ãÜÖøâç Óûú % ^hJöy‡ëJ÷§îê^ÚôR2ñü'H:gøA$ÎÓîÙìñÔ/Xø9&ƒ2.?‡Š-é¹ùïlÜyî™*ñ›(† óþûJæÆîÐÛ^ßcäyÓ«þ凼ÊàÖÙµû$ϱʵênÍyèì&& üÓ8ØkøtËŒ×Oä—Ò’ ÞòB³& ×#Lñl ©Ýhì–îšÝ;+ø )Þ“¨$Âøg ÊîW÷nòòùqñ,ùàZ :0%+µ ƒ4ÎÒ·÷ÀËÕÅfómÊ„øøjy)÷ÄÙðó¸ÐŠÓ±íbÓÙ ûöÑãßø‹ß‡õÝÞâSì@ä̆óçܪ­UÊvû«„ýŽûÊ ¯ü£Ú G!k!‡ y ÷íjÛÅåÆêDÖoñA7d$é$!6—ÿìäÆôKñhÞôÔî .."5IêÛ”Ôðè)ãÎÞzøˆîq…ÿ¹ mš« Å"ú·äîÿü^ù™åç üU²«ËBäùNãßãøwà²÷v= ìúóç6ß’ø}ßYÜöœ Lrâ²þÔîáÃâ@Ù0þÉêŽ ¸ Ò =TöÔ Íëî•èëÍ+Ò 5ò8 4½é§ì½æ¬ÛéÛX¾ .úæË×ò¯ØyÏ»þjÙ¼ Úýq¹‚öïû]ðóPîtîMé2éjí…å>üèïL9÷ñsß>ùSàãáÝýÌå%%Â<Ê-".˜@¿ )EöÒÿÏ"ïã&¦\2/>#Œ8$üE&ÚÄõfó¶×Z$ûí5T"•(ÃØþ1‡Lïýõóܳޑí]ÛºcýÙøÿìÒÇø"ÁæÎrßEÅ…ºçEY!ÃëK˜å©áQ9âw!dÓ (ÿº! æý’ê,æ~5íE' a2±'$:-—{ÕùëÿYüìòF óŸ¦ü®c O96à!úÑúÐA݆Éð®Þïÿ"ýUñÈ $ÒZñðÂüÌ®ß'Â5dí¤ BçûÙðæªí­àõœôá'ûŒåúU¼û÷Àø,lÿKûÐau6h¾<ü5/’5"ˆóâõNûÍé6vúh"Ùó-Ë÷) ãWõrä£Ü¼õã)!5 $¢$¤ô¿áÞ¼ì8ñ¼ßÑ¥ñnÿ×ýtú¹éýîn 1ûÍñ“öŒøAí—íû~¥iƒcþo òmö?´íÊ$¯ ¢/ .0 $/gÜäü/à;Ñ{^âX# ÝÅùá&è}÷èÅæ?õÿähóyxÏ“ ˆù7ÓíXðhí~á®û1å… Þþåã šûú¢ eóÕñ»ÿCíàæ(#åý#uÌìCÌ5Ã÷tÑð)-ô3ÐÛ ó™ÝóáëÐà ÿèìæý t¦kyôÿìêMòþç/åÓ÷}æ¿Ðýr'u ›#h9›&ñóû·Íñ­7º>*=µ"ˆ;Œ ºòòýáüðkuÇíÚ&÷5MñEüNôówñîîêÓí¼êbïƒð íôûä¤êüæ§à#ð¯â‹øòç õ ¡ÿ§»ßŽúÆÒõÔÿKÖÙ+® z'€3ýS!çÕóp¾ìÞ2xJ=~/5ð;ù0)lÈûèOûû÷GülùÑý1ûÀ  üàäöXßÊ×ìöDÚáù[÷QºÚËñ'¾3ÏÚ¿7¶þÙÓÁQùå-÷p=ÒîóVÕÕÑÊù“Ûø³ü¢E” Š•ùëÅóÇ÷Ø’óš^<"¯©#„(,¬,½Y&e ¥Vý„ö{÷!V b))…î)Kàÿ†ßÜÜ3øái Hû:à OíÏšß0ïWÜðÝúå÷ÚgïÔæXñÜñ8ñQõEô?öÃûù<6ýâ ¨ºÔ ÇÎvYdÿ ­ù#ýûiù”B¨%Ôªq,…ûSÄãõôÈó¾ß‹#'û]0y*¡]&³ë  Ø]çßÕ©ñ¥à¨ýïócü[ö™þQþ×øüeûÉûÍõÓðùêSû\æH£K!ó1cá¾ëÍõÖáAïוþílêÿÅè]䮚ãråOùzÝŒñ)ß°Û†ñ¢âÝèóåµRù=ë€ù€æ’ê~îáùøuäÿéí!Is!é^¥$…õ( ˜æSæà 'èã2N—.ì;ê.ÿéßÿƒöôë¦úš"#M(÷’Ð÷â<ð÷1„0­ôKþêèóµèÁîœâŒì.Ø[ßúæ®Ö]–í ±ƒýVÁèþøBç“ß>ûôß(rù«2†#ÖÃ6’íºtâÊì(îD,ëa98Œ-<@Ü0« Àï. !lËݧ‡èüF| %ûÍîÚXïÀØ€Õþ÷žâ4Y#îÉ WËGáÖÍsÁ¼í:Ç<×è&Íïçõû$ËRàÕ§ÇfÚÝ*c ¨"’ßô"Zû?öPü¢#‡Ä3ê"ø5<4'ˆ6ƒ#"]è ÉOüR W˜1 v÷¤÷טí@× ÐˆòeØÄ÷_8 ýîž%ѨãN×íÉbôÖEÑô¶ë¥ûÝÏMá³Ö2Ñüò^ßÞû&ŒQ„ Ý#€!-®'‹é®þ¤½‰/­!' 2ƒ p(Gðâ ññô®–÷(³Œ.5½ø/ó= èî¶êïàì÷nç«ümöô8û)è\ô)çùêMð¶ì˜ìðnçépñìÏýûŽ € UñŸ–ëç‡ìª!õ %( ÿj…óL:û¬õ‡ÿz3Ñ"@Ÿ2“ë!Õ<âîÓëAòŸÜÌ &¯¼ Õ ~ø2ç\ì'è­Ýàøç9cýŠç GñWJãiênè÷Ù$ 3çØ+œO(5/ q#F÷—ÿ½ï·ÀþÕ`ˆãÜâÈÿdÜBâüårÆû ý£ôøŒúèöœö£÷6èRëJäýÝú¶åT:pî¼Ûµíö×ÜÚ¸õ£Ûc§þ¬3$ĵ8ýê7é¤ÞmîÑ,t#&/%ô&" êo6 Õ â Ê äæpªü3®ôzþBï¿ò$ôÔíxýøöCü¬×éÿ1Ô‹ç”ÕîÑZæÔÖûé¾7NäÕÍuÛVÛÉÐ Jáì-O¸ÿ*ðý­“üwý4 uw™Ž/p¾­&e#ñ"^*0* é„ f}¾!{x-Ë °!›-ÌAÑáøjà—ÙDøáêÿãý×ì@×LíùÍÕ¨ÝÌßù´ßQHø±êÿ÷±ÑÞ"Ú˜Íëó×Þè?ö»ýÖÿpú‹þ¹_˵æ= $ Ò#” äæ»ájà@êjâÝìÉêýåìçæýémí ðÑð±÷0ížõ‚ì“ëüDîÔn´€:ø¸ >ö—øÁ Fø³-ÓÉ?¼29+ª?ñöë–ßíõâ$ ý¾Sønþako]ý;ýfý¿ôPûÀ 1 ø (ùÞÈå¼ì÷äôÜþRåtK tˆ"µéd ïè½LîÁ.-Ï$¬¥ó§ûÒô·íž§ôÞ† ¤ 4øšxíºûð÷AöOý¾Aýšµý% ]RÚÎøpÚîºñÉó¦èÓPðß— Rd&6j!!Ý5úûÙõ̇ qÙÉ„ ¦çý' &åü°ªJ€Í‹Å-Âò¸äòåö–õÅö,òáõ<ä(î¾àñã_î ìXôÐé^àfêÜôÂß” 6øÓmç:ÈÅ߯=ÂæânÈשéí¢Ýðm<úæòTýë$ñê#ÿXe}‘-m 5¼œ™.ב  Ëq&7ÿçêð|÷"ù‹ê¿ nûÊ!‰ÜáŠÌ)ÚÇÞ ÏÂ÷•àÓ§û3úƒ áÞRûÍÎÛÞßbÏ»6ã— ¹–œ¶w€ !ÞÊxT¢´,}r;N2Q2{@þH-C)  ýzôó.ú¹9!%(¹;·üÆå@ðžô«à<þö‚{&ì)ÝÓ¯ìFÍÖ$ÝïÑ èÉß´ãçå%Òôß-Õ!Ó4òá þ øÇó öðÛåö ?ì©/™”ÞÃý4ã$ÙcïE?=1à0D}œ3ü‹›ütúwxþ§øŒ†ýõXí}û­ë‹ð^òqïšø«ó˜ù ø ø!üSùœÿ9øþTî°óaÜbß Ø&Í\îÏØhEù¡ÿ†=Þù×á¸Õï÷èó— [RêòôrîÇô6ÿ óÿýc%#¸ Æ$¥ø3 ¤ûõü,nˆ## UÉÉw·<‹:#ÿ­7ß ï…î<Ø Hõ %Hñi*›û®éSè-óGÜñS ŠØ©çútæcãùúåÑ ¹ú O H'² ]Rµ,ß ¨oóù‰éˆêÓô÷îmúÇBãüCÓkÞî.Ö»>ÿˆ"7.¥ùÝ‘ÛÙì¹àÕ¹àÇä}àhñÌ Û4ìŠëƒÛa Êñ¬› :SÑT ó‡r<yêç±ßÿþ \½:fñhGâÉæ²í™ÞPø‘þÕRâMÎkâCÞòÓÔõæ+ý-÷Z ÝæüeÓ³Þ9å‰Õ0çÆ]ìgÿçè\ê§ûeóa Ãi D> f6x:Gr>I5RF¡J)p„I ´ôÛ³#§"ç-úñ‘Œí†óÓóæô„#žÿs÷'ÿú£ó{üíó?ô$ïîêÝé’î‡éMó‘íÔôîû¸õ îÿf å ï¢þݤâèüŸß¦'˜.·$b ŒJòòÇ zô†±”¡#° œûŒ`ð-õôî‹ð5Xù}"¤ |9õ)ö©.~ÒZõÑÂãÆÐëûÍÜ‹­‘(Nþ)yáãïoÑþÔwߎÖýþòµ%Ó˜ ûñŸ µ¹Gذ§¬–Ú\»+î‰3Êq$ç,;ýM Ùcì™Ü„ØtþVñò‹Pý× Èíµù|ð¸î·øE÷¡¾µ Í3ÿ!Ì;¸70øòhîuð‚¨ý†4ØTþ›ÌzÌÝFÎYç$å áÃëÿäUÞhûç*-‘Å#‚ÓÖû.ÅnÈ ë§Õ# }n[Žý» Éñ8õRôó #þŒ4½XC@ä`%Z1=T,UótúísðAE$ æÅüâ ÅúE÷Šôûýbêùæ¢óIã —ø bý”ñ¼nÝÅèsÞÝ ëÕëSòÓü?ê=×Ö¡ì%ÉèÑÑÉœÁjáXÉêü!éU ó©ÄùÄbü&õ` oû1"¦ë Ø)CéïÑÿŒ8óÐ&'÷ [*= ó+ ÿÙÜÑËû… SùÄîÆöÏ%´&ýõd¹µÜì¾T°Þï•Ë&X":@í25æNïUè3änôÝáBœìíþéq%ÆÍ 'ÿ}ÿ -Wà×J ße' ÿf Ñ^¦IC?U9çÝ‘üðÛÑ×8ýˆà ä[HÒBéN*ÞåËéÛšønæÈ&ú-ž2ý· [ðR[èÚðUéµçË÷cê²uþ†ý6 ÎéÒúãÕã=ö}àá õx› C{‘ã.ô]þ.ö¶ð¤ L÷€€©ê·òàŸÀÌç:ÉKÁ©õ£Ôôí`œ]úaèô„þNõŽú«é¹ôêÞÒâçø^ä ' QÇ WÃú4¢ÊµÙŒ­5’êHÅ»yáú Çùûøz çýñÖ åø; éìnúŒõíïЫù6'f%)›$“.üíþx ûÙ Ÿ&°!ÁG*½ÿçNýìÚæø ‘ò)0û'5ò2£è-èó Ðä¨êû)ÞË&mý-.Í%]O%uúè óù\øWÿâõùõiöòÇÔød ” ŸõÚÔáêoë€Ø‰ðË)< |A#­æ”ý­è¡êùîî˜Ïþ, ü vGbø¼Þì\ûzë3è% šëü5_39 :…q1bæÐ#çá’ú/é/¯ùJ6Gÿnô¯µáëíœÎѢ٨»âô!j ºäüÿÎÝñÝÒöˆç˜³DõUÒÑõsÎïÔ[çÙØy‚óµ À .ƒ3 oîy HºÆ H8¶%;:Ý?',9Ûí^Àã¨änmèz/ÍÞ,þ)`x# '›öÖûkùŽðu …ô¨% 2$)BE/âÙ¨ÿ¸ÈÏî™ÐB 2ùôÈ}ܽðbáéä¢öœí^¶û$ œÿµ r%ÌÆ((ò¬oè>å{íའå ##¬Yóqõó%ò ˜ñ.q 79*!%0lýã¶õßsÚQõ<ݦ•ø-£ $õ¾ Oà öïÖÆÞžØiÒ äÏÖRö¨è¦D®èŒÿÅÙžããöÙ¡Žì.¦ jtïN ÐÖÁã¥ó5Öƒû×'"³þ„ ×Dñ×çdÞúëðÎ hr]Ïh ý3ìPÕÒeáYÝeÍlØè~ŒœöNë  èÿì_ûìK_5,  22Öq5@ò ’Ô–ä<àáËÁûèž1¦$é a,vï$yùOóÂÝü5!=…𱽇?ðþÔáùè¾çÝ4ðš º^êÐûBçvä üSååŒý2d#ÚQ.<êß ò×–áéìvØÒËõ»(Q0%0[(2û¼ 6ôÛò)Eì¬ F0p óA.²ù…ÑãÇîæþß@õŽæþÜöÆÿßYú+lêyúõÖNãLÒâ̶íÒ”Šö‹óVºÈhël¿¶¿¼ô&É1F_¯û³ã{ý€âXççíUäלî$l"N1"%&øw ¢îFíwêL%¶*1ª(¶ Â0öüûNÛõ+ÕTÖ”çNÖ­üÃí´Û*ö ×QòÖÕoÐŽôQÚ#ú× ƒó‚èÝe÷²Öä6âtÜñõœê$ÿ›ü°@þé¶ÿÐÁ&Iö6r&Þ7Ð5-"À4÷¨ ã îÎþæë1 Ê?³9Ób6ð±¡åå›ò›ÞÄfê! óü´B ñ'ëçÚÿöщÞÐÜ¿Íöøšây,XüO ŒéüÞëóíÇõöíIÂôð gq!j+­ê hö€ø: Ðñ"7`Ä1 ;ÄûH#ªÓïدÐdýÝ\†^e¦ë„ýRæÂäSïßKêh ®à¯®Äà!HÂµÞØ¾ÓÂ×ç7Ëž˜û*¥Þñ, <å@ìUùÅ䄲ú«%dòì(e­ ßî&èSð+÷ìÇ–ýfÇÉñåAøÝêáßY”îØ/¡Ì‘,Nùõ¾ îÅùìèíãð çÛÕøœ'Jd ' Ò3úEÆœÄéôgÌ_+…¶E±8P.´H-+›ô-¦†ò -›&Jeµ ýÝÌì›öbç}çÏï÷ãÃmñáuøCY¢öVúàï@Hûªµ  îçê”ÈeßÉÅìì ÐhüV&8#QZ%ó7 Bæ²æQ Pæ‘8Ô<Â<#7Ì÷=™öÃøW÷—öYû)óª Ùü‘(3.®0¿ÿà!ÃÖèé¾áÍýéß#{ÿ<#ïã%ÊúãÝÐ4Í2î,ÛÇýöGõrß>ôÄÐWÝ؛ҭòe࿳ëë#ÿy ï7‘öXó;ÖüJ0¶;2à*8 #£ôtó°ë9ò÷4ø>ï;}$à;Í÷0‹ãïäôÚàNbõX ÒjûþÎTòâÇXÐÕß!Óùï‹éíƒòóñåòÎüÉþ]ÿ›‚ñ_üøíˆéÒ |ñÒQ¹HàÕúãEßþíó2:ÎA¿2H1u:–¡"z$L °ö„-JÞ# ,éÿ‹ÿÛ¥ô/×Ö´ñ¿Ø÷ )ôñx\E –ò(väwìdÜcÝ}â©ÙÄòëÍýÄyõ] fÝ3ùRØà6åÞ­šêž ( ™âêÀ¯àÏå& ˆéï3$È"64~÷<„åwñØôKëþ8ùÕÿEúwyü ñr‚â&ÑϮ׊ê’Ð¥óH#¯L#»óÔ â–òMÜ®Þæ^ÙXëQ e¦¼ÜÉ7ׯô9Ò '¼+o) !nùë§«ýhÚø É©þÿ$úW æÆõ-à&àBõ©ß¶Šýï‘ NìÖóجßíâÔãâóÇ"×ÊH"·ØýnÇMÓ¨Ö^Ƈù¶Ø9eþ[*½Jò%W ú#ÿZòÖ(@ š2û+Ÿo0%'°òZýômòàö&ñžø¼ôÎeùW„Úò§YÜ€í2ãÁÜ7ïW"…½î5å»ƒÞ ½Á¹©ãåÆMü&ìðùëû‰ó‡üóúcò?÷÷ìÙí?¾ë|)õ Ê:‹1`%ã8¼üÊñ®õÁþúñ@ñþ$ºùÕ*y ¡#)ó Hò»ññIwÍvIvLä8×PãLâ ß<éÂéUçìîZÛ‘ìºÑ‹àªÕpظâ€×ãõâêcõ; ìç JþÖÅø'û]iö%¹ ƒ=}37/¡A£ór íÛCá¬\ßÙAX#@›<áa5³“Ö ¥^@ý¥üZúŸŸ + æGæ7vͰÙôÕ&ÈTìÖZï=éNêIë2íœíÓöÄôõøjù%üŠõæ üÊ ‡ë)#½å(Ù×ÉZÞ½0â>17ÊÒ%!ô5ÿËÿÇõ¥ Ç4rÙ2U/¡.åÿÔÏìýtá}è“åà7óëûûØÂ×ëá=ÏXãÎÅŽô‰ÍÓµûb-vï5&àHï†ßVâï1å=Åõ†~>[ õúVóóð7’ó¨/W0a-e &&¼êUd÷ïñg<¥Ô‰±¤ îŸò<ÊÏãGуÂj¿Ü%(Æ~ H$rä6ÕˆàBíÜË`ñ3ÿ*3:·Bõ\†íhí[ùoåÍèöà êŸ[$Å‚ rtØ’!"'!´Ôÿdþ,TêgùØäræºô‡å¤ ŸúhY\þYáàõƒáÂÚ+ùe⦪üs)¹X %šÞ#BʽØÌéãÐúAõ©`¬H±wÞÏõ`Ñ ¦ï A£#ô=I^3çÞöò îïç)ú¢´ eó2é*ô.çÞîòç§è×ìJ䥸5êóý.ûÿí£þiËÎçÁÿÆvå^̤óxÿž'ïpüØè‘ì!ô‰ì­þèó¤åû¹Úw6Ï%-eAšÖ%¿ïeþѯñ+ô£6M6Ÿ$Ò8Øÿw$ràÌû™æŠã©ü¡ìð {wÓ #÷ÿÿ¡ýžû3ýü¬òGô ê¨éïcé6ó´õÃä‘õOÎüávͧÍWèïÔ^þSñöC³ýq6úåûœ-÷i$Q@>ó%>´=_"Ö:,ü˜ð¨÷®Þðx+d %>¼9¡À50vìûjFü‘Yÿbþþú³¦Zº ÿÂøÑÂôuÃæÃ ÝÆ‹ö&æâñ:ø©ÚÅê×ÖäåpØUÿ$èÕ(6ÖÊüo CôšûÓþnõix5¦±…&®ò'íÀïºüQîtµ\!¾:%)ÿ‘pìbý3æ­èTíÐá°ùí‘Dþüÿ8ã:û"ƨÜé¾"½æ‹Âg õȘëZjÛ âŸàÍÄ SžüßNŠ A*ñû ¿û¹,³(8¬Þ"òYúå’ìO  ¶a!z£†ñMêàâî!ßõßbìÚÝ¿Iïf¬Ï éßZýÏ<Í'êûΗ 2ïØ†ÔŸ øqÙ ’ú%üû”ô _ú ˜ 'Ý&l;%õþ¬ ×üsù{ Œèþ "'[Kïä5ø’êƒâ% kï˜ t å¥þÆSÕŦÁÔÇOèØû1êú ÿ ó1BÖLä¤ÚRˬùfÝ6±z›É,ë÷ìßõñöóv÷ý ™8ï(œ7@@’s4Ùìê ¼ë<å‰ô;+_ø%šÓó_Ô¢Ù(éÚß„ê†ë)éïçîêVôò)ëhò Ù ßØÚ‰ÐÎò@ß  :ö¸ êöñóBæi!øh<&Â5–>[g.ný¶ =°ú:²`4¿"?,?!2E~·1Äý ôÕõOüDñ¿ RäR²ù UëióWä¼ãåáßð`æÙ—÷ø <åÊó³Ù ×#ñsØ? øç+ÔÚúõ_Ý Ó˜ cå:V²Aé=+Û:–U(³{C 6ÀþãYl!ʱ(mÿÑÅã›õ„ÞéÛ¦ò®àé /û°ÞIÆ çý<ÓËÛ÷Òf̘ãÊÒõ ëðÈúÏÛVî¾Ò ÖäGÏzÕç° [ƒ “ ‹ÿÔB B£ó£ Kêž$<ÄÓ pÿ¢ÁüŒû`)ÿ}!‘‹&j.‰3$ÉÙ;õ»Ý)ÖË éÞT À ãûT’èÛùqÝeá¶ÙžÒåªÑvøvååvþkÿäóö÷ùdìCùf!MÔ&•%`%Û(Í$“” ¹  %ë|ÂeòDŽ"ñ‡(h -3|1 i2žåt5ÛÐß&âkÛüêzàëò»ë-ôÁö¥òúôæXòì݃Ý%äÓìÿ‹ß<#±u2_(6$ƒ/“ø¿õá»çý¬âq-?&-&å#fc ºäÿà(ÿ>ÅÇÚœ BýùÀ Fì÷ êè~÷Bë£þó*5ÜbòBѱ×]׈͚ß4ÔêxßÂ÷ïzþ(ýºßNîLßáÒ¯ >âù â¼çÿþçI-­Í]0íàû UÌœâþã^Û–ñ+ ¨ý4¹x} ÝGvøÍ‘ÝCä¯ÖÓÏØè_Ò3ïÊ"§Í 9bßaþÆÌÏÂìSÇ¢aó5Ô$”) 53ÿœ ãúò¹óAâ&<ý/ n îÒ€ ò"¼ïKù—ãõåëùñèõõ¤iöíEøeâsâŽò¾ßÝ öã| ëôÉ LÉøâÏc¼ArÖ‚* %a&9à,oàß›ûê £u, .0É0g(Ý5¾ )Iýè îôôúô¸ïÊ—d Ø,øN hçró_ì£æŒ”ò“2 o ë÷k0ÒKèòÂáÂIã`ƈÿïôégÿÓðäJÛŽÓÿîßE!É'«da%Ç ìÎ~• ¿òƒ OJŒ7!: È&ª %•ÿ‡ó™ùw ró|4’p3s Á˜ Iñ ¤ ˆB ¹ [ñ ÿvæëFèñàþõçFŽøîùû­éÕî`òÏÚõ Ìñ4„'V¤+‡âzlÊÜØðߤÍV"íE$ëÈ4,æ0õ_ûeÿõìÄ Œ ¬‹ì’%^ôÚГçmÙ’Î:ýëkvþÔ’áüðúãôÖ¶úüà$ Óö… X8îì±Õ}è©Þ™×üMå·k˜ßýìêYú¤éFé0Rð‡%a&/æ-Ê)ã2Å+4 ;Äôæ ðñd Ý÷„0Ú—-è>DñL'ñÁðßÉÆÅ¿CíÔòþ–ŠèøB ßî àêÒôØ©øÄêÐî‡î°äêçDíä@ø î6øsóÌ–õÛòÆ" ý "‰ ÎX¿ï7ÅJ<9«GåÓ.Ëð3 ãé&ó¿†ó’˜u, +!0bHêg÷uí’áƒîf%l JköÿÔé7ï Þ0àèÖ Òó݊Ξù à¥R±e‰äoõLàÖŽxã_$e *; LÊù”ÁóýL$Ê7Ê*N'¢6je!;îÔ‚óƒôÿ&û R bmˆÿ~ÿ0ŒìbúáåKéŽÝèû‡ì“&þäõSþ²ÕBëyµƒÆí¸¯Lá*Ây póÔ@8"ÖéÊ÷!çÔßy×åÙX [ª%ë¾ Åß½íý§í²ˆSÆï ©XP…† ènøÓÞþ†ó4 Z %HêK§ÐWàâÓýÏçðÉÛŠ “üá.'"Û "˜æ[ó`à!Õ'÷4ÙË[ø/5½ªûåCîN÷ ø…ît 7þTB`%€&Ÿù(îms ¹t¢Eê¹$Ôþ|óëÝùMï4è”Êð<,y[4ê¹üÖËÛzïèÒS÷?'žà ¢Àõ¾ü6÷TîýÔô4þ‡öäLú†kYø’þvväMùÚâ©ä$þMï$":”-©1‘y3¹óBרÚèþíXÜÖ…ø‚ý- ÕåÂÓͼæ¸Ì0ОßÑ ûMåBŸý)ð¸úæâåCï áÿmënþÏõ›õ{ñòû,ñdþ þ¥ôkÿbèsò1õEéŠ7>6L+á5`?4ñ2¬éÆñïõê[ÿú7±, @ÔìŒݧèdî\â¯üYõ2÷†û×õ¢ödýŒýþTèõ©×ÓííËI5ëÍIXi É÷Bú)÷êëÁ©íž.ýË„ ï‰ú-|ÿ­ü¡â<¢ N¦CŽ6éJP ¾,Oüß ½? QôÒúƒ ú×®ï°û¨ðŠêË…ðÏñ“MÂôp »×@äˆÉSÈ%Ü$Àk4ã”q ™è%ƒÈèÞÔÅ·ú"ØSü˸I [² C¢h [Xá#Mü1¾ðÓâöçøµþæê ¤ ^žü·Né¸ý'Ü0æÃß Ö ÷ÄÞèJû– ¤Þ€ü‘´3ưéí:ÇxüñÁúîâù­òCóÀüòô^øøÊü˜ @ )³Ï QŸ I î ›  `>«d‹ ´ô¸ôö2 "öù ± E ¿ !·÷/ýÚï­ì,õÑç½ÿbðµýAüÿeð[ôqåÈäCìPÚ“ è×k|ìOõ”ý© ±ñŸø ôÅðnÂûrB Õ)¯I-‹þ9úŠúÝ"›_@?0Þ> ðRßßžîPñ¡æ5Ùøjÿå™ï%ùyÞyéxÔÙ†Ø=ЦñÕÕ3ïd™ : •+èœüHÚþÞ_äQØ­ÿç±õˆ +d"¹üGâù[üR°Ã1 {9)CåÉDÒïëÙãÖò­ø¬î&‹ÿô÷âÙóöØ¢ãbÔÃÝsϦОÍQƗиÄ}ÞçÐëŽäMøøîNþcõxÆïܾ÷Ö-5•,é*ôü'#zØ;íöð¿Û#aýA&Ÿ!ë,µ'‚6%}"%Ë$&,(Ü&^+/K–3þNábüæØöÞiáÑÙîì6æ§ôôWõ„ú_î‰ô‰â4â^âÓ–ïšÔ”î–bŽø™ âHçÓñ1Ø«÷îÁ ò KÅ•‡Q %$ÍÙ,&/.È0j"O0lh&¡QbÏ ª 2"©ó& , *wà.ˬׅà7ÊžÿhêüËFç ÷oÖÉÜÖôÈï¥ÌÜ eìŠèüü âáô•ÖsÝ5ÛöÔzéÚ7ú™ë[ªÿo  ” À5 3 _ š®q'q$H _1@ &(}áo )ÙÞß;¿æîŸ-"\ã=Ö­ÝŸéÄÕùéø½ýÄÿñïC÷Âߦå›Ô(ԕݖθøxß«tþ©ÏIúNÿ †øõ@Dõõ)Ÿé-!5Ûþ2aô² ý¾ó\j#ž6"`+4ð(HùŒÿí˜ú8ñXñüþ4õ£ ¡d+éòþ7ç¯êÇï·Ý2ˆèâù ”Gn÷"ý«è†êhí+ÝŸséˆ-Ú ¹*‡2[ô_ÜPáìýnÙ’0á.8ò7:sŸ=ú ®øxÿQûËün.þ” Yýµ çUþÓŽáºÝAÑoòôÛ,¬ñéšîM×î«Õ ÔmîÖ,ªð× þ­ùÓ‹çìì‚ëKÝ• é35·N7ß;Úµ7”ý3~ïÇü9$÷d(:3;÷7×#*Eü?$“æhû:à0æ¤ã€ß ðDå™öùŠ=`õ¾ ÉäëuÃP»iïŽÃ à÷*÷Æ eÒMò²ËÏ6çËíåo°? ´ew^’ –7³"|â.Ù ¬.ã-¶¸.Zb²ôk"øùöä&/‚ G.ˆ7ÀÝ-æî eÝ2âþã:Ñú9áo„£õ2 I×µô¬É ӲѓÅì­ÏПï!/¸í%÷äýÝHøn׊ñï:.Í‘g)ë ·Rþñ ‘þ)üÎlK:k%0€A«ÿ£*åæRù²ùêGþ(´'F.æø>cäù°àñáÛîÖâûdõ×!´ï`©Ís骿Ģݟ¿$õä쳸ƒüãéÈöiæÏâ¡õýà[üôµØÿž%öí~cÞãípùìä¼(” L1>4†¶5Òþl™çoúýóéó gûÔ(mý ÎãJû£ÝÙå^â¯àÐïZåºÿíñœŽñÅú†PÕÎæç×¥ÅùÓ§ e ò$ÓÒÆë±ÓÚÈšÿÖ˜'B&1-^0vú†‚ìõ ÷.ìƒÔüݺû¸³çüéÃïý~ö°™t /2× £×}û ®ààëò_Öð#Ùô³<.&ƒ Ð5£ë_ ’Ù[ÚìÜÓ õên'¯¦å'âûjˆêÃóûòææð ¦õ wÝ!Ø$h^#iøÞâç¶ûè³î¢ö‘ñܽ]"jò¡lßü/Ý=åPôSâý›3q*e2hé1öÚ¯ß*êÂ×Ô 'í€ç éyåÈþ)àà} æçW¼(úí#­„òz½ðñýí„ë®bñÚ#só*Zꎗϼë4×­Ôæõ¸ßã X "ì^;ËéÄóÄAØÛÀtõÛ6ùCüZußðpןÒ^ìßÒLJìÛL'-e}þAÞýG þ©Ís.Sƒ70-#É3ÀýÍ‹å·ôWô¸ãçÿ@*­%ÝL5{î ·ÊIæÞ´Ì® ëÃ%;{!Gï;ÚÜéæÈÚ×ÞYÔëÚ]Tî‡m‡ûgëêLõ¸õºä_°ñ|,Ôr)á ãü\ '÷úì 'üù!ÞÑ)Ë0{€5Ï÷Øë¹üB‹ö)9Ø:>ÓF‰59kåƒ5߆ãƒóôèïýŠû–˜Œ¬ i Bðð ¹ÒþãOÙÇ9ú¹Ô|ÛùˆÔøÆ Þ2ïëÚOÕÂÜߨ*ægR,îëºÑòævÚÑÔþóƒã/Uýö ­„ý#¸íƒKìíóÁý“÷sÁR 4¤ÜïCÙgèùÙÖö.ÚÌSý(«"{%3ña;êúã«?å-þ”6P%Cj%“Ú{ø‘ÇJÈpâ­ÆSè²yr6ˆÿ îórVíŽõ„ñfìÙ,ðø E°í¼ÍÙcñ²á“àÈî‰#Q+|.~­+ùßçøfó¢ßPFö|,Ö"ä"V;?îÈöÕóXçaöö4Ön8s8Ä"<42¤;è>øIìYåE´ìsçdŒ° ›"´ö0öæÍüî|ð)›øk+b,¾â«ƒ½úÚ³Êç¾ù¾Ô…: q Úífú?éÍéÂö…ç` cö¨ .iÀúð áÇîáÕå×Vì3ׯ3óT)3'T-ž w&§ðÏ uêlïÜ Dñé,$l"I0hþ[ ¼Ù½ûHÊ×ä­Ï Žñ¦c… k šï â8ê æqÝ=ô«àTîYÑø÷/øqè5îWåà$ôƒÞÔï6!p õùb|´âöéSØ5#µî&@ú)ê(Z>õL!pâåõÀýñ2"·Œ4{0d(‘>ž +OìÛ+ç)êåùìèÝXï*m¸)KÃô`óò¶ñcþeîr äø]äV rçÿ‡ÌsÛzÙ+Á†WÛD1H¬u3zê Üßräû¬æÎŽ!Ô é’SÙîÔæ äô.ø|t) y&þ'!û^ê«÷ÊïÄæŠaï‰5Ñ!vìœÉ2àÑüÇtö1Õ-Îøœâýø ÚðÐËÊÊÌݨÀ<üCÚæ ­ù ³ îcv׬æÔâfÛÐ÷CëÂRÿUþ~Êõ‘ñoþCóö¬û5õ¦áü01ç“'~8.øœ (ÒMì[ï¯Ïì) úQ2ž,Öy*nì4 ‘Ýõã¸ì½×jiç"Xw'‚Âw"ÞxMñ«õïå¼øæCù@ ŽÈý5fç;âê¡ù®è$z"#9#•+ì©•ÓÐãbéÑš sîö@3ðèQLá‡æªò—àY^ùt8})[®5¨û"êLï+ïûá#ý‹è| ú^Gà ”%Žo'»ÔŽüEÒÍͺjÝrCò“-eLBçÐ%ø¾‚à›À¼ë·ÃäRùc#*‘þ9&mØü{ÌÖäœÐ¿ “ì«, P»Žâ ÷âØØ×½î½Ô ŒìÆ"í Ö&Ð Wž#§ùà jåþî@ëaÜ(í˜6"%4ï6*⣠×wàúIÛ)Øu9£.‘-»>£Î-ÐíPïÍévú³ìæÛùœÿä$ yþ× Õñbµç<ìÛôµà׆öÍ#Ê=’"ýðcÊÛÍÜqå¯Ë¿ÿàV x ï7 ètî»ûÌêÔ?$.;!Ì*ú3 f&®òtïbçMïœ ~%y1)xøÀŽçÓ÷nè~å’ÿýíͤ ì!™'À"ŠÓòïjÓÝdzò²Ïõör+$Iß+žÑûÀÅ×àÊÀŠê¿útOç’÷°ÜäCå+Ý3øMçè ƒü›!À™"n%Z L ådÛŸÞ/÷jà€šþ)=% 0(û¥lóô· @ñA6­òGà=,ÞDñû}Î>åÔ×…¼(Û4/o«è+´ÖtÑXÒ›ñ˜Ù.þ‹òŠ3ùóô+ ‹ûx —Ù/ñÉÐbÑñ|ÒÍ„þj§'"Iïe¨ôôð)ÿùf5Š"£'_7í÷5Û´èœêÕW ëì!î '±&nÓ)®Ìì±üVæ,âÝ_â+%æ}³%…òõ–Ïå.ÙwÊÛôDÛÃOóæ¢Ò0cžäkcËQÛùæòÌrö¼¤%œÙâ ó¿lÌSèBÃ}%½ø0m3o (ìõ!’ê÷mãuç6æ äLùšé[H»+»jèIùÇðQÞGî8;Ê=j;87vÞ>ÞzÖoäà"ƒ7 Õ*ÿ",ï÷!ç‹íFøžç_^ÿP,ù:8,¨ù©,Ò™ïÂÓÇÐ×ó¹ÝÁ xÿw.ïøƒ*ð.ü‡ëñóEð‡ëAþí þœ(°ò¶›ÛÖïSç,Ð%DðSCÒ0p#h>´ñÇ/ß|êêóêàÛ &ùþƒ#J%¶Î#ÃýÚë»øòdê €öZ%®ð"[)Û¨$×à+ÌÝäÏm’í { 0ø /àûóeؿ؛ï¢Ó|pï¢!ܬvVÕ ñ|Ê_ʬàýÎÙÿèë|¼€+èì®#ç’î‡ûní5G»;"ðL7äû÷ÚÖàf ôæc7c23¾BËþ.-¸Û~òóêì×z é"ªèXµåÇò¯áÈÜ~ñZßDÝó’« ªãè%ýØÚK䬿l܇ü/ë ˆþç / ㊒ó‹úê;ôöôëÏý°2K#D"²:¬îÒ&Öæ¤ði×%Ðûñ<ì0,fAäb%QånööÚÝ@Íõy&ò˰H ò€û¯ãŽì7ÝüÜVäwÔEŒäÐ39ûF[Ù^ðâÓÞϲíbФ “ñË ñ Ã÷ ÔÙô5ÊhÒŠëlÒûºGF ‘"Pë äà£è(÷Ýâ÷ßþ¦'ž ÃH"¹ö5 [îö!õñêø]ô×5÷ñ%­('/í& Ó³ôfÐ{ÄÅÿ(Ò,˜½\üñòãÓYì¨ÓÞÏzïÖ•bû ÌÌò :áµòùß^ä ïæìAö· é s{íùëçý>ù`þV ¿4x!< …¼ê½EÜáÓóHÙ–!‘ù9{)»38kø8"æïmðÝŒÿð´/ø7ê.]êêMÖj雿àëúþñ*‡F / Ã;û'Rþa£ï†ôßûêº4þê­ùÀåkó0å¾à üÑàh…ýÛ‰»â4èG÷¾æÙ§þ¼áÏýLËßúúœÕ4ß§çWÛD­îÝCÕ÷0³ 6XÂõˆ¯í-ïæ ˜ï¼0NÑ*4:Âý)_èÁÚí»â^µíòo âNIðÑÏåàßÏûü¦á« ãû(Åõxúºðó¯í îhè²è6î#äFtò°% lôž øÝÞöRãÏà¬þé?(â ¾9D0&*º>A—#Éõþø að'); ¨6™2ˆŽ=<òÈ§ßøï¾÷áN'Fþq2%Šž#ü- 9þ«+~é^óGÝOÙnïºßfsÿyö@À̈ò&ÊPËLãvÎw¼épS ²zçêýìãAà¤|ß)ÕûÆ-6úYsæíùyí|ç­ „ð*f^/’)¿·*¯Pkù¦üjýòÞKó#Ûÿ@–ï#ý¼°ß1ò¿ÒÓѺÞ=ÊKöä—ü¤ñ»éÊ\ñ¦¿ïÁî«Ã±hù¯ ý烆Û,åaô½ã;aùã;ûõZûò•û ãø½íÿFûl¸Û?ï<:ü:ÅóAøÈ¡ô˜Tù¾)õÊ nòÇúîóòbñí²ìhé¢îÄêæî\òëÝñOæDêÏæ’àô\ä7±÷¥m Fû7jØóÖïÎ$dÛ`9<%¬<Âò‚ó‚ñ%¸öH3w&>û2g/";,2@æûÆüCûíøYûkø*üGŒ t<øv`âìïŒä ×Êø.àúCûôbuÔVì°½Ê\½ŸµŠá‚Á\þªî¡îÙ÷ÖþëøÙ ÖýHÞ»xK" òö9ó'óǶô7Ñ % †!Q*ϼ*ŒA%V Ä þ(õ ²÷«&¤æ%T-&âÙcúáÝÕÙØã Øšþ¾ ²ì ÈÝì˜ÞÁÜTç5Ýðkèñó ñõëô™ö@ülùTÆý ç¤P å6– ˜Éþ¯ 2úÇü”>ù» %$õ!‚•+FõÒÛëèMþ¯ßÎ$îk*$à ß#°å@²Ú¿â!àذô(á¢þùtùϲ÷Žý¬üñø¡×øÏø•öšðºçC뤻_8î âæaüåx”r"Ýøáæõõeíbã.~èrbò½óg WÞÅì™àÕÜ>ô~ãas÷_¬Wø<[æi÷ñæWæ~ð$âwø~å¢ið¢,Í!{#-ð°®æEã(öçž;\"G&ºEQý<#øérý_÷ðíeý옱i%éÿÝÑ÷_ÿ¶³ø ÌËðoþÊèï|è)ïRßêNÜÄÜÛéáÛ kð v øº è8ógæîÞeþ›Ýn$ËÿU1-D2é/åìS •ó’+§¸87L*ö@+t ˆTy%Èy ( $Ña °ü÷b xÙ1íÄÜ«ÔwÿþéY F —è9 Å Û>ÑgºOó»Ìîùí!­ß¨ù»ÌpÙ5ÞôË–›å`¢"—ý[^ô‹ý_üõ=ßý(ºE5-'ó2ø4$62)7a;ãüô È>ƒ"pð»dÔWå˜ØåÌÉõûÚå ¸û;8 ê1þºÒuàPÚNÊmùÛýÿÉøkæÙ÷˨ݥØ<ÌsùNäÈõ 7 2~S"çâg&ÙÂTàý¾Õ21™$Ë#À4Cæ#ïü’ïuò½1øY+'é2ˆ ö(xñ üæ´ê¦íIáwøêŽý²÷‡ñËüå@ñ˜éöéÒîAíŽí•îè@ëô@íëÿë Yï9ý„ìèäp 8î$¨1)ý¸îºûÆót˜M,T#é!-”å ØÕÜùôØÿ¨÷‹¦j ,Ææ [ö=üŸæyëdé>Þ2ý‰êÖÕÈîˆÿ€à8åµìÆØôë›1{'8PØø—ûª„õÀ¡ÿ©ÙúNà5ùOÞáC-æI o ±ù ¡ñ,÷™úôókõ÷ÚäÖè‡êoÝúìÈþ3ÿÀëó%×`èòÜÙö"ß$¯þc7É1/†3ªì§Ií{æWŽñ:-Ê1 .U …"¤ gT d r " # Ë  ’È -© û*¦óYü4ð òõ°îËþéøMú73å9ü¬Ò"ãÔöÏ…ì"Ö¼þ®ðXûiäYùÆÍ(Ûfã@ÊÂ3ë³*ù'—ú0 ÿÜúb.¼Áƒa2($½­) ’ 9 jˆ%HÚ+'$€ ,xúß±ÛïðäÒÔùæ,þÌý¡ê­ÓgéÑ ÒxáÐ ýÌâþÿüºä=õ`ЮØÛõÍÃû•à$Õü2û3ÿ©þÛ7 ªi§  ó})*@<¼*‹8‚:52Á÷÷ 3òŒíû Ÿñ£2øN*ñ9w )Ìé8ÿ”î¾ëÅû…ðâúþû¦ï·ú7äañOÝGå‹ãöÝvëÒä„ëÈê9çëGçŒëwîLñ,ðoøÏëOó î¥êþzïz M'HõñâùÜ÷ÿgû#1(‡÷'áY2ûË/Þ’³ÈjÙÝÊõÅñæÌ¸ åïe×KsdïxÿŒüSðR·µ&[À#§ê0 †Ñ8˜ Ñ ;è¨Û½¯"^&þýìë!õoüOåg¡þ ³Ýlû€ÏÜ>ázÏtýoåmUûøÍ ÜÙÜúGÌ'׊ånÏÊðéÉÚu¶ªÍ ŸG ' >ˆâ0¿O<77 .=@ðÜ( úOÞ ï Uò1P$Ï#C4¾ømŒæŠërüäý>´ çcгæqÏÈÔ†Ý÷Òuë ààËçÌÒFÜ3ÛGÖùóÂäœýÅù+ðJòôäŸ 4ñ }Iþ.$:Òèð4ïäÖ!-ýÜ;‘-õ.kD¡ Ž.Sý”,þœû$ßþ_ ˜Ìð¥ú' Úé÷÷oì€îô6ðjøNôÐù…ø²øÿüÕùdÿþ÷äýë*ñžÛ…Û×)Í’óÿ× Gq÷È[àçðXæïÛH ëiÞr0\ô,"î\öÿ<ð¢3É"Q ‡¼#fð9 åÿg÷"3 KÕ ^ ­Y®Jô%»÷ÊÞDé­ð™ÕÛ¦ú &'(Ì &'ú ÿè†èù}Ý´ªø‚ù„e/åöŠèÙà‘ýé55þp–¤p»9 º ÐÓ£q3íða÷çë®ée÷tóvú¹HÞˆü?ÏÊÕøÕ<#‘ — Ú+ÊôãÙÞèéŠÕ­ðèô–À XßêAH܉çríÜ|ó$„R®°/ ÑÁ4Kk» £o«A_´HYí]üÈâìàî±áäÈù˜ûŸUÝÇý¬ÓþÞïàÀØsø~çƒé-òóÜù=РÜxé˜Ðl¤ï @í¬ú¬ëkï©ý\ô' Dñ »ú y:|!?IŽB0HŒ » ëÿ;û; ÐôN —Z&Æ ¸'Oò³¶ïÊñ» 'ø+==#Ùû£÷÷ù ýåó…û óôÑîwí,ì¡ê+ïœéõórðvôöýÂ÷-‡é` ¼èÝù6ß?Þþ0ã)3Ù †,+b³û˜òŒ 8ýȓΡ#ª ÓþùÖÿ ðôCîãñ÷î÷d-ó”3“-; $ÛÐô‡Ç“Ä>ø–×Äó +2$ÇùRiÝÄè=ÒlÓá‘Ø¯køP±•­0è^v»€Ðµ_±ûá Àžl÷º0ù"$R)HôÖõÓÉá¤á Ù…\øÿ ÐZù ­î,÷[ò²ð×ù¡øÊ›Sñ 9G(¼7&9Ô,\é¡.ñÓæ‚ n‚ü/ÂÛÁózÎIÑ®ßýÏxç³èÞàèuéÄÜÖ{ê3üªš%?ËHì÷Ä ÉNðÝÖË, I<þÝÍðÕ÷üõ·ñ=øå9ç 3^IE²_?]f5 S{üi"hñZú ƒþ‰!ÝÅ)I%>Ô³áwô»ÔÖ÷¦Ü×j Fî"Ä¢ÑdË®¸-Jß}´ƒþLéëøÿ‘ö!bÞC/ÊxÓ¡ñ:Õf#a‚{"ÅŠãÔù¹ òãõ2ùè<-A%í4Ì$÷¶2ôñùQõpÿ›ôÍþíî“ù9ã^êàÀÞ¢ßFÚ¡ðâQåøh6 _ ˜¨çœöLèÜß» oôZ*š"wü¸.ļ:æÀÀįeådÄ¢ýËëœú×ýdþLñ ãEááæüÚ²Úèéîl)(JÙ3…ÑÆ4ÙÞÌ&±ë}- 1UûÀm²þ [ºøa]îÁíØùbìe_ #Ø#Tú> í ›ý~ü·þáOK ‰òv³ìþáÙÿëY   v]"<ÿžýºøk¿þ \õûû5û¯¾ð#iåý²ÆŸÑèëÌÃ<Øü?« ZМB’JAýmØØ÷%ÜúÚR ¥ó¶"ý"ƒö½‚ÝÜÛ“Õ>G\ú>¯Oka#“ú‰òs9þ­šò ñqùFÐÿÿ½óíù½ç]åh÷åà( ¼ÿVö³ÔàêzüJà¸åÓßâ@íòì*òuÿkçýý˜Ôîè)ÇJÎsËã¿`ãìÊ‚ëï0 ˆ J÷ Çû•$üýô³Cþ!;ÆM(ýôïG÷û iõm'(k$t+8 ¼5!Ýèrý­Ûüjñ+ I´!ù+9ë űÆÓ Àà§íüÒòI¸ëˆ©äììLãèó0äCíȵ| ýÊ Š‰þg E¾Ñ0$cb"@Uù!–ÙAX#ÙK9AÂ5(EÖô)Œýd­ùhõTôh %@Œ±ìá ÊvÜÒ\¿U%ÕÎ#Œ¸¡XÔ(ðÝÌ’ÌTïÁ͇áìÃrû5„ !. •VØúõ|Ñψù¿×y½ œ['¼ðLFìð§ÿcñ­(_Ø' Yþ½½â%ýãÑ“ß‰Ú ÑÁû“ã5L´WÇãŠØ@ÝòzÙ( ò!W ¬€!ïø†1Ü£ð´Ï'ÒÎÛ-Ì¢õLãP'@ }Êö…ã˜ñáÛÚbãG(öÅG,JæBÛÄÞÞãØµÌu÷xä†ýHù—4ۂܨ*ÿˆû WùN2íÄ(a4[õ5µÚ<æUýÛ´/ü ¥6f3Â.}‚ý[ûÖù ŸûŠ% u4 *¤!ú6;î÷ñÖðáúògØEÀüÍ!z#ûÏ¡ä¬û÷ÚèÊÚ Ø&ß=ÓMë1Ú còõ–ƒòúVå¼çf÷íÜ#ù÷ï&šÒ'ý«çéôúúè¼Tÿ´'2!2Q8êü´ï±ë¯ ÷ìP÷!ÓJ!&ÁN(Hî qÈYçÇ\ÅŽã*Ðò²î°ã{÷ÏÑ×ãŠ×>ÕvëYÛg,î’ 1Ò~ úÔï} Ü×&çÜârÓ’ ±ë- ãòMhÒ|éûê ×´{ü,%ÞÕ\"•æÑÒ—( ³Ñ õKÈ'Øëþêí¯ì@ó((^É/É-(g,Ëøg>ï4òÚˆðú æ%[¾5£ýü `èúâãJå·ñváˆüΠ$¬ÿû Ñðÿ‡ñÿÒ#boÝå ^º UﻟäYåôé (S —+ˆîÝFç·áõ™îƒ?ð"r#e9?ðÓ¼àhì‡å)èèéæÔã¹àø˜áÑ÷Å€áKýÈÏÀÑÙî͵ô1 œÄõR ïíŸøòÞô¦å²îoÞ`Þ´ç‰ß4ü-óZõaÒíð¨ÊÝάï„ÓQç Ú#ï× =âŒñf÷]î¤ëcg~òóŒ 8âKñ=à8â(êÞÿ1éoÀý¶" #_ü©ré(ï4ú”ã$ò{'ò&ü†¹ÓióëËGÕ£Ù®ÔÙè;àÙöýî·g „‰Ýfþ,+?þL$™«f%›÷âàäÇôdø˜ìµÏz4Æ,nÆáõiM ”ÓþlCã>Ñ@Æ ×.ËØ$ö€ß9ÓJøæA ¬ þØ5æû°ß äAëGÜú‚éßû3Å ú <ðÿ­ågðêÃã]ûí)ˆËüS ³æýøàåsâûùõãö{ø‚ü v;þò*úùªï¸ 3ùÏ~,;!@éû àÆ:ã¨ÍPÆ}üÚÙÜGÀÔ÷ föîû"õý5å`ñÚáµß<ù¨êp ‡û ±ºHí3°.³¤Þú¹c¼î¾$ ½ûhú÷éùGCö7TÆ ’ô²öT ·íMùVøð`ý‹&“z$|(Î×#®þOýéü± /ûœ"°4%»$Ë='ûTè+ùlïJèØõH4™³/Ñ3 ¥*ëßeâwÞÄÃá¹'q,ß$¼o#bú3³û™øÿ ö ÷Ùô]÷ñïãü\Æ Gö'zÚ©ç¾ðÅÔ êù„Þ¢5 æÄü/ê"é”ü£ñ8Ñ‘Ê Íüoö õè$öyñöæYaó46d13…:E?)¯ç”ûéÇâýLêåÅüý‡ý)8ôþݦêÍ¥ÌÍåÈù <í_‹^‰œÝ¨øQÒÚ€Ý"ÕžüçU ˆïÕ ¸ÏMïhÍðÓèíEÚ¯…÷( © ú* Ä¡_Œð ÷"r½8ç)>Æ@ €;âå†ìLãÄÃñ9/¡*J*x fÿå Ëö¶ùÐøÐð!¬òL+G/Å/ä )ÏÑ•ûÖÎÞÍõ ÙÉü™ñOEÛ.ïÌäâä¨ù’ðÅÚü5 Åí‚å( #ÒZ&jòé¢ëë^;ï"ÿu­#oc"ð`Qõ¼í¨ÇóN1 9 /“×/›üQ¾ãºñëãRÛ*÷EâƒÉúˆw :ñK,ß ò°ÕsÜVÙÎÐÐæë×>ûñí7"×ÿEÞæÔýaÙ¯àLê.Ü2Sóõõ û ßçÅç×*Þ®òýÖe*ÿ®#*Yô–ÞÕîÓéµã=òèÝ×µ¥‘9æŸô͉؎åÌZ {òé²ìјéLÿ.ìåëòþãï\–/e1í5T3ï5$Ê Ý–çÇ_Ðô3%v$Ü µ"ð™ü9òÐñuÝîÜ ¬… +ëûýáÖã{êNá»~ò£ f ³û” ùë‡ö³çGætQäø%·1m(+G-šáÖvÖèô÷ÛCþJ'%å"/× î$ù¹!÷ÕðõÇî.%l2ú'Èy.›óñ Œß4êÀègÞ‡öbéSþÄøÿ‡Pù¥Qç”ù¥Ó%ݸÕèËÑï Õ™ úeíÇÛ¾£ß‡Ê>¾‰ùB×?7C!­õÝää]ù/ã˜çðíãKýðß/ m"-#Ý"BôQ8ð ê Nï~% 0g(nƒ0/ö*ûØWîÜÖ¼Ô×é‹×&ÍðÚ ¥ï'Ø^ì3Ù´Õ`øºÜéhÿ"B.ò( ÃÚö—×ÚßÛåÞ*ø¦í»iÿ¡ÍÿPÕ H§(a÷7—'.7a8)û1¶ô}-ßÜê!QáV<¶ì:3@~=.}ëN}çãNö¿à²Æíú #ÿëÂÿüãâ ûQÑ?ÙÝßÍ„åé"®÷» ~ì`ùÀìËï¦÷î^¹öËîqúñÜ6ý6 ô¥ñ+Pó]7Ÿ4/ý6¯÷’ Ò0éØá|Ò§þgå9ÊŠåQÿÒªêÖù¯ä+âÛòÜÝ:ÎîEÆr èOýDÝŸþá¾ÙƒÅ:Á2ï¼ÒQ ³ëÖ.æfçÎý}æô½ÿÉ%“¶¾'Ùèî ûè‹ðnû^ë'j+ õ)ÞŽõ©îHÕÃÎ÷_,C L†%*Úôîø èÄë ôƒç•Jü©*%%}õ&ÒÌÕîŸÅ¥Á‚þwσ5wF>-<*oA>¿'jõºþò Ìô#1 Å& ôá ’ú\ ’é¶òáèàä£óç /ô}ñ¢ ˜ªÿÈ 5÷GøµðZÏýþaÎðá~þŔڼÌ|Àõõ×6Ð×$^!Ü$ìì}ýêãdë¨AÀª9FJ 06öP îô›÷“ù,ô†ýYölÿ,‘g(¸0ZøÓ(áué„ËÐòó&'=Š%·ÚmuÌÝåÕ½ÒdðVÞÕý«øòZ¯Ü‰ð5ÑáÚÛÙ÷Òl÷9â\¸žüâ&ïùþ‡ùôW¬ÿœ1Óõ9“2„'£6›õNòhý¶ôéͬõ»=(l?HB°ô8øDçœðùåâö°ûn£ôܕȔì§ÊÑÊYåi×#ðìZïAó‹ñÜõËþMþOýûvïøô(êe îö|€_ùÆ#ÝVó`æÞwÈï×׃×3æ»Ù#÷öäºøŠ ¿ Ó! hûó×÷×õ¼øÙ&TÉBQ5$ãAFïuèBâQPéàDiž:‚>Ìý/Aäã² 6°ßûjøÂ *ý‘¹âûý@ÎÕ‡ÛŒÊèì]Ú}î›éÓé8ë‘î'î@÷QöôûèøLû³÷´ lü† Ò Q-æoþøÛ&ØÏ ›âa5˜m*N7ï3!,ïÆùÓò# 6v ª0B1x;+eþ޹émúaãõååkâ÷žê$éS·ƒëæ3ŪÞÈÒì¾ýÂÖ•«ý ãìv ß?ì“áùáò ç[ÞøW Ú œþm Óñ“üâ÷‡ð±Nøe0ë%).S©×ìþWû+ô ” Ó ú ' š Yë#ÎËRÙ‘ßßÅ£ mé"& _Ó"‘ÞûhÚrÞŒîZßW Šò"Ÿ }àRZòzþÞîÏë‡û»çyòúóhM"l ¦Òj¾‰ —ö!h8Pù_ }èÎõèÇæñö^éP ðüŒy¬÷– áÆï„àÌÚ-ÿÈàV ˆ)( %Rk"äÔûlÏÓ9ð§ØM ãøs-7ùÔŸ«=4œ#MÿªIk12ƒHîý¤& ßb÷‘ñ5ç! ½þB ^ò!?èôÌæ×ííç¢çîfãëøäìþDûTè*þýÈTáÇÉ Èåç"Ôø‡õ™üK ì[ø ê@ëðõáì\ÿ6ö£ £ú®# î3‡+Î)d8À/#tð©ùQ ¿õŽ*ë7{5Àö:ø[ ßéö äᆬìi QzÿûÀù»ÿüüìûÍú?ð5ò‚êsè…ïë€ôGöá;÷ŸÉgÜÑsÊ¢ëpÚ¦ óÐ͸ûäŽû•ùÊ éøþ'ðßAR*è:_AhŽ6ƒù×épñ¤íî“0Ò3 6zß-9ÿÏ h¢úLºý ’þ>ýgú­ s»÷®»Í}éàÁ˜Ã¦á±Æ"÷dêžêöõVÜå[×zØéM×EÃí‹P m!ø* :óëõJºö“Wb?”à,ð.þÊïKî¸ýãð„Ò«"¢ :±(£úÕè<øÜæÀåˆîcãÝû”ïàùÚÞú-Á"ԀØ»Wè"Æ~iùÑ$¡áÎsâÅßåé<”ÚEüA Œªò:kúýðÂüá-:!Û)Y8dþA!ñªòf wò‰ïþé÷Üí÷à÷ëkß´Þ8íýÝ/ ÿïppo’²ÜŽóžÑÒî‚Рeõu Q L°ÿ>ÅùÅúVû»ò5 ÃûT#¬Â&4)Òý öç¦ýžü$h¼"9qÜ&Gþ˜ëÝTôöî0Û\Æöɪ œà`÷NÂÑÒÅÒÀŒ×ÇÉîé0ÚQýìÓÍË Ò9íd¥ÓµÝKÚîɽß.~ ¨Fq¸j÷»nö(ö5sø#˜8-8e@¼ Õ4æ³þ“ñÙß7Fû-tðí±ÒAí°Ú÷Úçæ@ãÎê@é›èèsïäêšô·óŽé$ñ—ÕŒÝãÚ3̱ú“äõ ª½¹~ôa‡èéíÊù çÒûþy?_*¼1BD 1(×ûïˆ=ùß2 7Œ'¨<“?ò,¾Aï+û» ô\òOÿPó §Ñ{ööNè“íOã©á÷æNàyòLéçkùV ¿úmÿâ™îÙ‚Õø¼Ø}ÑÍú=õØ4ï/ÜÒÿÚçò>´!I:í;5+ï5ǘ(g„¬Eý O#ŽG{*²öâbáíîà2Ýéô_ã7Ëþ;ÒKDä^øÑrؽԯÊç¬Õ-ö ïÈðcü]×­ì÷ҬрêÔƒí š*"s¡ZÃeoï!¡#oc™ÿˆ´üAüŠ êŸ".Q"¬-‚ø/ÕvëTãLÔ· ¾ïà Ø$FöÁ ÷æÅô7ÜmàõÛôÑGçžÔlû5é_I'ý<^ñ˜õú½é¶_ü#wä'e'¡#Ä(Á"— #m^ Á¥ö‡"ýwñFý ˜ðµ0µ 3½;*Ü+OåmþmÚ;ãlã2Ùí›ãæñíxõ{÷¨ð¯úDæÙï ÛÛÕæ°ÏŒçe&a 3+€-7õ6 œæÈç÷æ@#ã 5#(½J   Æýon\‰òJ5ø8ïêªôƒì!èùoîQÉdï²Ø7íŠÑýÓåשÌã§ÕQì|ã×úµñÜrœ÷éÞ ê¥â®ÑÙßè£@Æ$Å6E‹œ)šá…ðòøâO ¤ú0!{£$„)ª€*° …² è "ÿö F¼x êý%|åËö¦ÖÁàSØ8ØšãAàJí˜ìÂìYñŽåBêØáÐÜ.ïnÛ² Œí:"Á¨ D @ÛuûŸÓLÔîðÚØSGü12Ä'‹#|5 i!N÷̶‡÷w.kê3´3¾'³8w¼,2oû¾6çYô:ò®è4þL ±ž1Ú…ìáÒ ÌÇäÖͰõŽãÁôÅðÅçî¬Û|ã¢Ý´ØâïyÝÒ›ñó´¸øˆáƒñúÚHÖäóÖØ¨±ü©%Ù Áã+9, ú † ëu ÷%¨v1Î-_!5ñoÃUáÆ•¼yó`Ñ%Dõ[†ûyÎÜÖïïÛËÔ–ð¹ÙˆòZø¬tçÂõßå¹Ý;ÜìßåÜKæƒãAü‚ò` ]S9B9^ ~-û:¸4{';ø#‡ëi”è‘íý‹ñî‘ ¬$«&Ë#,@õ\>ßTï^åÛ}ÃêS!OÆ"_ó$ |áûé@å„Ýåæ ä×ëäæ/ô¨1 ZdÜîVàÞåÿÙáâ&… «*-ž""ªûÖ +!&s!Y­'ŠøOîã§ø2äãåôžè/û´ ä ìôëåû@âXâÕì Ûÿ_êd ­þQ^ 'í(þèÏÁÞjÁ÷ÁîÎ¥ºîü|Õn'« ï(û+› ¾§öyþ©¹ïn90ß!Ä!ž/Eø œà©ðlòåí çýñ[£ÿ}_þîMPûóúí¬òSíÂêªVö§ &êíÊÐÔß"ÏýÉTí½Òô ËôÉ:ò‘ÞôöåáÕD¼çÑ.w§2[32Vîº Îàé-û ç²&Z Ü7Ç5´*q=Úk,ªƒúøx#ñ÷XúäöZ ç ýœ!qô iæ¾ìiéºæHóAíLøøbã§õÒÏaÙ•ÛÖÉáü ÛYý^³ 7î¥úàìéWÿ§êïŸ-] !{-àÿF`ìLüóí§üõ r-{ 9Õ1q18A '5ﬥædå³÷wçV­›:ä™nÀÖIĶpøMͳZÂ…AÜÊñ`ã+Ôcýüå5ù¼øyã$í»ÞÎáãþ@îP† 2ÿ Mù4ø×üÁïÃ);,¡ÿ(P 5€ÿ'É CR0´+KCTô’"6ÔìæíõÙÆû\ëd–zà’üiËpÚ?ÓÞÈÉíÆÔ÷ Ïò·Á V ´P çïnøýçfèTê“ä3ûqï2¦úߨ ©­ô¸h÷šî¾!öëJÙ3‰D¼V':ç~ù¦ 0ñ#*xeI-ïØ¢HÎ_Üæè$ßjCó¥ ipÿs !rÁö4ìÛàãØ™ÍÙï|ÖSÞô¦&ͱ#ØÕxô£ÎÅÇó"ÍÎ!'ú5't!ã2Vû6‹åIðÛøÛäŠF)> €Èƒ¶ 0&}ëõê†åLù‘ïÎ ÏþÝHêó¡æ.áåññâw -ö‚ž•îMŒÇtÛÏþ½m jÚo%˾ãã²ùšâ«á,ìöë )-o"´0»2®'÷5;'ÓøªjïŽòŠö]ôpšsî­õ\ÌæöðÛí»å Jõ~û_/ ôl MË+ãêÉÐÁéqÏÞø;òÚç¨ùÑžâ(à:ÑE`æŸ"/Ž)Ÿ"¯'ßYÑÊ ù²!~ÿA"Œð&Ø"´ú5zóeò4+öJ7®§6?@$ü!)ZÕoìÝÚVÓlíÞ)ôËÍú0 èzôÙ“ÙÓqÅOڧĬèñصïäê ò=ïö§íáþGðâ +ûBð  ï%C ¤ _ý,ùº3Ù$;“9=ç{ÞÂåWJë·6ȉ2Ÿ; :*~î¤Þ§ê*Ú Ù&Û²ÓèÛ™ögðÿý|ýoòËú(ÝÇäÔœÏ1âoÎê aé“'óƒû'üîÓäÝèáEôÂÛóßýö •Cùpæùœü#·&/|!‰&ì4ƒé(tñ Qð÷›û~ö™ y Ä ¥´ €üpAðÉùæéSß5ÜßíÙ Rõ¥`ÿvÑÆÿèWÄбŒú‹Ìï ~ˆ"ë ÄO0íYý-ç˜èñäWâõ‚ R ‘ è' nÐ È ) z7 ·ó ® \…¤þv åï`ü¯å>é%ê8àÔø[ê³zú­ fÏ÷h!ç…æèûYÞÕ!Šü61Õ#´ ?)kÜCüzÏÖ«áÒî ¨ï'ÛèÝ+ýþoùï¾öeõò»º E §”g—ß$hì„ ŸÎÂà£ÜÏù çïeóþëàèÍåNÙü¶á† 4úåïêóþ„ÕpåÎá2×”é[ ŽSæú—ßåÌöíTçù D÷9%¶.¸-A(¥2'Ò)÷î7óÞLîœï?ö;8¬&°$ÜAkêͯ½‹ÜÎË¿øô ܱ »Ê«ò° [ݵåã€Ò„õïÙeûí;é;ñ*å¢äøî‡æ™öŒîú<ó”º÷ÒÏ!öQ iy ?éKÊDÃ3gD] W*dïüðí9ò…øâ ß{,),ñ-0Æþ°²èØòTìßX ™î« ØQXô_ ç\îÝ;ݤÖZÑOá£ÏÒýÂä…I˜ÿ#Ýãì±ãLÑÍ `ëÂ(Á@[-oǘýµép)(¬¶6Ã/ô v4ëýBXëýªõò¸þ£Z óÎW L4ý¦ Èéˆ÷´áxâCìÚßÒüïh…ÿNò§þ¿Ïaæÿ²³ÁM¹ú­\ê¤ÆªýÕÝç÷õté+ÜUÊì_¾{^&eáA VàWãCõ—­¤Ý_Xº 'ODRÙøsþo¥ôE+`|å<ûÈ̽ÛÔÒÍüúHß# A3']ÑPfãÄîBã Ó´ûØÝXŠüvû+8ÖöÕ îÇònúœð²–9 m^#0']|&¢‹M <Òÿ&ëìÎ…%tøejèó(õ;èŽàõ­csÎ<ä›öGØÚ×·ôAÖ9"ìûá%<# …€óköŸõûíÿœóœýéøªŠúZ ½Ð\ûÄDàõ¯å«á%¢ô€$?Ü3E7Øg4öë© ¼ÝÛæäínᆡùú. ÍàÛüÖÍ:ãõËÏ÷äMÐÕÿ×ëè§ÿiîøÃãqä·òà ÿNîVû ôï÷´ð¢ûˆóþ ÿñ—þFç²î7øré“!žM>ë4³-°Aˆ (XíGóíåÓ4¿2/®:èæÑ ÞJâ¬òôåü·÷®÷^ûEóÍ÷ÿÍûDúAÝã*îIÝ­Ñ ðÑG Jî~Ú â*—÷/øTõmê‡ìd“Þ)ò¢,ûîžcüß|Ï>&WMÃEM/ÆIZ7$%ý° — §]`!Dÿvü¹ dö¯Ãðk÷mñ°ëØÄñúè Û{òE »ÏcßÉü½âzÆ´ôçLkâcÊmØÀÛpÈÊý-ÞÙŽÿ$ëë(Û` pä ˜ÖÁ”Ù Òú—ÖðË ùøÉaܹ8 ºŠúçÇäúÛ†ß3ã0×]ùQâ>Xü0` ÷Ö?òa¶ÛÀ‡Èp«§óãÍ0hø"øÞ§ì&ùôó0ð™ÿFö´£ù -ýô ÉC“U_  { & 2 ^ØÃøÇËó° ÄôÆôvE÷ögæk’½õÖú­ïÌêòöèÁò?þ;üzÿíëCò•åŒà ñùÜã ìZÁn[F ónû¹’ñ‡öÍóíð‚Aü#ÓÀ+ÖÒ+kù'¦½ød(A ä8þ1Ü´8ÖêhLâÛé"õê§èúþ@:ë=öxÛ¯å2Ôa׉Ý{Кô°Ù´fò0ì T«åï÷sØ;Ü*èW×·Šë:³ f$^ ½!‘úÀ þ-ý{N3Í$`7£E¤w@©ëqsäyïõüJîD´òµ²àTï¨Ö äYÔ–Ù±ÏÑáÌòÄ@Ô~Æ]Þ†Ô&ï%åøóíþ+òèÃñfaøƒ5«$…/¯öûàì¿óâIÙ;%F#i+4ñ&$ó$ˆ" &É$ß${)0(Y(Ã/qÉ/‹ù}à÷ôÚ}Þ¼âõÛ5îÛçïõüõzô[ûÍììñãàßÞÏÐ3÷#ÕøÍøí´:øÞ ÷ágæ¾÷rØè÷ ; É›î 3‰¾ ´&£-2)£+w/æ /A$t–Ùš Ú; u$éó$T,££)gÖÊÊüÊ4èüϼýð‡ú©ÿää°õÀÔ+ØýÙŠÈ óÎÐ9ñ¼@ ÷ßÙï°ÔcÚ÷ÜòÓÌì ÝôúBî4dš ¦ E ‡ # ™a ~M&Ö%®"(2ÍþY)ÕÛ¹üåßàMï-4$œ Uߤû¬ÚØÛ´êÀØÍüâê̳ûæüÿ6î»ötÜoáÕ±Ò½ásЋþËäÂ5dçs ƒ)üöäú©ô_CöS1E$,%>’+·ô“ÒþÝõ=‚Ç#÷v+« f%>÷þ‡ìé÷Uóð'Ç÷õ r~ Cî'üúçÒæ)óçàŠYë:ü_U´\ ô"ùÝéBéRêÜÊOèã6Y¨"²2”òUÑÜ9âˆÛò2h0ƒ7ê†5ùcú! ®ø–ÿ¿ûüGP¡“ úL *á²øß×=߂ߠÔéô+ÝJÈöƒ-ì<°Òbê>שÎôÙ• Ï÷ £÷>†çøëWñHÝôeñÐ4!ú6Z<07Bú‰àïJûˆ¨ö 5 :EBµð>*ù å×ù*áå·äÉßÇó:èqŒýÅí¬(ÁˆßºÇ™¶éóÖÉÙdû²ð? vГêTÓÏ-é±Ð[ wè³ Í­FBâ È g]’%MN1N$ê*ð.+[òó¤ýiù6a2ê$‘,y: +Øê9îÚ_ÛºåñÐÓýrä™Q0ïK× î;ÉzÒœÕÚÃòÕÕÊoó·dç4ö±äÖþtÛ¬0ôLÿbü•Ѳ-þ®›ûà"=ý=í-+*Cvû%vßüñFÃæ(!Ï ã!¤'ü¹)eõq`ã õ’âá òÁåq>ø2# ëa0ÄßÃÉ¿fâ8ÅR»æ‹ k «húà oè:ôâæ²à´ù¯áút¯9'åáà¨æ?Ãì²%€0O1 6ùÂMé'÷³õkégTþRFTù„OàÉö‡Üâ$æáÄðÐç•xóìZ<Ìó® EÑ€Þ‹Ý>ÃûAÚŠ!F øê"šè <ÒãåÛHΠÞ*Œ ƒ.0|+âø4¨éUòÄû~éÒ›'0ùÁã²ùðímîþ%úc" áI+ êåŸõuÈß1çLóþÔ”. ö,@—5Çx0;êEzÛ¡ÞvðÔÓQòe%bó5&÷ Žégîæõ”æq¿ø5"n!e&} Ö!öé £è‡ùCêÃïæøJó+e”GÀï vÞðù~ÛÀâ+üfÞ·'cô-.zó(ßæÑÄÜÜÛåðèÛ QòzרÿŸQá÷EâíÝ“ çz'H p'å#ÍI ©VÅ …ð6û°î¦êÔOõqeVê+üâ¤ôÊhâéÝ{ѱú¿æ– [“‰–æ´ ³Ê‰â¹Ç‘ÄÂÛ@Äõúß'ÿîø¤ïÜì‘Ó·Í1óÒK ¶óùÛT «%wšü³ wÿTWM12e8¥/ó ø5¨õæVì?ø+èi+Ô(³à2鄯Ê-á›åÂÌqEô%…+ ÀGéUêÛ…ßÑÚ]ÖàéÔðÁÝTòîº÷²-ë ñúXå$êôù/ä'|#\l­ù Dø¸ù” ôþæ#ËA&¥1Ž1Vóp§ìsöÚ/û±=x$FARKt×:hÞXùjãrâ·ó4íS¬ùZ=| á4ë«Ñ7ÞƒÚ—Ä+œ×²cXtõ ÅÙêIåÅÕÊ MëQ'z|%)›åJ Ò*áÞÝGÖ`÷æ“ Ü ªú»í¾þƒï±õ‰Lù{R ž· ?}ì"ñÕ¨ä{ÜæÒßûRÝŽ ,+1'É õ$«í ýñ9åÆ jë.0 ù3\)°  ?Õ ðãÆ ÄTéŸÆò¡ðœR Ô+¨üØ ô‚í÷ôUôÓëü´óFg >cÊçîMÙ/ëuäàá½àð,ÿf$ã2 þ¢4æ˜öøöZä-AùB,3`¸ ÂoøéEó+öpãVý¹7ü$D6¢8_1ÔûÆêõhïØæÃîïZ`f£ #‰óuPæÍú^îñí Sû-/øÏ%±ßk ⼫ÖLÓV¿‡ÿ)Þß«¯ý ôéÛöÂêðæÄùpé cù8~ %eüö±ÞšêˆÙn×yðfÚDµ÷a*¸%]-wñ%¿æ#6îÅè9Møº'Ðõ+Iù2ØÕõçÍ¢Ô¸é#Ó˜ ÷æ ˜šÙì-à{æåçÆÛþ÷^〖ïmÒø¥ô*÷èì¨å ßXøÞU3õY!Ï ¶÷†×áÁëÚòSÝn"l÷£?)+×Fÿ%¶çvþÔè)ç‡ÿäìfðü(síÀ'¿(âó$Uô‘ð•çî»ú´` öý­ eâb÷ôÏü×ÝgÆÃâb.äoD.ÌæP >àŸàéZä ¼L"Ÿÿ £âs(܆ê¥êÕèt”ûA•}'Uc&s÷Ñèê8õ³îeå® ï¿a¢ã ‚èÞ Ê/àh×lÈiüÏÛì%þuèøF¶Ôºé&ÍËÅáàt€ÿwÜ“#ØÃéÕþÏÚØæ?åïÝñùíí~_ïüalô~ò}ýCñöŠþ7ò¶­¬/®!¯$]5ð6ö×4äXûäØã)òa1d-â +)Óèç•ÝWáî7ÖÒëv%¥ & !Ùj8¡ ^ðÚóJðùã‚û5éz:üŽ }:ú3sã{þ2ãÅäëývë&3 ä!&A '(£çV Y×®àcíÔi÷󽨋GËæžTÜpâÃû Þ;%«¤4;+s²/fø?ûêgì“ñÛâ\ÿJë §ü p*Ï+ÈüK#ðÏôõÐÆË7úà>*ãÒB|å5i»¦ÝÀÅ·êóïË|GÀ#/Úö&üͽíNÏIÐDêíÕ© šî^ ³ýr pàÞð@ÜÑÖññ7×[Žð(%Î &C$>!ðõÌ ˆáêuô‰Û ºù|-’&r Î/†Ü |ÛCÚ‰Âá+«>4](ºB*%YëJý¬ï]è§û¯í.›ûÏÿ³¨f Øý Gîÿ;çÀç€ø²àNŸú[(’ ý b#Ãé¶ýÝßÚ’èÐÍ€äx Ÿ í¥/é-ìZþ+ëXû=0'<'þ3¿ö Åïôû•ò«çH·óÐ…ë*&p é'0ô{¢æèòwé.åî#œÐ,ñöÛ×vê¶Õ‰ËKøëÑL„ÿ¸&''é' ÇPóÁ¹ðè_Æéóªúž@ãpöGÞÑá¤è,ß–úŒêŸ~ÿ'##™ ‚'v™á¿ú¾ÙÃÚ/üïÞc!7 Ž'*©+*ÖûÅæôóôO:wãCï?9)ÕAêïÌôÉ×ÁÛ=¼· â“5p"´x,7×þöØÙYÖð@Þg¾ñÕÀú¬ ~” ¹ )ù ÁÑUî$ÏHÅTü±Ø8f0Ý þKäí'þ;ü,ò`Èÿ9j'; ;öï5bÙZâ‚ì[Ñ«¾ð–# 4&‡'ß(1&TéÀù½è!Þ­Ïç¾&; a3(Üæ€x̕؇ÝËNøÅÞüÈö‹ ç¼PŽWÒßXÊÖ½ðiÍžV(YÔ4ųÁ…÷+Àã,ø æ#P-Ç !'ñrRê¦ñ¸âŽèƒæ[â²ýÞëÖæ"ö÷Ççäïcô³áw3ò.;0Q3¶;¾-7ÛËø†âÊÓ Zê"$Ú¨s,0ˆ!jéÜjç¸èkÿGë!E+íëù+ÚñZ Ñ£éÖßÏûüàò &ˆ-ªõΕí ùÛì8òðlìBXîüut$sDûrÌׂå¯îÔÏœ,tøðHT8YâDÿæ _ãäåùó‹çU{ùæþÈ `$Ã!Aùè ÉéÁô”òûè>4ø–&Ò˜ ô)éÿ¸!ÙÙtþØÑ«Ù®éÖîñ¡ à ‚ó³ QÝüíVÙèÖ”ñqÓÌòb"PÇøÔqé$ÌÊÎå_ÐäòI$¶¶Wè *é!êþ¨ð†ã1O0¤ PÿRšâ3ýÎâOâ´/î;7%ü*MDœöŽ# ÙòëÐíôÓ[íì"(õZÝûÿä>ð*ä„ÜõÂâÉŒ÷ˆK óþÉ‹äÙø|ÚÈàèçÈÜÿçíÍ ãõ ]áÿy äñ/ìÔò´ùNì!Yð2)¿À:¢äÔ´ÑÊÙ;ýµ×^*@=ž2­'Ñ>˜ýмé“ñû)â–áú»$u€ôýx |ïwø5äyëpÙÛyêyÓܪîtS1ø–RÖ¦ì×’ÌËóuÖÁ $ölã·ñ\^ΑíäÌ.ÎßïW× Ðÿï \ÿ§ê•“álçuýýã5 E¿%#b ð Éðo9ðOðÜôÜòû”óö üÒ#Ü%¯-Áú !£Ïé_ÖÄÃÛ¿ÖÊ"U mK!QëH вçÕÍ“øOÛ¶ñÉ€ñð †ßoðYá8ãÿñ çP,ùš a 0B×ùeOúŽÇH XjÑ!ÙÈ|ç¢ýTÛÛÝ9ù}Ø6*(98Ç0y¤6Xñp ä®èùùdà7¨ú0,(!¤^,æäk Ý׿½éwá¢þõueñnôÜŽÞ$ìÿ*ï^õš·ê ìA¥÷ý ÀájðýçmÜ.\äžßÿŸ¨Eý(:æ»î7ìxÜýÿ¹æ¦¶ÿ:½Zú–“Übö ÕdÜ{ìÛ[•ó÷$ Æ02 Jm±òsòýìzœöK1Ü1(e;Iþ£%†ç¹÷ï 㬠Aî:!¤"“í6 ÕÖä ãëѹEå4 ÝþþþÂóçø£ñþñ0ìÍîèæ;ñåå˜Pöyü ð+ šÞñ?édâOÚî‘-àÕ9Ö65%2=[‰ò1ô)Bí‘0ÁÕ0´6“‡5ðlfá¨ëù¼äß*´Ð0‹'í!¿ÃìŒÉÄðèðßÚ²ÚöáÖŸ$íS ãÎ`ëRÊ Ìùê¤Îüòg£òáYûiè‡ÖHé& Ìô(¤öÓèæ!õÆòÑèØnôÿ-¦….y-ß(¥ÿ˜´÷ÈøÊÿ‘ðr øõ(•å?üGfúóLÛèí¬ÔªÌ¨â…Ί÷ùæàÈ—èr àÇå\Æ\Ãäî<̪tûì!äµþàEä0÷ØæÂ+ü‰ñ¯ú³ËúDý Öøô[ úé#S9 %N4P6Ÿ¤,‚üÅ 7ôiñêê!ó#¸)ª ²,ïÄ ›àÜêÊìÜ?ïï6 Ì"©çëTLåæÊ÷¸å]ö’þÚÿe÷ˆe—µ¦ ÷uýó­òTÿ¬ñFNÔù– 3ïù=ÞðaïÊ +ï32ðœ,¿7^ýa%…Ö íFêüИaïÂG Ü ôk ç òéêä2ú9çŒ éøH(,˜xSõèþ€íøê”ñPâÚ»ìæ)çм¹õ#óëqÎîÐ#} s#é(\í9pÇ Ü Ú)ÅÿÍáj'ì|Ûfí²ß½ßéñiâ’Pód ¦ZƼüg±ñ"û¬é°îIíåQþ¢íeÐ+^+v?;£÷t¸ôüïEõÖB$Ÿ8¸C9‡2ýÃô“øuõÖ+ \zÈò ó–÷ÐôÛñðvìîpéèíªìƒï—ñÂê÷ñåå¯çœé á9ôcæ OùŽ Rø›»Ô¯îûÓðË´ØÞ}3$±2÷šøóvó¿ˆ÷ô5Sð<4‡.Ä;Ó *   „ú‡û×ûk÷ üÎùÑáýî% ×;õ' øà;ëâæÕ¾ý äÖÉ(ò‡>Ïuè¹¾ÃÅø´yå@Ë~úOðØìÅþÃÔé¸ßlÔÐÍ䳌ÆO»õó²ô(ó< ö× $t!÷!€*³ *k($&³êþBý,  ù;)Å+M4`õ|%B×‘íææìØIþãèó $ÃûS #êÅýlÞ–ê©ÞœÛZêÒÝ«ð™ë›ñ˜ó~ñ:öwöÝöžþ^úq'ÿߺ‰øY õ‰ü ®ûúa‚û€ÿ *n'„ñ,Oð=Låëêí|æ\(íê%Ë'~ˆçäãýß×äßÛäÔÕÑövæ€ÅûnúáFöeüõÿŠøÙúÈ÷ ó óQèy%î¥f¦ ÔvêDNߎáá“â.“í"h÷'Îç`ö~ðZâÇ SííFïÜSè¨ã™ÛÐöwæAäú¡ ôšÛç¹ôÇè¡æÃñ•âQùbç4_ñÉÈ !ZÑ+$Cæºàè£Ô Ìò©8-)‘%p< ù˜"ì˜÷ þò¿@Ó"ÿ¶1(zýÝAø$þm‘ö9^²ÿ¹¾îúÛé8ñ}æîEßèÝÜÛRîƒÝÝÍõäæ öæ ÒãïËéŽÙàä& y/(,¦ Û.ûç {ìeíJüø‡1¢¸6…<(%>À*Ów /#G¹: 5²ý Bòb¡×¾çß‚Ô"kíÚ ã3àõ›Ä¬ÑÏÒb¼‡ù ÏÏjóòù¶KÞ-ôzÌ]×¼äÞ˳ ©íZ)Av#Vùöšóé÷ÑÿØõ²B|)žÙ4ú(\1÷4¿ /0 ÛÍPûVš/}Œ¹"”ìû áÐËáJÞ~Íúkâ~.þ÷ ÜãˆüêÒÙ1Þ+Ì0ýÔÝïµýDÚëúáÉîÓùÞ¶Ð,úYé¨7Ü ß ð…:" #8¤$ñ–ú+#, ˆ1.*‹!D4ÿ–!†ì)öó¦ ÿ¾*"Š$÷2hw%Zï&QçÓèåíwàXü‡ë…üuü˜ïîúøå ð%é ëÒïí§ë†îê_ê—÷ÏïÅ =± ë¼÷^ïWâÏò½*/ˆ, øu}ôûà÷í" Ð*H)8 ª(áCÍÕÕÖýŽÖŸ6¼‹? ª›ñ Ñò†ûwæOç„ínàßýÛí ò”ýø ÑìïúàäíSÖ¥ð^1|"íÈ/׸ø2üÝô; &ûøLÙØ…õÂáÇÙS úík!gNVºöW ±ôô`ú1÷ñôèåæ™ëqÞñý&ïÖüt‡èMý ÕZçܲÔÁÑâ4&Å d0©-Ù. ê–Ïô”åzýù2-m!ö /ü qÔßG”  ° á … - !Z’X Žú–#òû€ðõïQö³ï†ÿLújú‡ßûVÏMÜmÙ Ïçì7Ûrþ ñaøÛ Þ¯ógÏ<Ö éÌóƒñe*ã#$%žø 'þ®ø‘:£ê,’")%•%Œ'Ë »$ /AL&·…/µ&Æ/Êó ÅÜ0ë8åkØÎûèqúD6æ5ûˆÓ*æÇÏcÐç&ÏçÿSéÒþèü€àùóµÊ|ÔŽáÌØûdç‡ûTý3ÿ³þšs¿ @'úÒæ­ -êA»/T2¨<Ý*þõŸ uôjíh÷,1±­#à7„ûæ"$è«ù™ñ_êTüõòœú)üîìËúá îÝÞ%ãäòßGë%åÄê4ëdæÆêvèBìÐî òŠð"ù\éòäîßæÔ½ó  ÿ¢õÈ­üTöÎÏ1„B< 8_Ž84í{ äà äþßäÂ* ~ W‰ùúSþ:rÿü¶úyDö éþä ÌÊ3 žñ–ÿôàÆäuè Ù£í±zýÕÿç®û\úÅçGúŒ)Kê´¹ýi ðÿõÁ÷Gë’øùœ‰*EZóWóîRùcû(÷ù†üv#ý+;þ: Ò‘ çóÿÓìëíöç&Èö›!±…a'÷öˆÚ€êuê%Ðpêÿlm÷ñöø¤o mMé˜nüØ `ò{ý>ó+÷÷ôùõ îØôXàué\åÃâeòMô³óö©ä$þZßGãUþÎà«}Õý: Ý&ú3Ʃ׬ζÂQìÒÑ‘ ÐóàùGíÕõ@ÇñrÜð&ƒ‚!Ì iPÜóTB  ŸËÚ þrÈ‘" 7&'÷´Ûîžî;Öì ƒûµØë÷°Ñ0ÖmæhÓÙþåç MzóØ ÝÔ"ò!Î|ÕéèeÑaÙëo bùübiÕå–‚V jc¿2›D@„<<)ªAÑ É ¶ý;Öó× Û1Ä(8R1Ôò€©æ æœ±ãïg¡¶œâjýnÍOäÑÜÑæààÕ6è®álÞ{å[ÑâÚEàÎÕïõÏêýËøVïñò ïfà¿Àññî ãôrܼïÞñ‰Þ–'íÍ9]5(‡? û*£øJ F…÷¸¤o!å0õp íêÈõ“í\ïôŽðæø¯ô£øùpùýù1>÷Füxê™ï:Óp×Û{È[øOá`Ù°÷¿ îÝjï”íÓÚ÷ *ô[ïK³hîÖøð¸ð»òóò Z$´$i!*õ[u*ý3–÷¿ß pæÄ‰ï(Sî~Ú/ÙRù:Ú4~C%#^%zõаë1æbüàØ\ýD–Dú”õâñð•èiݳžêaW×ò2/ .ÅoœTrîqõîé[çÚý(ô’÷Ÿ ÎØíò<ÕèÕðúÞ–%E Pÿ,Ëî¹ !ÞYæéq×¥¢êR= ( qÃçBÕsßö–ÚÒ€üÖg7jtÿƇÀ% ´ RÎ Ö²]Ž aÚÿ]½ëÚø³ââüó¢ä3£ÿïõ &Üù£ÒÈÜ;æ3ØYú*íÓwò‚ îÓUõ'Ô1ÖFï¼×8µðþójëxù„îQîÙ<øö ì! í Iß @á&›GÏG ,]CS€÷t:úÌ  a&8V''ð–½ñ`ñØ ùu!ˆ; &(fùM E÷Éø’û›óÛúÜñ¶ò¼îíNìÛéµï“êŠô|ï%ôõ°öŽE &æÐ÷ÝÝt æc/?+$ã#…v Ëú„óŽGj_ƒG"£Œøaüóìöòóð‹ñQ ý+A)0ê+’r^Í€èVÎcÅ#þvÞ ÄÑ&÷¨ œÖjãýÐ(ÏÈè–Ý0 ¬üQJ{þá2üжèÊz»”®áê¦É> ’ýA6*_ú*¢í-ÀÒ*á”ä§ÛÒûâl«÷˜®îsõ/ô÷ñ¢ø€úòyÄ;O(¦:=X(|êÌùBö¥ðw|ùt$×,îÞÑÏpâýÓièìÍØéÜíÕ[#~ö.ò4¿þƦì Î2ÆG÷í⌠ÏÏ xIù‘ñ®ó÷±óp†NCø(qcCK [÷^¡+[I=ÿ¾öð‡ý 9ýö#¿%"æ ÕõîˆÛ=ÌÆç£d lëçö»ÕÌ&ÚÕºµ¯ìÒ|5ÿÉ { ­ø|!•6ëh×"ÿQÀJË”Åѱ$E ûÐüÐÙßôÔ/ÅR\æ¶4÷(8å ¢‘ƒÝäóŒÚRÚZ ‹òR3t-:05rÕ[óIæòÖ ÔóTxü+=ýsÃû ”(öùê/æ%ƒïÑ1 _$ðôJÞÛ„þÛ„ÿÊ¢.ñ—øì#ãö uéÁ)bñ!%T ;ÿý9Cþ¹ý³.öæø^ƒ3—É&ËÖÓÈÂFô÷Í`±—â!ò­Nx7ÖAn¸Ó"ñAäïØ‡Åþq"Œ%ówÍùÏ Ñ?JC&r0 @Ú®‚ø:øv:ýýBw޹úk‘ÃRDïN÷[êÌá2ønæuY˃þÆãë½ùLÝ|å³ãKáEî&òtòàÿƒåýýøÑ-åbÅvɘΠ¿Rë4Ò[óÁ Ö ,Ö œû¤ü*ÿƒ÷…vÿê'oÎ-úÆQíõõµ-¥c¡+ªÂÀ í 5@}]ûéþ@6ó% &r1:Õ3¬h¾MÌ® ü*ÝÅ #ûÞê¦ûçÑì‡ëã(÷ä-ðv½BþlÿD hüù :¦Q>ô"¡Ù"SM¼&ø+Eø%˜NbFð.bF ) NúƒûûóªööJ;> ™nèÿrÄ'Ö3ÚÚº[êߌ%¥ü“¨Ç×èÓ„ÅQñ<×ÀÿØì´Eýá°ÒÉ!ût>Ôfî"ѪǦ¬Øw"* ·&sðÈ =ì5ñx ñ[’ûåO(°úý?ßÄøÑ£ÛUÞÇÐ×ÿÈç÷üH_ßO÷!à­Þ†ó«ÞÄóP €d´*õÚ£ØëÈÏ•Î.Ý;Ì\þ]é'ó “ >›ôa ÏáŽì¢éDÜx Oê©+4—/üÞ÷ æÃOÚâÛÒÊ+ýè(ÿ[‚ûµså a <1új)ý4ød$z5ëüÙÚvß6tÅ8Ù6ÎS.[«ÿáìùƒ(ýâ)µ4n0¤Þ4Cè&¡Ð'ÛiÓD% c#ÛümîànüŠÜqãÛ!Ùà‚Òï$ÝÎöï¥ ríÏþ‡æ×âú»ßl(ûç'ã $¼ü|óénóZéè¯"š$þ!aä)÷ÏôÓçô©ý( ÌR"íâ”&æ N%·æ{ áǽàÛÊKÅãèŽÔò‚ô4à’õqÐTáVØdÒ®ñÎݼKó#emÕ^Xë“[רàÊé‰Ö˜ÔðÚfz猛Î#ØÁó/Üðâ'ÌqW þ ú˜Ø ­¯¸ v Ú;¤åÞ”ÿ7üæïúËï´èZùß%-ÿ-}+l+ ö" §ò^ñÌ ¾òŽ$ x$&K!÷ ç”ôôäcå:ôgâ& ¹óó À óüã <1ýìÒĪÿT Ò þ.ïZýëáèz îp*q˜)[鸪éÇÝp&¤ñ£AW1Ö5wé² tÛ%è=êåä3æ_è|ç¾Ý¶ýâç*ñÛWõýÑ$Í ó(ÏÐÀù+cŸò¡ïføíNôÚæÉêÞÆßÆìáàÖýöøÙð‹ÐÌšìMË^Çõø­Øµ’ ^§$è” ç•í…ýEôŽ(.ë¿¡4òà:ð¥àúÞ^íeßùþLêõÐŒ %]’Òúæ ÍéÆëgçI&¹D#Y(%ônTвë»ÌôÓ,ÜòÔ4í äàøçò™Ôä Yâ –%ê¯Ç$ óAoá¿ï¯ÿ–ê’Ù  ×ÔQ^j €Û!C ìõ"UG½%«:ªDÄÿ+(Õ±ì£ÞÏÐ[•éI!”§týñäXù!â]á<íñÞõû`ë vÿýf Žú3 ±ìïýjæiëßëæ.ýï= ø†ÚæOô¸è™â4ý(æHÿûÅTfåü (ï¥ö@ûêí!ªý ëR £ã’¥ÅÖÛKÕáÇ ›àv< ke÷tõIýØóvùmæ ð”áÐá‘íýMðM²·é㳄¯ÔêÀ€sù¤VVŠ÷ ÍúžøÝžøv3ʲ›ô~Äí“öÇûðnùÿ¶)p!–*ê ) ßûA ¨þCúLWþ"O#¹$#7%š÷õjæÙô¿óZç%jú–8!ú.;$>%­ê þJçKãqåÞ)µ ô&‹%V ˜}ùÜÕý‚÷ïü÷÷æñÆö3òÒDý Ä î†;à²á9øpÝ$\ÿ4 ‹ú˜0å÷ÿë-éqÿ²ò –tñ Õ´ó‘<ëRôô‚è£Ù÷Ž6i X-9Äb#äiôì=áWÿýì{]!1ˆü˜âïÞüþÙäÒ/ÌåéJÍATóÐå3þ˜Ú%õ4Î{Ô‹ãŒÓíþ=ðÔ Ïêq ˜ÍÿêýÓÇÓnñÙßHSúú f ¢= –ÿ|kß o$Ñm>ˆ.`5ÚBM ‚/Pè;fðÅä¹ ÷+/D“&c)rÔýÖ èó¸÷ïû©ì,!øñ+µ,¸--þ³#UÒ«ñÕÐõøSÞÀ ìÎÿ¿Ù±ë”å¹ãÿùñ¨´5 ¾ÿ2«@"ý!\ ¬í,^ðšç± #öA"ã.%áþ%Hï¿ü÷ í5ûõ5)¹3ã.ó* ùï×ââí–å\Úwû åý`ÿC!Gî¤ÓÚDí-ÕCØýÚjÑáé1Û‘û{ñ ­ú› ˆã/÷ Û$ß°ê´ÜÀ Xö À®èã=“Õ#ÙCÛí%® Î#ÔñwÞøë¯îå¾qõƒv çÍêþß4á¬ùÑ<×ñç¥ÒéÔöô¼üªªéüûÓìëü™ðk1û#û0H8Â0‹ä VÒþ×ð¶ÑXôú#¸$Ï‘ðöýÒÿ°ófE½—hr s·û ì÷áÕäFïxáÏÄøÿþ ùú] ræØóSë}á1ÞèË)Ù-50+³ÞãúåÛ»Úð÷Šà‡¡€'",ë.g x ÷T<õ íÆ í5)ª ò0œ)4"+cïj âWçÑê÷àZ÷­ëÛýùx”õ=ˆåÇôÑÜëѮŅúõ×$¶yæQ4Ä&ÝX΀‹Uܪ ßÝÐò.â›õ¦ãßäò§ã× ÑôÒuÏ!¢#{  —ó*¶ôgëú óÎ)H¤-ü,îŽ-çð‡ÓVèÉ×¡ÎøîRÛÐüõ‚¼ìQÕ$è†ÞÔùü)ã„ÂåÖWìa lÙñ„Öeݺè‚Ýrû`ñÒ+ÿ±öÿ» L.µ,tì7þ,¹5Æ6Cõ2Êè=ãxßV@ëÂ;VJ7<ú9)Yëðú<ê ã÷1â6ïy¿ ”—ü!Þ±ö¨Í˜Ôêä\ÎGòìÀO ÷âêøJîAî ù!ï‘`ø9ù—÷¨ùúf ùóêøùÈ91 Ä##7RðIÕ›åóâØÒ!gèï.'wzühå_óéænß=õ»àÿ‡ñÙ 0È Cê÷Rذ÷³¼U֡Ž7údÕê :–×è#ÏæqåþåèèÂ#G&/CÌê‡Áë£ìwýÔî‘ò_"!#e÷àë‡öÖßX]üº+þ‰É$_ÿ—ôå@íå÷hå~éùìäË `(y'¦ü‹ Å4éÑŠ¿Ç¤Ý³3…Í?Á=Õ eAkÿUàô½û± ·òú(8×#œ%i vÓöÁêBñÖé|å3öè2Ý÷XŠ ¿ ‰çûÅ :÷ôú}ðbPÐØ°ÉÜÿùöÆŸÕ‘ÓËÇ…øÃÝ@ù§ h&w Âè1çŒÜ¤!ãë÷Cé* 4öBB B,×óa×öÞõÿø>õvþdöP é0m ˆ# 5ðÕªÏ:Û)ëÊÉý ÇøP )'Ùüó¥ÛDüË?Ü8ÛùѾóEäAý!ûàï `ÙâíúÎÓ×àÝÑ|ýté7â GŒøø6©ðû>þÔö`¤5zª8N6#5N„Úí6ööˆç@ùùÃ;²%Ú7†>T3ôñØ­é7ê~ý ç4nþ¹$Zé’fÅKáeÌJË?èºØàòÌîêíÈôõäõ¦þä"ûy„íâôø¸è¸ùüêöÀæÓíÀí¢ÙÊ.úŽ9! Õ>6ƒ&ã3Ï ,ñ¸þö-ù‘#] /X$-˜.óð–›ÓçAÛ„Ñ4üAß±ý ) ºE óîüØá(è_Ý·Ú©æÏÜ,øëñä‚Îí- cØxï›ÚÝuî¦àÊUõ¢ÁéväãüµéÔ߬™õz5€,!‡1jé„ ×ãfçjûíçÿÆüìú" èÿZ¼z,ÁÚeõCÓØÑ÷Õn"­èò)9£ í×%ÞÔëBßÛíîóÞ^øõHìú\¬ÏÿõÑ,˘âÝœ6×F(„2Àû/%ûœú ßí­ Ù £ ¶X&A „ô‹Æá]ï·ß«ÚÕá#– ­!ÞÔ–ãÌ÷‰Þ;ÚøŸÞgþ>¢ö8 ÒmïÈ Ì™ÞÆ¼ãÕ&6 ×(ž&Ól"ð=ýÜúGüùõ+ //‰.\µ,ùjô¡÷õsòÖ÷²ñõúþõœ3þКÃìÉÝéìðŸß‹¢ÿÎ!Ým:³ÂÎâÂA³ö’Ò\ÿ ù¹öÚüñôIüIòúVñ3ô5ô^ì•*õ¯1ÞÒ9Û8®S5žôB pòïç.ô K%3"Ñ)ØS°óÓªöÚðÇ ö>¦€h"åûa×—óÎØJÛÅæ¥â^é„ìMåï¶××é>ÔZÞ8Ù¬ØPèÚ­úÊç ›úP  ƒ¹¡û0Áø´÷7 Xú .f;Ð8ý:ÄêÄ £îÆÝ|"Mò˜Gð%Ì8ÜAšz-‹‡ ®ÿyvú” üsêiÞÆö”Î$ÒKß±Êï%ß-îCêoéRìüîï5ú±öúøù,ÿºöTŸžÈ=+¸ßõkàÕãÒçM>a#¤%@=únXóüö£øÑ'® ’4&=-Â.O­)úf¤é÷Øà§äûèÒàì÷Æï3 @ðácË<ØÙ±ÅTÿ’Ûæ„‡ êºýuÞ„êtâá¡ôÍçîúýªM „ý® ójüû~ðûgý¿/hè$ž-°üòí€øÿO÷o[ ÐÐýû‡ üa´æ]û’ÐÙ×ÒäTÊ­šñf"õèÿ@kÚÀöÚÙ0ôÖá “÷µ¯ †mNëñŠü­ðmêxÿnêFÿë MÔ¼#7Vp çâ"ÎØŠyq²ö AèóÀéŠæäú‚ë»Q8*ôß -ÜÌéOå>ÖF¹ç‘$ §$¤%>þôGÖäó¿ÓdÔ(öíÜmíýëu ÷-å T Çûn)´ ØB1ã,xCqõÔ=ã0ñ÷®ì² Tœu ²ïgý èkó‘æ²ì¶é®æ1ïRå~û;îËúˆý±ã2ù¿ÈoÝ”ÍÀÇFïÛÙ. ûeû¶jèsöoìÅæ<øìï’ôö`Iþ$vX5é,="û7»þaó€÷÷î185;‡75ñÈhÛøî4ì,â•(ò¨ ,ÄþO¤úÈþ?ý=ýZûtùÓî%ò£èTæôë—òüŸÝ òÖÊŠÚ8Ó‘ÍPð§Üª÷O¸5ü•kûÕø” ªøñ-P hCe/9:µDë3»óð 5ñÂñâ ÷;1δ0£6w(ÊþU Uõùû œÿÿÿÔþ¢ö‚ a€óîóÝ'ÉræóÉ^ÁååïÏqôóë¸ê¦ôÿ×PãuÚ1ÕeëäÚÁ_îz  L™÷™öí÷Ýfø•/­ xÿ ï)ü½î¿ëd4ñ Û v#}""‹(Ù÷õ°èªõèråóðåÝûgñÏ[œôo!ÜÑóའӴ¾²†ø,Ê `©o/åæ§ä÷àþ¢ìåÀ éfüj £“è-úü†×þJ3g$xà77ý›÷»ô' ‰öoz ¢¹-fëÑþ›ÞéßTÜüò.ßr ìö˜e‹ý—„Ø™ïõ֞ιôÏ×/ù€Aʤ:ðýÔ›ùƒ÷Ïü‡ôádþc#ÕW"&¹Í.ýµÂù»~´$ÚY«+•óÖÞè)òzà©Aø#Q uÚ«ó¸Ã„ÎÈ"êÙmÌ ìëÛ¦ïÇÕo µ5èTQΡ×!â@É®·èÔÎ Ü›[ö“³øÚôú {ûw&Jß>2ó1jGeL*ùäsûcò=äô‡þtµ‰ë# iÔ}ê9ÜÈÛZéää êìé¢è‰ç¼ï2ës÷±ôØä%ñGÖ‰×fáÓÎúìéÍ b ˜¸òzéî#û.åð)m@r4ÿ,ö? k#‘úÕ! íú#-7N+:Ù?¡*þ?(›ù—ˆó/ñ§ØòÌu¦ÞóX•èíÓãøáè;áÖõ$ë±ý ˆúøŒìÛ`êÝè΃ûŸßãÔtùX(ÓëæèLÒu°ôJ<Ó#ý9I;õ&Ì4–n%Ëf<üö¨é"P#Ü&ÞõÁ xßì]ä¥Ûjù×èµÎrýæt߈òéÏPÔ§Õ›É%ê¨Øgø_òéêÊú‹×uæ™×UÓHìM×&!ðp 鋎 &ÿÏ_Eý{ÿMk"_y"ž ô¦c·þÚþýÊÖí'ŸÓç1)òIÈÐñæ³äÁÏÕ(õx& èˆõT #ä2òÞÛRÝÁÜóЩéøÖßýîëaéPù@ðHîýÿëR^÷$e&M(5"ý& Í { »¨à gëU,B"-ÿ"èê÷¡ üïÄ0u*ˆ4_%Qà$ûSÝâÞgäŸÜ2í[ãEó"ïÿôjøƒð³û/â¬ëë܃×íéœÓ’ Ãé–(î÷.G*8)‰îRÍéAâ›Ãì¢&Ù#n,OSÿº+úýsÏÁ+ §1=“ö êãònì æ1ýùïÐþ¥Bë‚©Ôxé_КÐ%ÚvÍ–ã$ØDîçä8ü¿ó_bñ½þ—ÜHâ¦äÍÑbëLL.,­Løää“îsò,çlãûÇ ÖÕ"P(&ª)€ ÂU E #x{ BÚøÆ÷ø² ƒããò¯×òߋٙÙWåŽá î>îÖêÚð*å…ç5áRÜ$ïÅÙHWñ¶ f1ÀÞ!øhÔÖmùäÚƒ#!0({!Ø5?ÿm½ùÇù,û™2D553:™"’7ú(±  PøI Ðç}ò˜öré?ŽïxHû6OÓæôÓÅñé8ÓöÆæô ðÎå înÛœàÙàÙ0òੳóÈ0ô\{Þºë§ÚÔï÷Ù¶$Ñ&–‡&Íï × @Q ˬ'=ü1`/ã5´æ¾ÕÍËq¼ù¸Ø‚¤fi÷õœ VÝ?ìkÝÔGõŠÜËÿ?÷Eöœÿéä{ô¥Ý%â¤Þ‡ÛÖßÝwê¸å2þµö…™ ?Y ” cÏ q0¼Ã:¯7É"â; ÿÍHçuúÊéì #ô“õb!(‡y'’ò¶KÜ·ê@ëiÙx òl#TÓÙ$2è4Yá)âå?ßFç ãð¦éþq÷ä¶G ®ùé>ü}àMÚ/RãÑ+Ñ:(I.X k-ötvP»-"€!œé$ô‚|âô0æõä1ö{ê“ý8)]ügJèYõ¿ãRàîÞ=ÿëå «3ÔNéºùq̧ڿT¾pÓ»¹_©ÜÕ.ø¾)Ž/4‚ù[úVHô½Ê«0¿#QÑ/±ò½ à í”õ<äž«} %Wþ§ þ$îhmú@ÿÓë·ñ¹ñßê0û‰“ çå­þlÌÎÙÏҼǤï­Örù%Qû7Ÿâ3ðÙë;ÛjxíB2TÇ-o6Ûú+ýéÊ §ÝxãøCæ~.f¢8À9)(Q< Œ*s;÷„¶ñÖõiýù%È |Ì## °šñÁ5ãnê²ê,ä öŽðýò^øªá‰ï&Ñ_Ø’ßuÊ~vàÌ0ʼn ¦ë…øbìÕåT•ëo!þÆ-@#AŽ,ü:nîù³õ:ï§jø,t H0# 9ð3÷.[8 $¼åùç{ßOýí •IÝ;üð¾¸ÍÁË.¸žÿ^Ôc: g÷ëÙéæ ätÕÊÿöæ‚ú¤ùÊÝÀíÖæ4à÷Åö Z XÿJ³ö÷„ ¤üž *++‰Y,°Ó«¾#5 ï=š.l%^@Ãë¶ØØfæ ôrß(Tÿ/S·ýóÜ'÷ÈMÔ ÕLÇ"ô¼ØC µöÄ  9øü½Ûî~õ³æ&ç?ìä$ý^ò¤¿ «¾€ÛöÆþïüøó¨&Í¥LF;Ö7‚S>þs.téŠó/ƒôU+D%fþ)ŽÒcÿrÊ3ÕßñAÞsÑú¥ )þ \kZ) ›0ðéýóÚ½ÜzØPÏßñbÕ”2úµ$K#lÓÂî;ÑdÇ@üóÑ8$Ò1Å'l 1Yójæ4é*ý½ç7ð»&\¼Ì  O ™ýÏáì=óêŸæÇþoñ¥>üþèÂñþãëÜvøAãÅ zþÚ³¥è~äÅ‹Ö~ÜÁß´æñ dƒþÚ½Þò“æuàÈï·!_ ‡/(ð/Ö3`#4“ ¢!•÷f]ïÅòÉùÏõ´’•þÿ~ñèfå#ì9ñŠå‡ •ù"ÐSMì£Ìd݉Î0ijí¡Ô.ù$÷˜á”÷’Ð~Ýìá¤Ð ¡çí'‹þ%Ã$h$•«Tëx NÍú‰]"¿ (rB#¥ùv õñÍñÞ9÷=s%d)z>½÷úÅ×#ëœÛÔOò¶à \ø…©ßø1Så˜ñéÕÓÔ†ÂYÜgÈ<éÚ”ðiëNò9ïQ÷\íÏëñg ýúiÜ ­Os& æ“þX÷–û m,ï)©¿0åj ËàÜâÆ<ñÂ8#0<éH(}ꛎÜ+ç/ÙþÖ–ÞhÔþè ß„ùXñ×û7ÿÀïMöÜ%ãÒͲéÎÎ0ò8+ã› ,åãqá|Û•øðäQv‡€Ó÷‰ ‘ýDüc$e3±&ê"v8Þ³"8ï¿·ðÉõÿ?÷ ë}  ¸Þ ©ú6ð~ø¢âÝæâ ÙîgÝ0@õã—ò³:ÇìÚ“Î˼Èý^Ôì$Š pn#cêë×ûEæ6åêõêäòYú# ¼õ ã* «ð ´ D ¶} – ´ \ AQü½ î€ùVåäæïê5àlüÌëŠ EÿŽøÏõµüoëçc>â(è¤+è(Æ=" Ø@÷§ÍÂÐê6ÓaÚ÷,!Oy.$ùÞ]ö[öK­÷z0¡ ¥ 6¼È"çR Æ_Ø=èhÊULý@,kõø ñÞ>è,ëà×óþèw \ûCx ñã–ü†Ôh߬åñ×ñXív ã èg÷® ¥èiôò–ê(­ûÎ'?\.A/a&<2ÃL'\îsí’þ&ôëb…W5‘) <&àM+ÂÔ>ÒÄÆì÷ßßb Þ ƒ8ðý¸Ù5ä;äëË(ûäÝÁ÷ðAêaí^ç›æAðjç5÷ðäúœò¶JúQ2 °$‡!¨q à=ËAb$‰IþB-{Bqü#–ï>ið’òP ³úž%”>+x/Äj.%û:\ã ëÂòÀÞŽ ¯ö«Ê ÍòoþdæØë…ÜËÛLÖÑ/ãIÏŠ»èØÞüúÛ 7ÜAêQåøÒŠ`ï %‘ïÿ&N]û©÷õ*Ï®8@1j57=óLìÚö½ø½ô¹ ÿØ. €=! ï´úü -è‰ôÞá£àeïðßÝ®ó ÃîàûËÊˆá ±\½¢Ã± ðÒi¨«>qúŒPèœîÏé4Þ ìô&Š’?*‡à1~å9è]T÷9J õ™ i BD‡æl¢÷öøvÀõ ¢Iƒ ß8ù)Ϩ×7ÜèÒòÿ;åñ$è“#¢!ðÿáuéäÐÐÀ à†0Þ° º–õ<ïÜòþ…ò;Úç }b#÷'­ù%º ´ÔÿÎì"@ s%‘÷÷ë¤ó8ö ê¶ Ð÷}K У Oà­õÂÔÑÿ¦ÕÉ'§ Ð$–"t1JòÐöKù•íÕý:öýý°÷ ùûwÜ`*ö¨Þ ð3ååÞu ÷ê)P!¾+þ4Dx,çéðQßä¾óiå¢þ÷÷7 ±Ý{ûÅÉ9ÞÎÎÓË8ééÔèzîãÿÑgëöëçÐâáôäÃþ[ï“úóø,ð ý$õ«þ]“ðŽÿ·âkë>ûèw(ãÞ8É3Ñ+Ž=8%Ãí,ý÷øñº$R £7G: i:/â–ÀÜ­Þö¿ä‹ÿãúˆô$ü˜÷Ìöùýnÿ¼öÞÿ¢áÂéÑÝJÎÎõÔL ô€ ‰SÚôáòÜøÕé<9ñ¼›}ºŸš%üÊ/Jþ­!â ûEd+äKvLF+!F¡þÐôú[ýdãI nÂÿþáúÈ nö3Ñîõkó6êl õó6üÈ o 6é˜rÏqØËËÿÁ›ç,Éž=ï/ÿË}Ýú"ÊÄÓ\á'ÈÜøä/ûÖï ýmÙ Qâð >Úë¯å $ ƒønñMàú ø{ Ç» õàõÝ cãö­ÛUß‘æÃ×ÀýLçˆüþ§ÿþ iÍ]ë(µ4·ìÍ#­YùªÒ|Nó7™íWôö:ôÎþiõ•6ú *ÿõ š †õ¨ß8 .ý\ ‹  89;‰òÊò¹Nú±ôÒûÐ×¼'Ô ôÐ÷–ïPé÷¬çÅØôÔNTøÛü1ì‡ïiæ‡à»ôÞe žð´ ü– ¹ùvUï|óøöoð|+s²·½,m ö&ü; Žû¯.Wâ6k8žB4›å ¨àyäMývêaqûÎÿMê£ô·ÙäçÕÖíßqÑKùyÜ]ÊötZÞþãLàXñÞØ/Ù¿ê°Øi ¤ïwÐ]w#Îä ûï¿ÿðüRËC5å+Þ5ÎFuR@âØ æè–å”ÿ ôBSXòˆÿ(Ýuï<ØÌàðÒ³Ú-ÏŽÍÝÍGÅBÔöÆbâ×5îÊè¥üGò~ÿeõæýï‹eýP/¡=z)ò€œàç¶ú{åkÏÃ%À&ÍY+fÆ$@¦%'#+%r%¤%m(Æ(þ&0ñ®-‡õ«PÞóHÛ5ÜnäݬïÛé6öÚ÷jôDûÔêùïÆà¿ÜÂä{Ñ´úÜq6üI¥ñqOã7á û£Ù‚êûØ<S¼¢) VŸ‰ b&š5-R(ê)K0Ä»,Ó "§$Œ W š/—&cd%C1ƒüg$6Õ]ôÏБé Ótÿ›òÃõf“âšïÁÓ‚ÖyڭŰùÓÒçø)@—ò  Û¤ëÅÖ¨Øß0ÖœîàÞ›üñÙ“ sþ ïƒ : qe Õ8(*ñ¢1|û±¶ÜžúÌã#áˆõ±U˜û€šÛö[Û‹Ö¸îJÛ´þ·îjeý`ùjÿ[ëûñôÚ;à׈Ñ7æÀÓ«‡è(´‡Ý ¡]ùÂ÷¸îåÎøÝ0ž#'›9ã+(<ôQöÓÁT#Ct*,­q#®óð Hì>õ>ô¦ïŸø: Þãþ¨ îJø+éæ8õ±áí3ðý¨ âxò>ù<ã<ã7òØÚœ-òf1¿´’/ë ïäBÝë Må.4™-ú:£1$:kø5˜ùÌüÙû|ýª‹ïà ¹õÔàLõ†ÖLÝéã¬Ó\öâÅ“÷Û ;å$SÐÀàÏÙÏó÷ ܨ &ù«%ô&ýzè érõ½Þ]öÜ8C%b2£>3òÄôî÷óÜûh1¿*6Þ>Z;`õÅeä:ö³áOãCåEáÙôcçœÞ SFçí ¾ûÚGʨµËûÐi‚<ëàåЮçVÓÃÌÎï8ÒE ^î¤  Y›sk‹ z¶* '¸n/W%ú(m.ƒR)ÝþßDó<þY÷§Æ>4´+á)ø;Bû%®åçúxܳÙlêÔþ>ꔼìa½Ñ;êËbÍ]ÖñÅCö‘Õ® túIü½æLîŽç.ØÈÿÁݳ´öµj \¨1egÅÎù§Âøb% HBo1•':EHð‹Óè*î™ðÒí î!)< `(sò­¤áañÀâÙÞËóæÐYý(Þ ÜäÿPÆ ÝÆmÀ{éûÈ î} Ë )ÂõÏIæ¦ï÷ç~Þ%ûÍâ`þL „Cä$½æè/æê™Ððý* x+}5gl0Íôq|åFï~üè9&}_ôÑZßîó³Þeâ÷æâVó¼é^Ñô‡ü ­í}îåžÌUÚgÞ›»uBà2Þa ç•Ñ àbá Îé Öäã+L-æ0a+ ò )êuêHþÆìÔ~{#?ôúöæÉö"ðRñAÿÎû%OžÖú¡rÒò= á×jÞ¢ÔY2Î@7/F+äìýà5ÙpôýÙÍèô('Ÿ$'uó„ fç£é‚ù¿æÔý±!^·¨%Ö 0 ô gçY÷†ìäîhûIõøí8f¢ë !Ú¥ô[áÕßr>èÿ&Ö ¹+Ö-r¼$ æ£ùíÝÙܶó4ÛOû÷0ê‘þµDÛsô=äâÙó §íœ$Š &P!»Æè¸þ Lï6÷ ïäê¥ Qõ8&yÂ0^ÜÕxÍîß4ßùÔHÿƒéx … •hâÚ…ÇýÜçȪÀéàAƧý„äG u÷õt2שæ‹ÛØÍGö9Ú€ ¨ôÀP\»!œþéý éÿ_! Í5;½6f5M0•ó×-æÙìsÿ êð š(§(x2·ÞÖ ìËà×·éÅÏï ÷}(1(þj£æ§÷rÛçàRÛúÔÑâ.ÖÝòà[íõ¶&õå8èíÉÿ¾â3#müÊ0á'$i#C ™üø¤0üÖùäâ¹$K¾&’4÷ Ò/¶ñˆmê~õK·öìIf/l5ÎN^á) á#úoãYãÅ÷/îµìüI × d˜päe©ÍYÕ,àYÄàÝÃ[ ïžðndÜêç é×PGñž&¬‹ Ê&ÊßåêÎ3ÛãÔéüìâ RÙ^_øç èì\ýôñdõÓôûÛ’ €„§׿ÀýÙÓÞ•ÞœÒqwá_%‰ ¢#¶'“MîdûÕñ¹ã îø1±¬3¥)?Ë lÉèæÛËòº7ï Ï› Èô^· Ú Tü? §òŒ‹íó öfëÈ ö÷<(ۄ㳂ÖVèDêêàHÍ÷W(hÇð.Gù½+æäð.þÝåŒ ÿç-ȯ°"÷Ô œé}ì!ûUçw>w8ó&á1š8y¹+ ùê‘è”ñ€òÄåøò û eýÂ,#ïZçbömô’ñ˜ eZÞc%#ØRǼÒÐ8Øe¾¬NäÀ LùþèþóYëèæ»ý¸ê…Xýl  Òƒó2éÛçwÚ_Õ*öpÜûý 0#}!81r ëtÿòÁíA=ü®(/uö+ìôËÒNñ7Í]ÏòÓÓ›-ÌA#ß®*è‘ýÉâälêœÞ€ù¬äa•ñIÿªùóKöVæsêáækÝÑù_ß8èø×"¢íê”÷x ã^ë<úºÞº)®J<Ì0ñb9“éEHé´íÆ ™ù.P¯6ð9ça=Õý2 äèÆúŒìé9ð â (Ò"kt&¬\æñ ý»ôIî¦ï×Ëþ Ú `û ëÞìó½ÐûÒ åvÉÐ|éæ0E#ø ¶/ÔàHÿ}á6Þ¥ë8"^ ¨%]ûžâÿÝŸé4ïãê |ÿ¨·'à(p ¡$½óô vç‡í-ô*åð Iõ›º; e!âÖÚÌÝÚdÜpË{µßb±;æó… ÓͶâ#Ëh¿dèwÄÛhåSž,T ŠæÛûâÛÆãÃè0àCü÷ðâ>fûjyôH÷ï¹ûµôœôcöp0H&† 2QëºmÙ?ßÔßÚ?0 ú-k1ë¹&Þßú„ÞÍÙ õ<Ú" ð®%ʦ#!F-åüL ]ð÷ïÎðdänýQé •» …‰÷Öâùû/ææGtïΧ°ç%ðt%9âyØÚfóTÖhùñø*üM¿áªø6ãGávÿJåx(ýs1Æ-ùÛ*ŒõT KëUéóÈãÐÿYìØíþ, õT*ÔöÏ?ÍCî¯áÎo´ñÒ:Ç-AÕÙ¡™¼êÓ Çw·öú>Ìí,·6ŸïAtÑðíxÑÅчðò×k –õ'Å pù ;ÜÑëÞ;Óìõ–Ùª(ôû'”$Ð%¨ñçÁå‹çùùJà#±+Ñ,+=Ù þ@Û Ö« Vá5ƒ»;Ò9µ#W>dþŒ!tì°ù#òµé¹ü8ðªÿGýX!üð Çûµ úíïûå;å:üÿß åø ª!Äë%êpúßmØÖíÑázë! ™Ìåç´ ê{æ±4ï!| L0')T#V2 ñè÷­õàé ›öÉT)w J&ò[ ªãÇí$ì¯ã‘ vôîÚË¿'Ðó€%Ôeå¦ÜçÈNüAÚœ#)z){0NôË&ŽÀSà÷ÁÙ¸ìíÉ õêôäÎã ñ"àÒâxêVà£ý_ì‹€:$°„ ò'¤ý¢yØ£òtàˆ××4é­ ´ š&*(4ùN Àø^ñWø@º ›EÚGÕ B@¿éÄ ÆÑÒ~〼dŠì¦( %þñÔÖVòÜeÕ³ôüáövôÊœûc @g}ïÕ ÑÃã˜×>Î}ý$Þº Ȳ#¥ü´íÿÜýáíŽ'Ã!8þ/*9GæÕ$ØÑÙ&ôÓÖó#÷'$ ·$¸'›Ñ&ý’éióÉçØÞz ½å=0Ê7²,ÊáCÍ`×àáÌgûÃá^æùÙ Ú ,ƇýŽ ×húÍgͬò­Òœ'®ß X/ÅWüxÇݾ$ýºÓ|)© ß#€-ecòYÌèàñpâªæDæIâ›aíQeãj ÞùÙŠæ5ïªü3áãæû¿;… 1w?¯ü<'—Ù=îräuÓ9|ë)% n.:þÛ³êŠþëêœ1îW ]£(•$ZÑ(.ë¦pÊyàÆÛÑËZ\ê ü nô_îø"í„ñÕòØìúíîì$y iU"÷fDÑUâóÖÅü=©x@ÇBC*4ÞéE lâÂåûFèº d! ‚&©ûtõB èç$ð÷ö¦éÈÖüƒ(ÛP*lùÙØÉø²ÓS×mïhØE Fø<á lñZúÙüëJØÐUü…Ö®—üÆ)Çõs ÐÉåôÑEÌuè;×FõqA¬üqŠçóü)è¦è>ßðæ/ J}ÔlãV £ûÞÁÞ-ø{è!ß%âõè?U+Ã(OJ‹è¡{Ù,ÝqôgØu`òL#ɹi ø ªäÞì‘å¨Üøvä ‡ûóhhýu eßkõŒÙÝÝíiÞÿñY Û¬ nþr Åï¹ÿ°íð•ûSîp!t:|1mÃ>[Þ…¶ÖÇÙ'ÿ‚Ýf0X ²7ñ7W!O7µø‰ è]êW¡âK <ó%™þ AFùÝØí’õqàÛè‘Ý…ØxîߨS !ó[\=ñJ €Ô¶åºØ‰ÊSùÊ×M*ýq ¥í§ ‰ÌÍé}ÐÝÎ øÿÜW RdÒ(åâý£ä ã5\çJ&W¬&û+Û…ªðQÿ³ï‹ótõ.ñdýëô3wþ—&*ñ!w.[óùÅÆnßGÜÚ»³:ãŒ#ÂÑ € ï忺Ñ7áÇÛgÑãû6áÉs®í³»Þží¼áëá#ôNè| ýwY³ Ðåÿ ú™ƒú'ªåY"!C;áaø.ÜÎןÿ¤Üž/O¥85Ž2…ð ŒçqéÄüŠáü!º¡(Ý&º f(éÝqdÖ]Þò–ãÿNúxx l”ø%#ÔúÀñ-ñYùí´ FÁÑñ€ øà ìkèÛÎêåõ§‹úq çþì‚íBÜÄÍé•8«þõÔtÕéð¿×ŸÖTðëÞ[~÷ÜÞ97`Sðùþôqêx³ùŠ4©'i$/<òöÁ AáDðÔõ»ß_ öŽR ËŽçí«Ößuç3Ôñè Ë`ûÚÀóöïèñ ì6ìHéçò€ç¡Ãù¯- ŸìÖÝÑî¢ëdàøŠòï/"<ÿ8‚ ·?qøŸøÔî‰x÷l.Ë0713 íK¬áËéKDãÜ3—&+%-B ±ýäýƒÿüþ‡ã3íâ‘Ùkøé{Oè$ }ÉGä^Î-ÈíŽÒ¥÷€å€ù@åó[ìzÜyîM%žº'ŸóõåÓò¦ôÅæÆ÷0j.-P/ 'û’ ú/÷‚Oò ¬öm-C6ÕáyôZ ùØæçóÑTÉçŽÍIüæï ü‘ÃæúÆ-âÌÂîöÔ *ÿ*”Þ ùÕâ£ßúäê¬Ãþÿ[Fø¬²üÕ.ú ÀúaC ?ü'> ;) /7i(Þ÷ °ñë³#ëš V ‹)©Ñ(lëývâàæˆñTß—ôX!p(’èÑþ-äóãzûfäæõùúÄûèÿ°KËš ÆöRŸòÇð}™ò5t ŸÍ¡%‰òMÿ öoìƒ`ö“0éø%7ôÉŠÚ…æeïåÕ¡Æó`Ÿ¬„€ñöäädïõëÁáxþÂéxJýØ<%Ö‚ó¦û*í§è ótâ®îî[ž ‹wÉõîý·ó1ì Ððp%« & ç ´Æ¬Ömà£Åþéé éò”Õ¦ç¢ã Üõ2çü‚ó¸ ‡Eü«ÃîúêKëƒíçJwì Ò 9*,2›¿6àó<Þ÷¤ì‘$üû7>'+4…>°³/ øª $÷“õXÌ÷m 8«!¬û¶ýñ ó!÷+óð¬ðUìäìíéïí/îÜòüéÝïdå+ç×èÉÞÂøúçäÿÖzóÈ.Ò>è¥âÎåHì·0ù ¦ß0ºñ ¬ù´ð-\ûò:9= <ë(+:[~$fþœ û>úqûEøCý‡ù¬¼ÿï + ç/%ð+ºß«äzç{Ôi;惶ðéîäˣ༑°ÈÄ·°êˆÐÙù©õÉçIüÓåXá¯Ñ dçí ®»äiûòtøUòØ wúkƒX#s#>!Ñ)7F*Ñ#œ¡_úŸû÷Ì.µu2“ó•NÚ›í­ènÙØìWúþDç-ýÜóågàÎÙrëÔß ò=ídñÞô òö‰÷U÷eÿÉú[çÿgnËr ­(ûï”ú¡öŠ Nü3 ¨"$['ð&¾íÍ qçç˜ “ê)3À#M(0þ:¶ßŽöÅØæÚsåëÖáú=èïþïýù?‰ø&ü šømúõóAðéõaæê$òÓ+‚ q$ÞVþ:æRÛÈ.î[r1!SòÕéâðZó_äÑïÍÑ  J»êºÙ˜ã"åpÚSû9é.£ýXb‰òqqç7ò”êùåÅñâàûç¬Üô£ 'þ&Kµ!zç÷¿ïðßÍŸ÷9°,LN:“÷Тì«÷ž¿ïz³  L%'ø³Íø%øº Ÿù ;ýý,îeùê«ðqåÔîˆÞHæ:Ý1ÛñèÜÑ JúDýñ=/åwë;ìÜS _ç) ^)n-+~)Á䦨ïê·þû2 $Ã9[@|" ?ß ^"ä í oÜ !' „]B o ;í¨kÐv߃éÎ÷ EùLZxÝ’ÿQÀÏ6ݽžúØ+ó¹÷-Hטï ÎºÑ˜çRΧï¯"ò J —"5øÊ öõðùO¶öÚü–+mß4i+”/:5ï). à8îxµúš’X*  /æÒqÕïÜêá«Òñü€äp Hü` iã…÷çÍí×jâüÃ\ÿåÁüc&Ü7ñœÍb×ôá Ó þ6íŒsŠ Õ ¨2$ô%£î$þ³ºaûJ&¹ À2T-ø•2cý2îù!ó“ ¼+ó#c! 3õ"wëiþÇæÖâYñTá,üÙî‚ûùúˆî&ú†æœï6ë¡ëáïÚíê¬î>êégü›ñN $õ Øè ÷{ðÓàÇ÷½#8 ^¢$yøMFóløD Úø`#oV-z)•Ý,ÝÕvú_ÜõÎá{Þ'.irg§Ø)"ÆðøæpåÉï»ßØò2Þ}ýG Üè?ùàßÿö ÛŒ:úh/#æƒ.$ˆ¹ûù‡4÷|š!ï ¸ÙDî åLÝ ð¶ òŸ zÃ÷b5ö ö;ú¥÷ïhó§äÂâäí.ßÙÿñ,þ”ûá‚üUÖpàbãÔÙ è9+ìH+‘1Z''zç/ýø¤á5#Éÿˆ,!)="-ns ¹ * \ X  B O ¼æ %| ýøJcòUù‹ïÄï7ø6î±ýLõ,™ÞõñÑÇÜÀÚ›Ð7ò-ÞýîõüöÕÿ‡Ùíò`ÌøÎ°ògË !{ûÓ,ø&S &èõ¿çú5î‹B# Ç%'wH&" ɇ ìÏì)ˆ´*¶(Ï);ð‘Ýòæë³Ú-üLî‰ù†)ãGû ÐSá×Ҥ͟é‚Ò¬jë7ü½ÿ*Û•îÍÑ„Òýä:Óâü»ég€üŽüÐþQeÒK °scÿ3ÍÄþ©1ù >1n0X:† ¹&=öGðøöî!òúq4D'q:úö éåöÅó=ç{ÿ,õ—÷!þì„÷ áèí]ßbâEæ÷à„ëÈæÔê6ëÄå¶ëwéë§ñ@õtî"úCë×ïFñ!êÙ&õ§ B ÈýDI÷€ñþžøÔê¥7À K9¼<Áí4á¯à|ÛÜ¿épé ôúÖ·ùW[žêÿåÿÊýÏøš×÷º Ôþó† $> áðÛú&àªä|íßÚ® =ö÷! úFèQ÷ ç¼!xU(ßÏÃ÷ðTðCýFîM þ4L ùñ¤€ñÜø;ýnøþšýLßüG Oþ@ ùCc dóÛýlíqëüÎèƒHý1!U|%(aï|êÚÜâ+î5Ñ)äìÊ9MÿÔùùYþÜ ‰þôë ®Üg Uü– uñËý{ô'õòõfø§ê‰óeáç.ä#å<õ“ôBó§á/ù‹ç†á¯.é»Â ú~êÖ§ôäÇÆÒúβÃ^òÒX*KÏø¸zðù;ó‘î$ª!zßô2²àM× Oñã ~G0%@ u"@÷ú åðÍî_RïH TÝôÖ ~Õ³òÐ)Ò?ëtÑ´Hð[Brð±­ÓUðuÒ¬ÓŒï‹Öd ñ$ Iß ²?Þ ¼]1É ¥V¿8å!þ9l>A%î9 Ïüý» Oô¸# F3+ôÓ3Ðéé›é`Û«féÅ=ˆþ)PÝHûÇÏeß×ÒŽÔ0ã Öpè7åöÙßãÓKØËß@ØÝüëé_ýˆÚêî§ùüàAýÆßoñÜÛêVý#áÖ+€ Ÿ;`9X$B¨$”ø~ül÷nÇŒ¯ ¿°ô %ëiõ™îVïcõ6ñÊøõ[ùùnøEþ9úÒÿöýûÐå}ëã×ÕµÞâÎÜú>åÖ Üð$ß^é0ñ-Üñÿ÷ZvPîÕfð^ð ¸ôe!fÞÏ"ŠH£õ6‡ .ÿX¥yé‘0§ %(Ê8›*8ì‘™ß(àäûïÝûÙˆ&µ ñññ›vê;àôÍá[%‡Hô¾”ß ë$ïfß 9ðýG¡ /®€Œ£ž fË¥èºè!ðüî¬æûÊùÚô&GÙ1ò"ÙBÕ¡¼å#hŒ(—éŒ =Ü­Þ ï›Ø ¯î€_¾)ùàÁýøÝ›ß³ø¿á^ŸþX—7Äûê,(½8½ ˆ¹ûM NÅè üþû´véìóÞåøá{öUè#Q³ñ Øló£ÓãÙ¥ç!Ø2áð'eëÅRÖyðŠ×n×óˆÚýô¾ú2‚é÷ðgì[û¢ $ é3 #™A¸)§E'GÝ"Ë=E"ÐiúAóù’ l|Ñ(yÏ#ãé›=ó]çC·ÿ>E j W LùŸ „÷B÷(üÎóŸùuñ>ñFîÙì€ëÑéªñ$éfôbó3ó­ùKó$ý5âòÉæ†ÝJcî¸/¼€R"ãÿ²üƒò’_–ä!tU"€ ö2û3í¹ñ”ôHó\Ñÿî-ê;+w,t÷ÍÅ2ßìÓø¿AèÍ$a'JíÇØ?Þ¢ÔÒÓÊì¶àO rð– Ñ=ÚÍõ6²ÄÒºœ«6ú~Î=) ¬1D*w¹$@çÜ ×ëÝ[êiâoøÿÏ>[ô8ãï]ó£ò—òüIú^Çþ$æç=-.3/09=ëýø†úäò>4 ¢ôÖ ¬Ñ¥é­ÑKÇé˜Øsâ+ò˜Ý•ã³óÝ"‹÷Æ)!´ñCËÉcááÒÌêû2æ ÊBùªì’ñ®ûÊòIO ŽGu+b`ÿºÿõtÌË€yÿŠû:‰øÙþ†Ýïðô$éoâ¾ÿíèö”Yõü}Tç{ô®Þ^ãâÂâîñ'ò»ñÙÝá·ùgÎàyÇ’ÇmÒįí¹Õ"Ø÷? ½ ñ' „øwýµÿ¶ò@mÃ%N!ɧ*Oôpòõò:Èüš(ðñ'r‹}f 5ÊÖë"óÊþjÈè¢/•ã§3¸Öøè±ïÃVІ²rªä_ ôõùn‹èbû¤æÂèíUãb÷[ãÞ´óÚ$Åw ­ƒµéÿá ¬Û€³#Oîš­û) UMµ.ÊIWI)Ï?ßâƒúéþ­û.ôŸkø£­v§àÞåþ$Å}Í Ú°»câ7%™¾ï¦Î×à_ÖÌïòUØÖfï…Íþƒø!oöÏ`ÉåæÙ,ÁÉ©åÍ 'Q Ä$mì“òÞî4÷‰ KÉ0,÷N…ÜÇô\ÎW×rá{ÐÑ8îf›ù²àö/àÊÜìú‡àuúT"hUD#‘ï8 ‰ÓuãÐcÊ{âÙмþEíftX‹ ó½zà\ìëBØò ð *¶-íÒc ÀhËøå·Í&ÿ ñ»üZáw¼ W“sÿOùоûA;'K“9Aå´ŠÙÚ" 2âÑ8 x/76sã& ,Yþ˜€øÞþÔ*(81„`9>ÛhÙWÔá?"ÌÙÙ"%õ1âêö0Û{ãMÛ„Ö9à‚Ò¼òOÞ Ãþj„ ”ìHü#æ á…Lâî/ó%[b%\õF níéî‡ìëÁ*$ ð$À+ÒõáãåÝðÙ÷Gì¦âýP ÑÏ! ÁB'^"Eà°¸ÃjصÏÃ)ëFÚ$õ¢÷.Ü÷‹Î™ÜÞÔ´òâáD<õ®=€ P¿è§=ÒÞ„íCв \ý!ÿ;åûÔÅà¬÷ØÞ%#A¬'U+ò j`Ä' k1p ^ ¨Nñ¾¹÷zØê˜õ<ô‹îêú¥*šÓ(Æ-À%’ð óÉ꣗õ<'×#“› Åöe‰å¸ò=èÃãpöêåÔ ïô«Ñ\ÑýïŒ.ý¤ôº· ^¿µ ke ¡ÿI dì5ú ðæ ~õ{.‹„/ÍÝKüÛñ"Ö€+¹þ¢Cw1¥Ÿ4™ä._â4çÚçdèúå å éêÜ’,욯 (ä3ÓåóÏ9ÀÿFÓt#Råuó8$î÷îuó»ãwéÓÞÞKð6â<zþ×êZ5ÉWáöÌ-ÉéþvÛAè GÁëêpéQò3¹óý*ï§§¹mì%7ßzêŽáÐÞöð–ßZ®ï»t…! ½Ù÷‰7ê;ê– †ç,PÚp,¢ïˆqÈßå4Î Ïá/؃íUæÔûõïœ ' Z 9-ÿ2*ûd *Ûæ ”åìébúðrË  ¿bצ©T”y®7 éþj*´ÓIk.:oFÌó‘$ΪáìÒÖ ¾ów3E;_÷AÆäÀôâ¥àÚðÞwýgïÒûÒ  ‘÷Ø´ìÕúOæ¶ëäï5æ;ÿôážkõU åŠðŒëËà‹ôè)þñ›O -RøÄöòJõ™þò*Æÿ(àÈïÞÑóÁ•ØGÚÄ é–சuó¼MõÎúªòýø‚ã íÜçAâ¶bó®Ðæ­ ²bØK´L¬Úó À“%?Ö}#: ùÈßú÷bú•H#ÑHòòI÷ì]ô:ÿãî('á*‚ €u*¦ìrýmŠùûÞ—®#~§!µ&žc#ô1äXñ@öãå#!8í)n)ó3P!Èçú5îráò Oìî,ïÞ#7)" ‡ú€£ü\÷ËÿÖõ“õùó®ø:ò©Û§ ¤ì¢ýâ¿ßbÿ¹àB…!ûôf]ášðWñcç)‰÷µ ½àê lþ» Zò.ÁëÕñ×÷(é3ùû£:Â'(¬<ûSÞtí_îeݤâðç0¦µúf<íKùà×+á;ÓZɆñ„ѵú¿ ¥öµ>Õ+ê¸ÑcÔæØ«†ò‡® èåðºÍçØÙÓ‰öä¨qþÔ ßÕ xe®b2Z)ßÝ;_161j@ü¸)ÂçÚúöêå NûÛ1Œ!#h*« )ú÷õQõÏþ•ïiûl-Ûä%4-œöØ ÏMéDÛ[ÎÅý"å#,äøôÕBålíiäÌþy÷Ýœýâ ¿A–!$k.ì-ü¢ñ1æñ‘ø$¹ *$ˆü©äíú#ýììç{üí4·&1ú.âÆ(¤ôù ÑáZéŒç¥Ùýþàæ) 8‘ÿxÿêFÛüë.ÖD×·ÝìÒ}ìbÞTÿeõ~. ùG AßôùÚ•Ùfñÿß? ûûÈbˆæ’à øgÞrÚêâƒ'‰ý#¯ì„ &Ýîè*óšã‡ júãÕ Ê£@6Îþ*JÞñõ ÔFÔeðØôþ‘*Õ÷Å<èâö:îoêXmò¶" Æ2«(/-o7¯ D*ñâ‰Ô§Õnø’Õ¿ > 'yüŠhí¯÷¾^ò¯q Ÿ+&¯úy@è]ô§ãYâòåêrûø9böñæÖð=êäà– êÖ-<j+ˆ-Öœ"rÝ$÷`߆Ù3þVåÖK(Ÿ%=¾/Mêó¡ýôøuë@pòH+ëÃ,ª+¢Ÿ%kí”ãàåÄìðá³øí·ÿÑûþÙAövàÚò=ÏºÕæÛ*ÊJùyàÛEà´þsÂöÕC×Ä6ä¤xz  cïÒ Fàòkåñâ÷Äæ% —ø±±µ$kF„ñÆý÷ÞèôW÷Þ+Hã/k/³à.Âè” 6ÔŸâ&ÛÒñµÞlù’ÿD è4öÔäã áýÒ¹aæÑd gæµèmÖíœÚÏÜ_ëÑá=üFó“ÿµî cµ= D-7¯<˜.Š3¥;ºÖ+9ê?‚ç¨äß²ðŠ=e#Á.ì:&?!Wé¥öxë=ácù&ãŒ;ñ–ú\ %›÷² ×Ú*ñÓ§ÓYèhÔ SðdlôÇ”ê;öƒïbí{úÁïÄüùH^ÔÆ ÒÎùû®üÏñ0 êÿÐ7ò&õN4[è¾£Ó±Üdè§Ô tíļ ã[|ø  TæÌñ0é©ß“÷aã.ôü# % ÒÓ÷ê¿Ñ´÷8·æÌæÎí¼›þyß N»(Þæöüêì4å8îèÍÈ#" ë&yû¢(êìüréZꈂîyŽ äß—ö„Aâ8ëˆüÜáA"„§(‰"É.#ìüɸôôè\õ)éžæGü0ë\!óÄ#)sïÊêÈòÞqÖ3ÅsOá¸9=;\@ º>N÷V!õ>ñ€²ö¹'H!n"X çXô}úèŸî×ëýä"÷7ê< Ðø½Ö 4  úÚ‡ö¡ò<óð[V0éèúéÙ_ôÆŽÒÞ×ÇŒŽä0$Ó'ŠÔ mâoôñݰ ^÷íAE*˜-Aý+% õ¾÷öë÷ öáþXô;Â0W&N"Ï1Uæ§ÎîÑkø_Ñm|dŠ!eøÛÕÕlõÓÍ(Ù¨ÜhÓOøÄåˆþ[Ôêÿ²ÕézÑ¢ÕPá¾ÕFíÃÙ„ só÷ æï†úü*õÈV7G"9’9Ûf3·ýS ïDô€ýOê7 WBÑŒ4þç·7í¬Û¿,,ðŠNN6ó.ïA`)%¿PJi~ÿÅ¡ú~ãÚü#×.ð«ÏÌWá£Í7ò}àÔííÛèIìíñ³ïÁøŸ÷<úüø´öh®¾»Dm×pòRß”Æþ!ð«9I/ Å3iú¨Sóüôb úT's¸5I% *–1Ój%ý÷Éå^òöáëáëè„âÔü”ñâM;ÿÚ ßÞòúšÊæÓ~àÒÆEXãð;þq ³æ‰úƒÜ æ'å©àW÷ŠëMþÊ+ ú¢²ô'ùãü>òÐþ:2>!Ÿ-.„ùXç`ô|>ô[ )ùþMG ‡ÿm ßàoö9Ð&ÑúëÄÍFÆ÷Q$3(Ûõï…×ÝêÏÜžÚÛ÷wã múÉÈ›Z ý öð"ùQñÛè6*ëq¥r! ÌŽ%Ö~­…>‰!½7B@ ‹3ô)ŸçÚðêÙå©ý.ìÖc‚ Qtï½¶Û%ç°çdØf ¹ë #µ# Ü"S÷µÒõëÙ¶Ò®ùÕáüNÑžo&Åìí@ L-ÿ“/I-Bë7X#’?\ïTâ÷ê˜ý?îÅ Òÿiì•üEæ¥ð+èŸëtéðæ™ñ æöû±ðÀø,ÿ|Ý‚õ Æ}׬ÎÇPõÛi «áõû-éñíµëùø³î¿Zø0h(Ø’41ùÁ6¥÷%ôðĉû6œ"¾5I><÷3ïÂÞ6ï&ï‚ãß öüþ´üøQþÏÿ¶û3úµûëÚíÊêæ#ò”î‘ð ø9ÚåïÞÊìÖ@ÙsÏ…òÊá †ø|²úÌþÂü(øúè3  Ef5à2™A&z,_ód ËóðÏ&üé2Ï!‘.Ë6X E&«ü×Ôù#Íôý Zû†b” “ê„ `ÉPÞ•ÌLÂØê/Óõðñ¢æ^ò›×eáõØqÓBò§Û_ zö‘ ‚2™öÀ_ø*÷’£ú¡` ƒ]6€ûåhìšõïñÐë‡õÇf  % )$‰õ~è±òÄè"åPò±åCÿ™ôÍ´¬õØ·ÓòÖ»†ÉË|¹hù½ÔÙt©UAâ‡ø€ê:áî ²ðÌŽ_ }þÿ(  ‚“]ýªú¡î-¿)gõ1¤ùºáùñŒœûo¡r Ôú‘vè÷øÞ‹çÊâTÜšö·ãØIúíöá _ÖéJØ—Ì‘ûäÙca!u‰¢„{þíøñ÷ñô<¼®$g½ )( J}þ5Öÿ‰úzº'"´(–òÇCàºêsø·â êýÇüdÖ_í-Ã%ËåÊÎÃÜYÎ ð&ÞV–ô… Þ‰ãDùAÔ‚ÕAæ»Ïæì¦ï™S3qõŽþ%ø ôF£û.-ùg, -þ¾ÿj ~þa&š&:*/T9¶Bµ&>É $Âóß¶ô¶ì_3÷g s úm ¼òeþ—çwëägá€ê2âÌöFî”Íý¦ > RôGIÜÁääܶÑ÷bâÞM ðF UØðäï—׎#£ú'>É*7.;¬$d3öú"ˆ # ßüzš #!þ!6Ù#¢ð=Lá7èå¾ÝðýŽé÷O™’ÐøfÍÙãì8ÏrЉÙÔË.í˜Ý•õ2ôüè±÷3Ö'ä%ÚYÑÁñQÛw+ôô éš!þÆOþó ¢w¿"ôo!Ø Ùa- UücôþŸù"—Y'ó!ï´0¢è‹°ÐÉÝÈïV׳Ný)d@Lòp ãåîÝÛJÛ{Ü—ÐQì~ÖŒ.óšoùò'ñþíîëR$>&'õ!×&E5 p 8k¤x qmé‡%@ú¼ÎôÐõüZù­1×T%ø5éû݉à”õ¬ÜºÝ çÜ îæ²ôrðvô û‡î øvâhêcÝÖ ñëÕyñÅ,Î.à/‘¶$ýë…þúåóßµì+T¤¶+" Þ—ÿ ðˆýv C‘ 6¼VLó(_èVîýï*årý”óéþYžèÿÔ`å;ӤоۣÏwäGÚåðØå=ÿÅ÷¢6ò´üMÕ˜âÔé¬Ê)¾÷UDì6H(â?*õ¤å¶ê@úyêVâš#P("¡+ß(Ä ó•§ ¶­ƒA Óáöí†ábñ8×®ÝÚÛjÚZçäµîšïºëTñ§âæâwÙÔö\ÝF+øÈáÿÔ^Ùhð]ÜÉÔžü#â@%Y @1`0î,4Yþ‰…ö÷Q×ú05"y0W8Ë"¹4à³'V þëó$ˆèâî¿ø'ë|¯ (_ï.ÒèÛ Ö­È!ëÓ÷Yéµð†ðÏäEë•ÛàßgâÀØ öSâ¼î÷ôÑ_òGÚ¹è¾Ý1Ñ7ÿvàŸ #$U &A Ÿº P •ë Û+™5?6ÂJ4_à“ ½µÒYÑ{½ážŸ #c×ð§ ¼ÚõäÒátÓR÷?àÎêù.òèùáÆïNÞ•à=ÞBÜ’áŒÞûì»ç Øùór Ur  Ï6^ N9>Žˆ7uù¹AçSøþíªì§ãøå ,!4(B o'íç’ܾåîêIÙP°ô—%Þ"‡ü “ëáý áÏänåjÞ‡èmä5òÅê£Âú|ûkCâÈôdã~ÔP “ë°1.z% 1ÜÑzå> ¥›!„$s #ÿðÔ ÐàÓðÈæÐâ–úNìF k 2ùúz Ÿç¾ó™äüÞ?òàªøïŠ I½ ¸ä ÷oÈ'Õ;¿¹HØs½‰ _ã:.¶ð ˜+þ÷Âù¦ûÔ %óä% j.ã'ô:.²é¹Ý,â þGè¤ -þŒþêò¶]÷¾ý6ë_ïôxëtAÿÍÿiªáeùëÇ9ÔÊÔrÅøÜùÿœ{øÂ Íáyì•ñ¯ÚÂ0õ‘2l.8Ñ w,sàÔý<âªßTûí06µ6j;¨#S9hQ&˜þ+õÏÿ ò™ô†uú#¢ ˜% Ýëªýèåtçëè¡õCðPñoøíÜëëÒÓÓ•ã:ÌêÑã¨èªûâ Té<ò’ïNæ?ï$R c*%¿à(1úoî;÷Á÷éî• Áúï»1z%@ó æ åœìæãy£õ&úoGͽô:ü6kô³+/’O©?K/ŒV:ó´!0îìæÕù2Å'™ýÃ,¡ÊïóÖîÖ×ðæC@÷ˆ *þ#Õ­Çî­ùAÕøÙzÚ„Éøù+Ý׈T! øÃ[Ñ+æ\ÙÈ(hÙA)u`1).'].$ð³ ÷ßãÒÀåü›@#Ü@×9«¿ú®~é3ïíìjæ´(ôù ˜ l÷úÄåþ釿]Þmú«æ •âgûÕÆvÒÓãGÂM>ðWãø¨QÙ'ï8é¿Úü õv# /7'e-ù4? 1Þ ½üôe~ð¿ñ–úó÷o + s¢ô´îôšä“êpôçž -þw…= PPæå…ÉÖ¹ÓìÃVñ¥ÚHûú ݯúkÌ–Õ{ë6Ñ®íñ'|Q$;&t!üHà¨Ä.ãd…$uŒ'DÐßôÿ¹÷Ûï³cV8Ý)’#9GðokÖÛãàÞñÔQô ã¤aûx þôY:âmìMÖóѦÔÅé޷ÊEê„ÝWð"ì·òžîÞ÷Pí7eò÷þpB± "ÃÃ%õŽ4ÿ3šþ ó –+Â-Ø -Là²hâLà]€ò@¨-°'b>$ÚÛå•ýzÛÀâÁÚr×wÞ¹ÕÑì,á ùîôŒüþ%ìwö%Ø5ÝeÓsË7ëaÑá ÷õ)&3 V#Ìç1û¡ãaßÎûkæ\vò¢k×§ó³ ¥þÕö}%· [2¸,áj5ÓþÌ 5î?Kôöfÿ°ù2 F* • 6' Xù²Æìiõ´ã’ä®ÞžØ¼ôªÝÖ}þ¥æí6 4Å×þÔø» Þà#ªmW#ãýæ”èÁõŒçëâ²÷¬æÐ0þ} Y , {² Ò w Êe q … *#zû-ýé˜ö¦ã‡â îóàVýòîÜzÿÕ¢òhùìAä[å¿+él+É-äû/ ÓJík·Í?î Ö­æþÏ&K$^Q'Ôø .øõ–ÊúV H ã í!U má (–Ü@ñÌ}Óé?ÖÎxýˆ ãoî<â}ãbë Ú*èÊšþýAß.øuÑ|Ü·êq×… eò–À ì ¿}ôìè)ñ ö$ë<?*ë-ý1u%ò0u%¨þ{Îï'ú÷'ð*›-4z/&7+Û(H¿\ÏmØæÄþ»èñ[ Ð úçöƒÚÃÛ¢è…Ñœø-àïõï¯èì¢èæñ”èæø³ð³ø,ô»÷õ’X! #ÌV‘ <À#1D6*ŸC˜CÝ(=6Ò ¤ìÁÿó'ñ¶ým(Õ–,Ã2ö,ÁõÐ ²çhêmõ8〠±ù>þTc +ð›û æ"é-ÚZÛëÔ,ÍöéÈÑ|dð·Š øõ[ ¤Û­ãÏï/×%Àø>&ðô„(æSýÁÃOÑ4`7ž8i1Ÿõt,í.ø¯ú>õ|µ>þçH ®ú÷ Üä"ð^âuÜíñ_âO öÜ×Úèÿø.Æ ÛG´ÏºŒÈµü÷¸×V» ޼5úk ”à_ì‡îËÖ„möëÒÛ ”ß1ÿ6êÇç´ Qý%\v l ¡ ÀE§èaõ†ø3–ôƒè ¡ú< ÇÞwñ*ÏeÙøß„Ñ«ëú%¬ ¤#y"‘ø–ÿÙ,ÜÜèDÏž)笀õ †øÌóZ¹ñ÷ñJÞôI"´}"N)ûŠ$o ôÖ¥¡Êÿ¹ø Y± á ô= îëÊîIû_ì ×úy¸ ý-Ø‘êWÛÃÏÁß„(÷ y¯"N ¿ômôúðYýIöþ+÷6ýkÐÊ‹~ðXÛ êµíÝâÚ Q*Y!³)o6„ œ'çæÅà1âU÷æ%S*ô/ žØ)õ¤É’Ú«ÑŸÌøíâ×9·ò¶ûÜþ­ê|òeé7â˜÷ÖåöþMñ6ú]óýõ%ð³ÿ¶ôoþ[gëNüëWê@Ëñ°+…a9¼8«#4: ~¹ç‹û§þ¨ê¯-ø4ÎAQ 4$ÛZþ áÊÜÉø3êJûÙúÛõóù¤ø-øÉþƒ3óAþjßÑäÝ>̼ù¨ÒôÐüÀ áúþ•.öôêùðéÐ Ûó+©íÜÅú4×ùû¾'ç ]I2}LbM"RC*þ6ûÿJ E™® äÊþ%û] Uö7,ínô3ôýåpøág³ È=æcýóÌÊÓãÑk ë²Ïuó‹û ‡ÖûóeËyÎCå.ɼ éÖÔ "=ß1ö ª¯ë8`ÚÙ¦ Ðpõ+ð3üÆþøâ ¾µ 4Zód ˜á ò:ÞÞ³çØÙqÆè[úXùü °Éåg­S°þÕlªú³ÞÂ.£õPóíU÷÷‚òËk÷«ú¾)¯ ¤ otY #± 6 ð I 섉 ÇMÿïòKýžó™rÿ^ åRˆ] 'ñßóð8ç°úRêóÓö.ÿãöü?ê¹ìPèWßN÷â߇Tó‹ÎÅ…Ëåiö0ðó™ø…ò§ Ç4™ûa+ ë%|ú˜ Ÿû`2ã:Å;_­7Ý®üç—âìúð'Eý8ù‡Nç²ñ°ÙârÖCÕzâÀÑýÞ‰÷û —–ú UÜî\ÚþÖºðúÛæ µõ$0me#bèú™,Žýìš -<..»3_PK8XäßéMì[óô@Aí”üuÞ}ì²Ö£áìÓrØüÍn͛ΌÂîÖDÊÌã´Ù]óŠëûòó;~ò± pó{",^09 ×K(_í† ÍÞ¥ãµãY"À$v*E*î&þO$e#“%Ò%Î%ü(ã)»%°0>…,ð  Ü;íNÛyÚ2çÎÝ)ò;íŸö›ùó™ùÉéÚìmáÝÛÕæ&ѳØà¶èh”Þê>á©×oÙÜÛuÊ¡$ѱ ðÔ(Ý-+)Œ/!K- µ•¦ „¢Ê&Ž"S¤-Ä÷™Ò“îÍÓ¿Í|ïžÙÿ§öNõ¨^ÞòídÒÖÐrßÄÅ¡ýÏØH]üS PŸîˆðÛåèR×!Ù¿áiÖIñ©áSþ'ô"÷§ Ƙ òª ¸¿‚ …!Ê&h*¬@0<õ—‚ÜŽôgèQ≖ùÞ'"7ô'ØÍëcÛ¾ÔOò­ÛªÄñ|þöø½ü›èðOÚwÝÀ×IÑáéµÔ·“í q (ÈdOøKþ0ÿ ñs×ÿl1©%‡!@9æ !Æöõÿ÷ølr “$¼#î6-°9 Žðüì›òX÷ñ_~ûâž‹ü©.ìGõÉê@ä…÷ôâ¿ït­¦ « ßÿü™ïRô èãˆõÉÞpQ÷H/< d+¹æNøç÷×ðôê¾6"‹-àøþËþTý¹_ðZ ójxÝìðnØßÚ\äÅÓoûœâºÜÿBýh OãëÿâиàßÕϸûõác ü€Ûñ¼û(çÄäùùèÝõ Cü$?.-Ï0ŽC ó-‹ô_ Ïôå÷ã­ÿ®4I%|1–@1I6=ñhÊäÎñgà;ãæwÞûæìy5< ´ëßÈ¿ÿÑÕRºý]Ù5}oä§÷ÎäßUÖ+Ìò!Ó’ñó/!ÇuYÃIš¬˜ ˆ(ÐÍ/…'–%¡.á['úM3ò6ýF£÷€"U '6¥.g$Ú9PöLåçõ]Þ+ÙxíÖ|ïâþž|è~†ÐËæÃÆùÇ©ÜWÃÔû¦ÞÀ üŠû‡“åìŽëÄ×Úâ úlç ×z¨«þgûN—…ú&-? >m7²=ÇñæÙêeïº `ò"PF*" ?'5î ß}éåâÝ|ødê“3ÿ:ÿ¿ ñßlûåÄX×!ÌËÀ©íÎÖ ˜òûn ßÃñ·—ã(ë©ê`Ýo!ç t4<Àzã62êvæXL÷,Ôú-?8K2oï† è“í­þììà J,ñÀ .ßñ­àaâ4ç³ãíõ,é ûø¨[´aäšàËÏê€ÄÀé±¹—Ÿ»á³þãÒçÛÉäÄÎn~èN1i7*ü6m _%ðà¶ë!줗ïʱ o L“ñä çœôùòÑò3cýP`€ “Öá ™ûêÐÿxãÞz:ßI5N ò2%360#üã^÷ÄÝv×ÖúØwý°'# Š%`îß|êšè¥ý´ê¯ë\!g¼Y%øVJñþæòõœìøíjÿNöq küc)éó&Ü¿ñŒäÜà)OìÛ)?‹&Ü.4ïíß/ôúßìÖøBßz«ü7¬£õkÝîüë‹ÞƒsóÊ%Hg$…!T B}þP «é†ö¥ñÏäôCý“?"Ý|'³ÙóÎÅÛ æ]سîÞ ¨ þ›ÔÛ}ÿ[Å{Ö4Ê÷¼håÉ”êþ¨æïóÿxÙsâ#Þ Ðåú=ÝÛ sù« ö‡Íÿìý'Òü®¿$0 Þ4à!A3†2l-&ëòê–ïI"ÿ $(Ë0Ò /ÉÛÞØÃ³ÐˆôTÍ#è‡#!®ãÞõSÛÛÝÇÛÕfäéÖ&ô âþÝ÷ Ëí(ÙêëèJŠæ–$ýÿ.`%<"’ÙAøtšýMùLñÚ&[#ù#-54 .ŠèR ðKïl A3ì/’F9úg%çßjóè?ånø°ðƒ0þ ÷+²>ØCáÙøÍæÓÂä0Å„ øä² æ ˜ZêAÇÚÔß´îOØðŽöb,9#‹+V×Åù\Ò9×èä¸ÙåþÝí¡ õÊÐõä Ùì£ûsòõ% ü‚È êqQõáÃø±ÔþÜä½ÓÌ[è‘$F¦ «&ÂHì…ó§õ!ä[ ðT:÷/2öLÊëÝ€ÍÁkó’ÑÎ ¼ù / jÉ«úé óðyþoì„ðÖø-ê‹Çù,ŸMþ¶1àþ™Û…æOîðå—ïú$*z! .fôZcãú뜜â§)ðü+!"Õc÷‘ëì©æé $ÉÉ6)*60a7Ć) ôÿ ³çYíóå— ØõiŸ4Ä!PV’î˜ µç]öƒ÷¬ðGnzÿ„6$7Í4{¹‚Çä×Á· áíÅî ÆöüçOðMï&çÿØí)Þþ?ÐÉ ‹ïóü~ÙtãÙ¼ÒƒüAÞ¡!“+“$…h,ký:Mê+üNø·í¦ñI(Â!+,gíotÌŒèÑ[ÌèöÚaµ|ë#ñýTÒè2ùQâÎãÛìºÝ¨ûçdXóOþÚùGñ£õÞãèÖç ÛÎ8ãs›ýà i^]±ñÀ+åAç[þààB2#<;I ü5Ûㆸé„ìX0ü‰/P!~1¶:Ÿ¨7*úg-ç"÷Çî;çF^ób!B **%Mâ'ûúà~ñ’ø)÷–î¡ñh ' Ù FöÌÝFïÎÍ%жëÆÅÒ!GôL/+tÌ+±ØNû8æ1ÙMòòïD° QöÛà†ùËÞ^èµñì‘ :\Û¹+²i!ÿðuéãíiöxæ;Kùú~TH…Ü ÿLËéÔ»áÈÊ2/å8 Œëöë‰ôÍŠÜÏÑEÁÇê‘Ê«ÕçM Çtü äï÷"ÚSá”ì®Þ²þ”õ¸3Ôù=êò€ ñžúNõ@ôÉ÷ó à0/)ÑD3Pâ_–Û²×Ä•ݲ;§Á*9•û—«ãFõià‘Ü&ùCÛ“eõ{%¦K"Ž!©6¦úýuí¶ìíòá’í¤ }¬ó5Zâ÷Pë0çacô<ææ*]"ØÞüý²ÓÖûÚÓW¶¤ô ‹ú,á½÷Òå¸ÞP’ê‘*š´0…/v¢)µñDöêüæãôâqÑïÛD!Y­¢* ï¸AÏIèüç!ÏÖ&Fú‚9Ð7ù=$Óœ u³eÆÐi²ëªÚ™&ÕóJ0꺫ÏçèÖÒhóˆÛ-óøÍºÈò{!Ú³æ–ÞÑü-Ü:Äú¹&*Æ h$ s;îXæƒâ±4ãÀ&dÒ-V1û;/ÏôîŠäÓSâìO5Ëã8<}«9Óûž"ì§ö~ôé*ýöòâÿ¸ý’o àûÞ êšùîèÞâO˜æZ‰éG nç’æ¢ò¨à>Ö8îÒ- ÆíX`ú¢Ãç”ú\íéþ9ñ9$d .4+ "/@êßî0ónø»è ˜úµ5÷+°$1î…ªå¡ìþðöæ] (ù$!9¾3(±ìÎ ÔŽßRÛùÆÙÙÙA+8`"›1Âî忾šß0Êi¸ûò½Ò–µøÑò‡áïRàQà¥ì¹àvóîÈêÆ'f«) ÷KlÜ%ï¦ãJÛñJìû"%>#œ*dÀ%øôéÝønë!uú¤D!,¨AæG V9Íá¿d̹ÌÈíxÅ™"òõ6%1)–ôtÖÔÍébÞÑÔRú>ã6ÎúwûÓ1 í 8ÐÏâ܋̢æ"G#Yõ;ŒíQöèÕïU*ôæ:÷2"Ò55åàëÝßÚIø¬Ûå$üŠ%ŸÍ#3*Ø$ÙütÆáUñ<í5×ÑYðÌ)šA$²ÝÍÿ@ÐÔ“å°ÐhýååYüü‰ ¦QùÓÕ€ówÉÐËhÿ Ôœ"¨‡ýÃ#öÉŽðH˚ª|ØK+´ÃS,%Õµïþxèªïà^å.íHâÁõ4†+!«óß eìëëÌýúäc(ÂýN>!,÷+O?yù7$ÛÏùåHìØÎSÖ÷œ$š #*yû°EéûîDèïHò¾! •,3' -QáþÊýÚAßÇ϶'ì¶c d‡ºó$óî´÷¥ëxñ„õìé\ ×ò‹% l$Ð%yêòÓ#ÖÂEÕG8^2==š )2¢æ­ÿ­çÿåƒüëz^—" ›(õ .óNé²ï†ûëCb %‡¿'óã`Õ÷ð£ÖøÔ"óëÚ*Ãû:~?ë¨Ù å ß„Óµÿ«Üº!‹Øî„tÐÅà?ѹ˩ï¬×% Èý? ûëØã«ùÕíÔèàõOÌ Hâ.g"/÷ü¹Ý\ô-æ&ÝÛ(ùõ»Dn¨ïªþVñ¤òöºñ§ÿ½õÕ¨Ó,ذB6äÃRÇZÔ¶ß-ÁÆär!Ï3FWåÊRÐàßwàÏ üç[ AHùèÊÜ+è;å¤áùãì¸ rÿêX »zþð =û1ÿ²ú¢d"ÑÏ•#Ùù´àGò‚Þ‚Øœ`à 2Ï¿/d4È:+‚ëêëâ¬@æi$.}+#þ÷+.ØùQÛ ÞÎðç>wù3&Rß$"7"Ëö äñ™ëÎ 4ðæô ûHíià çíîôÜçkìØ– M›ÐõD çàç¡ð(ÝóIìf¡cý"ïö dÖì7ÚÂÙvôáN „ûË7n:åæÿ}„íVù=÷Àè’oÿ9:1,&µ>}ò†ûèDîL÷Óå¨c÷}”,Þáä$ÓXÚùíÑ( Šó/"úZòùööïóïkë„ì­ê3æzõøéƒVý…ÿ0Aè,„Ý~éîÑàRSöæ6|7ã=úd6¨ú‚ìúñì¦ù1!j+E7ªÐ0fã® VãÅÞCˆêf5-+¬+¦'Ÿé'«ÿþùõü â­èläPÙùý)î¡"åär‚ÁãÝûÏãÁ´÷TØï ¦ý©õöÒä×í óEÝ@õp%+a ›(ãí }åWíÞù»ç‰üh2ˆÅ(ö.± ã!Êü˜úäö%ò6 .øØ‡ºšzîÈ @Ó>ßf×"ȱè¢Ô×ü-ñû™ æß¼ÿÆÔÛRÏùÁ»Ò×'Î ‹ø–2ÛÀññáQÞ™ÿ[ërýšÐú=Šû<XúÍÑúëþ/ #ü +:V=, .?:ò ³#‡õ–‰õ­ëŠ ßï$ ãÏd+}ž#†ê¿çà~äœöÝ5˜ûâ ¡‚üàIû®ç)ßþýé<™úü7ÜúH9ޝÿ ½ôuþcóMï^òÍíð£ *Íônÿú÷íjû^/Ú o„4£ì¯TÙâÝÃö;×Ñúíkâìí°æ ì´îä—©ìÏÚÿÔ,J ãÿ¹ òù–ìæúó#áf¶óB_³¼šõó’úøFì˜Óõý'Ù!()åÝOÝÆVÏ$âÆÎãïÜòŸá6ÿ Ü’æ‚ã*à¥ø™æ1º÷´ 5kêéø—*ïí÷•çáê{ðãEžòÚÉ*"3Ü4|ôð 6jïg(‘y@Ò/á.H@Ô~)ôöB šôŠò‘ ö÷·“˜( ÓüSnñävóNõXójð7ïšëÒíùé­îDî/ï€óÿçRðNådã:ëlà&ú°êº kp–í¸ËÒzã éäΰSõñ.Z(vU.ëÖ±ü3ç”&º#;Ð%;H8% 8u ¸Qþ˜½úú,ükø:ü£úÐ!ÿ¨ pj,ëíVbÜaávì[Ójõì4ÿÈçîûÔÇfÞ:¼B½ÝÍ̺ïíWÕsýøù§ášÎ<ÚòéÍÒ£ÀïZ £ רnÿ–^ôÐýÊùóü;Rc# $à  +ÉÀ(š ì)YÿÜøÊNÿý)X׆.»ìùÛ¹æaí©Ûó ðeö Lä‘÷jÛ©ã‡áÄÙ‡ìká^ñ‡î*ñ•ôXòòõ¸ø‰÷Ðÿžû{Žÿ@ŽlÐ/µš"•ù þ9ø\^ã O™"P)³ e#{ê—>è´äˆ!ëˆ/ÙÒU,Dø±2ÜòŒÙ:ÙHêáÙ´ùì–þþ÷¨núÊùÀú„ÿpø?ôÛïÂðêähUòl œ±„ä7ù|è´àõ |ðGj.Ô“ñá æîëöhà$Èób;øäÊþSÛá¹é‘ÝÛûœì×þ²ÿ=ûï©ÿç9ðpêäøô†âhú¥éh šö ¥œè#=Ìåjô÷Àß%È 5C1à 8¹ïØîþðþó‰à!# ,%Iù¤ "üûù‹ .û¸ ÙÎú×íê÷é”ïeæuîÛèæYÞÑÕA÷â ™ÿù‰¿ïÆäQç)ñÝØ õë;-/ '3„ý-%â‘û9ñûçàŠ7Ä+"3S?ì€8D  $Ä N  iìó ¼ÿOf Ã)+Ãå}þ2Ô¨ÛHèbØì ´ø!Öhõ^É*ËìÞ(Á®ÚÄÅú‘óTÚÕ´îÜÉÍhñÏÍè»ûÌ; ¹ös÷ã÷,ø{CF-öË5W-~.*7ß+ÑB[&ÿûü$Á ÄPÛ¯ÖâïýcÓûØ\çÀÑçÿSë l›üF ¯ÙôÉЙψèÅÍš<émù®ýí×+îÐþÓçûÖ¾éð‹G˜ > ŠfÜ!]'ü— 5ü/í ûQ)¸–,.¿:.Zø3Ðí@û ü ôs‡Ò/¿'s57ôû]ê‡÷Åç ä ò¢â™üñéø&ûºì@øûåWîRíëïîïéXìÆëjé¯üô Þ¦û· YéQðÆùtå60ÿ$' 5 9%Óõ« Øõ•ø¨ [øÊ,z—(Æ2«ýŽ"Ã×ó„Þ1Ó+ïá²÷ŽqK N¶ÞþíŽõ‰åçáñäà9èó%–TøÑ½çžóËâÄÞ“û”Ýç!ëW-æ''‡+Þü¥ù{ô, *÷   7ÔîÇ eÙrìGì}Þ#÷:zŸ‚õèÿì÷ÁõŽùsøFîÄñ2áêá¾ðàÝÀH÷‹öÌâAö¸ÕÐáaçd× ïù+pÙ,†46ýH'6ãxï8ãœ%n1-*Z~-éî¾ î ° Ï y A ò ®"G øÜ-ð>÷äðî5úò±þÕþóyÛ»òKÒSØBßÓdòàðTøwò¯[ÒœéxÏSÌäöRÑw'²û%Ý)? (ú5‡ˆüù+ o 5×µ#ê»&(Sç&\äã –µ$ *Z)•*uÉ&{ì‡nÝãtî{ÚÖÿró¨ö‰ß'÷ÎÎ Þ!Ô¤ÌZïkÕ:'ð#öðû¿ÙêÓ(ÑèéêÕ³þàíWþRúÿG6 Þ¡gÂW54X=õ4–)á6ô ×!‘ðJÿýbé‹"ÿ5&,Î:fîþjç½îÔöµë6ü‚ön÷QüÅé«÷×àòêTàlâØæ¿àyí-èÞèÀìïåÛé¦ê’í˜ð´õËï5ø²êäï¦ôHêåîù÷Õ !ý4ô´ÿ²~õyiÜ:È%Ú:v?ç Ý1üâ—þ?é»à×ð«íh¬ù# gûBu”ÿÿ¸ÿŠûO÷R÷á {j m/ ì@÷&ãùápñ¢Þàpûc[õó™ˆèÂñ†Aæ[*±'¥#b +§øÛ"ñ¬ñï€Jr˜+–ïúõòÓöÓþ#úqýTMý Þÿ }ÿá|ñÄùŸð«êÄü–ë|ÿc!ip&…ìi SÒ,Ý6øÌóbù#Ôܨiú?û oý{&ü¢ ‚÷oLòÂùšô÷Põƒöˆê8ó+ßÊæèæåSôàùï,â?õåé—à(•ìÑ 5 yöÈ xÓòq§ʽՔÁ©øcÛÏ-ZÞö VóPöJ ¿öèL k%—˜®Y‰"Ôfb ­ª® K ?V>’$¢ Ê XôÊ=óÿì·gñùƒ ²ï *ΟèDÕËΡî?Ø®»ðYS wêÜhÓ.ëQÕçÒ8õ¡Ø” ÷Ñ ÎÑCm “1•G‹#­ 7ø&á8= ‰8ßKý™û\ êó_,y J4ÿ8æ .qéáÿê—áóêžq n÷P <ݬö#ÍbÞúÖaÑ<åôÚè˜äªÚ¼åÄΧ֟ç·×´ýöñùqúWïXìüRåÂaoÔwë\CÛ$åVß{5êû8ÕA¥ ž=þ!÷ø­¤½ùÉJKÏ¥¸^ò,uëãóÂïLïeöÅñú‰öÿø¦ú#ù¨þ¥ù½ÿÍóMùBä¬èˆ×(Ó’ããШþDë— FëSܰàå÷üÛ„A©•üx4í^ý~÷nñž ´ù1$– &¿ÿÜö,õ 3þD$™öØ!’í× Ý#íç»ã–Û¤åJ è X!C(t(·ï¢ø2ëƒÝyã«!Œd4ðNäžéXðâ ÁñÑ…R ¤Ð¨Žcm\µ} ¸üêãê ï€înêŸü‚úbïsMדë5ß Õ` ‰ëû&W W)<æm‚Ú¼Ú¢ò×[ÕõHPþþO›à6ú ááÝ•þ0æÛ÷9„)?"h®®ûhÚþ;çŒÔ 4,úÖ sææð¥çôÞžú£ìð® ûÑCð£ÕßÔîpÝ'þÑôQþÊèpîÔÛêûÛ¿Ö×ö†Ýšýøö#RçMñíóî þˆF ÷í b(¢¡C’0Ó>›F q7žFÕPõ‰$ÿ› FŽc-ìü­"ðêvüsùLï#Ðúɼ\ïøQ™øßöý“óè÷÷ñ9ñì°ëJíré‹ñŸë)õfôßô¶aúó Y~ûá^àqî0ìúÛ@©õ˜0Õþä!Œ÷þUÿôëøŒ $#¹  vô|øîòŒ÷¦ô¦HÚ3V((ù1ºí°€Á™Ô¿ÔyÁfÀìÁ!ú!¯ +íEý:Õ Þ§×“Òsò7æè#N ÎñâÌ<ïÝ«Ë·ÇÆâ®Dü+Ù©+ /-û å]û ֳܧñÎã`L[×-óJZíïïSöÓò2ý¾ý j)ª®<®/D)8PÿùìšöüÏóå ÿsì þÌÚֺȺæ#Ý[ææì–Þ¾åUüòÝá&£!Ë"ÓêvúÄñÙmÚÖÊ.oîÒuŒ¬ôóŒûHò„òþ‡÷¼#:'Kù1 a¥P‰NsZô›<£õY|ô÷õ˜w)- m! #|öpÅÒGâÝå„Ó¸ ?ñ® ð gÖñòùÄ ¿\éØÉj°û 6ÛõÑ[rô'‰˜ý½Ïóì¥ÓêÌo éмŠìĆÉ]ÒÝæXà "Rÿ–:9«6U>‚r+Wa Øô8þó†ûðöÿçðâý£ë÷ñáàoç±ßyÚåµÝ\÷ êc±s‰?ûµÒàcëÑóÖàHâ w®+qáz'¹™Í©Ò³³Nô¦ÚÛúoöú4ý°?E›¯ø „ØÒê'êIߪJ"4–òF]ÕŒæ"ï:ج²ÿD$(óú M™ý]Oòwñûñé÷døžÙ"é›[‹õ8þzü” êP\£ÿtìñJõã™]ø€ é91þ3 IUú ­FöO’õÌóí öI.ZNºÐ'î(ÓéÄÐ$Ûeî G&À‘ ÈSïóiÇ­åKðØ`"4!~$/çüýæ×Ð÷%–îbDù.…$â9‚øÛ î|9 †DZÎø»ÍåüÕŸì ðÓînâY7în Ï Ýú —åÇó{ÛMß!ç-älñ÷ð æÞñöíÍ0ܽÇéÆNÖNøò¥Ûü­ ŸñýK &úT÷íöüùK&•"šK'kô¾ Ôö²ócf˜&Ž#P-$ùPå C ¾&U£õZóÿôY Óô*&ÛR*ìÌ(ü3µ'»AÛ¿º›¾ìÐ Sô  èÅöæKè¾ïÅàŸúóçÌ UõK¬ º‘ ÷Èþ·C˜ýp é¶!&Ÿ Œ¡ Ú/bœJW0ÃE§G2$e;§YúØü¶ývô†úŽérý±‰Û¡ù­ÀiÉ~膾àþð3å(ì0 HÍùÜ‹ÛdÌQö]Ü”AòçÆŽÂZ(-íËÒÎJÞ<ÜžÉEöéO µwœíçÜò¸îé ´÷5Á ;™¹ ÐèñV FØËï•ÑEÕ éÖ,qôkô ó§sÞ®îƒã8ÝMýeá¶ ý" Ÿ «éóÓdàFÒ'ÌWçÔs>ôÈ;Ô…Dìx³àËã‡î8Û€ó*3{$#/yÎú¢ÈÂÏQçèÓ2ýñmþ1OýDên 4 žCýã,½ðÊ$h 70*ƒÅ1@àûûã}מzî6‰g-<4Ø”$t3 –þlþøª^µ1Ã3¨7ø ó-'ßþ[ÜØè´äj" &3 þñ}$ßðòïÛEápÚ$ÔÊã1ÒßóläÈ ýýc oéQõí–àìç¯!Ò'#è"¥  óQ ååÞé êü+Ö9]&øöfç“ñxü{í ˜J €!ë·µ'ûþ#éÚ!%ÁÐѺÏçÁQñHÝMïfù~Ùhð ÒÛ*à{Öïôõãv[÷}ò /ªÆ@+ÜçüÔÓ ÓTöؾZÿ[n–ßýêÙrÜ»^ç#Ì =“M zG bêÙÛ "Õ È@ÙŽN_>ø/ æèöÙøÿëŒÂ+"O+m0¨…&0í†üyö>ëUðøÑ&¥éÒù iIòAMæ3ðPçãTûHåjžúÉylùVùR~ áú±fN#2 °# –² {ü  ìsø¹ìõã7côf/š*~K&@àH÷äô‡Ü÷2+–9Ô5.  )uå£ÿAãáçÁçõèåEã½êÝ#}íA€³öò3Ï÷äÌÚÇUý3Üå Ë<¸ñ6mï$÷ðí°òkàÌè§ÞEÙìõ¯çMÿžîéÐÃÆÞâáÖ)ÈïƒçÊp’QæSÿní:î?—øe;2{-Í#êÍýúÝéäìÝ™òøâ?¯ðþ– 2JC¶1ïÑ$ìRᯮìó.å¢â,RäþÊ‹àÑhÒ’áñÙ>ïºç þáönz £§' ªàü$©ÿ(Ý!Î ä$ßêJ°çåìN£òÛ2D‰„Ôœ /™¶ F ü3,ù½Nå0þ+ôDíì(F×Çàèì­Øæeõ¨L".µô© °ßdðÅãdÜ’òîà4"ò´OŒ± Êõ¶ê„÷èç’êðQç¹Íõ1§çñ:™ãîïëÞåëâ˜q2ø8”òõ„ño†Ù*Y«Ó°þ)ÀÒÈlásÇïŠ –œØô«WõrûUðVøÞßÌéUì%à} ÆútÝäV¢Ï™½£4áÑ3 v8 ùlÿý™øw$úp M;ÿîøï¨ñ³ò:åÐ(+#²'o™û÷ðßùÖ‘Œ$‰Ë 3(‡ W"Óí: @åì4þ­é€#Û7X)ó"v4 ÿùkç)öêíNß܉íR.¤p!È(÷æ°ö>ýÿpõXü¦öCö?òûØôÖ'¢ ±èJ÷-ãAÝjõáÂ' M]*Åí ¡äÄíûðdë!÷~ u߯ ý] ÉðK—émïáûdæÂ#Z=_/•%K<ñâÞèó8ãAóè¿9‰ùdÑêÒöEչݼÐ#ÇUû$ÐÏ ’®s!¹ó\ ºÒ,ë$ÕÀÑ’ìÒÝ_÷bV ÃÞ çÌxßEÛÁÔ-úÁå· ^ Îð< SSd•†{P-×< 7¡+[>c%õà(ô˜ûÚß'BÀ1È%# 1(¹ÀKù÷röô<Bñ»•þV1çàþ0ôî*|ËŠã2ÛœÈÓ ècl âúñÜçîìrèkÍö ÿîF!Æc!ñ%dçvóV÷gåõÿá$ðço$Õøãð÷6èî´!nÿH6êì-0Œ$&‹ð9ÊÜÇâ÷ê×#™íÖyýý âçÓÿÚ£çJ×”ÖäÞÇÓ ïèà»ÁøÜTó¨ UÝkîOÛ;ÚÍôpá–n_ûþý×ÜôãÚÕ8 µé)„H&ZáLëÞ?ßø!è výn b˜˜wù¢¡Ú°ïÕÑÂöÕÙA}ài­ó †å˜ó^ò:és üöµ$}Î1À*<*46•ñ&øÝ7ø•Ö¼ÒAý_×A+Û V3jõ ¶ñÜônd÷íAË¡jü†tB÷‘ÌæñÐâ9àböÞå¥ d5 ’“òdøärëÁñâˆñ,/˜a&M/ýûpÛ…ð–áBØîøçÑ þ ;)&+ª .dó¦ô—üÏûLë /÷ø+S$*_,“"+êÀüeãíãpîëáüððÎÿgþþXòöŒÞuíÑúÔàqËèýgç{þ-qÚˆú˼8ÏgÞK¿Ž`òÊíNòêÎtáðí£èÉä“ùêè_ü5 Z_ƒ%§iíï+ø.÷Ã篯ù¾0 Í)1 ¾'‘æ ¶Ôëß#ßSÓ0õ‘ã“Kü ÿæ çâÿrÒ Ýç¼Ò4þë0 ]ÿ·På+Ø9ëúÛyÝ@ï½ãýÀöÁõ¹þk[·á þ_ æ1Ë91c.o7¿ Ç'HçkþSï’ä{’øp@é):)f=Týºyç€ñ—ìßsýOä5 öYY W}óJ üئìhÕ[Ñ×íÁ×^èõ?[ ¤ð°ýé ñ§ñíÀüèñ7 ½ûa® ²9à ÈLøÙ"<ñ 'iM:|/PP3±ßd˜Ï_×¹ïÂÓwô gíŠô= Zç’íÆêVà«úyäÌqø†/ É ’žï¤Í²í¢»]ÌŸÓÂZ‚æSˆ=þúLçÛø“ïäÖ ñ°  H#Ò"x&÷XèDø·ï<ëöôͦydåðé.ågçPåA)< ø%«(4 Òú½ ¹ñ·þïëžòyêé4×ía#Qæå'é•#Å]×Åß,Änëœ>H"Æ:ÃF\ÿ9ã÷§ú¼õœnúû&hÕ!}_ò»ÿRé0íìÇä¨úÁê• Gþvņ¾²øtoúvñþ Gõc€—öí ÕØïNÇÙÎdÛ)Éÿ ê˜%·t(©Ñeæô§÷€àÊ'hÿ6>c/à&—<ùheö{ÿ¸÷­÷0ù+ôyó÷•2‹&k.>ãgîØöÓ[ü ÚÉ ™¸y$ƒó Ô3óõË­ÕÚäÔUûgíübªé.üšÖÌçÔÔ\ÖøçÚ\ ô?¬ˆ{ýó¿ ÙñË÷̈÷º#Ö)9’&Y27‹?,Jü”‚ñ ó…¢ì&eË:k-Ä/¯<¾ ö)uçTþdë8âå\ë åÆcÞrÈíÛ<×¹ÐVí0â´ïËñÙïèò{ø×ù†¸Uù;dëçðsþŠéˆYY j:í 0Þ1éÌûkä¨$Nã;/&Ì7Š7ñí,¬ !øÖ•ûª&—J)Ê&*¾$#ée×áåæ/ÖØãéÐs¼ ` Pú1}êŸõ¡à»ãLàÕÚ˜í+äû6úIüa T肃Ú5êÙàöß?ø3åÿôÜÖù„Œß¢òºõÄáp(I-/¼U%.é:ÿ˜îüêÃû ô?8ûRûˆƒâæôʰӱè<ÞHÐ\Ããz!u ²#þp6é—ýà€çðâ@ÜPöh㎞ûÍðiÌ·ê¸àÔΪ'ïŸ-Òu'ØûÞ ºüyfÑ} B½ ²\ ŠîØÿ]âËèê¤Þ( <ïÒËÓêÔùh Øßjî°äö×kSåð8 =‹$ºèœOέã•ÑòËíÒâgðœ%šÖ @$_Š ÿÎþ-öqý£/Oí&‰/€ g"ÌõÄ]ô õ÷{òÔødôüü÷_Ýáü" ¬æÝù!Ý äû9ã#_ ×@!yÎÄöì»|ÇÈÕrÁæ÷xàíüaùmöËýCô6ü³ôûùqïTô«øØéë4ý¨3È!G/I6 Š&wó#ù›ò "ú ª o#Ú%Mü\¨ñ‹ùûqï§Xûzc`ÓñÇ ´ÛùîààÑé;èøê^ïuã¡ðØØè×BÞOß}ÚñÌßXÿqïÇ Ÿæ 8g +üQÿgû°÷vò/² y449361ñÛm÷>ú^×l1["@|.þ)Z7q l!¼¶þÿ›çù”¥n7öÆ×ÝêkÓ6ϾèTÒ“òòæúíRíÃì@îôAóaù}ùåûøwÖøÚ)ÞÆ$ƒôíÚcïÖå\Ýký÷ë’Hýb÷ / ü¯ –ñ‹÷òô éëâïˆéŠQˆ9®¬ÂÐ Äa%DÛ(ò¾èšï®î)æcñÆ ”؃ìçáÒä ðàf —ñu#ÿn."zò¹%ÔþêÜûÓC4éþ_ÝSé[öBŠ¨Æ ‚3ôÿ.ºÃ<Š5B:]êC fçøêpþò, HCúH/í3ù®éfòýé¸íHìèõ¿èYüÎô2÷ÔþCß§õÅ׭ڱʕûÈèIJi÷À˜ê¾óÜñöìuü¤ó°#ú£_^'z.ß0Æ6.ö´ 2öÙòO6ÿ^/%O'–3 ÷(Žë -å´ìÃômêð´øDP,ùÏûgûòþÊü9ùæøõî|ïšíêuô-ò¨îùØÛMîÒ‚ÚŒá?Ö øêSÍûïÿ†¯û-ÿ9ÿ ùâ,ý%/vt8¡.X)d6‚ñ#ô¨Oøkójþâ1Ø!· Ô4ÆæXýFþ˜çúÚùýÿ ý@8û¸ êG®TçyXÍ7Û×ÉCïâÛ2ø­õúå»ô$ÛØßáãhÛÌõä; ëø¼’ ø .÷úÝ÷ /üœ¨ 4.ß ƒ%úp ðÛ÷×ö ï‹„úÏtµ^!ôýëwòêìçêõêÿJ÷4:#ð¬sØ…í"DzÐgÙlħWã+? ü,ôàÕôíóëà–Áú¹È ˜ zýÝ!Q  ¾Ýÿ— àcüg² õ&d(·¬(ÜøÔúŒðèÛüôU x«ú« ~éGù"äÖèôé¢âûÔé}þ¸QHò 0Ù×äþâáÑæaä2Të ¡Ÿ "ýüÉûÓ÷Þ‘÷ —`û!—mªûôþ.4ûŠ I êSïiðçÖê_ýôéa )p÷6À×fèŒË–ÐuÕ´ÌÏæïØñô•çèöô zÿ¢ •å)õ;ÜèØ ï`Ùeõ2è 6ýñ ÷˜üEýJ÷¦<Í&‹Ó0*/gØ2Úû¤Èêóñàì-Ô Ç:dâmìÕåÛçÝàNïŒí±ìÍìPîìôñgô™õŒæ¹ìçß ÜìuÞñüõ 87þ„¨ñcüÖï–íM‰ï÷*îY2É/#½.z—€¼þ†)Ì#O1Æ*K.Ù9.290R÷ÈýøÅñйùt2ªþU¹ò¿úëìDé»å&í,çÕú»ñôÍþÝàŽæçéKÚƒ„í… ~%î$n߯äÜùöà #’7Ç'´('3ºƒ%a‚ÈD Ö…¿ /ÿ+› «ÜS0`ð¿¦ã è©ëCáWDñ;ã- S™ô€»ß#ëÚbØdâ´ÖÞñ$æMö<øê7øO݈æÑáEÙ‘ùœâÈ(û ££ ÿý|ÿ^'^Ǧƒmµš ko Bþ<ÙÿýûcB!??º%øêtµÞôä–özã]ˆ}îÉñY+è–ïPâl߈æú×¼ó„âËíö'̲ùpSø¨òHõJ#À( j2 p MES ä’3Òˆûm /ùs÷TÞýÉ&™U³)ÑõÕüâÙðQå†âæë‹ã9óéê­ô;õõ>ú‚ï²øªç™êè£Ýñõ(ß$öú# %å$7 ½ éùïò0äÿíøI)t¥ Ï`”:Mœþ² ©ø ƒÿÿ) ªólÿízð÷ò<ëmý4÷kùÂ;é:üªÜ¼ç0Þ\Ùä=Ú_ëÇánô|íøÿÆùR¨íïÀûêâæáŠ÷mÞ|!Ùÿ˜4É+F‚/põG ™íDð­ý~ð ,Ÿ? $»¤ Hã_iP ©œ‹ tê×Рõ4æ[ñ½ÞiäãÐáNï}ìóõõAïÂóÆêLêhì¦âxüçYØü$gúL Þàìð¼åDÜGWìu!4Ö&‚(Ýû(û|>ù´ a(ÃÑ",˜¨'ï(~pôZ¦èMï¸Aî[š #®ió&ܽâÎãÂÓäñnàWøåì*ôólèï4ã½ä|êÐàìúúé†ñû’ë‚ñšþUå)ê«ë‘Þ'÷ìw  ¦[| ×¼/ ó ðrÿ ‚ ²!l&Ý "ÿàÚUÏÓ×5â3Ò(ì(¢ ÊÏñgzáré;éjÙÒýºè$ÿsýñòÄýŽé¾òüæèç­åHê„ç’óOñ)ŒþÒtA å† çT )oñ(–-øê'æøF\îµú¢óXó0éü¡òŸ!5døìmýâ1çøTãVŸþUÛï ÿîdü`êNêïì~ç¦îìºõ”ð àýÞ¸ZoëõÔíƒâšô#Ø«K ò•äoj‚jAU%îE½çôðDð‘ëSüÉó›‹u’ Xø ‹íhò1íPæ÷ çÍôäÔËübQçô§ÕÞÚ ÓbËžêøÑÉ |ñÎ æéÌûT ÿàøà TùøU ¸&–!M ##[쨹ê[ìëý‘ñ| »˜ÖgüfÿmÞ÷)ý)ð ðo÷Þñ¶‚$ÿ# þä°øÚÝâåZÙ üäèc_¡ Î÷b}ëÌî(ú‰ä³ú*9 $,Á†ëcÿ²îë¡ Áø^#à#$_+š)ñ ý†Ö u÷bþ‹ôq÷G þ’7ÓôYòýPì2ï“óƒî$øÄ÷"ôSùúå"ñSÝ^ÝÛñ×- PòF ¾ßû"ðˆö'ù–í Ð÷=§ RO è]ùá [ôƒø¤ü+ôÙ 8ÿÌ1%™v(ï&¶#ÉHþí%ö-óóé†÷óü¶ü¬ ÿÛQõ—ÌÌÕâ4ʳ ™ë#Ø &ñ§<å*ç™ôäñÿÌòDôæ÷ûçªíØñëíA…ÿv > ¨úÅûQö …„£ ¡%[H Q 8 ¤ ´+¬(¹ ¿+7æó!äHå|FëÆ csõX(áòòÛöÞTéXÛ›ýâéí þ6  §A ±ú.)ò)ô…ïÏì·õ¾ïsÁü $ J @ZT Šúìþ¶°øÏ'ºˆ6–7qP:tñ[xðàîCJþ· :öÑÜÛöÇásæú´ïtû¥ ªþŸ §ø #ð<õ.åá½é3Ý„é¼E‡]!÷ Oß êHéÖNè™A Á#<#( ß ð^ßðì ‰ôà Õ  y,l *ÐûÿÌíòógëìûÞörúSâìmðÿðæƒþQñ» G ¢ä–ø¯Ù6؆ñU×ËöEíŸó:wæÇîFõíž 4û«TGà†'$Ñë î?vöùý ôûóíübýôõ ï9¹—´òpÿ`ï°ð1úÓñu •ÄZÁpÊë;ü°ÙràFâ%Óý™éDùV‡åÆöœãëåïõøä4ø[épÎG ù–î =Í~ zÑÒ ±£á迹k|ùæ–ýõõäO{&c!%Nò‡ „âpê½íŸá*ûªð›µþ½l÷_Ðìð‡æ¾Ý0æùÕ°ëwÜóNê’ö1ôßùôzÿ@ô$ù– ¾.° ôø@ž±™iþ£c þ1ü ç‘VÓà¹þúð åýcÿ,&º@$Èúq®ì‘ù_çÐëKæ`âáé7âHñ(ëšûûøPüPÿ>òGõÓ俨ãºÚùzã4ãÿue(FïÓù°ññéŽÿoó… ‹ [ùýÎ{÷Ký NŠ <#mûñó‚ö÷-úõõüؽ3¢5ý¿ù5ió^÷-ídëIî!åŽû¢ìivï }åïbáÚ$àqè‰Óç Ì죶ǖýà èð‹ù0óuí}ýÄòžào»G " G 3°6¤'­Ø Þ Cûýóø^ð™íö¦ìýÿ¶õÞÛýs‰÷¸ùqôIêN µë¤ î ôÃù<ßÑñlãßdù›ê®Ï/ ¦‘Áú}uü•ùÞ7üÁÒØß D Aÿ{:èØü(ÞÁâ3ôDç9ä]¥òáþ‡ìÇèB÷jæòŠ ÿÚûê§ç’ø>å´è}õnéÄcøš  î ö ð¸õ€û’ó&˜ŒK–!ðQ* …_þ| õyûˆüâö¸e´ !Ö;$ƒãO0تߣé3Þ£iõp ò ع ò'þ ì¿è)ö âý ìŽøpò‰ðáñ×òtîüwóýqùÿXùÇÀþ, ™Õ I æ#Óã, ¡)0+%‡$èþ ·ñÿÏør÷. ‡ê7O=i TUùNóÂðý»ï— EþD ~ hþ\~öû ïEðéåXçßòãß4ö…œá÷ÉÁëUë~øÔæ?ÿ7èÔ !„üT ü{þñ ³«lÔ@Qõ‹¸ôAûãýhûhXÍ f[•,ø–î>ó¿í éøíèÿ{ù²ý`þøïkøÛµæMÒ…ÓâÚÓ~ü®ëRÁÒ Á0ùÈñ,ðøø ë düVi.ú'së¸ýöwõ‚ b *c Ã)¾  o HóÿÝ@ýXù# ý3üŠüèöþä)ç‘ïèŽhô\× ‘øôú¶0ðnëV÷½ä#–òƒyVr5¡ø°“ø±÷óÎú_ Ö\Ém× ú– qq¤j½ Ûd> CF÷¸Îó÷ô~ý£ñÁ QýÑ  °ûºêòñªîlãùNïFSùbý3ùØôƒýëõ ÿOøYý·ø-ý¤ j .2°óïrë6ó{ó„ñIv ]ÿž¬ë ý[íPêøúôYþÐôh¡åÂùµààéLè€äøÌêFù¨ûFþÅòxôXô¤ìÛþêî”÷&ý3ø‡ûÀô2þ ûÍû!æô·üaô˜ô!ùòpö›!D6$_ü}Pó¦ø²ÓùUä?'þDìLü òÂïûý)÷ýCþ–ù ü¥ûüüƒÆûìmîîížórÞ¾Ôê% ý™s÷þDÿÈü®ôxòføÛ úŒ¬ eÿ3 üýÑouS ê*“Ë%+«!½ý iF &à »¡  ú CûÛø±:öKöüuòfÏû× y 'é 2ðOøä„äÀåÙ5øã² ü÷ø…‰ãªö×àuß×õ~àyôïi7*bÿ:œþHóðí  \ Q€þMõh uöPþ³ÿªü¿ÞR :n b÷%"ðZùcî´ìö¨é'ô ±ãûfÕÜëٲϜí¹ÔüÿQí˜8ïösýö1ûþû?ü¸ýì2ÿ‰/P²ÙÎsù ¾>^î ç@$¹üÚÔù £úXÁÿaƒ ¿ Ü ð@ú”÷Fúkñæþó¾ùZÈþüüÿõ³ôžôqë¸ÿ<ë– Îùæ-*  S!ùÐþ ÷Ò÷íúMúå† 5w “ŒõøúÍK¢ù¹!#/!%sþçcíÿ‹ñÌñ7ø²øÿúŠý§ïËõÝèÓìéûå}òã0#í*eýê í&ù™¢ë ò‘ìÖè¨÷1ëótù 4 û>ÿe >úâxkýš% Òq˜›*ñýEYïHÑ÷˜ø “þmÿÄLó¹ýKêQôôèð=èãéžè*âoç ßÕê:â#ñíÊù®ô••ùÜ÷¢ ö’Ÿ¿ ,eí©(ð©ëè^öh_ ‚é ºLm¯áëç4BHÒ ÄôÏ }ë3õ¤íëòƒî7öÂõùîû ùjûTõ¡ócójèÕõÏä^%ìªRV™ pô¢úõ@éXí¯)+ ð fàÕÑ~Ƶ_‰§è£ ’ ù²w… <‚ ­=®Ù z÷`¥å²ôLè—â©ù±ê*ÿœýööÿòí}òþêbäôYßXë /ý9|¹óáý'êbñ}êÊéóðéTø³îÝý:úVãµ’»M °Ñ‚—¨úŽÿööfé»ùöüï¾ Ž£ zæ÷y8ëÅ÷´ñRé„û%ï,‹÷ºÀþ‘üwþ¢ôö)íwìsíÕåœöbéëóö¨ ù| U 9ýƒþ)ö¼Òh 9fý0¼üJþý+ ›Í2 ‰>®÷3£öûØûDù þtAýšØõÑøóöŸî²îX°õÍ¡þ1ö$?@ø°÷˜õ\íYýöì–iùFž°@Yðfúæõ%èBLôñi"lý³åû ôü¯Ûþêmÿ/ökêëþ÷Êì êµöëè þLò‰)ÿ3û1æíïÿ¶èñî“ñçýÿ÷ï¢cþÉåë÷ËúNôáí‰ë@Žþ쀵 ó?÷ËPü¡ü6 úVtÉ£%ú·˜õÖñ®øKóÛñöÇñþøøKF[q;í©Ùà æåïÝÚûGïœ rï°äHîŸî&âïþê0 ùž f ™ ˆ=lâþ1ð Á(õ^š¬>û` åùîÐþ‡Þ ¼þ Ëþâçò\øGòÄè?úé­u÷!ý¿dï‘3æVð@è3äòçâCïú3þ†ûÕýKóiðùvæX³íê `ûý ÂéÚ¼UÖUwüWPÛü‹Ž [Ó›±Îôÿ÷²öbû2J  s ÿM¬ô±<ð®õ0õâïþ÷í_Ôü~8íÍü‚âsê×êpß¹ýÏé² ÷üc 5 W` ¦ö6àòRóè÷ôì(oòz ý (ú†ïªÿƒõ óå ýÀÿøúôö’ öSö‡ø EÖjõõqïù4ñÓòy÷ ó÷ý&ø2´ýÇ Ö 0ô€ý•ìWçñúÚáÝ Þôl ë‰ûH …ëý1ç8é¨÷{âð õöÚñƘ™ˆ÷âÿ:ù¾öšQúb Ë SõO.ñmû»ùúø$ægY ƒí w Ø, 3ø\þ“ö=íÐ ð1£™%ÿ”7ïæõfòØæöÿÉêgSü¥è¯zKö„þR÷ó¦õÊ ‰-Uó ‡ÿª õqiòüŒøwú_ÿÿ¶lgú] jñ¦xî¹û2ö”óÏ}ù{Ü `þÓÄï–öSñ9ê±ÿÿîB Pÿmo ‡ö1çìôBøñíH ‘úeÑB´ï ÷ <áþÂÍ÷ÅùüûÕö•Nª ¨úáOêsƒé^ñö…îÚÄúÔ uû’?ìË[åvëzëÉà:øàç~åöî—Yõ1ü4ìyë¡òåëî´ 3ûÅ8 ýâêüyÈý, iÿùñVÆ5eµö4vö_ór8øî <¼“òyéÔ0ê,êºÿ0îí]}ýF.ò¾øÔðîŠò”ëõÄëÉýxñÑ(ý¼ÿÌ»÷Rþsø¥ó”nò5Ìý¤Ÿ¨ ­ 4º õúÙ§ÿý; v»S¾ WþwEó(úûm'7 <o#÷øó{ïÌùNøÈõBÿjþ¢§PD #ÿ¼»ï—ûNìRçˆø€ãüð ™‚Åóÿü³ì=ïóùlé‘ÂýÂWüâqê×û5éÖì—õŠî©®úap!B ÿ÷³çô/ÿû]ýïñel í жÿU °òHüÅï)ñrøít ÊöÜÌ Ê Z ÿI ö—÷BþQíØö(ÙòT÷®¿ä˜è§ìßäý¦ëfýt}þ1û“ƒøòÿkù”ùeÿeö* ¾üÅîûß¶ïô)îøö$ü0÷ æ^š ±Öó kõùòˆXôi°H~ î oú¨þøñó!õíØL{ \Jð÷ëö‹õ ÿdôÙ2üô ðwîŒý‘Ôô­dõÆþ=üýš÷Ë×úÉ¢ä–ü?âÂæ2öZæ[ vù§¹ðùUõ)ø úŸó Y÷ ~– ܰqáõæùFì¦î>ñŠéyÌñÁðŽz@Éû² èõ‘ÿ´þjøiè/ ؆Âó1 é?÷·î`ë:…ó& Ff *°üe øó­û,õòÓû0ðÌcó×–ø/ÿ.ú]ø ø.õò:ùÉíŠñÆ )ü êÎö×ýüð@î\’ì¢(DK†™ï¾CùšúÒ AO´ušâ²ùé `õü¤ú öÿGü•¦ ûô¡ýÖdûþéÿ]øGøï×ýsøÿëÇôúê’ã¾ú8äÛ7ù‚» ûeîûø}÷ñ“ü` æ ëp‚÷î =î—ÿßïTø¡û¶ü6`– ·6 'ܦ¥ùÑùâøH÷š ÊþH ¿ ÔþÒí{þÍè÷êøöç}„ó @ËKôþ_ëõëµï6àýmåÎôðsý|àï û ðñÄø,ôÿ¢ýóý®ˆùØã÷ÎùÁÿ ý}ýÜ%ýpwìŸNnPï¶ŸðÑêw ØðA î Ç¡ûÉ TñPù{ö¶ìÌí' Pø Ág ö Fµbü€ýOú„òCü'ï €õÊ[ÿý LöY òLþ{ùV÷æÝþÌ U È ÷ û{í¨ýÂñyëg"ð˜ C5ˆäøG kò·ú÷òjöµ¶ „‚ 7÷û½øðÿˆðz÷ R ² -¿ ó’ ’ê-óaú€ë ¥ÿX4 ¸ý$Yæ!CäcéÝñuâÐó&R/ãiò ê÷Eðjíùÿ7òÅ «HâêøºOðiò÷ºê JñŒŸý>U  SC‰ÛöŸûOôQïŒÓì.·É äößËìÁú5÷Ùí¾÷@ v›ÏÍ{úÜ dù–ú¨ý$öáwúuþ..ýæ‚þæ^üÛYø]ý©ùóß ôX œ šý·2ô õïókäüøåX>øû$ø òüø…ó,¼ù"Çø1i wÿbÑúý÷õ7ç ké‘ÖþÊ!øÉöùül÷Os "Q0 ÷ÄðýíBù½æqýòš¡Î“qñbÉã1í«ë(àËû.ìþý½ösþáñx÷eôÖñ)üÝó,üf ðT~ ŠùåZð¾÷ž÷sñ%ºûú z2”4êýúÖBùÓ[ëilH žó þíåyÿ8äF·ýÈ é×ôA ¿æëïÂóIéÁþ6ô—•øCsû§tþÊÚ!ôtþ“ë]ðpó^ê öV R ¹ð­÷$ ´öçüž‹øÂ9ê`ïó®ÿÒõ—î‰ñB ªÿ.` ï U.¹_ù+ÖóüòŒùRì9 ^õ‹ 6|þ8 îì2ù÷ë]épøæéV†óóÞûÉ’‚  ø’ …ëIúòïZê.ñóv å >ûØè*øGððç° ÖôUc* ©+þZ Éù'röGúüõ¨öÉø›ö™¢üM Nþ ²ù¯¬÷ ó¥ðêþ²™’  øË ìñÙóýýEði ½ÿFžÛ¼±ûC ÷ÿÉúˆ÷?ªúMæÀ[}¾ò\Ðìjó÷kñõý:kþüK÷üÿ6õü÷öøüÅôv¨öŸYÚ… jöŸþ!ñ_ìÎyí.–ž­{ýsð(ü_õ}ñ?÷… c: z ä‹L_ ]ú…ùÙú û œÿå Tà£õ¦ íëêûBñTíåÿ9òQ€eýö1ôµþ‚ï@òö‘ê–hïlný˜À¬ó ý­èí™íWçœùdñ'Î\r òø’ šôþ–ûÌùâ?î Bº ´ Q ÉbƒüØ óµþbþ„öØs|Ÿ›öô›u÷óú'òëûáôDñÕùÞþõ­òÂø8ïÿpõóÊþ£Ñ|ø9€ð\÷¿òÐñÚüaõïûüïݰÅülíøÑ£ûûû¯üwuöÌ÷þB´ðMÿ*õ«ðx’öš ! vªŒ÷ƒøWðlÖñþþ: »ÿýÓ÷´úèò-öñìïSòÒêû^ìí'ûýþ*yòÀþî«î>ö#éÈò.ÞîþppñI éÙôóàîPú¹ Ç M¼¾úª çó”ý?ù¸ôD1ú5†Ï ©Çýy ÷žþ€ù‡ùÌüSù}ÿYù !ÿã Ù ´@2õŸ íKíTü‚æ| Ä÷@ ÐÌÿN Lò‹MðŒóßøðY†ùº _ ¿÷¾öÈüAú©úÝÅþ* ^: q ÙýÙþàÿüòüc „ × u êù7ò³öËö±íS¿óîS‘óþÏ ¾öÕýú÷÷ñ9xñw2 @lûXLîªÿKñö"üéùîbˆôae žÞ~×ýþø÷‡ŠùŽ 3Ýþ½¥ôWýóóúî–·ôÓð°Ð÷LüŸôÖîüSìkcöWdW>ÜòëÀõñ.ðÄüäô uÿ2 ˜Žÿû! Žý?ï€úG±à@n û¡ jù¸ùfªõÑ öý o™Ú FñlþÉò»îæý}ïN¨øáÿªû'ý±ø7ù]ø÷Æ÷«õØøMôœýDø¾Ð ý# Ìô£ôµú¾føªk¢°wLk 5¨ô)ûø¯Nƒ™yÕýÓ¸óiËøêôë m÷_ù© ê ÷ˆGÿ&ý×nü]ý×ù?ôñˆóöîüžù%ü€kíþvèSñ‹ð(ë²ü6ñyïþÿ½|ú: ÷É÷&ÿ•ðs yüS ý TþØ”ôjÈõ øÒxõâ þÏ0 w ®ÔÇ Uý¢"XùÑÓö"ù ›ÿÄuº ËöCÿ‰ð¡ïÀðæÌöAìŠùpû€øFƒïÏŒç~ðåò±æÞ"öÞ NCü™ ¬òÿýû­÷hNþ|* Èý#9ýW „þD| ôÿ½/¢9ñ ;Ó? >þ þüæõfù _™r=ûj?ôm?õ]ò5þ?ñô öüª ¾ þê ‡÷…ý³ü÷ÿÒùY"T &ÿúkþ ') ðýS³ÿþùƒþ«DË [¼=ý=ýÿÿ÷; ùüñý {ôÉ:ïOíýIë9&ûA¦zú1TôÉýyôõ¨úÊñåâõåþNÓhëÿ’Xü¥úýØòLó –ûЪ ÕZ°ýýQÿJõ úl òÈ PîaþZèéðøïèà¾ùû†(ñ ÿ×î¼ödöÛò&þ}õxßúU>¶ÿ¶–ûýßùyüü°øNÿ’ørÀþÀ Y ° ÊËHóÿ<žû°Üåxô¨±ô]þaCºýk[Ýv ¾úk GúóÌüçý$ý]ùˆúKöøäõxø¹ùNø×ûeøútøPõ)û•óÿŒù¤¬mš÷Ï'ó#õHýñÃþy©cóBüý)ÄýIS¼e Ò¡Ù Û¥ Xÿ­_þ¼ü8ýØúûüìúýFÿ)þ”ûR¼õ£ýô²î@ù4ëÕýóóUû‰ýÍñÆú ê:ïœæ å›íFã“ûñ&ûŽÿ ò£ýñ$õü òD Òúc Ì€¬ †ÿuüÝÿ¿¯ûZSÿi ä /¼°µ 5< t ,¢ûÓ Âþ]  3Sõgò4öŸüñ*¦ùÐþO×ø?vôpþ[õëöùRômü“÷%ýðüšüÅÿþ¼?p  ×h K: ? é˜ 8ç ¢özxÿ¬± 2  †Ïr¢õÐvû4ô¦ |ú•ʼ Oø›áòáùsôøòú°ñ þOø@üßþÏûZ'ÿ{ÿ‘hý–(ü›÷ŽMõŽþw  ? oø8ÿÝüŽ÷ÛÉý+(uü‚ èõ…£÷¾õÿþˆñÑ™÷á# ý)?óCû¯ñó÷YñDý]ö›ý©üÜø×þ òüîò_õ~úñäÿYð÷±ó›üÀ c dˆûéúm½õ\â"¤AøwüÎý¼xýF ãþD ekü¨?þ_ÊýÄ¢ÿSúÿ-ô¼ú®ó"ùóžø–óÀôu÷óîý¿÷8û¼ý{Žúªû"÷Qûò ^ú$ç ¦ ²ÿ`møˆÖÚÉç –0Droâ  ¥oï8Ðtrþ;÷õ .ûö€þeðêò÷Øòfþ÷ýüøîC÷sï¿è4ü¬çðRðˆpú°øXÿºð7øìô¢ðôzôÍ —l Æ û >üTþ/þvþhnÏÞ 8Ä —ó ·9@úû]ˆûߢÿUºŒù´Wð ùyï›íèöÒëJþŽôýOýÏö¹üÛðõ§ñì/üí|5÷Yü\üäð¤÷ðàð›üõô&oÜ • ys \3 _Y £V… h £oùh ç÷"×ÿJü‡  Iò¯&úRúöÊú9ùPóïüâó{ÿ ùÜü þq÷¶ýÊ÷Ëûúû_üÄþwýèûZýû’ü•ªZÌàX£þ”£üU‘Ó ß¾Í ûøÿsþ(JN Ô¸ ¤õúØ öñiùÖø•ï8Åôï Åir3ÿ¥üDüƒø¥ö[÷ñKü|ò…ÿúúÙþ­@ü> ù¸ú¥ûòò çó«¦ÿm·³ÿäÞü#ïüÖÕþ¥$õñBø5ú·ô}†ýÞçþ@ö&ü5ø÷?ûP÷ž÷õA÷ñÝùœôûgû¤öÌþ­ïÜù#ñÏòFøFò…ã÷gñ àgÿg ³û\þ‡úZ– ¦†“çMg ¦Í[RþBBÿ­Ùý3ýŒÌü9ÿˆü*üƒýxüü;ÕöõÞðšúþïòKö_ï¹ý ödþ–þÈùTèò‡ùöKð÷Ÿôb[¾S #*nV¨ ²t }ª † C8 î]û Š`’­ ìU [“ m `z ?õJ ô;ò5ú¶ðAýÒøøÿ¶ïûíñ§ñTìùú£îK÷ùùýðçòEñ‘íKüQñ…*ýtùæþûÿh5à¿ZÑ_J ¡“\* ]ÿ´ßûü(ã÷x èÿ½ œÜþûôe£ôúåøDõzú`÷ˆ÷ôù3ôÑù¡ñäö’óó’ùÖôü’ùÇù[üöKüŸöuüúûÿ‚üƒ°ýÚþiTýÂâ+« Xþ÷ ‹ÿò¾Bÿ#šA¶9i–ÿö ñôþ„ùOõÑ+ûqà þB¼øWûûO±üíü~‘ú Nû¾aÿÚLc[®þlþ»ü¡÷0øÉ_¡YIý– Õþ‡ÿã ûÄj  @þ•§ý[úöYúk7v#ùKqò~ÿÂõâú*þAúöfú©5ùLÿúÌý§ýOü¯þ­ú§úxüYõ“~öB›þ'ØÉæ ëö‡ ø~òZ€ð yúJê¶ÿ]CÝRÓ, åõÿ^ì÷“†ö“üúûûøû˜÷­úZógøbòTù ô¸ÿ†ó GõäûÐûåõÿøñÿÆJô³ŠíãöïˆîÐõŠìÞTõl9ä› ¦ül ÿç ÎÿØ$¨- ù Z ž% js P.tïQÓßoúó Xß d¢ ÎýæèþóøiPüþÿ©Ïõô ñ,ú‹õ¨òÿ¿ô×°ý­ý^rôÒîäù3ö‘ñ£êôS þ³Ñá ]| m 2W –`ö–z 'ì17ùržøZ ¹ © û‡ ÷ðü ûJóŽ:÷­ÿïýŸô·þ¥êý÷uêñ ñï>ùXòø¡÷GñBöˆðóè÷7õdkûÃóü•ÿ“÷@¢÷@º  õ1ù«öúÀúÀ™ B > ‹9ý ýÔ¾<üÀÿââˆü©öõJþßõRúÓùÃùlü>úYûsúxø\û$øóü€ûIþþßü´ü3÷øÓñêú òX–ý‰% þýÍûžèúÚ Bo¶ ØüñEû,-3ýÅ lé ™ Þ ™üà ãû&4w seÿÅJœ¦·D÷ùø*ýÒù÷ñ2Øõ]³  ›F¦üeôŸ^÷€ Y#*Wü`äú¦ù=™øà‹ýX÷îüÌK„ÿDˆþÕ³þeÿèüPøùî÷¶ú,úB{õQ§îI÷+ô­ï79úÀ$øû Òò*ûËöîïÁþíñÜŸù™ß/ü$éöÿlú‘øœÏùÔ èÁ©Æ¾±Ÿ+Œ*–ìèÿÑ2þ¨ºÙîÿÀÑý#Þú×úñ÷Ûö úÛúmùE)òŽŽïxùúóïôšû÷ƒÿÿÿú…Uôëbñ¯ûÃøó?ÄöÿŠþÉYú0þãÿ2B´ÕË W #Ûç'¶ wºj5û^ %þè°¿^‘ü‹ ^ùšìþMý(˜©D ©úùcøÄýPýFù<Øø»TøÇüzøúø„ú¼ö–þUø´ÿÚþ?ÿ †ÿo›þl¤öWùCûÈõ ‹ûÔD¤ Рú§¨ü"• “tïþ£ÿ|øÿùôù÷§ú$'ÿ ÎÏÅöÿ¾êïDòÁì“þìûà*¬æWû/ü“ô¸óâñ©ò½ùgùŒ} g x0þôÃ÷tìWìRò1ì¶ø4áë (â°ö¥ûUó7öø‡ý”ÿ&þü÷¶ø1ÿ+ù‚úêù±üÜû€ƒ0œ XÔ Ü Jý©B÷ùø1ÿ±Ó Tû9œõ^÷ÔöñSú÷Aùý¤üšú “ú:*ÏEŸû …ïìöÑõSôDiÿ$ícÐ’ýäþúÚüþ9E j$*oÇ:; dö*_úÿÓ E š€CbüüëóšôHüÌóü L9øøJï‹ë©ûÖîŽA×”.£ÿ Eª 4iþlÀïø$ô©òâý: r eø ÿï;ë‡÷èïÝZ uV =Å\®ýê®ø¼ýÇø9–ùÜŒú¬ødû÷Éôöò}ùÍó©ÙûŠ— ®M<ûùŽþJúÌ™OÒEöÀ›ññmøôìzÿõÇýOýpü7ÿl´¤ûßeð(ùùôøí…"÷Í ðŽô'ÿôcß9g Èÿ¸vÓW˜ˆÿ ýäý ûq7v • Ú M ˜ *yÛý•yý»Òz0þ,û¶³öX›ü䢎þê¦üõþ†þ|yÿ.ûLþ“ø„ýrÿÑ : ÿü—õüø6øxïû|ø*æVXAÍ] Y Búh¶ñ³ùÄú­û| ânK™ûø÷~ýÉí%è÷Š: Í Iþêý;ˆû  Ã'÷…øbÿRüZBþþdûÌ÷cúæòøýTø 皈‚ÿ<:ü›þÊøüs÷%ýó÷ý~÷Ø÷ÕÿôõŒøöïñÕøäðûÿÜ÷Wžå ûATãý„üõ{Š UÿŽ ùžþüûÓIêçÏýq ú¨³€_ û ôÜÿ’ûk¯üP  î …õZÿ,ñ¶ïøü¡ñ$¹„mmúäÖù¥ÿbÿ û2Èù­·ùÖ÷ý‹ÂAUå0vÝÂ)èšÿ hþ óÿS“¯µ%ÜÞ 7Ld? ×ö þQûwüƒB»ÿ øLüu÷×ðÍ¿ï ~û=‡2ù;ÿÖóÿõüò‹A÷¶ ûoþcþqþ·Úý?¿÷/íòvôù®ñ2!ýC¥¾ùüxöÓþüû3wýköÿ”ˆù‚!ö…ükö ö'ümöcÕþÍN+ü¯ùuþÿ_÷Å3øÉ Êýü:¥_ûÜúø£ù=ùœô1ûP÷_þ2ÿÿ1þûDìùúÿùßøGTöäLÿ§ ’ûí ¯ðBÿ·ó©ömý;ùbþþK[ýXm> ›§Ø¢ëü‡'5Õ Gû ^û3Üþ1÷ PüeSh B Ðo8ìhÃÿ±_þ;—ÿ¼ ùÕ ý õ\ýhùÓõbÀùˤ¾ýæíóÚþ/óúŸøâöAüBôvüçôkþqùÿ.÷ý¶ü–úÿrôDU÷ yÓ Å}‚”û._ý£ùQ“ûS Z¢&Fúý^ùlý÷kýó¯çY¸‘ÿ: ùùÓ àò­ð"ö±õ*óñø&ù5öPýeòfûKõ…÷ ýä÷é™ûÈóq› çýÔÔ÷8þš÷‹õªŸös·çgõêþLõ÷—=ú qø§èá:pÅb né³b)¾ÿÊ îÖ2üãdýßÿµ9ÿ® ÝZ ] ² ” ³þ§cü# 5þ# Ð>Ç7—ìù¼ÿ’øû(ý¶÷k€ø<Tý9Ÿàü¾'ýŠÿÆWüÊÐýª‹Úÿ½ÄûDŠý'ü÷n÷Eúüö¸ÊþŽ  †@ mûeýâÖøc¯Ð > ÿš÷TÿTùïýxýcü$üé÷ƒûËóªmùgÓàúR÷Ï÷-ý“òWùÐrý/ ø§ú6Îùôýèöcø%õ`ötø@úåú·øô?zñ1øQøˆõ†’ý%îþ °øZñû§º?_ŸÿØRúÿþúýþúÍ´øuÙû›· âçE ÿó‹ù) —ü, Õ é‘ó[˜ïùôø%ùâù—ü:üxþzõÿÀÿ‘¤ÿ9eÚþ f q uÿ ´ø«ûÑüvýÛ`>n_jðî7 ëÀ&ÿ‡ú1µþC% ùÁ bùBvõAö’ý ö(-üWÜûàvõ(Lö-ûœûÁ÷Îÿù þôý¿IûýùØ#ú)ÿkü§ûTSü˜ñ‹þh#ú‰»úÞùb¯÷( û²ÿ'H)sÃûÿƒûù…ÿÏ÷”@ýŒšLú•tñ°þ2ñö;ûJõГý¢¤ú:·ùûaýfýŸûàû&û¹û'“Øß üôàNð9ö!ùÊò›Ëúƒ 市‡þ4kþ@Ê´ýÉçþãÅÞmý‡}ú#ümüΰû¶[ÓeRÅ hþQÿ•}’ýLSÿCÛœ´ý[Wû¤)ü ýΊû• ñÈ øs Àþëçü¥ü–'ø; ®ûn Þ‡à_ü ÷ý`ü2û™úúðý)ú]ý‚üEþXý`*ùüçúÚôx²ùù<þp±õûÿ>õHú˜ûÊúªÿ-þôÿÍœþ$-ý¢Óü%ÿŒý:Øûe †— X ÏB tûJûÅú. ú¦ìý½ÿŽû²úúZÿâûUü…ù¶ö?ûóiø[”oR~÷»ÿìódù:ö®ø­üÈû¦ÿ£÷ù½¯ò>ÿðIúDúÓøopÿ?%½¦O˜,þ@  /´ äò®ZÈûr÷ûBú'wúl ~x Ú}Ñòÿÿ:ÿ°þù’üâû ¹ýɲ3Öômðõ´úÅô#üüZþÅ õ=þªó§úûfü©ÈÿŒ€†Õ\ Æ•L ÿ©¯ûèúµùn~øJÿàâùhQûnû‚FùJ Ëü@ —YtÉÿï>û¥ýÌú|÷pýìõ˜ÿúZý'2÷B_ó¡þõ˜ù×ù°öýwøYý+ý ý›ðýl8ýZoû+/ýtýy/›*ǃ¹ú€÷ û¸ûzö¼œúÙ :O¤ò÷@äø¸ûÞÿLü vrG"¦ÿ¼ˆû:âöcúÙø:ôvÿmúÈÿš_ú5 üè‘è1Ó Rõ å åÎ Ù¾úúÿ üöù´ Š}[/ûÄ{ûcýV:ýÐ ERÁeqüN³ùDýWøø§ø/÷¨ûšùDþPþÖûÿÿÓø&üÖøEøWþ+öæaúf ’z¦ ü`ø üØþEù¿~þ´àŠƒ ¨ÿ Ÿÿ€wúþ€tûN Bý3 ±b˜@ýntøgü%û/ù‚þRú®ýZü úuý½÷¦ýŠö5ü©õþ÷øö2ôúLô²þ€ù@üÈÿÊñÒûöðóiúƒôêtýÿùþ |ùJ¨ú”ûÿ«ýcÿ?—Ï ©k ÆRpÿ˲üŽ(~ ¹ÀŠØþú40úüýÝùæþ6ü™ýðÿNùpêõ•þ_öhù¶üs÷TMü 3¤ûT%õWyôªü¥ù‡úRÿ®ýÝ­St9Ûhm °™ ðã Ç Ò l’û/Kÿ2ú– ,þ\ §>orüz—ûNú_jöBdö}}ùÀþTþzúŠíö*ÿ§õ‹ø½÷-ô¿üö×þ@ý û`jø®þ›úñýíýrÿš/ÿãûè›0¯ÿ öû¢ Ùl /Ý3 'øqø#÷¯þ„öû[õíþ^Óú„–úDú6þ&ö” ÷Úÿgû2ý^àùË>ök²ò«þ«ó¡ù úšùÑ £@áýªúü®ü ýC >ÿ2 !1 +2 "ûTûdÿ þ‘­b˜ŒeÞû¦`ûsøp€ùO qéBþý‚Qù¦Ïù+ÿªúúüûÞúÿ»û¥=Ÿ—5úñýõÛ÷ þ2õ~ Wý¤ j¿1ýÿé ‡ý®Tûýì ªþ) ˯(_ýsûæûGü'ùìþ}ù.·û©&þµÿŸOýÝÿ\ý(ütù‰…ù¯&ý§þ›ÿù?ÿó¶ú‰òlôùõ‹Lý'ŽGøFvšÿÊü’¨ú‘ ¨þ F µâ Øþ+Ÿÿ ]»Úÿ‚Ëÿ÷n$ôý¶üþÍüùö‡ú2É’ÿ!õQ$ðÆü}òÚø’øïøCý€ýçûHÖ÷§ÿ@ö|û2ø`ùNýõûæ¨2 ØÔ ­ý¤@}_ 3Q C ð ’ Ç 2xKÇÿTãý| 4ø ίj  þbðûÿ”þ³øZøŒzýãúIðKüïöPöÝö>ûûŸú•ýq÷µþ¿øôÿ{ü þÿøÿæý™ˆþûeAÅûè üÐýè4ý  HT¤É @Å®LõTûÒ ·û… $ø"»þ Ø÷Þü÷DöüûôS¢÷aûëû'þ1ú6þ¶úeüöûVúXû]úýùýù]ø÷+öüžõ—ÿgú‰üpïýΟçþ·âüjþXzýñ |G = 3¨eû%ÿ¤ü†ûmÎûåóü€«ûîlþ/4Ÿû¦ÎøXû½üùö9ûüõÿû|VúHJûñý½ý5û1Õý¡ìÿø%ù„÷âÿ³ÿdü) 2p Že„ŸýÝßþ$Úÿ®/üï*ÿaüîùEýÂøø’üuö/ÅúÓìÿˆZûYÿîø™ùpûÉõKøJî1ÿ”|ø•öXü¿ú%÷¸;ø«"þZ n½?•É£2ÌÛþï A2 Xÿ;ý<ÿðþ-üYûfÿÕú ÿ üý:ÿÁúÔÿøõýröAøòýèùrŒÈú~rð>úðyò¿ø¿óÎÿ=úˆÿAüÝjüKþ;²\ÿ(0ÿ¦ 3 ‚ ïCM)ÇfïiÞS ÅýYœþòÿ|%üʆýüzý®¸õÿþ~óæú(÷­ú9û|ýrù]×ôïÿØòÍýèõZûNüuúü6ÿíÐ1ƒQDá𱆈 ) £ #WVÿÊü±‰øÁþÐ spÿˆþ¹ÿÿÓÿUüõÿ)ûßþúþöûýR÷Èý²õ>÷/ú‹ôâþž÷sþú€ú«ûùý’û¯ÿÿiÈ ÿüÞþ”B«oÿ6óûÀùutøÌ Ap ^ ÞçŸý-ydüï`ý¥ 0; ÕdEuü‹iú“þZûû%üÑú©üëûýÿ¬û¸ù²²õ…ýRøèôë6õô|ü(tÃüÉù¿°úmÿfþ1ÿ'uTP¤1þ#:+ýî¬ýd0ö B Ù{ý^ÖýæÿHîà ÿ ­ût²üÞ÷üœùSú8ú5ôMïø Â¤¤°ùø.þ0ý5ý‡ßýG‘708þ™2þ‚üÞÕþ˜¾ÉüâÏÄøØW I´¡™`ý±û³ýÚúeû2û?úÄüqûÚýøþÍûÁùDý,ùSø&þ™ö9ùú¯ÆZò¾øÁ÷ûúaþŽú+øþ³x•â¡ËÌë‰`9 Kþ § ¬ Ù: ·ù^û„þæÿhÿ•þÀÿkú0ý[÷,ü=÷üùøÝúøú|ø²üúøeýÀü¬úBÿ‹õŽüÍô5øù½ùéþ)ÿäþ>Pû‹ûZý¯Fþ¾íe2&^‡ ^ É v¹ ë-O=Î'D æ=í Éü ®ø˜ úý0aüŽÿ†ýxÿlúLý†üTý£ÿÊýmÿáüÒü€úBû›úÀúý¯øäþíõøü—÷0úFüüeÿzþ0‘ÿÑ…Sý…Í Ž µò ð Èa ߀dþoI ýRocq¦ù ý:ÿ-ü&iüãü°þNûpûcû&üÏÿ üV|ø÷þHô„ökö òªú½öxù¦úw÷ù|÷sø†ûiøú~ûZ%Üo#çÿ‰.%ȧElÔ%w¦˜+{ÿv]þoÁÿ7Þ~ÿ)üô÷ûü¯ý÷üÚýIúŠücùGû¦ûÊúUÿù./ö6ývöÛöõúÔõ£Rü÷xFûiú­þÞüýy -S|ÑFÊr טj{ ÷ JV ?+Bü¦ÿ·7ÿûÝù¿þ¤ú„ûüoùÿfúü¤þŸÿÓüû=ëùYúÑýV÷ì²úª¯ —ÿY;iõ°ÂþÑzþ“^ÍØ¬82ÓÿÚÿ´ý&…ÿU<þ1zøGþ0ù6øºÿ©ùÉ»ý­ú2ûôö-ô·ó`ø¸õû9øüü«ùèþ•ü¬©þò{üØûlüÀöÔÿa÷ÝÆþþDõ(‚0¯‰³Þ¹€+¾ò D ÿÐ9ý'þ©Týjx ý¿·ôÝÿˆô+ü’ølüêû‰ühûüsúªû_üáüžþùý,þ“üîûúdü{û[þwE¥äbe‡ ÿ†I— ªÍ  ½ª ñç0?E å ㈠Q  1 `þXýèý:úýsü]üîþ´úlþ¾úLûêû?ùîú'ù€ùbùˆúöúfýÓýûþnÿFýUýLú>ùüç÷´bý¹Qý~:üéûßü ¦Ë c | Á¥&dÈl8ý«Eý[É›ÿIýTúüÌøi÷[ùÞõ?ü{ø(þbþñü’ú}þ4ù†ùpùVö@ú÷ûªú®üÁþTûãÿ”úxütýøùÍû]±ÿ¤l­4ä " Z<½j¡ ¤w]@ò7‡šÿNþsÈ•ûhû¢û(ÿjüM×ÿó¨û'7ûï¨üìüÙýý÷-þ ö”þùàÿëþ( :ä›êš¿¾UÓëG_y– ÝjßNÜ@0H‡¶2ÿþ#ÎýùÿŰù/¥÷?ûyøL÷MúoöúyøöøûÀøüýîú[ý‘ýNúÿHö›!ö'KûW/PrÑJþýýCÿ®ú°þ¯ÅȸúY5· ÿÒþ½¿þ¹ÿÎtýSHû1ÿmú™û¯ú¨úÊú5ýbù~ÿÇ÷Âý“øùvú÷Sû{öEû¢ø¶üÎüƦgÿ/þ$ú„ôùv>  à› ¦†Iÿ^ÿÓhÿÒÚÖè³Þ ²ã ÆÔ¥uÙeÿ…rþÉxüùƒü ÷¤ù,øGúŽú÷üëü©þNþóýÎþœûlÿTúd‹ûÚ¬ÿîOŠÿ{û¦ü~ýkúkƒþn´Jè ¿ òß$SÿacȶúÿÓùþäsþÜû•ýjùˆùñû„újOþš©ÿŒüØúúÔõgúôWü¼õ;ýÏ÷ðûWù¢ùŽú†ø(ù2ûâ÷{þ¯úëþžþëýDý þÄý<úÁúè†ÿ7I[° YÝÂ^\ýŒ(ç¹}Îùþ^ÜøÕýÒõ‹ö_ù}õAÿ{û&Îc.øûß—úúý÷ ÿ?ùøýÍü}ûWþIûÊýWü,ýÒü`ý…üÿÙýþßMó‘WÇÐÁ ½y ¶  ~Ùsþ6[üfÄüvþSÿTÿW–} üý Æû1ü®ù IùØÙþFÕ±ÿý¥þuý ûZýÃùîûòùsüMü¹6 9rÿ2þ{GüÎ©Ž¡vh®ªÆvÇÅ*Ì[ùüZµùÕÿkùqüãú!ûSüwü8ý¯þTý KüFþ‹ûáùxüÿõþ§õìþxùqþtýý"þ7ûÀú§ùöùˆôküÄõUAü€$K,˜þÍSý ¿ñEÄsûiàûðþÝþ÷þVüJÓûzCþ®¯ãþÿ"ü+ýü þ›þpþ'ÏûÂý5ú°ù£ûPøÿ‹û±2| »¸F!ýÝÕûÁ ‰2 '¢¯ “¹þù¶ÿ ÿ`_wR- “° Ήû¤þXûñþ{û£þ2Æ„ÿÙ<üIþÌú%û%ûÛú™üAü?ü¹ýiúû¶ûv÷œÿÈ÷xqûFTÿlþUÿ2ÿ¶ý-ôüÕíý¸—Ë67ÃýZ¤ÿ`ÿ$TþöÍÿô’jË’Yµªÿ£¸ûbú´ú.÷—ü úý‘ÿêú!&÷ïú_õzôœúfó¬·ù ÿ·þ«úYý¨úúÔú}èýlþTþ3ù×ýÆû¦ÿD6 tŸ“Û ‰w¯X=R ÎÎîƒ@ÿs Z´ýÛ0ø² ûû›ü–gÿuÿ «úÁÐ÷þ¼ù¼ú¤þKùí[ûqÿà%Ûÿ܆=v3ÿºÿT¨[8x^îÏE&úУâ) §  ô &üì;ÿpýF´\«wùYô?þ'øŽüŽÿ~ü•ãûŸêújü™ÿÂýlÿÃùºý+öZþ½õ]îøhþИïýºÕû/ûZÿÁ÷J©ú+1 Õ]è…þ½Hþ®ýo†þ¬´¡þtüŠþ¬õjþÎýû©ûûJüÿ ÿ_xÿOþ"ýŸùÕûá÷<þºù µþbÿ‰àûRü-úö‰tö¨þ&ã…þuvýiæÿW^kÅÓ]pJa¿Uÿ¥ÿý®ÿüÍ'ýýµþdjþ{üg’ûšýGýPýÿ½0‰yïý þ ú!ø üÊ÷þÞüÖýá°ûm?ý=ýCÊüµÿ§/"ßE¡?+Dw¢l밪퀪USâþˆÿ{ÿ¥ü'°ÿŠÒWª$üGÚû°üuüùûèüˆûýàü¶üQþýÙý½ý8úþöYýÞôqü¸÷þüüžÿAþ5Èþt—ÿ«óÿ«nèÚN³ü™qÍÿƒgþøñ™“û›ÛøˆÿAü/ýuÿ¦%xÿ·ƒú ùšýoú¢úTûDøáùE÷EùÎùÙû'ýŸþ•þÿWý/þwú$þ“ùc»ûy1Æ(rÕ¹á;þ¾goåÿ*Ñþ&þå uœ†È%ÿ•Ïû¯ü-aÿÍÿ¿•ÿ]ÿ¬ÿ0ý~ÿÂüäÿªýÆÿÅþÑýþuú\ý÷÷6ÿMúÄ "^jÿG¿û«øÿŸ÷ëýälpyÌþ-!ÿ0þ Éþäÿ¯»ÿãÑ8ÊiYÇ)ÿ\þŽÄþ‹§þpøýËüƒýžù•ý´ùþgû7¤ý—Çþö«ûº‹÷ÉÈ÷è“ý?ÇLþúþÿú:û¯ÿƒüÌ4)*ôÿæþ\  ÿÿ'ÞžþK=ÿô±JþE×ø¾þpö4ú™ùaüŸýyóýížý±üÁþùžøB¸úF+þtþñÿKûÿLüüŸéú4|ýŽ[X¨:þÍÿgª޼°àÄø|Ixf÷ë(þ`ôýBÿz™<fžÿeSùøÂû´û0üóþ¬#ˆÿÖ6þæý¼ÿÊøô÷+tøqÿÂúŠþªûŽjý^-ÿQ¦ÿi‰ÿºÞ¶b¸!v! 4ž î\bxãdþ6æüú$þ}âG„-—ÚþŽ´þÃü8ü˜ÿ£ß°þ¿ÿ@þÀý%þ3û¿ü‰øûøö7ü–øKWý]sÒþµþúµÿÜúHäííþrlýþâÿ+ÿInAý^÷ûyþy>ø:§ÿ"Dþ(Fý¦üîûbýyùnþºúnÿ]ý‰ÿýþþSþ@ûÍû÷ø¯øuûÂøtþ(iŸˆ]ÿ¸üêü¿,fy›ü›.û¯ÿ¤WÜP¥ãþvùþn ÞÑÿÊ¿üíþfXLþñrüýý üVûkÿÓú‡ý`ÿÃ7­Êý{ú~åúuñý ñ:š““Íþ€ÞþïP/ŠÛ×PÕDRÐxBäþÆÕþcIÿ(þ.Aü4ÿÊü¤ú•þ9ù]µú¯‰ý¨þªýâý{úë³øñïúµõþ¥¨þ”ÿæûôÿóû'0ý•ý%ÿsüªþþÿ ˆþ™gü¥"ûëÿ~üVÓþL3R9ýúJ´úÆýêûyÿDû,ùa¼øØ¼ùfý¾ü'ûKÚûé¸ý'þ©ÿýûíúb¸ú“»ûåÕûÇÿüHÿÂþfÿÿY×ÿ"þ>þg¨\7è%ÿ©y¬nè ×ûyúøÉýŸÿ[IOÿ&Žü—ÿYý ÿ0ÃÿûöúÊ®øÅÉúš¬þ~#ÿŠÖüí‚úƒuùž%û•”þoÿm!ÿßÅ£Òø|ƒœV!þJšÿB*LþÈ·ùù5ø˜ú ýÆbþëý<ÿ—û„ý¸öÿðÿJøýoýÍüùÞýR÷±ÿÓùÓÿ£þfü‹ ú®ü¿ü»øçåø‡÷û Ïþÿì•þΕÿ±Ë Ì„,ÿüU…ú»û ýýÁ¬ÿöÊÿ8ÿHáýöÃýbíþ]^šýLûߤûê­þÆÿËþ­üGúRý&ö#Š÷ŽòüõÿjRý;`þd\us4¶¿±ÿ\¯þnÚÿ²‰r`ÙŒWj’ÿ¾†þv RþŸðÿã]ëü–§üÉ þì°þñÒýîü,”úùúqûF°ü›ÿŸý¹ÿ²ü]>ûàú]gü{þuÿÕÿ…ýKÂüüÿQü¬ÿfü<¤üðý3wýÔ£ü¤îýF`«ÿ•FŠ¥ÏûhGúþþ’áþtÿ„þ,ý6üû¤ü'ú‰ÿôøý;ø÷õøaü×þÊþÉþnü/ýýûÝÿ¥û Fý3›Žý«|ü¦vþ}Ô…»è4 ˆþT û• kû–@ÿÔC =µû°ù‰þ‰û§ý}þwüÿŽúÆþ`ùiýŸú þLü0lþåPþúŠü<¦út Èû¸(7;ÔþùçÿPþt\ÿêx­¤ÿ¦KÓÁŸQ©äx° ÿš>ýCÈûè¡û7›ûGý(ûüûÍú†ü%û—ýëüDý†ÿû©1ø«ö³òù©¹þ±˜ÿÿü‡Ò÷°Ýø ‚ü°ðþÿžÿ@ÿÙÿtÐ`UL-þÿ‘ÿ-"}º«P˜ÿÿ‚zûüñù0þýúúþØû4þñþÃü˜ÿòü¥ü®ÿtùœ†øW–úüþ/ÿtÿÌû|þìúïüêü©û¨þŽû¸þÆüíý+ÿïýá®ÿ´"?`¨¸›Ê7ÿ¿þ ü'üOcþÿØÜã¶ý_þGáýìæûÚÊüoùýžþãþ²ý†þüûÿþ ûuGû"ÑüòæþïÔTžþ¼ü-ýþÖ‡ ýQüךþ9:ÿÎ%á;ãþW„ýãýÍÀý¯þÿáüÝÿŸüTÿâýKþÿÿ—ý…óú!-ù9ù!û`[ýÝBþ²=ý6(û%aùËüwhþˆüNþŒú%þÜ¡†ÿQ?ü¡cü8ÈýÌBþ9þ1VýÖÔü!Cüqý‡ÿ¯ÿ ýo_û šü½0Ïþ– ý„ËýøüÜù§·ù6Tý¬ÿ;iý´ ú[¨úãþþ–÷ÚñXý^ïü¶)ÿûך”>ÒÿR ÙüsýÆñþõÿ)«ÿ®ÿ‰ÿ·ëÿJYþí—ûüêù„ÿÓöûú ÿ )û¹žüsü6¢ønŒ÷õ–úgþÞ4ÿaÞý¢üÕaý ÿvþZþÒùüfÐüˆ'þ¾±¤X§þC1ý{åýŸýÿÿ•ùÛùÁþIþÛû˜Zúþ:ûÎý‚ý-ú&…ùû"Íý9þ ýÿ„øAö9¼÷jûCÞýƒ®ÿ*xÇÿ¯…ÿÕÿkþí¸£¹þÃ[ü:•þOL½ÿ£•üâü¥ÊýýÿÃþ[p}“þõ”û\ÛüŽü®¯û‚oýiãÿZûþùçýÇþ†þ ûa¯ù ûõDÿ‰ÿÅ)üM¦û«ËÿPÿY2š‰ó˜ÀýÕLý9 ÿCÿáÂþ¸5Hþ®ûíü3ÿÿßÿqÿÈþ;ÿßÿÚþoý‘ÿñø^ž÷ súÿ¹þ^ùöÿÞ÷Æüú¬øQþø&NüˆLêÿ‹aýÏýÑÿaÿ þïþy\ýïËú5@üÿ=ÿT¾¯ ûÍ3p+d½·ÿÌàûïÒúÓÿ¦¼–þÿ~ýÎûgÿ¡øÄø¸/üÿøûݺûYþ¢þòü Óþ”äÛO“þÆý‚­ü;ƒý§·þ]ÿžÐýpNü {üÅ3þÄš‡¾~gC¹=bþYûOû#ìüSþéþüúæýöû£û'þúú×ü\ÿ}–ÿ«ÿˆÿ@ æþ¶™þ.¥þçýÊ´ù»søþ¯ûÈÿ_ÿÑþ+#oþ[JüùÕû*býý˜ÿ4úû÷þMÿžþŠýLûŠýj÷Fåõ«*ønåùMÀùyûçêü0Iþ‚£þWÉþÛž¡'-þuÒüÏ‹þÁþýk÷ÊþÑ~ûüùM)ý–þä}ÿ`’(þEÒý†þøÿ¦üïAüÅ™ü?Býaþùþ8ÿÂþ"þJü`ûBüOÓþµAÈÿ¼Sÿ¨°p|v_=¹ý“fûÀéû)iT<û.J rüOÚýDþ*÷Òö†ÿÃEÿaGþdxýÍý°þù#)÷Ö¿ù>ïüÊþ?þ´ûjü6üiù=ÿ³ù¡­ýÁOû¹ùþ û~ûyþü0ÿƒÿ“§ýšXü…ýíDÿUê}ì8NØÿ{gþveÿ´þÛaýdêý «¦ªþ ÿZþCûpù/!úÅüWý/ûoú3ùÍö°ü–ö›ÒúpRÿ¸ýÜÞûv?ü0jýŽÜþãÿæßþäãÿ©Š±ýqIüßÔý°±©¯l çùþÖÿúîýõÿC9qÿÿƒý yû/}üêûÿ„$kÓý”RýöþÓþKþÀ˜þK1 ëý)!üWÏû*ýÓ ‹ø·ñ˜týÝúLšü}ý•ÖüêYÿçLIÿŠÿFþy©ü1vü6FýÜFýÁÿüAý:úJüùäý-ùžú“ˆýAþu¶û Aü’Ôÿ—ÿžhÀÇýK½ú–Jû¬¥þÖþb.@Çæ4þ Ëý³ÿñÿKýDœüôqü4iüš—üIÿ¥üÿ•ûlùøùähú|ƒüÿ¢ý“ü8ü7þ]ú^Qú#þÙ2÷ûOÅú"„þÜpò'rêDþ2ŽüêìýÖöqŒîO‚á\­ ÒÿÊÇÉTÿìýìZýwÿ½þäüþZü§û#þÌ÷Í5÷èücs4ýÈsü?þ,ÎüQÿ4`íþ±êû$†û„ÓüÆïþs…><‰a¢ÿ i|÷ÿ:è=öý…+ûŽ©ü“üs–úpÙú _üëþ†ý§üüýLøžÿö'õöîšúÎúþ¨ý‡Ûûƒÿ,üDþ˜ý1þïýÿ,ý( ü{[ýÂÞþï÷u]ŠÊüòœÿÿMÄýÙ2þP–ýÏEk,ý…­ýƒýmøúúÂ*û±÷üú3þÉÿ”þ]ÿ}ýÈÿûû6ÿ ýÜýæ5ü‘2ûSEürÿ¾¬mõå‘AÿßüþB¬þTþNpg†Îþ@„þY\ÿbUÿàþwvÞÿKÿ­ÿPücºú²ÜúV!ü©‹þZýæýhûýû>þHÁû;ˆ%â3ülñùÌiüñýÔèükñ¿†4ÿ cü+Æü±yÿ~ÿMßþ{ É%[ÿ«ÿSÿìü”û /ûÀ’ü{Mþ_Þþ±ÿ%þ…þ¦û ÿõ÷¶÷H°û“þÚ¶úüûˆþÇÿ”ü¦þ Ûz@þ5þ¥+Ü  Dÿ2[þOÍþPBÿR{ÿß³ZþXeý¹£ÿØ*ÿ ÊÿÒúÝöŒ-÷»ÿ'üÛýMoüØü­þþéû¾ÿ0ü°ÿ×dÆ^£þ>ƒþ²Hj?+ÿÔ³þ\Žÿ®”´,ï .B¥ß)$x hþ†û˜Ÿý–érA üù»úýùLýòöÿSù(þýüƒþ>ü¶ý=þÑüÿÿªüB«ý· ÿ.¹Rÿ‚jý%Ký´þÔÿ›ý¡ñb>ÄFkTž(¿¦‹|M 0ºþaýuÿ0ø~¸÷¹Âü5>ú·þ”ùòúüØùÕþiúÿ:ûuþ‹û,þ‰ü_ýIþxûqþäúßûþäú™VþœXÿ0{ý½—ÿ§» gÑ:\ï+ÊÿÒŸÿŸaÿ©›loÇÿ Zþ@ÿþý2û½ÿÃø| ú‚ ýüøýdùû}ûqùßþRúàÿüVþoþJýŒÝüx6üEÏû{ˆþ0ýâI“+«úü–}}ÿWØÔ*õg¬ñ°[¡¶JçÿÕÿþ–'þ%“ÿGlþÆþÿ«ú šø³áúifÿ“ÿ±ïü½ÿbýgüZÑü‘… ãýÈiüÙ\ýÊC’dRt8J—«þ)þÆQÀÿ¾x¤^ þâ?ý÷ÀýxÿêþýÀ`úøú±süm?þÿýãûxþPøAÍøjoý]%fú€ÿúÅüêücü¯ÿÇýùw~_¨ÿmÀþÀžþ2)4‘ Ë|C@oýCÓý<t-ÿƒOÿªOÿ9ýûÿÉýpü>{ùŸ ù³¼ûÝþfülý¬úXûòûFúßýýûðýnÿÈüç@ûÄ|ü4…ãŒe¶.F§9Íq\lŽªArêÿ½;þášþËýÀkþÑ&õ"Íþÿýcþ‡ûÎþEúÜþsú‚þûýÌü ûæü7úûûÆûû×þëû?ýpÿÿ`þ/Oþ€Zÿ%žÏ gãNèKÿÀºý‡ÁªÿtlT5ONBí-ÿ`ÿÀ£û"û 'ü þû×ü6ûûû†úâú¤úŒù¦û>÷âüSøãüOü“ûúþkúôýÚû¦úCÿ5úyý_Sÿ¸+ý®Éýßî“aW"P–SÔ\(_£.ê”ëÙºÿ–zþÙÐÿÝÿ_¡þý¤ÿ9þ¨ÿ9þ[Cü•þßü„úóÿ®ùöü4`Pý-üsýcÿ¸»þ©„$r{F¨ÿ¸RÜ`•ûÅâN×8 Oòþó’þ`ÿ¹þYÿ#ýþÏüÙûgþâùÈ‚ú¢?ýþ.ÿ}û¾þ×ûCü¤þ ú»úpÊýÿWûú ‚ùº¿ûfÿþYÕÿUdÿÖÃÿd!‡ˆèÁÕÿÁÕÿáeýþ—PýÞÿEþ™ü2mú@ˆú‡eü4þiýXýûÿªø¾•ø÷úLÿ(üü‚ý#ù¸ýÉøòüÓûgü#ÿýïÿ2ÿ‘ÿ‹»ÿGNPµ8!™´J‘#/~}Ý‘VNøé¢ñÿÇþÏâþ”ýwRüððü²ÿûü2ýqü™üü5ýÅûúý^ûvþ"û=þ±ü›ýªÿü9¢ûPÑý"9¡÷E½ÄÙúìÚL35Ãb4è’N`ý·gü¡¬ÿ“ÿ!íþUrÿû(ÿûþ”ýuÿ¿ü¼~üàÿ ûSürùyúžù úÝü²ø—ÿÉ÷­þkøÔû û™ùLý‚úþ.ý_þ!ƒþ,Bþ,íüóËþhª×ýàÖü“Hÿ­"ÿ[™ÿ8I•G²Ýˆ‡Èþ¬gý”ãüÔ•ýÚÿÿvþ¥ÿ$ýÝýeü1úòû&øûlùÏú2ýVú îù°ûŽý,þüËþšhaÿÓÿHQ÷c.Âqÿ ÍÿCoTáÁí¨ä]¯ïA{/uÂÿbýkxü6þ.ÿJûä¶ù¾&úþþŽû·û´ýƒúÿQügþÿìü"ƒýnÿÒÐþ^ÿóÐ4ÿ·JþµÃÿÙ5SŸ¾Šåˆ™ÍõcßXIhÉüª‚ýGèhý«¢ú1þÔûñù¤þ1ùŒþ®úDüØûÎùEü_ù•û>ûFú!þKùZrùK.ûÂþþþÿþÿE²þízý@þf!p¾®^‚Ûaz]öþò!ÿÍ  Iý=øý±üöÿüîÿÃývüšÿúùNÿûÐýþüû>ûVü~ÿÐü>ýqýÞü@þvþÿ™,ÿ¶GÿMàÉÜó. –éìG¯Œªl)wö¸WñÅóý…Nü){ýlÿ¿ÿ©ÿ×üþHûüLûéú4ü)ûïü8ü0ü†ýÆú¼ýú³ýûšý²ý}ýõÿný Âý¿ý“Äü¥H3ÿÝžÚ$JO&.íB¨Ì ^Æÿ\ÿº¶ÿB.ÿØþý9þüãþKû`ûóÿ:ü@ü%ý”÷àýcö*ýâø‹û>û%ú¢ûzùùù6úeøÉûùýŸûRýÂþñüöÿ'ÿ ÿ¬þ¨pÿDhzg†ã³xBäqìf´–(k]6=ÅÙÔ¶þÒÿ°‘6ý=,ûiÝüùü¶þùû¼þˆü¾üHþ3ûÿ©û¼þ`ýíýƒÿÓýMÿ¯ÿ•Cÿ †ö˜zú”ëËù, ØùçLÿŒ‹ý3¸ýšÿÍÿˆý,<ýN^þ–ýzþäúý@ú#üœû£úöütúöüNû ü£üÕû„ýuþáýÄ@þpÑý= ýœŒý·› ¹Ž¢l«\ÿ¼9ým˜ý»ªÿHþYÅü0!ý6Aþ¡ý„þŸû‹ý‹ü~üÿýüþÃûUü½ûÄù¿ûJùaû›û²úaÿúúóù6ûbþ{j›‘Î2L­g‡Š²Y€ŠY‘äøO`íû†@qÿÈÈÿÙÇÜÆÿ³2ÿý'þëúÙý¬úJþÙûOþýRýþuüöýmý ýUÿÅüþ¾þ  ýâèü#ÜþÖ£pãNjB„»f“h’^ï+A³^¿û ä¶âÿârýÙ°ýÂÉý@Žüfÿ8ûKü_û€ûhûäüúêü†úRúqýø°F÷ «ø¨þOû¾üjý¸üñýuÿUý`þÝ?h÷ÿëÿ{úXüß.Ü´ƒÈâf;éóõþºÿ=þEþÚþ¬ü ÿFûÿÎúýUüÏúëýtúý/û¹ûÚûéú#üµû!û/þ*ú¨«ú‡üîþÿýküÝýõý©þÇwz8Éêrn²^º—ávðâ˱þÊØüþ€ÆÿWN:µþØ|üiÐûfÿéüýü:þOûÿþûPþýûPüPý,ûVþIüÿ ÿ¨ÿtÖÿ0ÿ÷qžNg‡è¦ì<¾{~3ÿD„C¼¬ýdðü.ý´ÿû\øûË?ý ÿhýìüçü®üDüTý§ûJþú;ÿù#ÿ1úGþwü·ýÈþµþwÿuçþȉýhýÛêÆÄßVœÖiÌÚBÿ‡§üÔýüër]F6EBþ+üêýîü¼ü-þÚû´þ»ú(þ(ú;ýûtûýüùÈýgúZý¬üVü®ûzý¯ãHúÓê¾qÖ x¾el6û"vÉ*™T‹þYºþçÛÿ-)Å3ÿrÿ‡ýÚþØûLÿ˜úpÿ”ú‡ýtüwûöýÎúGþpûÀý)ýÕü&þlýtý‘üò­ýCáèä—ÆÒ þI¿`ÖlW—(-Þ¥Î2C‹KÖ«Ýÿ•7þ¿”ýSÎýVýþ…üãüéýÂúŠþØùºý;úêû’ü5ú ÿúËÿ°ú%þ$û³û¢úãý½ú'ôüÌ_ÿ½Úÿ}éþc™ÿ Brƒù€êQztáQŒùÌÿ±eÿY¾ûJ_úÿ¡ûUþíüÞüýoülüüIûjüQûõûðüüNÿý‚¥þÿ{ÿCý“ÿ*ý)ÿi`f®Ô«Üö¼Tß,–›Ãëm—ËÐ8•þ04ýlÿ·þDþËGÿAÿÛÍû¸hú‘ûÔÿøý0ÿÿ0þýý,ÚýÙüþž4ãÿ€Eÿ!§ÿøfõ·éÏ$ÛU€®_8ŸÊÌb¬ññÿº÷ûæú7èûÆþÎþèýôÿ üíþRû!ý0ûRüAüËüýœþüvãû¾7üÖÿëý¯þþϼþ‘ÿ‚ý¾Zý Oÿ70î`HàŽDXÛýÿ/›ýkŒý!éþ0‘ÿ5+þµûhÿú¢ÿúÖ¢û.Lþ@ÿŠåüIýHý£ýðú6ý û4üýVûtÿûûùËý*gÿ§™þ`Aí"g¡j^|M‘šÌfG + ·¸þšãþrˆÿ1—€x¢ÿ<¶ýÝüØ—ülÿÿWþ³6þRÇþêýƒÿ€ûÐþü,ý…ÿ ü©œüÌêýOnþ7ÿ =/ tžÀ¹å1¢v—ÌŽRÛÿ,ÂÿûÄ|~=þ§>ý6sþ¢þfÿ½þ”ýÎþdú±ýgølürù¤û\üòû4þ»üØýïü>üZüiü™û ÿ¾ü GþY~þÎíüÔ5üe“ÿŸÓ8Ø)è ó\’+ÿ(rþœ¥ÿߌ/Ïß¡ÿ@güXÿûÿÔûªÿý4ÓýßÿýýþÐûMþÇûmþÁý­þwJþùü- û×ûãþ4D'Àÿõj'Túÿq<ì¶ðD$p^ä1cþ7ÿ:^ U /þÿñý¯ÿ‹þi5þÎðý´þbÿpü –ýñÿÙÿóÉÕÿ óýïÀü¯7ý(ìþm¿ÿYcÿÿï}ÿT{Í?ìÿZSÿâr¿J²þÿB6ÿmýÈþ°üÉÿÚü)4üƒûþþ¼û.ýËüþtüÎÿsûÌþ|üoü§þ@ûäuûjIýúÿÿõý ý©ÿ©ÿÿw-fàíh Üÿ+fËŒçtp/ËÿãÞÿ,Cõ5ÿÜ•ýjÿðûœÿÃújdú`Vûgÿsý>þìþþ¨þÆþ"ýÄÿ¡ûÖÿŠüÛý¼üzqýùgþ—ÿÌþkþÿþÍÿ–pñºÀßä€ßbe©™¯#¯BÌLÿü®ý°Àý´þéÿýé8û†bü’þõþ ý’èü'üØþ{û‡ýˆü ýOþùýSÿ¿ÿd½þ.7þM8ýHýçûÞÿ÷ûÙËþZœ~¡ÿVCþmxÿrì†ÓFùØþýüÃú&ÿøû›ÿ/ÿ-bŸ ^üïþøù]û±úMûMýŽþÿïèÿ~ÿXýýêú·üûÔþ²ý UáSÅÎÿ®ÿ¹ütý(úŽþÆúêþT»²‹7dþÿþ¤üÆþÝýy´þ8oþúýÞÉý%ýàÿ—ü$Ñý²D:Týîþkü%þÅýNOÿJñþûÊþnýnÿäûÂÿãüàÿòýùˆý¤¯þ ‘J°…Ö³ý\þÿ‘ÿêÿKPÂòëÿfÿ4þ«ÿ5þá*vÛ΂¥ðüLú™ÿ-ûPÿyþTÚ Ämþ‚ýüü=úsþúûþÿiL®þZýÉýfúÏÿsüÍÿt´3&†Ör”òüÐþƒýVþÀðÖ£þíÿü¥ü ýý5ÿKw„JERþÿ:ügþ™û­þHüªÿLýUÿ;þ÷ýTþØû£þ_úÿ(ûðaýG™6Úãñ—½þ;ªþã„ûSþ NþÅþ‹>ý3|þ Òÿ…ÿÛª¶ËÁýCúšÿJú5ÿ¬ý"ˆýj|ûSŒü¼ýJÿoýXÿ4WˆÿV'QÒñþ}×ü µüêfÿ¥w°¤©íÿüÇþ’ÿ¤`zðõ-³¸þÓpÿ/ÿ(ìýþ¾ßþ²kþ½ü.üìÿMýÖÐþ,Õý[ùülý;þ…û=ý»ÿÁÿéÿ$ñþ|dÿÿãÿÿ'ÿÚüÉý)ÿÿ¡jìK{ýúk þùù^U>—î(¹- þð±û³ûÀlý þ~¬ý8þ`ý+ý<þþÿ:bÿpÌÿWÿ…ôþ^ÿñýSÿBýÛÿ²ýÿRÿÁýKü¬Ûûi·ýcŸÆžŽº­` šMFþ’?ýµ¤þØÓdzÿLŠüòüï”þÆú‚ÛKÿwþÐÿ8þ;T¨ÿ¤ýéþÚýýéÿþ±~ÿcÿc¿þ³ÿoÿþ­mþ¹ýÿtý‘+þ& œ’®âc VþâîüöDü•Qý½ÿ8ÅÌÈ=ÌÆþU¾þD‘ÿ9cÿÆþÖÿÓþfÿLÿêþ:ÿBþÂý›ÿ‚ûMVûÓ0ý5àþªþ¨þöþ\ý]ýæÍýû7þßÿRÿÿ yþâ“ýoý(þ?þçý+ÿÀžýÀûáøûwþHŒ)gCÿ‡~ýÏ×ü\øüÿ®ý¡ýàþ2ýíÿ?þNÿøÿþÿÒþÝ”ý êüáý\‰ÿĘ:GjFÿpÎþg¥ÿÔÿ×ÿEþõ3þ¨ÿýÿ£ÿa«²þtÚûfzúÊÙûSÿþôþÿ)ÿ2þÌþþ…ÿµÿÀÞš"àþIxý×oþÓ…+ßÉþgÓý1ÿSôŠ”w<¯žÀv0 bð+þþê¦üQqûCiý{ÿBýÿÍÿ©õüQ×úÊÿ®ûÿøý7þgÿöü•ÿ8üVÿÖüZÿòýˆÿåýÀ1ü²¦úeÜú.ïüiÛþ(®ÿ¬¢ÿÒFÿ8òÿîò™S=þ¼þ*Ò gÆÅÂÿ}Ìýìßûhðú¾;û!büÊþiüÿ_û^ÿ<ûËþ?ýöýðíýÅ þ‰qÿ¯HË©ýÿ,[ÿsþ³ü½¢üHþFƒÿÜý«þ ÿ\þs„ÿKœtð ?ÿ€Gÿ¥–ÿè¼ÿIÈÿ–Úþ_CýmüöýšÇhî~>ñ'ÿÆíÿ÷ÿßÜþå¶þèçþ1Áþçÿöþ¯þQÿ‚ÿgÿŒìþé¦þe:ÿÅ@ÿÎ*°tÿ”$ÿß‘ÿþ/¿ü×üs`ý<Oý¡“ý6ÿœþÒÿ«þeþ'²þjÿ‹þfïÿÁB@ÿôþ[ýDþêþþSºþõü¾Ñûþý¿ ÿÜÿ<ÿ}ðýÔÉýÀnÿB–Ñÿ1Hþ wþàjþÆñüUÿ²ûþ˜û\ÿ«ûÇ"ûõ¡ûðÿIþ”ÿ˧ò±^þÀgýè)ÿqó¶ÿ„þ1)þÙÿOþ ÿ¼þœÿ¬þ¦xþ¢zþî¨þ°¸ÿÖ¸ëýd@Ãÿžçphþ¯aûxãúæ ü&Oý/Ùý%þ`RþR‘þk~ÿhN˜‹ò¥òþþéCý‘öþÿ×Îþÿ§þ7)þšþÈhþ¡þ÷>þßþÁ!ÿÕ»[¶ÿÿCþ,þ«ÿ‰þ7ÞÿºþŒìü«õûÈ›üDþÿ‹%ÿ‚CþB^ý3ýO@þ Ýÿ"ÿˆ ÿÍÿ_ÿ¼þcÿËþ—þQ®ýN‡ýWþ!¡þxÒþ›»þØþ']ý ”ýÚþª6þî&üv…üÔÿþ7Ûÿa˜þ ãü†Âü_þ¯üþ¢‡þÁþÆ(ÿèNþ`àüÈ.ý‡þ#<þ§Óÿ°À|¨¹ÿ¤ÿ~ºÿ*Åÿ˜ÎÖòÿoÄ\ƒðÿ^w ªÿFtþ=™ÿ‡ àÑ2ÿŸJýdü‹üYìýsKóÿm,‡¨Øh%žÿÄþôËýN’ý‰áý{þ0²ýQýlýªý_#þ8ÿ=¾ÿ”¿ÿ-ÿ­oþNlÿ àý98ûtåû¹ýq›þãMþ"³ýȺýW“þÚíÿ”°hÿ~ÿôìÀÿdÿjþ µýû8þ¥ÿÆ^ÿDÿÿ(ÿ×þªÿgþFÚýÍèýßÿÏ”¬<þæÿœý“ÿzþ7üþO,þ÷Eü8¤úf9ûo¾ý…H§ò>íæÿèaÞºßw˜‘ŽïýèFÿÛÿ3\þ–þ¿¦ÿì¾ÿÿ"Çþmÿnkÿ;’ÿ£ÿê$ÿ[üyÿ%üÐþþ{ÿ\ÿÔýk–ûq–ûÄÿ%þ&œÏªg³RÔP½˜ÿ³ÿ3[þã–þª ÿlÿÿ^ÿ2ÿÝþþý\dý ýˇýejþÕúþ!^ÿmÿÔÿZÿöÿ}ÿÿ@½ý û;±ú“ÿû…ÿÉýqÿCþ.›ýâLýuÒýëÇþóŽÿ™÷ÿ,[þF óÿ—šÿ™ÿÞÿôý+ ý{TþKùÿõC,nÿÒÿPšÿ£†Z>Ó1ÿ’FþðýéÿQþµÿÿuþ éüXrü¾ØýöÿÒB+L!ßÙ›Y[¼ÿ˜ÿNþøgþŠhÿNüÿJ*ÿg8þª+þ¹äþ€Œÿ'§ÿ©ÿHÿ…ÿ±þÿþ©ÿ°þNÿÿvþ‚ÿ“ýˆÿ@ýaÿpþÿÿYcÿO~4ÿ‹Yýßmý òþ'fÛŠmïÿd¬ÿýu$ ÿÐ$þ‡Kþ™Bÿ§¥ÿ÷ÿÿãÿ"þÞþ RþÛÿpþNþþ¢ý±þOþœþ&ÿxþVÿþZÿöý£þSÿ'ýrËû\üŒÿ‡þpÿ6ã^ÿ>aýÏ‹ükÛýœ2'Ï0ÑÁ {ãJÊ,ÿÜÈþ@áþeuÿí²x.Jvéÿ©BÎÿ˜—þ²¼ýÌ~ý‡uþôÿõÿ€Æÿñ„ü~ûYûG ýÓiþöjþ þš—þ €Öÿëèÿœ Î7ÈANZì'ÜTÿhÌþíþýXÿ÷ÿg%þ´ÓýÁäý5;þ%Ùþÿ¯ÿEýZsü=Çü\ÿý“þÉýÓþ#ýbÿ9ü°ÿcü þvá%ä gär£5þÿ/ÿf=þ§nþózÿ¨|ÿ¥þ€äýBþÃòþ´Êÿôÿ;Ëþ“ÿLÿþBoþ‡òþ–ÿÁÿäýtHüCfûgûvßûðü™ÆýãýZ!þ¿Kÿ˜9õÿ„9_½'žÿÿgµÿ‹lÿ¨iÿ«‘ÿ }ÿ}þV þ„ÜþòÿÔ`ûþíÿ¬ý±ÿàý=þlHþ5BþËÿXþçÿØý£üå]ý<ÿ‘c&‹ÿŒÅþÊŸÿtKÿ;ÿzxÿ¯þ¼ÿžý-dýhôý‹”þ›†þˆ?þcMþ:qþ<%þG£ýóý=þ;þ ÿ¶üÈÿüÍÿdü›ÿ“ýïÿàþÔ.ÿ§ûþÏåþ°[ÿ$ íXà¤bÚÿë=ÿy««¹¨^6šÿŽ ÿ0Eÿ=SÿÈ–þÈùýzŠþv÷ÿþþ‡$ý‚‚üM\ýÅÿ_þÈÿiþ1mýˆcü_üœTýMÉþO¯ÿ÷[ÿ:þÄþTÀÿÒ“ëôm”iL&%ŸEÞÿöƒÿRjÿÅaÿ™*ÿšäþØ6þ¢|ý¯ý9zýý@þëú4þ[ûþ:ýµÿþ¦ÝüâÐû‘5üѨýa ÿiúÿ«ðÿ2ÿ‚Ïý,ýñÿþ3ø·¥ÿîþ€Ùþ'¦þ‡Dþ þýþS^þÐXþþÐþ±ýÿ£ýoÅý¤Ùý|ÿþdþ¯þCþ®ÿÿþ€;ÿ8þ‡±üؤû?ûO¤ûøüuÆþÏ} ûÿs‚ÍÿÕNn›e(e>G§ÿçÿ×ý»ý8þI5þîvý=ŠýBÊþ,ÿàÿìþÏÿ˜ÿ‘ÿQ°ÿ›ÿ3þž^ý»ý–VþsŸþ>Pþnµýú'ý‹žünýaãÿŒ`?%6gÿ°þîàþmÿ]Äþþþÿ þïþÔþ þhþÿ¤ýUïüxóüþ ý·ÿWþ®þ:ÿºþEÿÿ÷þŠÿ‚ÿáîýý¹ü± ýãhþjÑÿsÇÓ‹Öÿ%óÿ7À,Ï“é’ÃF‹ÿ£þÞþH£ýW›ý ÓýGÿtþ¶þ#ÿþÿÿ‡þuþÄÿ)ÿãýnÿ^üÿýÛþ•ÿŸÿ“ gÿ—þÙýC5þ^BÿCOé µƒ’Xa{ñú Tr;UÊÔ-èœK_ÿÃ!þ]åý8$þ“þgþ=ýsþ´ýÂþÉþ¦ÿþN8ýV¢û„œûÜýÿÖ:[óÿRGÿñËþÞ'ÿ>çÿöGE‚Øpù!õâÿê•þ'þ”­þÑ&ÿŸõþpƒþ¯ËþP¼þFþ^¤ý.ÿ×ýþ{þÚýŸþ­þþ,èþy”ÿøþýz„ü þs_ÿ;œÿjÿ<Iÿw>ÿçõþr~ÿÿ\Šÿ§ZpJRðÿ4ÿªZÿ÷Jÿ¥ýþ\‡þBbþÄÿŽþ‡ÿÛþÓÿTÿðÿ›ÿUÿÜÿWþêÿmýd:ýÔþRÿ3ÿ[&ÿ îþòý‰Óýõfþ)ÿô€ÿªÔþKUþËýþ!ÿ…ÿuzÿ¹àÿþˆ£ÿæÿBÿ5ÔÿG¹+¹Fÿÿ+þ\þ9þ‘þkþÅþƒþvþÂþSþ}ÿÿSÿYþ´ÿü¦…ýßÿZóÿAÄÿ~×þ€UþËÙþÞ ¨/E)1'Œ1Î<Diñ &úþ™/þ¨þ…ÿ‰þXÿÿCÿ@ÿëþ×þ€þzþþÇþ¥ýCÿ ý^ÿËþ,ÿ[3ÿ[ÿg¯ÿ½þ–ýQÈýÓïþdAÿ¤rþç þ0)ÿ]p*ò‡m„b÷¢ýLpôÿ–´ÿÀÿ¥ÿûý]þý9þšýôþÓþ¿ÿ ÿÑÿ—þfÿ"þ²þþµý\ÿíüÈÿ ýøÿ_þo1ÿ¸¬þS¶ý­ÿ;þÔÿh"ar~™¿çÆÁèÏô˜à‡p.jË*’ÿ uÿ¾ÿŠ&ÿ-ÁþQþÝþ©ý}þëýƒÿèþx‘ÿnÿ‰ÿ1þ–þþ¥ýKÿ¢ü§”üYýà þ;þÆÿÝý¼ÿ¾þ†7‘²Hg…ñÖØ3óÿðæÿcŒîÿG”ÿ ÿKþüÿ×ýjIþÅÝþ'rþ%MýŸþ\ý¦þüþÿs™þ"þlþþýÄýþ;ýÓþîü¢ÿÂýE5ÿÄRª dxÜÿãƒjXgÌ”àiÿÊîþf<ÿóÝÿÚ>´ÿQCÿwïþ[ÿ5ÿÿþ›ÿÌÿÌÿ²£ÿÎþ‡ÿþÆþbþEþÉÿÿýÝ‘ýî6ý±ÿ7þ¨þÿçÿ§þ…õýþëÞÇJ×¢=m³…m~ñÕj¢‡ÿ­ÿÀ!Â%­9yÿGZÿRÿÿþiÿPþÿWþ³ÿ©þ¸ÿ¿þ4ÿ¨þ¬þÐþÜþÚþÒÿTþŠ@þRöþ¬ÿÿdûþæÿX1y—ôÃ$wÿÊ—ÿ)Rªä³—ÿª—þŽ)þ©þ¡ÿHÿ+ÿ×þXþ$þѾýÿýÌýýÐý+þUþŽÿÍþÆÿ+ÿ)ÿ„ÿÿÑÿ«ÿÁÿoqÿ¨ÿ9ÿ ^ÿÙÿ~ÿè ÿ¿g'qî èéû·+p'F¡Ùæÿk²þì þ(ÿ×þ»ý@ÿÏý¹ÿÿèÿ¦ÿFÿ?ÿOþ¯þ7þíþJÿ–ÿÄ‘ÿöôþCpþùÿnþZÀþRVÿ÷ïÿŽ|š;&øÂt=OmE°Ãv"ŒÛº†Ó&òÿ mþ0ýŠÿ4ýÈþ8þ~þëþÿþÿZÿÛþÿMþiþïý.þGþÊþQÿÿŠ=ÿÝÃþûÿÝþLÿ‹ÿÿÏÿ ÿ5­ÿ"¥ªûf„Ãêo¸É„cåSÿìFþNkþ *ÿÍÿaÿbÿÎþ)ÿ™ýCÿÚüŽÿýÑÿ(þ©ÿ]ÿ‘ÿ¹ÿ±ÿzÿ`ÿìþØþ^þ™þíþ’þgÆþ.ÿËÂÿ__rpeg½Òmô&ÓHj|lVCŒ ï0:ÿôÿ9ÿ.}ÿÿrRÿ1 ÿBÊÿ»ÿ°ÿÐÿÿÒÿÕþ'ÿ·ÿ ÆÿáþLÿ þÞþ“þÿuÆÿ€‰+8i„\”Ê·éø(v“ñÞÀÿW{ÿ•IÙh|j+¯@ÿwãþÅÿåþÕþ!ÿ‡þÿ”þšþƒþ¦ý/þ9ý—ýæýœý+ÿþËÿ¡ÿwþ¤úýÛªþ_RËÒW̽*©ÍþÕÖþÖ»ÿ¡„äÉ5xrÿ¬YþT4þòÿ&ÿµÿA˜ÿlÿxÿ*ÿ)þ©þëýþý¼þ¸ýÚÿ(þ)Ðþ£ÿ ÿ ÿðþÿ ÿîÿ¾ÿCi#o†ýÿl ÿ¯ºÿGw¾"ùÍwp ˆôÿuRmwƈœºÿS‰þ–‘þýÿÿ£ÿœLÿÏÀþþèþ¾ýŠþþEÿþ‹[ÿ>ÛÿðÕÿ«ÿU½ÿ/ûV‚×ÿlT7w{öW¡ÑÜí!ÂDqÌÿñÿˆ\Áÿ²åÿÆóÿøÿˆÿ€þòþ:ý$ÿ ý»ÿþëþåÿóþ—ÿAþtÿŽýÿ:ýÿœý%ÿ ÿáþïÿÿŽÿ—ÿØþéÿþÛÿÿ™ÿåÿ¨ÿEYÎusòk®2SFÄÌüª°~ÿ÷þˆ4ÿF+‚ÿÅlþýÿÌý²þþ2þ ÿÿ¯ÿuåÿbâÿ,Üÿâÿ"Ÿþ ƒþ}ÿwÿÿ¢ûþJGÿZ=ÿÿ ^ÿ¿¥#dTè«V> óõ¸¥åÿ*HÿwÿLÿwÿÿ ÿòÿhÿâÿ-Ôþa(ÿCÿìÿçþš|ÿ›g ·F:iÿùÿ©þ\ÿþ@ÿDÿ,—/§i—õ”õÿ¶”ÒRvUë83vŠÿ6#ÿq@ÿ8ÿÂÿWþÌÿ#þ|ÿBþ ÿ}þ´þÍþÝþïþ;ÿ÷þ_ÿHÿØþÒÿ þ¡ýóÿõýŸÿÊþrÿCÿeÿIÿÿµþÅþþ1ÿ/þySÿÊYÆ-ÊrR…Ýÿ{$~ºÿ”=ÿÙ¹ÿ8IÿôÿŒþ­þœÿ>c,BeÆÿ§ÿœÿØÿ¤ÿ”kÿ÷éþrÃþ†ÿÿöþ¾ÿäþ¤ùÿÇVš‰ëà8í‰häì@ÕÔÿê]ïÿ£ÿ*k=øÿ:?7ÿ´þ`ÿ þ­ÿiÿ°X7æãÿ¯ÿ ÿ†þŸÿ1þ”ÿÜþTÿ¾ÿÿûÿ6ÿAÿýÿSþxþ_yþ34ÿ€¼ÿ?ÛÿÖ§ÿ¨ÿ–‡Œþ9£…5ÍÐÿ½jÿ„ ÿÃ\ÿ1>  ?uZÿ†þÿ¼ýÑþkþºþ‰ÿWþ ÃýÿÙý˜ýöþýäý•ÿ*ÿŸþ£ÿñþÞÿ_òòÿ¡ÿ€ÿÆþqÿMÿ¤>ë¦øN\pÊÐG}b¼ìéÿÊÿ?pÿëÿ¨þ1+þ„ÿúþ ÿñÿÿòÿCÿÿ§ÿþ äýEÎþÁÿi5ÿ" ÿØHÿ{Åÿm7ÙhQû¹áŠ#?à[èÿE=lJ®m¤Q¾eÿÖ$÷öÿÝÿíý@ÿóüÿUþ|ÿûÿËÿ…Ýþqõý9ÿýjuþ›Áþ­Yþ™ÔýwPýËný÷¹þgµÿY–ÿLÿRsþþÀþ Àÿµ¶ÿÙ(ÿ[ŠÿV%¿ÿùÿóÿÎþaÉþÅÿÍÿÄkÿÄÿÈþÛþ,ÿÿàÿf.¤ÿ•ÿÿÿõýwÿÖý#þ4_ÿ‚ÿ ÿxÿWþgþ^5ÿ³ÿòþ•2ÿøÁo½ßÿ&´ÿ×Ñÿ?èÿ·™ÿÿ©ÿ«þ€þ½¢þ°¨þ ¦þ<ùþêÿ‹ÿ5ÿF ÿÍy¥¹ÕÿÿÀþÿ2þ;ÿÀþèþŸÿ¶þzÿëûÿøÿU’C8ñ7‹—`µÿy.ÿ·ÿ2ÿ«ÿºÿêÿi#Löÿÿ•þÐÿ¬«ÎÿICÿAÿ_vÿCÿjÿOþ0ÿþÿÜþ8ÿtÿ“ÿ7ÿ¶ÿ9þ@ÿ¼ýäþ?þþþ4ÿ‰ÿ^O.Ð<àƒ¾½ÿ¤Òÿr¡qMRðãÿ ÿ­ÿØþiÿXÿÚþUþÕÜýkÿZøÿ3´ÿÿþrÿ9þÿ\þèþÿÆþOþ1þ–ý=þáýªþÿ6ÿÿ:²ÿŠÿ+¡þÇÑþéÆ=0âÿÙÿ6ÿÑÿjÿìÿGÿ¶ÿfÿnÅÿÜ~µ-,1ÿeÐþP/ÿÊÿ…¶ÿÈÿ1ÿòþÔþÕþôþÿ‘ÿëÿÌÿîÿyþ™ÿšýÛÿïýÇyÿ®Ôúÿüÿ¸ÿrþmÈýlþL*ÿŠæÿè‰ÿÔ…ÿÿÿ‚ÿýÿÕÿ w’½c™ÊW +ßÿCÛÿë¢ÿdÿ<ÿCÿ˜þ¬þÕþÞþ™ÿ§ÿQ+³ÿÐ_þ©ÍýâðþëR}ÿÝŸþ_àþ–ÿ‚ÿsÿÁÿ·ÿZ÷þ•.ÿöÿ|Q–ÿ©ÿËÿ¼ÿ·ÿRßþ?€ýzUý®þRþÿÿEÿLNÿÐÿÐÿäþo^þˆ+þ$§þWÿ’ÿöþìÿcÿ•ÿïþvÂþB0ÿÕÿÎÿ¬ÿEpÝ€l•/Ü‹OK°‡ðfdüÿ4 ÿ¹ËÿÜgÿoCþj`þE&ÿ²ÿáÿ½ÿØÿ%£ÿ=ºÿÓÿDÔþ÷ËýÓ^ýY¦ý]¥þºðÿáµroÓÿ«ÿÊÿnÿV¿Ê‚ÕÉÿƒÿwÍÿjh"iÿ’Oÿv—ÿ´çÿ+õÿdôÿÎÿ·wÿ}ÀÿšÕÿЩÿuÿÿïÿ~ÁÿœžÿÑÿ%ÿtþÆþ8ý9ÿAýÍþ!HÿÓÿ·þ¸þRÿbþ³ÿYÿ]ÿÑþP…þ)øþ¡ÿŽÿ-ÿÒÿ ÿyÿšÿØþ_ÿ—ÿXÔÿÓµÿ€ÿàÿ`ÿr\ÿódÿ“ÿ+ÿþ‡óýÀ°þ”·ÿýÿ`-ÿÓÿ ÿ›þžÿþ1µþ–Èÿsfÿÿ¾ÿ/ÿìÿ¹þonÿ÷ÿ® ÿ%ÔþmÿʼÁÿí~ÿóÿ>¼ä(t,=ÿ+{ÿ_•ÿÜhÿaÔÿÈÿŠ1ÿ~Õþâ4ÿ››ÿm¥ÿ’PÿÙôþÇæþS8ÿÆ"&}ÿ—ƒÿQÑÿÁ'Œ^ ~ù_ƒñÿX7ÿpÇþ;ÿ÷ÿTÿ’-ÿÉÿÿéþLÿ#þ’ÿOþ®ÿÜþ ÿ9ÿ²ÿöþp}þ*þ÷nþ<þöÿþtºþÅDÿ]ÔþØÿCþ²ÿÜþÈÿŸtÿú½þÙbþßÉþ¹ÿWï'ÀZÿÿ³þ9¶þ…Âþv›þ—nþ°aþ‡’þ0ùþžÿ×ÿôþ“µþÁ¯þþ¼þqáþÌÖþ‡|þËþ<1þ1ÿùÿ@­ÿôÿìþǺþeßÿžg‰bà³ê9üþa»þmÿÑÿ~gÿ¢"ÿÎÿpÿFõÿJ<SO¦°þSþIÁþ@BÿKÿáÔþÍgþ{jþ÷ÿ"ÿ‘ÿ¼ÿzA„²!•ÿ8œÿÇœ¦ÿÆÿÿ½ÌÿÄþ¥ý5ý¶¶ýpcþcçþ"uÿÈþ&Œþx•ÿ¬¾ÿ›¨ÿ•ÿøÿ~þ_Eþ.Åþÿÿ ÿÅþÿ>þþ1þëÿ;þÉÿñþ @G >Îëÿgÿ¸ÿÿRàþ%+ÿIÈÿ”4¡ÿIÿ°ÿþÿaÿZŽÿ0ýÿ¸w2 ²þŒ,þ­Èþ ÿi_ÿŽÐþªþÿ±ÿ“ÿS)ž‡ÜØv¶ÿCCÿxË_É _Ä 9½ÿi˜ÿZ…ÿ{ÿ„îÿìªoÿÜþ>ÿÿåÿðgW¯ÿ£ãþþ´ÿÿ°ÿ¿ÿüÿïÿuMÿõOþ5Ôýë þE,ÿ£ÿ<"ÿ—ÿC0ÿ×ÿoÿ „ÿçEÿžÿs4ÿ®÷ÿeY³ÿÙ¸þݬþzËÿX‡PÊÿ*ÿ³ÿöþ52ÿÚEÿz¢þÓõý¯ëýÔ¦þ¾ÿTÿYÿdÿãÿÆþÐÈý&©ý¶•þüÿÌÿ­ÿ¹ñÿ_&‚R ËÿeDþ°œþ°Äÿ¦WÕŸaÿ 9ÿQ€ÿËÿàÿ)¦ÿ5ÿ¾ÿßþIfÿf”/ ÿ$þöfþHÍÿ.ýçßYv*+>?ÎÿF<‹.D•›Dúÿoÿ&×þ8Pþ©þÊCþ?„þ!ÿ¥þÿËþÐÿAÿYÐÿûÿ. ÿCZþnzþ‰4ÿLŽÿHÿÎ(þr±ýZÏý™TþEÿ/3_„AÆÿùÿ Òÿêè5FŠQ¿ßÿ·)ÿqG„ÿg´þÄ¥þb>ÿèÿÊÿnÿ–ÿÅÿEÿéÿƒÿÖÿYkÿ>íþœ¢þ-gþ%þgÿÿ-ÿbÿ;ÿ2ÿiÿ[þ‹ÿÁý¨ÿ(þÚÿ?ÿ7¨!ýÿþxÿ~™Äÿ0Dÿ,÷þï ÿ™ÿ†Øÿž™ÿSeÿÑÎÿ‘”Ì×ÿæÿiEÿ5Èÿ-+2B·ÿø¶þ8þhþÜwÿ—4V ÿa„þ^ÙþBvÿWJÿ(Êÿ\9-ƒšÿ)ÿÖ¡þšþé¬þÌþ³ÿèþïþøþ<ÿTÿ ûÿh±³ÿ$™þ.èýñOþ´cÿžA—1¾éþõý8Dý+Iþ0ÿuÿ Þþ¢'ÿ&ùÿCÿãþMßþ$–þªaþcþ¬‘þ´îþcÿëÿm6…ÿ0GÿiµÿЦÿÄ,øÿìþAýüýËŸþÙ÷þIÊþ܈þ³þE7ÿAôÿ“ÿÜ’ÿj&s©7«Bª›ÿû”ÿ%ÃÿÿÇÿd­ÿ‰ÃÿP¼ÿ ŽÿØHÿ€aÿ¹ÿ2™þ^*ÿ¾ÿ.iÿÓËÿKªÿDþéøýc\þ¥þN³þòÿM.ÿõÐþ'Èþfÿ«ÿÿ>/ÿ8mÿ¶àÿq$¾ÿöÿ|ÿéRÿªÿˆ»ÿgÿ~ÿòÿìþÔþ®Hÿiãÿ…;oÿHÿ, ÿlŒþ(ßýîXý0ýcWý•·ýN¡þsÄÿj^pÿcŠþg²þôÿlóþºÿ“éÿñfP+eSÿ ÅþâþÑŠÿ™5Jxñ;¨»ÿ £ÿÊøÿÐv ÞÿûGÿ¾ÿxÿ]ÿuyþ°Ùý®’ýNMþ˜ÿ£ÿðþsÿaÔÿ±ÿ=fÿÎÿÐÿÿ„¿þ=ùþ5­ÿ¢\ÎÿˆüþûŠþdýþàÇÿ-n¦¸cVÿgÿל0à ÿf¸ý(bý×ý"ÿ\þ¹þQþÿ¯ýØÿSýWáý&Ùþ£ÿ˜ÿ^ÿîÿ–ÿùÿãÿêÿÉÿûÿAÿjŒþéhþNáþŸlÿ¸¶ÿcåÿÐ0Î…d§¤±leãþM»þFÿ</ÿÃþ|þ¶RýxZýÂÿýýAÿ|þSÿ¬þèÿßþgXÿr“ÿ6…ÿÒÿ¥ÿzÿPJÿR-ÿÚûþ¡—þh=þÐ6þ·†þ&BÿZ/1ó¶<5ËÜ­ÝâMÃÿ>ÎþdTþRÿVþÈþªþ:ÿšþWQþ÷ñýÂaýNOý7%þUyÿ@fÖÿHqÿ‰ÿYÿYÿÑþ%!þ6þcÊþLÿpBÿgãþ)ÒþHÿ+ôÿNgc{Z =ïÿBZÿ»µþG1þDªýÞ1ýzHý|þ®"ÿÕYÿæ›þý×ýØþ"Iÿqÿ:ÿ˜«ÿ&òÿ÷ÿ|ÿ<Íþþ¯þ³ùþj;:õG~<?8¢I°×Wrÿçÿ}ÿÊÿ»ÿìÿŒÿ™þqmý«Âü9@ý„»þØÿ ÞÿTÿÝÿ5ÿ=ÿñÿqþæþt}þv–ÿÊ#ŽŠkÿšÙþ½Gÿ?¦A[lÃëÉÿ‡ÿ. œþ­(ýâèüXý•ÿþ8ÿþ;ÿzý±ÿýëÿ•ý’ÿQþ÷þ¼þÓþ°þaÿ þÈÿáþ­ÿPÿ3ÿùÿ¹þµþEþþ¨¸ÿ©tGÕΤë+¢êÿH0@Ë*LY ‹N²¿ÿNfÿí‡þ¬ þÉýŸ¯ýKÿuýÿfýšÿíý yþ¯ÿÇþýþÿ½þ‘ÿ¼þôþ=KÿÊÿ§ÿIÿ½ÿ”ÿ>ÿƈþ9þÍçþ_Éÿ—&TÙŒÿ–œÿ^cœNéoÿ-ÿÞþ–ÿÿþ"ÿþµþebþþQøýÇÿKþÿ¿þ«ÿÍþÖÿšþÖÿ‘þmÿ'ÿ­þ•þ¹þ½«þ Oÿ—°ÿÁzÿ¤ðþm´þB;ÿPŒ!m˜ÿ‰Tÿ•®ÿ`c¾ÿÓûþTþÀïýêýqÿþ1ÿ.þ²ÿìýs•ý­†ýÿÿDþÿƒÿ±þ8ÿ$ûÿ©ÿ°¬ÿ…N½ÿÿË5ÿêöÿ­À¤ÔÕ!ÿ‹ÿÆéÿnNËS–ÿÚÿ°ÿ…ÿûÿxÿÁÿßÿÂþ¡µýçpý@Ýýÿyþ.ÿ¤þ§ÿ þˆzý´´ýÃÿªþþ´ÿ|þh~ÿ÷…*»!Z+ÿºäþ(ÿ_»ÿONætx)U¿ÿ¡¥ÿó˜‰t¤7ÿ/æþ€ÿtÿÿ¾ÿöþÿwÿÛý(ýühgýÁÿoþ"ÿÒþlÿŸþYaþæ‚þ7ÆþBÿ ÿ°þ~þçÀþZÿ/‡ÿ™¹ÿ„ ÿ-‰ÿìÿgÏØr_­›0£ÿ­5ÿu˜ÿ¬@Ÿÿ¤ÉþvrþìÿŸþšÿ¾þÅÿBþ}†ýä!ý©rý3^þ’ÿ;ÿ:ÿ‚ÿ!ÿSÿ'ÿÿÿäþÍþGÿ¯þDÖþTgÿ¸9“xߟ¼ÞÿœÌÿdÔm×Ê£¦lµnÿÜŠÿ° k ÿþbýC³ý$þàÿ:þÝÿÔý¼ÿdý_ÿ™ý¾þÃþþBäý ;þÂæþ,€ÿüÿÄÿPÐÿ½ÿ¥YÃŽ;CßùÏÿ ÿ­Yÿ—|ްÿBÿÁÿùþÚÿÒþAœþ…þ…ýý—ÿ‘þÿÛÿŒþÿ,ÿ‰þˆÅþÜÿ­ÿ×þIÉþ4ŠÿÕÿ]†ÿ¶K‚µÿw‘ÿ·Íÿx@á[†"üÿ_yÿš¤ÿL>{ÇwÿâÄþ‰¶þ:'ÿXÿÕþA¶ý{Üü<ZýLÿ“þ•þsÿàþ‰ÿ %ÿØîþ¾ ÿMÅÿªL9œo¢gFmËÿºŸÿ€ÿ+‚ÿ9‘ÿ]Èÿ¿ ‚½ÿúÅÿºûÿkÿçýàÿOý~ÿöý&ÿÿôþÿ.ÿÛþ†ÿÃý›ÿý½ÿ/ýâÿþãÿÿñÿoÿ1ÿBçþ6"ÿüÿ;½ÿеÿdêÿ‹8‹4±QîÿËÓÿH, ›ýÿ»´ÿWâÿò7ÿ_+ÿáþÿ£ý”ÿý-ÿþçþ}þÿ¿þ†ÿ~þþÿèýSƒýgÍý7ÛþÄÿYÿ¥›ÿm9:‚˜qo9;I¢MÑo©vþµo¨ÿ/Çÿ±2ÿ”nÿ¤ÿ~ÿ­þÿ“þ„þGÿ"þÕÿÀýØÿ:ý¯ÿýÿ™ýTÿ½þÿÿQÿ·þ÷ÿfþsøþ5IÁÿUµÿÄJ;.‰oˆÊ!ÏÿÚ˜ÿç5º¬w°àdu{¨ÿ—~ÿ6¦ÿ.ÿßýd^ý»ÿ‘ýþ ýGþýãþuýŒÿGýŸÿ#ýYÿ}ý#ÿ’þÿ ÿŸ:ÿ ºÿ}ÿdëÿz¸‡ÿªÿÚ‡ß ð,£M ¯|‘<&6Xÿ!¨ÿáÿ&¿ÿ–ÿ+Fþ»0ýãêü§ªýïÿ”þvÿóþ‰ÿÿôÿ\ÿÉÿ¡ÿ-KÿŠTÿ­ÿN^<!4c$kÌÄÿʼÿI ê/§åÿŒ{ÿ’gÿޤÿ6!«þ1ºý%þœ•þdþ\qýŒòüŠ^ýZºýVÿþÿþ–ÿÿóÿœÿ£ÿéÿŸÿ¶ÿÿÁ£ÿE#EÒ B’ ÇÿÏJÿ(Yÿ¹Ûÿë[¡ÿ%ÿÐ&ÿJ£ÿKÿ‚ûýJ$ýC4ýlÈýÿOþ<ÿþSÿsþiÿQþCÿjþâþ¸þÉþÿJÿkÿfúÿPÿ:¹X•<Ž ]~mMÅÄó|äŸÿ°¼ÿØ‘00§GDÏ ZÈÿ&Šÿ#ÿ ‡þäÿøý±ÿÃý…ÿþAÿyþ+ÿ»þ§ÿ¼þ7þ^]þ¶þÉÿÿÞÿŒ9üƒ¸®K™Vª®ÿ’žÿá;¥ñGõŒ˜94U£ÿH?ÿ*¸þv*þ²ÿëýVÿPþ)ÿ¥þ ÿ þÿ)ý2ÿßü%ÿ€ýòþ/þüþ0þŠÿÚýFÛý¢”þo˜ÿìÿ½«ÿÃêÿƒäÌ­€­0 kmVããewbKvÆÿç¹ÿñ ÆG+$ÿŠþœþÌÿîþzÿºþœÿþæýPþ²ÿýþ>ÿ©ÿWÿéÿÛÿñÿd´m’C²+I@°Y¦gKJS88±›Âö1‡ùÿùÿ°ÿ÷ÿ´ÿ´ÿþÿþþ,þýpÿ£ý@ÿÄý£ÿùýðý\îýO€þºÿQÿóþöÿ¢þfÿë–ÿO¥ÿ)§ÿ÷ÿuyË`âíÓa‘+`„ÿŒ~ÿ5ÒÿTG ÿ~EþNþùý)Þýv“ýœAýBûü¨ÿìüÿMýþßýžþbþGÿµþÛÿàþñÿ.ÿéÿòÿø\é’ˆ£±Q—Q<–ôÿËýÿŠŠê÷tê\ ŒžÅÿ ÿÿ'dRÿm–þîÿÿýÿ­ý“ÿÆýÃÿþÖÿþÄÿöý¤ÿ+þZÿÿþþöþësÿ>Ð×x„ 3æÑ êWô! v7•ÿ}ÿ¥4ÿrÌ]iè¨ÿrIÿ‘BÿÐ.ÿ[ªþŽÿãýåþ²ý‘þGþXþ ÿCþ ÿ¦þqþ@ÿ¿ýÇÿ€ý0þÿTl·d!þÿùÿ;XŽÿÎÿ?hÁF(ûÁŠÒHœÄ5ÿÿÿͪþ™¶þVòþËþÀÿþjÿlýÿtý¤þIþ’þHÿ›þ ÿÿ ÿÙÿ³þ‘ÿ¶âÿ…¾Å„R6}‚Ú_ T´ÿÉ­ÿ[ñÿHVÍc‡ÿ>±nêG Õÿ¶ÿ ÿÿèþŸþBÿnþYÿªþúþ"ÿ:þ‰ÿ ýåÿ¤ýÚÿ@þÍÿÿ…ÿÀÚÿPæpb J oµ¯½ÀÜSÄ“–qFMB(&íÿÀÿîÿ&ÿÌÿ‚ÿYÿÊÿ ÿpÿQÿÖþŒÿIþ[ÿ×ýßþyýþpýŸþ ý$ÿ˜ýÿÿ\ýr ýxDýkþ]+ÿ›þÿëT!Vš‘V5MØ~†µ•¬|šS–êÿzÿ–ÿUêÿÃÿ£ÿÿéþ‘ÿjþ¬ÿÔþiÿÀÿÒþo"þ–ŠýA>ýØÿzý¹ÿHþ*ÿ`²ÿ|³ÿRéÿ-ì<1eææg•ѱî+!Œl%ª­$PÁŽÿoÔþkÃþ–TÿQÎÿqÕÿ:ÿsÿ(þÿÓýÉþÿýÎþ1þ1ÿ þ©ÿsýüÿÔüôÿÂü‰ýõþùÿëÿ5õÿ´Çÿóÿ½˜L$c kcò±úÿP”ÿ‘}ÿ¿wÿGÿìþU þ ¤þ3ñþìÿZÿ¸þÌÿ1ý CüÕÿ”ü[ÿrýÿ þõþLþÿ:þfÿ þ˜ÿmþ¢ÿ,ÿáÿâÿ˜J¬X2?ÎV$Å¢luòƒ'“ RÓÌÿ¬{ÿĉÿäæÿ¿~ÿ§ÿ°ÿ>ÿÌÿÿŽÿ_ÿãþÌÿFþ³ÿüýaÿþÿ‹þ(ÿÿ›ÿHÿcÿ(~ÿ÷ÿ¹ÿÛÿDCг3Ç4lê'eöïÿ\Ùÿ@Òÿ¿µÿGmÿë1ÿ§ÿvnÿpÿƒÝþXþ`ÿþäþþÂþ]þáþ[þÿþ&ÿñý'ÿîýwÿ)þ6þåþ¨ðþ®ÒþgÿÄmF%ë YÛÿ wÿ/ÿ?Iÿ@×ÿêtS¥Ëÿ lÿBÿ?ÿÿ]ÿ/ÿŒÿZÿ@ÿÿþ¥þìýYþ¤ýyþÌý.ÿþ þÅêýâüýÃ}þÙtÿ~CÈf™{ŒNò‹´ãÿLÖÿ¶&£Y,%źÿtÿ|¬ÿ9&Å(/’ÿvÿÎþñþAþ÷þ1þbÿsþ¡ÿ¹þMÿãþ‡þÛþþÏþ%þÿVþ¦ÿ€þk|þü‡þZîþ0ºÿ˜Å6¦u)öaî?~íÖæÿÙ[šå2ú¹^gyÿ}æþäÞþEïþ"Åþ%³þËþ¶þ þ±þéý¡þÒýÓþlýÿæü‡ü xü¶ÿývÿþ²ÿòþfyÿØÿ60¸_b|“ÕXjžnœý’P²âÿ÷ÕÿÛÞÿ[¼ÿÃXÿ' ÿåÿÔþýÿþ÷ÿ¡þ{ÿ ÿºþCÿûýÿjýÄþ?ýâþkýIÿžýØÿ‹ýU”ý+÷ý¸ÿxþrÿ5ÿ¾ÿ#øûxŒÖl±…¤´¹«Ï…µR*K:ÿs}þ»=þþÍõþÿWÇþÂÿ~þPÿ–þÝþ ÿSþ}ÿÊýšÿuýnÿý!ÿâýÿcþjÿÝþÿsÿQ÷þ@:ÿmÀòçhñ è½- s¯ÿÂÃÿÈt1øÛÿLÿOÖþöÿêþiÿwÿ ÿÔÿîþÿ þ;ÿAþ ÿþ3ÿ4þ˜ÿ*þõÿÔý3pýF‘ý2;þWÿ»´ÿ Ãÿ\wÿ‡‰ÿ|!1þã€óžó±m"[ÿËÎþÒÈþ¦^ÿM´ÿÔMÿ;«þŠÿ`þ!ÿþ ÿØþæþáþrþ·þôý‚þæýXþ>þ2þªþ²þÁþ—ÿ‰þJIþÍ\þðÿä½ÿ"wA§ZbÞ¨e\Ù•ûÕ¨½õ0xyÿW ÿeÿK:ÿÚ5ÿ æþFÿ‹þæþ$þ ÿ þ&ÿVþúþÊþnþ0ÿÕýAÿšýHÿËýdÿ*þ¦ÿVþcRþ1„þk ÿòŠÿžßÿ]©åö;¶a#‡Ëb†Î]@ÒÿPgÿÈÿ öþ¢ÿÑÿüþ`ÿ²þ9ÿhþ+ÿaþúþ¦þƒþ.ÿìýþÿUýF:ýêÿfýÏÿÁý9uþê2ÿ'¿ÿ»úÿ??¼@3€LÚÿ Ûàðhàÿ5’ÿ'vÿßlÿ‚4ÿk’þìýpËýùÿ!þ\ÿþÁþ²þXþžþþ þþÏþþXÿ+þYþTsþ*}þ¦þ;Dÿ–4ïÊøÙ·»iõXwDaS*Qþé¯ÿÍüþŽþ2½þ'¼þ@Pþ ºý¼ÿŸý„ÿ>þhÿ ÿ7ÿÅÿÁþ2?þŠúýé,þ7§þˆ8ÿ›ŒÿƒoÿY‘ÿ (ÕÓô;z@Ý1¬2â¤G@<‹5«áÿyÿ7þ¤ þPwþ$ÿâÿBÿIÿÿ¤þ¹þ]þÁþpþ:ÿþÖÿ‹þ^BþþsþjŽþkÿ­]ÿ$VÿŽhÿmÓÿ¯[[á¶c1,‡„(¿ÿÅ>ÿ¾'ÿ2ÿhêþw?þð’ýEaý÷ÿ©ý-7þÿþlÿ‰ÿ·þ~ÿ3þÿöýËþÏýÿÍýˆÿþéÿdþ;¸þ+þþJÿB®ÿóÛ<+ÆO4èí®ßþ£í¢wÿn%ÿª-ÿ\ÿ*mÿ _ÿ¼?ÿ!öþôÿÔþèÿÿÀÿÿlÿ ûþôÿ‹þ—ÿKþnÿ<þÏÿOþ¹‰þmÅþtèþ'ÿ!`ÿnÜÅø£ä˜o쫲ó‡ÃMWïÿ%¡ÿ8?ÿ^Èþ?ÉþÁ<ÿL|ÿÁÿÿ ÿzþ„þ:þUþnþuþóþ„þRÿqþ;ÿþ/ÿ¶ý•ÿÁýgJþ-ÿV<ÿ2¾þ+½þ&sÿî^¨¿¬Ø² Ry®ÿ²,ÿ§ÿ¡GÿŸ•ÿ]nÿÕÖþ5QþÛÿ4þôÿqþ-ÃþþÿöþVÿ ÿ þÿHþQÿuþËþ’»þ¶?þ•Åý»Ïý7”þ‹rÿ¥íÿµ »p^ö¿ÃGÙòþ Ï5äÿ¸1ÿŠvÿièÿpÞÿ4_ÿŠÿÙÿAÿãÿ€ÿÿ¥ÿÉÿÍÿ÷þ-·þ}÷þæÿsÿÔæþ·þÌþâ ÿn«ÿ+‡'àÝØuµ(¯*Ò^q éCÿ½þE’þ¹þ2gþ þ31þYÿxþÿ³þþþ÷þÅþlÿiþ0þ€$þ¤@þeÄþ1ÿ-|ÿκÿVÍÿVÐÿ¼ÿ÷ùÿ1—`'W,½¦Œ ØâÿaÏÿ§žÿg/ÿ¸þ$þâÿâý Ñý9'þ–þµÿüþ1ÿÿÚþÿÎþcÿÎþ¸þíªþiÒþKüþÛ6ÿ°Ãÿ”ƒLßGûÙÁ1¿ûÎÛ‡u.ú5P¿®ÿhÿ¼ßþUÿÉ×þ.þ[ŠþÇÿÿ€ÿÊÿaÿÿþÚÿ!þ×ÿPþ ãþg^ÿ’ÿzDÿDèþž®þ]»þë^ÿ¯>̰f’ hiªOLùÿvÿ;Êþ‡qþ8™þfÿÛ9ÿõÍþYþÿíýúýˆþþ^ÿÝþŽÿ6Lÿäÿ]þ©ÿÅýÔÿÖýx˜þ~ÿ<æÿÔÿ?Íþîÿ]þY¸þUõÿÒ „l…~ÿª(ÿÎGÿŽUÿ!ÿòíþ)Éþ_§þ°þÿYßÿŸ-…ËÿÑÿZÿÿ€ÿ ÿÄÿyLˆ×ÿ Êþ[þ>¡þ|þþ qÿ°ïÿܾ͋·RAPxØÕÇæMU‹ÿÎòþ°ÿÿ†ïÿÑÿ/3ÿ„}þËÿWþeÿÙþ•ÿÁÿºÿaÿDÓþ¿ÿ#þ~ÿþ>šþŽÿü¥ÿ“Çþ‡BþÈþZËÿvi™¡ÄÂDïªÿ¨ÜþY.ÿñ¬ÿ¹{ÿ®‚þuBþUÿ“þoÿÿ'„ÿ¹ÿ†rÿÔÿºþ*ÿuþÌþ¦þõþ ÿ7ÿ£ÿ1ÿÏþFGþeéý¹:þ95ÿÖ%û¯`Jù¯ÿ +Ðç?­Bë~ƒe„¼Ûÿ+Öÿ³†ÿ‡þ$ÌýGþäÿæþ¢ÿëðþo8ÿ¦ÿíÿbÿ7{ÿ<•ÿ}²ÿ9Ãÿ²¬ÿöþ6‚þC×þ°ºÿï¦þÐÕ˜rU uÅý¾y}ƒOãVh+eÞÿ©‘ÿÙxÿ÷?ÿÑþ»SþURþðþ&UÿaNÿîÿ6ÿ ÿ¶ÿdþb‹þªûþÚ>ÿ\ÿGÿÅÿúÿCÿ¡ÿ9ÿc¥ÿTöq®’ÿmÿ”ɹ Nù%ø²ÿÒçþotþ«þ.úþMêþ þŒÿ´þÿùþ7ÿOÿ‹ÿÿßÿ§ÿØÿ³ÿ”ÿ°ÿ/ÿÇÿäþöÿ ÿf[ÿ̈ÿÔsÿ¢Sÿ›mÿêéÿ.”bû`1Å~ÁšeÂÿkcÿkcÿ6Eÿ ÏþÌEþ‹~þšDÿÊÝÿÕÿñÿÍÿÿsÕþËkÿ„Çÿ·ÿîÿŽÿ=¡ÿ>Ëÿ9Çÿ‚ÍÿÒÿ€Àÿ™¯ÿ–±ÿšíÿœ”A-­P 5mÿðÿÖÇþôÌþ õþéNÿ{¤ÿ1ÿ/pÿ<aÿ0ƒÿ¨ÿèÿÀÿ˜ÿ»ÿ9ÿ½ÿëþÐÿöþÌÿÿÔÿ÷þúÿÀþ~™þÞÿšhÿXÿ‡ºÿðÙÿÿçÿ¤ØÿDe¼ÿ± ÿ~©þþÿ¨þûÿ»þBþy\þpµþ9Bÿ •ÿ¿ÿ“ÿ‹ÿsÿŠÿdÿ¡ÿZÿ®ÿHÿ}ÿLÿÿ–ÿØþ×ÿùþûÿYÿ2ÀÿžÔÿ6¸ÿ¤ÿ›Áÿ~7X°0óðÜ¢«h²CÑõÿñ§ÿísÿÇLÿ}HÿI)ÿ[ÞþnqþbFþ ¸þ¦ÿ-ÿlÿZÿ*ÿwÿÿÃÿOÿ?·ÿ§ÂÿÙ[ÿêþmÿb ÿãÿ!¨)Zøša ãÖ±/zC|Îÿ3\ÿàþñAþ¼$þ_‡þ&ÜþºþÚÿEþ¦ÿþ~ÿZþyÿ»þAÿøþÂþ%ÿPþhÿ8þÊÿcþ)zþˆXþ-þcBþP¤þ!6ÿï¾ÿñ8IÌa6~Õÿ£àÿF3™ÿîÿ̇þù,þçýÑðý@þÅÿ>þ…ÿJþyÿrþgÿÿdÿÙÿ|ÿ.yÿI`ÿ\Gÿ~Hÿ¨Kÿ³4ÿã ÿCÿWoÿô±ÿ‡×ÿ¾$gö™modúÿ»ôÿ Øÿô«ÿeÿXÿYgþŒFþ²™þìùþ[ÿÙoÿ{ÿ ¢ÿÂÿÿÍ…ÿ.Œÿ$[ÿ½5ÿORÿ%­ÿN‰¥óÿ•Õÿkôÿw=­qñQ³0Ùÿÿ÷ÿ{þ&Yþ{;þ‹þèýÿþfÿGþ™ÿ[þ™ÿøþnÿºÿxÿ"XÿÃÿÿþ"ÿ•þOÿþÕÿýþ3ÿJÆþhŽþœÓþ±Qÿ¦»ÿÅñÿE.fû‡cnpëÿ…tÿdÊþEþ¹ÿþÍÿþëý0þ=“þ!ÿÿÊÿõþkÿRÿZÿìÿeÿeQÿvÿ[»þ)Üþ*ÿ_ŒÿòÏÿ…ÛÿÀÄÿçÊÿé×t¨Âhñ  j6vÿ©þáVþþSþ(IþXþ´‹þg×þUÿTeÿH¨ÿ>áÿÂÿìÿYÿ«ÿWÿ[ÿ¹ÿAÿK9ÿ‹ÿxÝþµÑþ0 ÿ£Yÿé„ÿ¤ÿ.ÎÿÛëÿúÿ7åÿìÿ µÿCÿ¡·þ‰3þoþuþ‘Fþ™€þ’‘þX’þ §þ¾ÿóþ‰ÿEÿvÿ~ÿRÿyÿ.ÿHÿùþDÿÊþÿ¨þ¨þÝþåïþðþFíþpùþ†,ÿ”tÿr»ÿ[G§ÿv'ÿ± ÿå<ÿ"!ÿ=ÉþÜ—þƒÕþ|Dÿ°hÿÇZÿƒNÿ,`ÿûÿ5ÿÑÿÿ‚ÿBÿPÿÓÿhÿs–ÿ|xÿ‰,ÿõMÿf¢ÿ¿ìÿº GV~Ÿ ÄÚÚàÔ3Ç•ÿàqÿûÿ÷ŠÿÑ?ÿ™Êþbiþ7Fþ)\þ,¯þDÿáÿ”ÿ«ÿ†ÿdÿwÿ3ÿ–ÿ/ÿþÿ+ÿo ÿÁ¿þΆþ¸}þÊ¢þ ÿ;eÿãžÿm¥ÿ1¾ÿãÿúÿÿÿùÿòÿÒÿ\ÿ¾þâÿqþÙÿŸþÿÿ½þ?þ2Lþ,þQþ¥þÿÇÿ˜ÿ{ÿúÿKÿ "ÿïÿïþÕÿöþ+>ÿ’’ÿ}¢ÿ=qÿ![ÿKrÿz¬ÿ‹Úÿ³ ìGëd€M çÿNÀÿ—’ÿŠ/ÿXËþˆþûÿ—þþÿÜþ,ÿG,ÿ!ÿ3ÿ…ÿPöÿtpX¯¥îÿT¾ÿDÿ¬°ÿ4ÿÿu<6&×èÿÃóV”7”"eÇ=,"~ÿ5ÿ8,ÿrýþ‹YþBÔýÁÿ2þŒÿÖþÆÿKÿÖÿÿ™ÿ“ÿXÿ¨ÿFÿ«ÿUÿÉÿQÿMÿ^kÿ\—ÿwÿ«ÿ1ÿÎÿ&ÿ^Bÿäkÿ?‘ÿ@µÿößÿ®.nš-éäÿÿ…ÿÐôþtþOPþcþnþ Yþ HþüÿoþîÿÙþGÿ!Žÿ'±ÿÆÿÃÿÿÿ“ÿ1uÿIFÿ|#ÿÖ ÿVÿžÿçµÿáÒÿ9…mh˜‰¶–oÖ°ÿ Xÿÿ5§þ;bþoþôÙþöTÿ”ÿ˶ÿ|äÿ8ùÿîÿÛÿÆÿ°ÿÎÿoÿTÿYUÿqOÿ¥BÿFÿ›mÿ¹¥ÿtÕÿ<öÿ ÿ0Î`rkR¿ÿ8‚ÿ${ÿ–ÿ!®ÿÿÿŸÿïÿVÿàþƒþþ ¾þ-èþ3èþÿºÿMÿŽÿ­ÿ‡ÿ"wÿraÿKÿVÿzpÿ}iÿ¬sÿÕ’ÿËÿ’ÿhšÿv¥ÿÊÿcÙÿÌÿ¤ÿÈÿXÿçÿÿÆþÛþ +ÿ ;ÿèþ<ŒþMzþ]ØþpBÿrÿ†žÿ`çÿ#XöÿØ×ÿKÀÿ­ÿ³Ÿÿ§¦ÿ’×ÿj%Ep¸ÎÝ™ÈjYy^pmkvs:’´ÿ–ÿ„Áþv¨þ`ªþPÄþ.Ôþæÿÿ¬ÿjÿ¡ÿýÿ´ÿ“ÆÿïÃÿ«ÿ®ÿåÐÿé(e ðÿ†Íÿ(ŸÿÞ´ÿØþÿé\»…QmóÿT´ÿ?Ÿÿ2ˆÿ^ÿÖÿ(ÿ’ÿåþqÿ¶þrÿ§þ”ÿÎþÇÿ÷þôÿçþóþÌÿBÿŸÿ´ÿ‚ÿ5Uÿ¦1ÿÿ<ÿFÿ«xÿv¬ÿ™ÄQ§y^“A}Yc†o™Št§¡¤ÿ‘>ÿ½ôþ ´þ8˜þ!±þÊãþ!ÿApÿìÿÑÿ¶ÿ5»ÿwßÿìÿbÝÿF»ÿh¯ÿ¨Ùÿõòÿ.äÿ-×ÿáÿ(üÿq1¿…¥±”•ph}[©=Èúÿ”ÀÿBœÿ?Jÿa ÿ ÿäiÿñ£ÿЙÿq–ÿÂÿ²ÿ&sÿlÿÈ{ÿ ]ÿjÿh ÿ•QÿÂÇÿàåàÿÙµÿì®ÿ¿ÿb²ÿx|ÿwÿ_©ÿÆÿõÿ\ÿLÿ|âþŠæþ†çþgüþ>ÿ2<ÿPÿyÁÿ†ÝÿeÂÿ7‹ÿŸÿõÿïÿ+M"3Ïÿ’ƒÿ ‹ÿ&ÂÿÐÿ/òÿ>)7½:g=I986=ÐÿJƒÿ1Oÿ)EÿsGÿÊZÿðqÿØ]ÿ–Sÿk{ÿjØÿe<5PÕÿ3Œÿ‰ÿ!¾ÿE €:Ì0ëÿL¢ÿQwÿ^ˆÿh¼ÿv¶ÿR³ÿ´·ÿýÿ³ÿŒÿàÿŒÿ'²ÿ}‹ÿ–EÿT+ÿ ]ÿíÿ§ÿÿÿÇÿÿÿ´ÿÚÿuÿ¨ÿ!ÿ¥ÿÐþÑÿÍþåÿ:ÿßÿÔÿ·ÿHyÿq6ÿ‰ÿ½Fÿ÷ÿ&ÅÿñÿÖÖÿ´ŽÿƒŒÿaÂÿx z9H ùÿØÿÎÿàÿßÿ3ïÿ‹¿ÿ·VÿÍöþ«½þa½þÛþûÿÿþïÿ3ÿÉÿZÿÿoÿ‹ÿWÿ¼ÿcÿòÿÊÿBKªX¨‚˜ÿÆvÿ(ÎÿP%*ÝÍÿ›Äÿi%9¤#ÍLÁeL6"#JøÿvÔÿ’•ÿŠ@ÿcÿ@îþ(ëþ?ÿûÿÚÿÛÿh¥ÿŽfÿŽFÿÓsÿ9ÆÿnÚÿ(¼ÿÉœÿn«ÿöÿlinphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Assets/ringback.wav000066400000000000000000000600541313432737600251130ustar00rootroot00000000000000RIFF$ðÿWAVEfmt @€>dataðÿûÿûÿûÿûÿûÿûÿûÿþÿýÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿÿÿýÿýÿýÿýÿýÿýÿýÿþÿþÿþÿýÿüÿýÿþÿþÿýÿýÿýÿýÿüÿþÿýÿýÿûÿþÿþÿýÿýÿþÿýÿþÿýÿüÿýÿþÿýÿüÿýÿýÿþÿþÿýÿýÿþÿýÿþÿþÿþÿýÿþÿÿÿýÿþÿýÿÿÿÿÿþÿÿÿÿÿþÿþÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿþÿþÿÿÿÿÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿþÿþÿÿÿÿÿÿÿýÿÿÿýÿþÿÿÿÿÿÿÿþÿÿÿÿÿþÿÿÿþÿþÿþÿýÿþÿþÿýÿÿÿÿÿÿÿýÿþÿýÿþÿÿÿþÿþÿýÿþÿýÿýÿýÿÿÿýÿýÿýÿýÿýÿþÿüÿþÿýÿþÿþÿýÿþÿþÿýÿþÿþÿýÿýÿýÿýÿüÿÿÿþÿýÿþÿýÿþÿüÿüÿýÿþÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿýÿþÿþÿÿÿÿÿþÿþÿÿÿþÿþÿÿÿþÿýÿþÿþÿþÿüÿþÿýÿýÿýÿýÿýÿýÿýÿüÿüÿþÿýÿýÿüÿýÿüÿýÿýÿýÿþÿýÿýÿýÿýÿýÿýÿþÿýÿýÿýÿûÿüÿýÿýÿþÿýÿýÿÿÿýÿüÿüÿýÿþÿþÿþÿýÿýÿýÿÿÿÿÿþÿýÿüÿýÿüÿûÿüÿüÿüÿýÿüÿýÿüÿþÿýÿüÿüÿýÿüÿüÿüÿýÿýÿýÿûÿýÿýÿüÿüÿüÿýÿüÿüÿüÿýÿýÿýÿüÿüÿýÿýÿüÿýÿýÿûÿüÿüÿüÿüÿüÿýÿýÿüÿüÿüÿýÿüÿüÿýÿýÿýÿýÿüÿüÿûÿüÿûÿüÿûÿýÿúÿûÿûÿúÿûÿúÿûÿùÿùÿûÿúÿøÿùÿùÿùÿùÿùÿùÿúÿøÿùÿûÿúÿüÿüÿýÿüÿüÿûÿýÿüÿýÿüÿûÿúÿüÿûÿûÿúÿúÿúÿúÿûÿúÿúÿûÿúÿûÿúÿúÿúÿúÿúÿûÿúÿúÿùÿùÿúÿûÿûÿùÿøÿúÿúÿúÿúÿúÿûÿûÿûÿúÿùÿúÿûÿûÿúÿùÿúÿúÿûÿûÿúÿúÿûÿúÿúÿúÿúÿúÿûÿúÿùÿùÿúÿúÿûÿûÿûÿûÿûÿüÿüÿýÿûÿüÿûÿýÿúÿýÿüÿüÿüÿýÿýÿýÿýÿýÿýÿþÿýÿýÿýÿýÿüÿýÿýÿüÿüÿýÿûÿýÿýÿüÿüÿüÿýÿýÿüÿüÿüÿýÿýÿýÿýÿýÿýÿýÿþÿýÿýÿüÿýÿýÿüÿüÿüÿüÿüÿýÿüÿûÿüÿüÿûÿûÿûÿûÿûÿûÿûÿûÿùÿùÿ÷ÿùÿøÿøÿöÿøÿøÿøÿøÿ÷ÿ÷ÿøÿøÿøÿúÿ÷ÿùÿøÿøÿùÿ÷ÿøÿøÿ÷ÿøÿøÿöÿ÷ÿøÿøÿ÷ÿøÿøÿøÿ÷ÿøÿöÿ÷ÿ÷ÿ÷ÿ÷ÿ÷ÿöÿ÷ÿ÷ÿöÿ÷ÿ÷ÿøÿ÷ÿ÷ÿ÷ÿ÷ÿøÿ÷ÿ÷ÿ÷ÿøÿ÷ÿöÿ÷ÿ÷ÿøÿøÿøÿ÷ÿøÿ÷ÿøÿøÿøÿ÷ÿøÿ÷ÿøÿøÿúÿ÷ÿøÿøÿøÿøÿúÿùÿøÿ÷ÿùÿöÿøÿùÿùÿùÿùÿùÿúÿûÿýÿýÿýÿýÿýÿüÿüÿüÿûÿýÿýÿûÿüÿüÿýÿýÿýÿýÿüÿûÿüÿýÿüÿûÿýÿýÿüÿûÿûÿüÿûÿûÿüÿýÿýÿýÿýÿûÿüÿýÿüÿüÿýÿýÿüÿýÿüÿúÿúÿüÿúÿüÿûÿûÿûÿúÿ«ÿØüŸ¯¿ à­,)·´ø"ñëAçoæÒè<î$ö‰ÿû&¯v°¿6 ÆØù&ò½ëˆç5æèíŒôÐýËeg§M±d 3ûdó³ìè8æŠçüë#ó4ü’¡ ¤Õ秘 f›ü¶ôÁí¸è\æ"çëËñ ún ÖÄóu‘Å ¸þöàîpé’æÌæ4êð ùŠ' ïNùìiä cÿo÷ð>êææ”æséLï‚÷ûÖ ûÉìR4ýIÃÍø=ñëKçqæÄè"îöfÿuð/Ç¢ì‡ 1ú|ò ìÆçdæ,è í‰ôÊýÓ}ß“¾ q”ûÂóíVèlæ©ç ì!ó,üŒ¦ µ?'óë ¿öüõîúèŒæ=çëÆñ’új ÞÙ«Ò Vþbö*ï¯éÂæèæ=êyðûøx ña§,P³ÿ½÷Qðzêç­æzé@ïl÷äà òÓzj< ù„ñVëwç‰æÊèîéõJÿ`å2ÜÆAÌ e{úÃòGìöçæ4èísô¯ýïÉ~ Â? ·ÞûôCí…è‰æ³çì óüs– ¶L$S)+ =ýTõNî)éªæHçë¯ñsúéU ×â2ÓO M›þ§öhïàéãæöæ7êdðÚøX æc/?Õd‘õÿþ÷ð©ê1ç¹æté)ïK÷Á§ ãИ”sËLZù¾ñ†ë—ç—æÅèîÅõ"ÿ=Ï*åÞBt Ÿ·úùòqìè‰æ,èêìNôýȪn¡ái3 îü?ôoí¢è”æ­çêëäòáûIv £H1nR] ;vý‹õ|îJ鹿EçýêŒñCú¾3 ÀÛ;è+z ~ÐþÛö•ïÿéóæõæ$ê@ð«ø+à ÍZ3Sø’Â,4ø½ðÌêEç»æbé ï÷€ ÅÄ«µ  †•ùöñ²ë¯çšæ°èÕí‰õÞþ¡ßïj¬J ì ûEó®ì6èæèµìýó!ýpdD’! ¬‡ M{üœô½íÒè¡æç¥ë€òiûØ c.;žŸÁ «íýöÞîŠéÎæ*ç´êñµù2¾ m³@}ì \ÿf÷ ðSêçÝæ×éÂïøƒP c+}O RÉÔøJñ5ëxç­æé€îiöÏÿÖEvÒ"Ÿ 1Eú“ò(ìñç•æièPíÓôþN¹Â¿+ã –ºûèó/í‚è›æÙç:ìPó\ü»Ò ãm6Y$ ö-ýGõHî,鹿eç:ëÞñ¤ú ùüFÞQ Oþ¬önïëéòæ çOê|ðôøq ûv@Qçy¤ ø£ð¿êCçÉæ}é-ïI÷½¦ æØ ­¯’ðqùâñ£ëªçæ¾èïí¨õÿ!¸ æðcž5 ×òú1óŸì0è“æ!èËìôGý‘~Ušžu 7düˆô­íÌè£æœç¼ëžòŒûõ4 w7<–¬ ”ÕýèõËîéÎæ3çÅê4ñØùQØ ‚¾BrÚ ëEÿQ÷ùïHêçäæééÜï*ø£m y,1{Eü>³¾ø6ñ&ëqç²æ"é˜îˆöðÿô^† Њ 0úòìèçšævèiíñô5þl-ÈÊ ³Ñ ¡ûÐóíxèšæâçMìlózüÖê ôs2N Üý-õ1î鵿içJë÷ñÂú7• EÖ> 7ƒþ‘öYïÝéíæç^ê“ðùŽ3 @IØd‹ñÿú÷Œð¯ê<çÌæˆéDïf÷Û¿ öâ"¥¡Ù[iùÍñ”ë¥ç£æÎèîÊõ$ÿ@Ó0ïïZ# ÁÚúóì)è—æ.èãì?ôjý³šk¥#ÿ‘e #OüqôœíÁè£æ§çÐëºò®ûO FB’†œ ‚ÁýÖõ»îwéÏæ@çÜêSñúùuø šÍHiË Ù2ÿ<÷éï>êçðæüéùïLøÈ ’>9z;í+ž§ø#ñënç¹æ3é±î§ö v–Íüv újò ìâçœæ„è€íõVþ‡BÔΦ· d†û¸ó íkè˜æìç`ì†óšüów1Bñ Ãöüõî鲿pçXëòßúS­  BÊë% hþvöAïÌéçæçkêªð,ù©J ‡=>ÈOrÖÿà÷uðŸê4çÍæ˜éZï„÷úÚ í%Ÿ“iÁAOù¸ñ†ëŸç¨æÞèîéõEÿ^é@õìO} «Áúó€ì"è™æ;è÷ìXôˆýвz®!öO 5ü\ô‹í¸è¤æ³çæë×òÏû6i ›L@‰s† g¦ý¹õ¥îhéÈæFçëêjñú’ ¨ÓFV³ ¾ÿ÷Ðï.êçñæ ê ðhøã¢ ¡D6o(Ô€‰ø ñëcç¸æ=éÅîÀö.* † Åëë[ èûùPòõëÕç™æ‹èí(õrþ¡WâÓšó  KkûŸóôìbè•æõçrì ó¹ü.8ñÛ ¬Ýüüô îé°æzçlë)òûqÈ *@ÁÙ Lþ[ö+ï½éàæçzêÃðIùÆc /‘?6·8Y¼ÿÆ÷^ðê-çÐæ£épïŸ÷ó ø&—…Wª'4ùñpë“ç¤æåè0îö`ÿtûJöã?gñ ¤úèòhìè”æ@èímô¤ýçÀ®ãj0 íü?ôrí§èœæµçñëîòéûP} ¨O;|`l K‰ýžõîXéÁæGç÷ê~ñ2ú«" ¶ÙAùC™ ¢õþ÷·ïêÿæðæêð€øþ¹ ¯J5e¾õdmøïðóêXç·æFéÙîÜöHA —¨½ÛÖA ÍÜù6òãëÊç˜æ”è¢íCõŽþ¸jíÖÿŽâˆ 3Pû‡óãìVè•æýç…ì»ó×ü*/ †,.ÜÇ ’Ãüáôöí÷è¬æç}ëCòûŽÝ ;A¸Ìú ì4þCöï±éÝæçŒêÜðjùæ~ B›@/ª#B¡ÿ­÷Hð~ê'çÓæ°é‡ï»÷3 1'‘s@ ù‚ñ\ë…ç¢æíèBîöyÿŠ QõÚ*OÕ o†úËòRìèæEèí†ôÀýýÔ­ÔR Ðöû!ô[í˜è–æ»çìóüf ³Q1jIQ -hýõtîD鹿Içë“ñKúÃ5 ÃÝ>í/ …ÙþæöŸïê÷æóæê5ðœøÏ ÁR3Z¦ÚFQøÖðàêMç¸æQéêîôöeW §±³Ë¾' ±ÀùòÍë¾ç–æè´íZõ«þÕ}ùØö{Ém /ûkóËìFèæè“ìÑóðüB@+†$Ǫ q£üÃôÚíäè£æ‚çˆëTò5û¤ï D9ª´ß Íþ"öúîšéÑæç”êìðùû Lž9 ’!€ÿŒ÷*ðgêçÐæ¸é–ïÒ÷K >"‚a&së÷øgñGë{ç¡æ÷èUî2ö–ÿ¡[ôÑ8» Riú³ò=ì÷çŽæPè)í¡ôÜý雲 É?ÿ ·ÛûôGíè”æÁçìó"ü‚¥ ÃY0_8< Nýfõ`î8é¶æOçë¬ñlúáO Öç>äl n¾þÍö‰ïúéñæøæ.êMðºø7é Ò\4Sø’Á*7øÀðÐêGçºæ]éï÷„s ¼½­»« ”£ùò¹ë³ç–æ¦èÈívõÊþï’ÚñoµT ùûOó·ì:èæ è¥ìëóý]U8г“ Xˆü¨ôÆíØèŸæ‰ç™ënòSû V%7Ÿ¤É µøý öæîéÎæ#ç¤êñžù§ ^§;ƒõ iÿt÷ð[êç׿Éé°ïó÷l; R(~T]ÔáøVñ<ëvç§æéoîSö¸ÿÂ6kýÐ)© =SúŸò0ìñç’æ^èBíÀôÿý9¬» ¾0î ¢Åûóó6í…è–æÏç)ì:óDü£¾ Õa1Y)' ÿ8ýPõNî-鵿[ç)ëÈñŒúk çð?ÞY X§þ´ösïíéîæþæ=êhðÚøU æg6Mé­øªðÂê@ç¿æméï1÷£ Ðȧ¯—ù|Œùìñªë¬ç™æµèÞí•õëþ ¦áîe¥? ãþú:ó§ì2èæèºìô/ý}nJ•  ¦€ Cpü”ôµíÏè¡æ•ç¯ëòwûã! l/;š–¶ ¡äýõõÕî…éÎæ-ç¹ê"ñ¿ù<Æ vºD}é úVÿa÷ðRêçãæàéÌïø”] l%/MLÀÍøCñ0ëtç¯æéˆîtöÛÿâOÐ – );ú‹ò"ìíç—æmèXíÞôþV¼Ã ·"Ø ‹®ûÞó'í{è˜æÜç>ìXófüÂ× åj2P éý8õ:î#鵿cç;ëãñªú„ üûB×E AŽþ›ö`ïßééæçNê€ðùøv ût:HÞl–ýÿø—ð³ê=çÃæ}é1ïQ÷ì 騥¦ˆäfvùØñë¨ç æÅèøí´õ ÿ*Á#çëZ”, Ìåú%ó•ì(èæ"èÐì#ôOý™†Z›ÿ•k -Wü|ô£íÅè¡æçÂë¨ò•û9 z9<†£ ‡ÈýÜõÀîwéÈæ3çÊê;ñßùZÞ †½? iÎ Ý7ÿB÷íï>ê çáæçéßï/øªq z-,s:ð1¢®ø&ñëgç­æ!é˜î‰öòÿö_ƒÄ÷y úlò ìÜçæpèfíóô8þk*ç ¼ l‹û½ó íhèæÞçKìkó~üÙé ðl)Aø Êÿüõ#î鬿eçFë÷ñÃú7— þ;Èî+ %pþ€öGïÍéáæçZê•ðù3 {6<ÊUyÞÿè÷{ðŸê2çÃæ†éDïj÷Ý øà›”pÈJWù½ñ‡ë™çæÌèîËõ(ÿBÒ.éãK€ ¯Èúóìèæ*èâì>ôlý²™ežñP ;üaôŒíµè™æ£çÏë¼ò±ûM ‡;6ƒrˆ l©ý¾õ§îféÀæ4çÕêOñøùrò “Ã:ÿT´ Âÿ#÷Óï+êçãæòéòïHøÃ† Š2)h&Ö…ø ñë\ç«æ)é­î¥ö o¾éí` îÿùTòöëÑçŽæ{è{íõWþˆBÑÇý™÷¦ Rsû¦óùì_èæèç^ì‰óüös'6òã ±æüõî驿mçYëòåúX° <Âß Vþhö2ïÀéÞæ çlê¬ð2ù­O †85¼>aÆÿÏ÷fð’ê,çÈæ•é[ï‡÷üÛ ë”†[²/=ù§ñuë’çæ×èîçõGÿ[æ9íâAoý ˜¯úóòqìèæ6èøìZôŽýаt¥çp; ù!üJô{í«èšæ¬çãëÙòÑû7h šE5zds T“ý§õ–î[éÁæ?çéêkñú“ ¥Í?üM« ¹ÿ÷Íï)êçéæüéýïUøÑ ‘:0n.Ý‘œøñëfç²æ*é¥î›ö f‰Éú| "úròìãç•æqèdííô1þf'ÂÆ ³Ô ‡ªûÚó&í{è–æÚç:ìQó]ü¸Ñ àf1V" ø/ýKõJî+é¶æ\ç*ëÉñ‹úh çñCâb bµþÀö‚ï÷éòæúæ2êRð½ø:é Ó[3Sù”Ç3>øÇðÔêHç¸æVéóî÷m` ¬³°Æ¼( ¯ÀùòÍë¾ç’æ–è¬íKõšþÄnîÒ÷ƒ×} (FûóÜìOèŽæöç{ì¬óÈü}&-áÍ šÏüíôýíüè¨æsçcë#òöúf»  =¿Þ Wþfö3ïÁéàæçkê¬ð+ù©I …=<ÇNsØÿä÷yðŸê3çÆæ‰éHïm÷ß øÞžšzÖVgùËñ‘ë¢ç›æÃèúí¶õÿ,À#çê[˜. Ðìú*ó›ì+èæèÃìô;ý„sL• ¤ Eqü–ô¸íÑè æ‘ç¥ëòfûÐ ^):¢Æ ±øý öæîéÍæ!ç¢êñ—ù¡ Y¤;Œyÿ…÷&ðeêçÓæ»é–ïÒ÷I <"†g1øùwñTëç¢æðèFîö{ÿ PõÙ.SÛ vúÖòYìèæBè íuôªýéÂ}§Ýc- êü;ôqí¤è˜æ®çæëÜòÔû9i –D4{ex Zšý±õžî`éÁæ9çÞê[ñúü ™Ç=W¶ Äÿ)÷Ùï1êçäæñéëïAø¸} ‚.,p4é*ªø%ñëgçªæé”î‚öëÿíS~Çÿ‡ /ú}òìãçŽæeèRíÓôþK²»µ Ü ³ûäó*í{èæÎç'ì;óCüž½ Ï[-U&' ÿ9ýSõPî/鱿Oçë³ñpúçQ Öç;áh lÀþÍö‹ïûéïæóæ%ê?ðªø&Ù ÆS2V¢ÔANøÔðßêLçµæKéåîîöYO ŸªµÐÊ5 ¿Òù,òÙëÅç“æ‘èŸí<õ‹þµdçÑúŽäŒ :Wû‘óéìWèæðçpì ó¶ü  x)5îÜ «áüþô îéªæpç[ëòäúV® >Ææ! fþuöBïËéâæ ç`êšðù”7 |8@ÍYƒçÿó÷…ð§ê5çÂæ~é7ïW÷ȯ èס …âesùØñžë¦ç™æ»èëí¢õùþ¯âìa¢< áûú:ó¦ì3èæè¶ìô%ýreA!¯ S‚ü§ôÄíÚè£æŒçëqòSûÁ V%<¤°Ö à þö÷îšéÔæ"çœêñðˆù– T¥B+Ÿ3‘ÿŸ÷<ðvê%çØæ¸éïÉ÷> 8*’wF–!ùñhëç©æïè?îöpÿPùå=fò ަúìòmìè—æCèíkôžýÞ½­îwE -üTô„í¶è¡æ¯çâëÔòÈû1a •G=‰yŽ r´ýÅõ±înéÊæ<çÛêRñúùsò •ÆC jË Ý6ÿB÷ñïBêçéæíéåï2ø«t }-0{DþA¶Âø<ñ,ëuç³æéîxöàÿãP~Ò"Ÿ 6Hú™ò+ìôç™æhèOíÏô þC³ÂÇ4õ ©Îûüó?íŒèšæÐç&ì4ó8ü˜· Ðb8f<@ Výmõiî?齿Wçë«ñjúàM ØêGñ1ƒ ˆÚþèö¤ï êþæúæ&ê;ð øÒ ÂW:d¸í[gøêðñêZç¼æLéßîßöND š¬ÀáÞJ ØéùAòìëÑç›æè˜í1õ{þ¦[ãÔšó¢ Moû¦óùìdè—æðçhì’ó¦üý x-?üï ¿öüõîé°ænçTëòÔúF  AÎô1 -zþ‰öRïØéêæ çXêŽð ù„+ y;HÜl“ýÿø˜ð·ê=çÆæzé.ïH÷º£ àÓª°—ö|‰ùìñ¬ë°çžæºèáí•õëþ ¨ãôq±Q õûOó¹ì>è–æè¯ìöóýe\=’'¿  g›üºôÖíåè¨æŒç–ëdòDû²û O%?¯½ç Øþ0ö ï¥éÚæ ç“êåðwùñ… E?.¦"A ÿ¬÷Hð~ê&çÒæ¬éï±÷'ÿ 'ü%’{N +ù˜ñnë‘ç¢æãè.îûõYÿkòCñâ?lü ˜±úøòrìè‘æ6èóìRôƒýÇ©o¤ï~O 8ü_ôŒí·èžæ¦çÔë½ò°ûN ‡>9Œ€— €¿ýÓõ»îséÉæ5çÍê>ñãù[à ‡¾BnÖ êDÿQ÷úïHêçãæàéÕïø˜a m&-|L MÄÒøGñ4ëvç­æé}îcöËÿÏ@sÓ+ª AVú¤ò3ìöç”æ^è<íºôôý/ü¦»É>ÿ ³Üû ôIí‘è™æÆçìóü‚¤ ÀY5hEH %`ýxõqîD黿Kçë—ñPúÄ8 ÅÝ@ñ4ˆ ‘ãþñöªïêúæòæê%ð†øÿ¼ ±H2b¾ôeqøòðöêVç³æ=éÊîÈö4+ ‡ ½ääT ãõùLòñëÒç”æ‚è„íõbþ‘IÖËžý­ Y}û±óífè”ææçZì~óŽüéõ ùr,Bú Íýõ'îé°æhçFëññ¾ú0 ÿAÒý@ <ˆþ˜ö^ïàéëæçNê€ðóøo õr9Jåw¢ ø¤ð¾ê@çÀæpéï3÷¥ Ñɬ¶ž ˆ˜ùøñ³ë²ç™æ®èÐíõÔþõ–Þôq¹Y þûXó¾ì@èæèœìÝóýüNF0Š"Ç© s¤üÅôÞíçè¤æ€ç„ëLò,û˜å >:°¿ï â(þ;öï©é׿çƒêÓð^ùØq 5‘:-ª)H­ÿ·÷Qðƒê&çËæ élï™÷ì ñ •…X¬+8ù¤ñuë“çŸæØèîåõ@ÿWã8íãGv ¦Âúó}ìèŽæ,èäì@ômýµ™eŸõ‰[ Gülô™í¾èœæŸçÆë¬ò›û< {88‡£ ÌýâõÆîzéÊæ/ç¿ê-ñÌùEÌ w¶>sá ôPÿ]÷ðMêçÚæÔé¿ïøJ ]*RZÓÞøUñ;ëvç§æékîOö²ÿº.fúÒ3¸ Mcú®ò;ìøçæRè,í¢ôÛýéš³ÌG ÂéûôSí”è™æ¿çì ó ül’ µR4mLV 6oýˆõ|îK齿Içüê‡ñ<ú±' ·Ø@õ<– Ÿõþ÷·ïêþæîæ êðsøðª ¤D2g!Êxøñë^ç³æ6é»î·ö" |– Âîïf óú]òþë×ç•æ}èwí õOþƒ=Îʦ º jŽûÀóínè•æáçNìkó|üÖæ îk,J Ýý0õ4î靿_ç7ëÞñ¦ú{ óò:ÔH G•þ¡ödïãéèæýæ>êhðÙøW äd3Iç|« ø­ðÁê?ç»æbéï÷‹w ¿½ª¹¦ ’¥ùòºë³ç”æ¡è¾íjõ¸þàƒûÕðuÂd *ûcóÇìBèŒæÿçìÇóåü76!#!δ ²üÒôéííè£æzçtë:òû‚Ô 0:³Çü î8þIöï±éÙæçwêÀðHùÅ_ '‹8/³3U¼ÿÅ÷^ðŠê)çÆæ–éYï…÷ûØ æ”ˆ`¸8Fù°ñ|ë•çšæÍè îÐõ'ÿAÐ+åáH~ °Ìú óƒìè‰æ!èÓì'ôSýœ„V–ö‹a %Püwôí¾è˜æ“ç²ë“ò€ûè' i*4Ѝ ’ÕýêõÊî{éÄæ$ç­êñ°ù+µ e¨5zæ ü[ÿf÷ ðPêçÑæÅéªïë÷e4 K "{SbÚæø[ñ>ëuçžæöèXî2ö”ÿ£Y÷Ó> Yqú¹òDìûçŒæHèíôÇýØ­ÒP Ðøû#ô]íšè—æ·çøë÷òôûU ¨J1pSb @}ý–õ†îRéºæ>çîêtñ#úœ ©Î<øF  «ÿ ÷Áï êþæææüéð[øÖ” ”8+e%Ñø ñë\çªæ(é§îŸö gˆ¿ðøm ýúcòìÙçæoèeíðô5þh&ÀÀ§ t˜ûÊóíoèŽæÖç:ìUóbü¾Ó àd*J ç ý;õ<î!靿Yç*ëÎñ‘úl çð=Ù V U¥þ²öuïìéëæøæ4êVðÅøAï ×\0Mðˆº'1øºðÌêBç¶æYéúî÷ug ³³­À³ ¢µùòÅëºç”æ›è³íVõ¦þÏuòÐõÎs ;ûuóÔìIèæøç‚ìµóÒü$&{"'Öà Áüáôóíõè¤ærçgë*òÿúnà $ 8¶Ð ûFþUö%ï¶éÙæ çkê¬ð3ù®L 42·>cÉÿÓ÷jð’ê*çÀæ‰éIïn÷äÅ õÜ–ŽiÆEUùºñ„ë—ç–æÀèüíºõÿ/ áäP‹ ÂÞúóì#è‰æèÇìôAýŠyM‘™r 4cü‡ô©íÈèšæç¨ë†òoûÙ b(5–˜» ¥ëýýõÜî‰éËæ#ç§êñ¡ù¨ ^¦9†ø qÿz÷ð`êçÔæÁé£ïå÷^0 J$XfÞéø[ñ@ëwç¢æþè`îCö§ÿ±(cùÍ-® DYú¥ò2ìñçŒæVè3í®ôëý'õ ²À2ð ¥Êûöó8íƒè’æÅçì+ó0ü‘­ ÈY+W*+ <ýUõPî,靿Qçë¶ñxúîW Úæ9Ú\ Z­þ¹öxïìéëæ÷æ2êWðÅøDó Ù^0Kê‚°%ø°ðÄê>ç¸ædé ï!÷’ Ŧ³ „”ùôñ¯ë®ç˜æ®èÖí…õÜþÿž Ýîk«H ëûDó®ì5èæè²ìûó ýncA ­ˆ N{üœô½íÔè æç¤ëòhûÓ a*9›œ¼ ¨ëýýõÛî†éÍæ'ç¯êñ±ù-¹ h­<{ç ÿXÿd÷ ðQêçÚæÒé½ïø€M `*}L QÆÒøGñ1ërç©æéyîcöÈÿÏ@pþÍ › .Búò#ìêçæbèJíÎô þD ³¼¸$Ü ³ûáó)í{è“æÒç0ìHóSü±Ê Úd-Q ï'ý?õ?î%鲿^ç0ëÕñœúu ñô?ÙL H—þ¥öhïåéìæçHêtðêøh ñn9IàsžøŸðºê<çÂævé(ïA÷·ž ÞѦ¨Žìn~ùàñ¡ë©çœæ¼èìí¥õüþ¶äí`›2 Õîú-óì+èæèÇìô?ýŒxQ•šp 2_ü‚ô¥íÃèšæ”ç²ë–òûì) m.4ˆ¤ ŒÍýàõÃîuéÅæ*ç»ê)ñÊùEÍ w³7hÐ á:ÿE÷ïï=êçÙæÚéÎïø—_ l %p;ñ3§²ø+ñëeç¥æéŠîzöâÿæOyüÂ÷€ !útòìÜç‹ægèXíäô(þ`!¾¿©Å u™ûÉóíoèæØçCì_óoüËà êi*D  Ô ý$õ*î髿aç>ëêñ·ú,Œ þù:Êô2 -yþˆöMïÑéâæçQêˆðù& ÿr4<ÌX€çÿð÷‚ð¤ê0ç¾æ}é6ïY÷̲ êÖš”sÏP_ùÂñ‰ëšç–æÁèúí»õÿ1Ã"âáL‚ ´Íúóƒìè‰æ èÒì+ôWý¡ŠY•ð‚T ?ücôíµè•æ˜çÃë­òžû? {40uŒ q±ýÄõ¬îgé¿æ/çÊêBñéùdå ‰»7ÿX¼ É%ÿ-÷Úï/êçÞæééåï9ø³x |)%g)Ú–øñ ë\ç¥æ éŸî•öÿcƒþ½ìòf õúYòûëÑç‹æsèmíÿôFþx3ÇÁûšúª Yxûªóüì_èŠæßçRìxó‹üäò ôj#5ôæ ¶êüõî颿cçJëþñÐúDŸ û5½Þ Yþhö3ï½é׿ç[ê›ðù™9 x/1¹@dÈÿÓ÷fðŽê%ç¾æ†éHïq÷çÈ úÜŽ‚Z²2?ù§ñsëŒç”æÊè îÔõ2ÿIÕ-âÙ=lú –±úôòpìè†æ'èäìEôvý½žfšän> ú#üKôzí©è’æ¡çÔëÆò½û#V Œ:-wcw X—ý­õ—îY鹿5çÛêZñúý šÄ8÷J¥ ± ÿ÷Äï êüæãæúéÿïYøÓ” “6(bÉuøÿðûêVç«æ/é·î³ö y“¸ßáQ ÝïùCòêëÊçŽæè‚íõeþ•K×Éû“ë— Ccû—óíìWèæêçhì•ó¬ü u%/çÓ ¡Öüñôîûè¤ænç_ëòóúe»  :¹Ò ýEþUö%ï·éÙæçqê¹ðAù¾[ %ˆ70±2S¶ÿ¿÷Xð†ê&çÉæœéiï—÷ë ñ ‘|O£ /ù˜ñlëŽçŸæßè,îúõYÿnôEðß<cí Š úæòeìèæ<èíkôŸýâÀ©âg1 ìü<ôqí¦èšæ³çðëéòäûJx ¤M7w]j Jˆýõ‹îUé¿æDçóê{ñ.ú¨ ´×@öA› ¢÷þ÷¶ïêÿæíæêðzø÷± ¬F0a¹ñ_jøìððêTç´æAéÒîÕöC: ’¢ ·ÖÐ= ÈÚù1òÝëÄç’æŽèœí>õ‹þµdçÏ÷‡Úƒ ,Lû‚óÞìPèæ÷ç~ì³óÎü#(~%(Ù Ž¿üÝôòíóè§æ{çuë;òû†Ø 5;³É÷ ê2þ@öï¬éÙæçˆêØðdùàw =—<-¥"@Ÿÿ¬÷Eð{ê$çÒæ­é„ïº÷1 .$Žt@ ù…ñ`ëˆç¥æïèBîöxÿ‹ RöÜ/SÙ s‰úÏòVìè’æHèí‡ô¿ýþ׎°ÖU Òûû$ô^í›è™æ½çìóügŽ ³R3kJT 0kýõuîG齿Jçë’ñIúÃ5 ÃÜ=ì.‚ ˆÜþéö¢ï êûæöæ ê7ðøÒ ÂT7^ ªßLVøÛðäêSç¼æUéíîøög\ ¬´¸ÏÅ, µÆù!òÑëÃç›æ¢è¸í^õ®þÕþÜû‚Ðu 9ûqóÓìNè•æèšìÖóõüIF0,#е |®üÍôåíîè¬æ‹ç‘ë]ò<û«÷ N%C´¾è Øþ.öï¦éÚæ&çêôðˆùš T§D+ž,Œÿ–÷5ðsê#çÚæÂéŸïÜ÷V) H-Žj1{õÿøqñPëƒç¨æýèZî8ö›ÿ©"büÙ$AÅ [púºòFìþç”æUè/í¦ôáýÏF ½ãûôMí’è˜æÉçìó%ü‡« Ç]5e=B Uýnõeî>齿Wçë°ñnúæS ÙëCé't uÈþÓöïê÷æþæ4êSð¿ø:î Øc;Zþ˜Ê4>øÈð×êPçÂædéï÷‰y ÄĵŴ ž¯ù òÅë¼çŸæ°èÑí}õÐþöšäùyÁ_ ûZóÁìDè•æè­ìóóýe^@”(¿ e“ü´ôÑíãèªæ’ç¢ëwò[ûÈ _-B¨®Ò ¿þöðî˜é׿+ç«ê ñ¥ù!° e°C"Œý rÿ{÷ðbêçÝæÎé´ïø÷rB Y-‡[eÜéø[ñBë~ç­æ étîXö¾ÿÇ;pÖ0° D[ú¦ò7ìøç˜ædèFíÃôþ? ²ÂÇ9ô ©Ìûûó>í‹èæÕç.ìBóHü¨Å Ûg8a20 @ý[õUî5齿`ç/ëÎñ‘úo ïøHçb b°þ¾ö}ïöé÷æçFêpðàø^ ïq@Vò‡¶(ø´ðËêJçÇæté ï8÷«– Úѱ¹¢ …—ùõñ´ëµç¡æ½èæí›õðþ¯çöm­H êûCó­ì9è—æèÂì ô3ý‚tOš'­… Jxü™ô¼í×è¦æšç³ëòyûå' n4?Ÿ½ ¦éýüõÛî‰éÓæ3ç¼ê%ñÅù?Ê w¹C}é þYÿc÷ ðSêçãæàéÏïø‘] n'2‚O OÅÐøGñ4ëxç±æéŠîvöÝÿãR€Ò š -Búò'ìðç™æoè[íáô$þZÁʼ'à ³ûäó+íè›æÞçBì]ógüÃÛ ên4T î&ý?õAî'鹿gç?ëåñ®ú#† þýCÙG D’þ ödïåéîæçQê‚ðùøv þw<JÞo™þÿø™ð·ê=çÅæ}é0ïP÷ë èØ¦¦‰åhvùÙñœë¥çæÂèöí²õ ÿ'¿#æë[•+ Íæú&ó—ì*è’æ$èÐì#ôOý™…[œ–m /Zü~ô¥íÆè æŸçÁë¦ò“ûþ9 z8;’ˆ¤ ‹ÍýÞõÃîwéÊæ4çÉê:ñÞùZÞ …½@ lÑ á:ÿD÷ñï@ê çäæééâï1ø­s ~-/v<ñ3¦°ø+ñëjç¯æ!éœîŽööÿø_†Èü~  úqò ìßç“æsèjíôô:þm-ÆÅ© ¿ p“ûÁóímè’æßçLìló~üÙë ðk+Bû Íýõ$î鬿dçFëøñÄú7— ÿ9Éî+ &qþ~öHïÏéáæçXê“ðùŒ1 y5:ÊRzßÿé÷}ð ê1çÃæ„éBïh÷ܾ õÞš“oÉJXù¾ñˆë›ç›æËèîËõ&ÿ@Ô0ìæO‚ ±Ëúó‚ìèæ+èãì=ôný¶›g õƒU >üeô‘íºèŸæ¦çÒëÀò±ûQ ‰?7‡wŒ p°ýÃõ®îhéÅæ:çÙêQñüùxö •Ç?Zº Çÿ)÷Øï/êççæ÷é÷ïKøÇŠ Ž7.m,ÛŠ•øñ ëaç°æ,é¯î¦ö qÀìñc òúXòûëÓç’æ~è}íõXþ‰BÒÈ›ù§ Vuû©óüìaè‘æèç^ìˆóœüöt+:öå ´éüõî髿oçYëòæúX°  =Âà [þjö6ïÅéßæçkê­ð0ù«L „85½@dÆÿÑ÷hð“ê,çÉæ•é\ï‡÷üÝ í —ˆ_µ3@ùªñzë•çžæØèîéõFÿ]è=îáBpþ š±úôòqìèæ6è÷ìWôŠýΰu¥çp= û#üLô{í«èšæ¬çãë×òÐû6e —C5zeu V“ý§õ–îZé¿æ=çæêgñú’ ¥Í<øG£ ®ÿ÷¿ïêýæçæê ðhøá  œ=-cÄÿmyøùðùêXç¯æ7éÂîÃö0( „™¹ÞÝJ Øéù>òçëÌç’æˆèí+õwþ¤XàÎüæ <[ûóéìXèæñçsì£ó»üz'.äÑ Ñüðôîÿè¬æzçoë/òûxÍ /@¼Ô ûBþSö$ï·éÝæç€êÊðSùÐl 3’>3²1Q³ÿ¼÷Uð†ê*çÐæ§éuï¨÷ü #ü%”|OŸ*ù–ñjëç¥æèè6îöfÿwüJõÝ7_ç ‚›úàòaì è“æBè ívô­ýðÊ…¬ßa) ä ü5ôií£è›æ·çøëõòðûU ªQ6tVc B|ý”õ„îQé¿æIçûê„ñ;ú´( »ÛAö=’ šîþúö°ïêþæôæê+ð‹ø ¸P7cµëYbøçðîêVç¸æJéßîåöRJ Ÿ«¹ØÐ9 ÃÔù-òÜëÆç™æ˜è«íMõšþÆsôØþŠÚ€ *HûóÝìQè•æè‹ìÁóßü44#…)(×¾ ˆ¹üØôìíóèªæ‚ç€ëJò'û–ä ?>²Âð ã(þ8ö ïªéÚæçêßðpùì„ Eœ<+¢8–ÿ¡÷?ðuê"çÔæµéŒïÅ÷< 6&o8‡ù}ñYë„ç¤æóèKî#ö…ÿ“W÷Ù)IÐ h~úÇòOìè‘æLè í“ôÊý ß–²ÓP ËðûôXí™è›æÂç ìóüs˜ ¼W5kHM *fý|õrîF齿Pçë ñZúÒC ÍäBï/~ „Õþâöœïêúæúæ(êBð«ø(Ý Ë[9\¤ØCKøÒðàêPç½æYé÷î÷sg ²·µÌ»" «¹ùòÊë¿ç›æ¦èÀíhõºþâ‡Üø~Éi -ûhóÈìDè“æ è›ìÞóüüOJ3*!È© q üÀôÚíçè§æ‹ç”ëdòEû³ý P%>¬µÜ Ìþ!öüîžéÕæ%ç êúðù  W§@$–"ÿ‰÷+ðjêçÚæÁé ïÜ÷S' C +l8‚ÿ ù{ñZë‰ç©æ÷èOî#ö…ÿXûß4Yà |‘úÜò_ì è•æJèí}ô³ýôÏŠ±çj7 ðüCôwí®è¢æ¸çóëçòàûFr ¥O=m a¡ý¶õ¥îgéÉæDçéêdñúˆ ¥ÐE ^¾ Í#ÿ0÷ßï8ê çìæøéõïIøÁ‡ Š64w:î,¡¬ø'ñëiç®æ"é›îˆöòÿò[‚ÌŠ /úòìçç”æmèWíÞôþT¹À ¸%à ’·ûçó.í€è—æÒç/ìCóIü© ×d2Y,* ?ýXõVî3é·æWç"ëºñ{úñ[ ÝìBæ!n qÂþÑöŽïÿéöæúæ*êIð±ø+á ÌZ7[¥×DPøØðåêSç½æVéïî÷ögY ©³¼×Ð: ÆØù1òßëÊçœæšè¦íGõ’þ»jî×“ç’ =]û”óîì[è–æøçxì¨ó¼ü}/9òß ®ãüõî靿tçbëòìú\³  AÉè" eþvöBïÎéåæçeê ðù˜= €=@ÏZèÿô÷‡ð©ê9çÆæ„é=ï[÷ϵ íÚ£¢…ãhvùÙñŸë§çžæ¾èîí©õÿµåñe¤? áÿú=ó©ì6è“æè¼ìô,ýykH•$³ T…ü¤ôÅíØè¢æ‹çœëpòWû W&;¤¯Ó Ãþö÷îšéÔæ"çžê÷ðŒù— S¢>'š-‹ÿ˜÷7ðsê!çÔæ¶éïÅ÷> 5's? ù‰ñbë‹ç¤æëè;îönÿ~Kõß7^ê „œúäòdìèæ<èÿìgô˜ýݺ{¨æp; ø#üJô}í­è™æªçÞëÏòÄû+[ A6€oƒ h¨ý¾õ©îeéÁæ5çÕêLñôùjí À<^Á Ò,ÿ8÷äï9ê çâæèéàï,ø§n v(*t=õ8­¹ø3ñ#ëlçªæéˆîuöÛÿÞJwÊ– *@ú‘ò&ìïç•æeèJíÌô þD °½¿1ì ¢Éûöó;íˆè™æÍç%ì1ó7ü—µ Í_5`7; Oýgõcî;éºæSçë«ñfúÚK ÒçCì*| €Ôþâöšïêøæöæê9ðøÐ ÀS5_ ±åU^øãðëêSç¶æIéÙîÞöL@ ˜¥¹ÜÖC Òãù<òäëËç•æŒè”í0õxþ¥XáÏþ’ï Gjûóôì_è‘æëçgìŽó¥üûu*;ùé ¼ïü õî é­ænçRëòÕúF  =Ëò. 'vþ†öNïÔéææ çWêŽð ù„, w7B×e÷ÿø‘ð°ê9ç¿æyé,ïD÷ºŸ ÞÐ¥¬ñv…ùèñ¨ë¬çšæ·èßí–õçþ §àði«J ï ûGó±ì9è‘æè¬ìóóýdY:"¹™ b’ü²ôÐíàè£æ‡ç’ë`òBû­õ J9©³à Ðþ(öïžéÑæçêãðrùîƒ Bš<(¡9œÿ¥÷Cðzê"çÎæ«é}ï®÷'ý $ú"xGœ&ù“ñgëçŸæáè+îúõVÿiñ@ïß;j÷ “¬úðòoìèæ4èòìSô„ýÉ©o£í{J 4üZô‹íµèœæ¥çÓë¾ò±ûN †?8ˆ|• z»ýÒõ¸îréÇæ4çÌê?ñäù]á ‡½? lÔ äAÿK÷öïGêçáæáéÓïø˜^ m#+|HJÀÊøDñ/ësçªæé{îföÊÿÐ?pÿÐ&¦ ;Qúœò1ìóç‘æ\è=í¸ôôý/ú¦¹ Ã9÷ ­ÔûôAí‡è’æÁçìóü{Ÿ ¼R/b;C Yýsõjî>é¶æHçë•ñMúÃ4 ¿Ø<é/„ Šßþëö£ï êöæíæê$ðƒø¸ ®G/]·ðakøïðñêTç±æ;éÆîÇö3, …™ ¹ÞÞQ ÜðùFòíëÎçæè…íõ`þ‘EÕÈþ™ø¥ Uwû©óÿìbèŽæäçXì{óüæô ÷n)?ÿõ Éýüõ#î鬿dçEëññ½ú/ ù<Î÷: 5„þ’öXïÙéåæçKê{ðòøn ôm5FÞoœøžð¸ê:ç¼æléï1÷£‹ Îŧ²›ÿƒ–ùôñ°ë¯ç—æ­èÏíõÒþö• Ûðo¸R üûSóºì<èŽæè›ìÜóÿüLH.…!Ã¥ pžüÁôÚíäè£æ€çëOò,û™å =9®½ì Þ&þ7ö ï©éÕæç„êÔð^ùÜr 6”9-ª'Hªÿ³÷Nðê"çÈæœéjï›÷ì ò"•‚W­*8ù¢ñuë”ç¡æÚè!îéõCÿ[ç;ðåFx ¦¿úó}ìè’æ/èçìAôrý·œg ÷ˆY Fümô—í½èžæ¡çÆë®òžû? {99…¢ ‰ËýßõÄîyéÇæ0çÁê.ñÍùFË v³: pÚ ïKÿV÷ðIêçÙæÒé¾ïø€H \&zL RÊ×øNñ4ësç¢æéiîLö®ÿ¹+c÷Î/± F]ú§ò7ìõçŒæPè,í ôÞýé—± ÊC ½äûôMí‘è”æ¼çì ó ük‘ ³P0iFP ,ný…õwîJ黿Eçýê‰ñ>ú·) ¹×>ó>“ œõþþö¶ïêþæìæ êðtøð« ¦D2eÈtøñþê\ç²æ7é¼î¹ö" {” Àëìa ïúWòùëÖç’æ|èyíõQþ€;ÍÆ£· h‰û¼ó íjè‘æßçJìló}üÔç ïk+F  × ý+õ0î鮿_ç8ëãñ©ú õö<ÒH D–þ öeïáéçæýæAêkðÞø[ èg4Jé|¬ ø­ðÁê@ç»æeé ï ÷~ ¾­º¨ •¦ùòºëµç•æ¦èÅílõ¿þæˆ×ñuÁd (ûeóÆìCèŽæè‘ìÌóëü::#‚#!ε €±üÒôèíîè¤æ|çwë?òû‰× 4<³Êû ð9þGöï±éÙæçzêÂðLùÈb -90³4X¹ÿÄ÷^ð‹ê(çÉæ—é_ïŠ÷ýÜ é“„[°.;ù¥ñtëç›æÕèîçõFÿ^è;íà?mû –­úñòmìèæ3èõìXôŠýϯs£äo: öüGôxí©è˜æªçáëÖòÏû6h ˜D3ycs R‘ý¦õ”îX齿>çåêiñú“ §Î>øG¤ ¬ÿ ÷Àïêÿæêæê ðhøä¡ Ÿ>0fÇqzøûðúê[ç³æ;éÇîÇö4. ˆ¼âÞN ÚëùBòêëÍç•æŒè”í.õyþ©\ãÐþé“ <[û“óëìYè’æôçuì¦ó¾ü|(/åÐ œÏüìôþíûè©ævçlë-òûwÊ +<¹Ï ó:þJöï°éÖæçxêÃðNùÊd .Œ6+ª)Gªÿ´÷Mðê"çÊæ¡énï¢÷ô ôŒvF˜!ùñbë†çœæáè/îöbÿvùEîÙ.Uß |“úØòZìè‹æ=èírô§ýéÄ€§Ø\" Þü.ôcíœè•æ²çôëïòïûQ} ¦J2oQ] :vý‹õîK鏿Bçöê€ñ5ú®% ·Õ<ð8‹ ”çþóö«ïêøæíæê$ð‡ø½ ±I/[ °çU^øãðèêPç³æFéÙîßöOE š§ ³ÏÈ3 ¼Ëù&òÓë¿ç’æ’è¤íEõ”þ¿míÏô€Ñw >ûtóÑìJèŒæøçƒì¼óØü,,} δ |®üÏôåíêè¡æyçxëAòûŽÝ 66©ºè Ù!þ/öï éÑæç‡êÚðjùç~ >•7$š0ÿ™÷7ðoêçÎæ®é†ïÀ÷7 0ÿ †h2€ûùuñRë€çŸæíèGî!ö„ÿ‘SôÖ&EÊ cxúÁòIìþçŒæHèíôÉýÜ®ÐL ÆíûôRí”è•æ½çì óüp– ¹S1eCG "_ýuõkî@é·æJç ë›ñWúÍ> ÉÞ=è(w zÍþÚö•ïêòæòæ!ê<ð¥ø!Ö ÄS1Uþ›Í9DøÊðÖêHçµæSéñîüöm^ ¬²­Á´ £²ùòÂë¶ç“æè¹íbõµþÚ‚ùÕñu¿a $û^óÂì?èŽæè˜ìÙóùüKF,†!¼¡ i˜ü¹ôÓíàè¡æƒçë^òAû°ù J9¤°Ö Åþöôî™éÒæ"çœêøðù š U¢=yÿ„÷&ðdêç׿Âé¢ïá÷Z- H'„^$mäñøcñGë|ç¥æþèaî@ö¤ÿ°'búÔ5· Mbú­ò9ì÷çæWè4í¬ôéý&õ¡¶Æ;ù ¯Ôûô@íŠè•æÉçì(ó/üŽ® É\1]04 Dý]õYî3é¶æSçëµñvúíX Ûè;àd d´þÀöïðéîæùæ2êTðÃøBò Ø`5Oðй#.ø¶ðËêCç»æcé ï÷| Ŭ¹¤ œùüñ¶ë³ç™æ­èÒí„õØþûœ Þón²O òûKó²ì8è‘æè¯ì÷óýj`A‘!²Ž T„ü¤ôÄí×è¤æ’ç¥ë}òcûÒ a-= ¡Å ±óýöãîŽéÑæ*ç°êñ®ù,· i°?‚ñ bÿl÷ðXêçÝæÒé¼ïÿ÷zG ],€RXÍÚøMñ5ëuç©æ évî^öÄÿÌ>pþÏ#Ÿ 5Hú–ò)ìïç‘æbèHíÈôþA ±½ º)ã –»ûèó.íè•æÑç-ìCóNü­Ç Ùc0S! ô,ýEõFî'é¶æ]ç-ëÓñ–ú s ïó?Û Q Nžþ«önïçéìæçEêpðåøc òp;Oéz§ø¥ðÀêCçÆævé'ïB÷µž ÞÔ¬²˜öy‰ùèñ¨ë¯çlinphone-3.12.0/build/wp8/LibLinphoneTester-wp8/LibLinphoneTester-wp8.csproj000066400000000000000000000172771313432737600267130ustar00rootroot00000000000000 Debug AnyCPU 10.0.20506 2.0 {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90} {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties LibLinphoneTester_wp8 LibLinphoneTester_wp8 WindowsPhone v8.0 $(TargetFrameworkVersion) true true true LibLinphoneTester_wp8_$(Configuration)_$(Platform).xap Properties\AppManifest.xml LibLinphoneTester_wp8.App true 11.0 true true full false Bin\x86\Debug DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 pdbonly true Bin\x86\Release TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 true full false Bin\ARM\Debug DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 pdbonly true Bin\ARM\Release TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 App.xaml MainPage.xaml True True AppResources.resx TestCasePage.xaml TestResultPage.xaml Designer MSBuild:Compile Designer MSBuild:Compile Designer MSBuild:Compile Designer MSBuild:Compile Designer PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PublicResXFileCodeGenerator AppResources.Designer.cs {0565952A-EA62-46A2-8261-F5B4B490DA42} libmswp8vid {5E94A00B-B14A-4E42-8284-8CB0EF099534} LibLinphoneTester-native Xcopy /I /Y $(ProjectDir)..\..\..\tester\rcfiles\*_rc $(ProjectDir)Assets\ linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/LibLinphoneTester-wp8.sln000066400000000000000000000606671313432737600262100ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2012 for Windows Phone Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibLinphoneTester-wp8", "LibLinphoneTester-wp8.csproj", "{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}" ProjectSection(ProjectDependencies) = postProject {5E94A00B-B14A-4E42-8284-8CB0EF099534} = {5E94A00B-B14A-4E42-8284-8CB0EF099534} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibLinphone", "..\LibLinphone.vcxproj", "{08DD0D38-D9B5-4626-B60D-B4D76B571142}" ProjectSection(ProjectDependencies) = postProject {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} {59500DD1-B192-4DDF-A402-8A8E3739E032} = {59500DD1-B192-4DDF-A402-8A8E3739E032} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibLinphoneTester-native", "..\LibLinphoneTester-native\LibLinphoneTester-native.vcxproj", "{5E94A00B-B14A-4E42-8284-8CB0EF099534}" ProjectSection(ProjectDependencies) = postProject {D22BD217-D0F8-4274-9B3A-F3F35F46482C} = {D22BD217-D0F8-4274-9B3A-F3F35F46482C} {902DAF1D-EBF1-4D03-B598-143500A50AB4} = {902DAF1D-EBF1-4D03-B598-143500A50AB4} {072FAD20-7007-4DA2-B2E7-16CE2B219F67} = {072FAD20-7007-4DA2-B2E7-16CE2B219F67} {0565952A-EA62-46A2-8261-F5B4B490DA42} = {0565952A-EA62-46A2-8261-F5B4B490DA42} {08DD0D38-D9B5-4626-B60D-B4D76B571142} = {08DD0D38-D9B5-4626-B60D-B4D76B571142} {9924AC72-F96C-4E56-94D9-2B025DA43C6B} = {9924AC72-F96C-4E56-94D9-2B025DA43C6B} {B16B81A9-BEF2-44C9-B603-1065183AE844} = {B16B81A9-BEF2-44C9-B603-1065183AE844} {36B528F9-FB79-4078-A16B-0A7442581BB7} = {36B528F9-FB79-4078-A16B-0A7442581BB7} {1DB09AFE-FC9B-472E-A746-0E33F8EF8883} = {1DB09AFE-FC9B-472E-A746-0E33F8EF8883} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip", "..\..\..\..\belle-sip\build\wp8\belle-sip\belle-sip.vcxproj", "{4C225A82-800B-427B-BA7B-61686A9B347F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mediastreamer2", "..\..\..\mediastreamer2\build\wp8\mediastreamer2\mediastreamer2.vcxproj", "{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "oRTP", "..\..\..\oRTP\build\wp8\oRTP\oRTP.vcxproj", "{FFC7B532-0502-4D88-AC98-9E89071CBC97}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libantlr3c", "..\..\..\..\antlr3\runtime\C\build\wp8\libantlr3c\libantlr3c.vcxproj", "{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsm", "..\..\..\..\gsm\build\wp8\gsm\gsm.vcxproj", "{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speex", "..\..\..\..\speex\build\wp8\speex\speex.vcxproj", "{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speexdsp", "..\..\..\..\speex\build\wp8\speex\speexdsp.vcxproj", "{6BD78980-9C71-4341-8775-AD19E9EC7305}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bcunit", "..\..\..\..\bcunit\build\wp8\bcunit\bcunit.vcxproj", "{902DAF1D-EBF1-4D03-B598-143500A50AB4}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswasapi", "..\..\..\..\mswasapi\mswasapi\mswasapi.vcxproj", "{D22BD217-D0F8-4274-9B3A-F3F35F46482C}" ProjectSection(ProjectDependencies) = postProject {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} {FFC7B532-0502-4D88-AC98-9E89071CBC97} = {FFC7B532-0502-4D88-AC98-9E89071CBC97} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libilbc-rfc3951", "..\..\..\..\libilbc-rfc3951\build\wp8\libilbc-rfc3951\libilbc-rfc3951.vcxproj", "{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsilbc", "..\..\..\..\msilbc\build\wp8\msilbc\msilbc.vcxproj", "{072FAD20-7007-4DA2-B2E7-16CE2B219F67}" ProjectSection(ProjectDependencies) = postProject {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} {FFC7B532-0502-4D88-AC98-9E89071CBC97} = {FFC7B532-0502-4D88-AC98-9E89071CBC97} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmssilk", "..\..\..\..\mssilk\build\wp8\mssilk\mssilk.vcxproj", "{36B528F9-FB79-4078-A16B-0A7442581BB7}" ProjectSection(ProjectDependencies) = postProject {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} {FFC7B532-0502-4D88-AC98-9E89071CBC97} = {FFC7B532-0502-4D88-AC98-9E89071CBC97} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsamr", "..\..\..\..\msamr\build\wp8\msamr\msamr.vcxproj", "{9924AC72-F96C-4E56-94D9-2B025DA43C6B}" ProjectSection(ProjectDependencies) = postProject {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70} {018A4428-535C-4566-9AE0-E93AFF0D3ED2} = {018A4428-535C-4566-9AE0-E93AFF0D3ED2} {7AC65D2A-6981-4D17-856D-C37A522739D8} = {7AC65D2A-6981-4D17-856D-C37A522739D8} {88191E75-2993-48D7-AA76-652F274EF0FE} = {88191E75-2993-48D7-AA76-652F274EF0FE} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vo-amrwbenc", "..\..\..\..\msamr\build\wp8\msamr\vo-amrwbenc.vcxproj", "{018A4428-535C-4566-9AE0-E93AFF0D3ED2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opencore_amrnb", "..\..\..\..\msamr\build\wp8\msamr\opencore_amrnb.vcxproj", "{88191E75-2993-48D7-AA76-652F274EF0FE}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opencore_amrwb", "..\..\..\..\msamr\build\wp8\msamr\opencore_amrwb.vcxproj", "{7AC65D2A-6981-4D17-856D-C37A522739D8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polarssl", "..\..\..\..\polarssl\build\wp8\polarssl\polarssl.vcxproj", "{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tunnel", "..\..\..\..\tunnel\build\wp8\tunnel\tunnel.vcxproj", "{59500DD1-B192-4DDF-A402-8A8E3739E032}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxml2", "..\libxml2\libxml2.vcxproj", "{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srtp", "..\..\..\..\srtp\build\wp8\srtp\srtp.vcxproj", "{B4B96BC4-2B72-4964-98E4-7FD048A43363}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswp8vid", "..\..\..\..\mswp8vid\mswp8vid\mswp8vid.vcxproj", "{0565952A-EA62-46A2-8261-F5B4B490DA42}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswebrtc", "..\..\..\..\mswebrtc\build\wp8\mswebrtc\mswebrtc.vcxproj", "{B16B81A9-BEF2-44C9-B603-1065183AE844}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "webrtc", "..\..\..\..\mswebrtc\webrtc\build\wp8\webrtc\webrtc.vcxproj", "{A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsbcg729", "..\..\..\..\bcg729\build\wp8\bcg729\bcg729.vcxproj", "{1DB09AFE-FC9B-472E-A746-0E33F8EF8883}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opus", "..\..\..\..\opus\build\wp8\opus\opus.vcxproj", "{D450EC75-DF02-48B0-A4FB-ACA79BD894AB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|x86 = Debug|x86 Release|ARM = Release|ARM Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|ARM.ActiveCfg = Debug|ARM {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|ARM.Build.0 = Debug|ARM {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|ARM.Deploy.0 = Debug|ARM {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|x86.ActiveCfg = Debug|x86 {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|x86.Build.0 = Debug|x86 {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|x86.Deploy.0 = Debug|x86 {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|ARM.ActiveCfg = Release|ARM {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|ARM.Build.0 = Release|ARM {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|ARM.Deploy.0 = Release|ARM {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|x86.ActiveCfg = Release|x86 {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|x86.Build.0 = Release|x86 {34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|x86.Deploy.0 = Release|x86 {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|ARM.ActiveCfg = Debug|ARM {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|ARM.Build.0 = Debug|ARM {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|x86.ActiveCfg = Debug|Win32 {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|x86.Build.0 = Debug|Win32 {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|ARM.ActiveCfg = Release|ARM {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|ARM.Build.0 = Release|ARM {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|x86.ActiveCfg = Release|Win32 {08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|x86.Build.0 = Release|Win32 {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|ARM.ActiveCfg = Debug|ARM {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|ARM.Build.0 = Debug|ARM {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|x86.ActiveCfg = Debug|Win32 {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|x86.Build.0 = Debug|Win32 {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|ARM.ActiveCfg = Release|ARM {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|ARM.Build.0 = Release|ARM {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|x86.ActiveCfg = Release|Win32 {5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|x86.Build.0 = Release|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.ActiveCfg = Debug|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.Build.0 = Debug|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|x86.ActiveCfg = Debug|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|x86.Build.0 = Debug|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.ActiveCfg = Release|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.Build.0 = Release|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|x86.ActiveCfg = Release|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|x86.Build.0 = Release|Win32 {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|ARM.ActiveCfg = Debug|ARM {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|ARM.Build.0 = Debug|ARM {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|x86.ActiveCfg = Debug|Win32 {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|x86.Build.0 = Debug|Win32 {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|ARM.ActiveCfg = Release|ARM {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|ARM.Build.0 = Release|ARM {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|x86.ActiveCfg = Release|Win32 {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|x86.Build.0 = Release|Win32 {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|ARM.ActiveCfg = Debug|ARM {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|ARM.Build.0 = Debug|ARM {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|x86.ActiveCfg = Debug|Win32 {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|x86.Build.0 = Debug|Win32 {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|ARM.ActiveCfg = Release|ARM {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|ARM.Build.0 = Release|ARM {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|x86.ActiveCfg = Release|Win32 {FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|x86.Build.0 = Release|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.ActiveCfg = Debug|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.Build.0 = Debug|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|x86.ActiveCfg = Debug|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|x86.Build.0 = Debug|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.ActiveCfg = Release|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.Build.0 = Release|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|x86.ActiveCfg = Release|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|x86.Build.0 = Release|Win32 {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|ARM.ActiveCfg = Debug|ARM {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|ARM.Build.0 = Debug|ARM {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|x86.ActiveCfg = Debug|Win32 {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|x86.Build.0 = Debug|Win32 {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|ARM.ActiveCfg = Release|ARM {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|ARM.Build.0 = Release|ARM {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|x86.ActiveCfg = Release|Win32 {746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|x86.Build.0 = Release|Win32 {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|ARM.ActiveCfg = Debug|ARM {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|ARM.Build.0 = Debug|ARM {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|x86.ActiveCfg = Debug|Win32 {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|x86.Build.0 = Debug|Win32 {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|ARM.ActiveCfg = Release|ARM {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|ARM.Build.0 = Release|ARM {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|x86.ActiveCfg = Release|Win32 {D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|x86.Build.0 = Release|Win32 {6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|ARM.ActiveCfg = Debug|ARM {6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|ARM.Build.0 = Debug|ARM {6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|x86.ActiveCfg = Debug|Win32 {6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|x86.Build.0 = Debug|Win32 {6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|ARM.ActiveCfg = Release|ARM {6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|ARM.Build.0 = Release|ARM {6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|x86.ActiveCfg = Release|Win32 {6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|x86.Build.0 = Release|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|ARM.ActiveCfg = Debug|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|ARM.Build.0 = Debug|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|x86.ActiveCfg = Debug|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|x86.Build.0 = Debug|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|ARM.ActiveCfg = Release|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|ARM.Build.0 = Release|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|x86.ActiveCfg = Release|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|x86.Build.0 = Release|Win32 {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|ARM.ActiveCfg = Debug|ARM {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|ARM.Build.0 = Debug|ARM {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|x86.ActiveCfg = Debug|Win32 {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|x86.Build.0 = Debug|Win32 {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|ARM.ActiveCfg = Release|ARM {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|ARM.Build.0 = Release|ARM {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|x86.ActiveCfg = Release|Win32 {D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|x86.Build.0 = Release|Win32 {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|ARM.ActiveCfg = Debug|ARM {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|ARM.Build.0 = Debug|ARM {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|x86.ActiveCfg = Debug|Win32 {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|x86.Build.0 = Debug|Win32 {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|ARM.ActiveCfg = Release|ARM {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|ARM.Build.0 = Release|ARM {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|x86.ActiveCfg = Release|Win32 {8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|x86.Build.0 = Release|Win32 {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|ARM.ActiveCfg = Debug|ARM {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|ARM.Build.0 = Debug|ARM {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|x86.ActiveCfg = Debug|Win32 {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|x86.Build.0 = Debug|Win32 {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|ARM.ActiveCfg = Release|ARM {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|ARM.Build.0 = Release|ARM {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|x86.ActiveCfg = Release|Win32 {072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|x86.Build.0 = Release|Win32 {36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|ARM.ActiveCfg = Debug|ARM {36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|ARM.Build.0 = Debug|ARM {36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|x86.ActiveCfg = Debug|Win32 {36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|x86.Build.0 = Debug|Win32 {36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|ARM.ActiveCfg = Release|ARM {36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|ARM.Build.0 = Release|ARM {36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|x86.ActiveCfg = Release|Win32 {36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|x86.Build.0 = Release|Win32 {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|ARM.ActiveCfg = Debug|ARM {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|ARM.Build.0 = Debug|ARM {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|x86.ActiveCfg = Debug|Win32 {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|x86.Build.0 = Debug|Win32 {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|ARM.ActiveCfg = Release|ARM {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|ARM.Build.0 = Release|ARM {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|x86.ActiveCfg = Release|Win32 {9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|x86.Build.0 = Release|Win32 {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|ARM.ActiveCfg = Debug|ARM {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|ARM.Build.0 = Debug|ARM {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|x86.ActiveCfg = Debug|Win32 {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|x86.Build.0 = Debug|Win32 {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|ARM.ActiveCfg = Release|ARM {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|ARM.Build.0 = Release|ARM {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|x86.ActiveCfg = Release|Win32 {018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|x86.Build.0 = Release|Win32 {88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|ARM.ActiveCfg = Debug|ARM {88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|ARM.Build.0 = Debug|ARM {88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|x86.ActiveCfg = Debug|Win32 {88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|x86.Build.0 = Debug|Win32 {88191E75-2993-48D7-AA76-652F274EF0FE}.Release|ARM.ActiveCfg = Release|ARM {88191E75-2993-48D7-AA76-652F274EF0FE}.Release|ARM.Build.0 = Release|ARM {88191E75-2993-48D7-AA76-652F274EF0FE}.Release|x86.ActiveCfg = Release|Win32 {88191E75-2993-48D7-AA76-652F274EF0FE}.Release|x86.Build.0 = Release|Win32 {7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|ARM.ActiveCfg = Debug|ARM {7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|ARM.Build.0 = Debug|ARM {7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|x86.ActiveCfg = Debug|Win32 {7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|x86.Build.0 = Debug|Win32 {7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|ARM.ActiveCfg = Release|ARM {7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|ARM.Build.0 = Release|ARM {7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|x86.ActiveCfg = Release|Win32 {7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|x86.Build.0 = Release|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.ActiveCfg = Debug|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.Build.0 = Debug|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|x86.ActiveCfg = Debug|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|x86.Build.0 = Debug|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.ActiveCfg = Release|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.Build.0 = Release|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|x86.ActiveCfg = Release|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|x86.Build.0 = Release|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.ActiveCfg = Debug|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.Build.0 = Debug|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|x86.ActiveCfg = Debug|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|x86.Build.0 = Debug|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.ActiveCfg = Release|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.Build.0 = Release|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|x86.ActiveCfg = Release|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|x86.Build.0 = Release|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|ARM.ActiveCfg = Debug|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|ARM.Build.0 = Debug|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|x86.ActiveCfg = Debug|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|x86.Build.0 = Debug|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|ARM.ActiveCfg = Release|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|ARM.Build.0 = Release|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|x86.ActiveCfg = Release|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|x86.Build.0 = Release|Win32 {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|ARM.ActiveCfg = Debug|ARM {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|ARM.Build.0 = Debug|ARM {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|x86.ActiveCfg = Debug|Win32 {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|x86.Build.0 = Debug|Win32 {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|ARM.ActiveCfg = Release|ARM {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|ARM.Build.0 = Release|ARM {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|x86.ActiveCfg = Release|Win32 {B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|x86.Build.0 = Release|Win32 {0565952A-EA62-46A2-8261-F5B4B490DA42}.Debug|ARM.ActiveCfg = Debug|ARM {0565952A-EA62-46A2-8261-F5B4B490DA42}.Debug|ARM.Build.0 = Debug|ARM {0565952A-EA62-46A2-8261-F5B4B490DA42}.Debug|x86.ActiveCfg = Debug|Win32 {0565952A-EA62-46A2-8261-F5B4B490DA42}.Debug|x86.Build.0 = Debug|Win32 {0565952A-EA62-46A2-8261-F5B4B490DA42}.Release|ARM.ActiveCfg = Release|ARM {0565952A-EA62-46A2-8261-F5B4B490DA42}.Release|ARM.Build.0 = Release|ARM {0565952A-EA62-46A2-8261-F5B4B490DA42}.Release|x86.ActiveCfg = Release|Win32 {0565952A-EA62-46A2-8261-F5B4B490DA42}.Release|x86.Build.0 = Release|Win32 {B16B81A9-BEF2-44C9-B603-1065183AE844}.Debug|ARM.ActiveCfg = Debug|ARM {B16B81A9-BEF2-44C9-B603-1065183AE844}.Debug|ARM.Build.0 = Debug|ARM {B16B81A9-BEF2-44C9-B603-1065183AE844}.Debug|x86.ActiveCfg = Debug|Win32 {B16B81A9-BEF2-44C9-B603-1065183AE844}.Debug|x86.Build.0 = Debug|Win32 {B16B81A9-BEF2-44C9-B603-1065183AE844}.Release|ARM.ActiveCfg = Release|ARM {B16B81A9-BEF2-44C9-B603-1065183AE844}.Release|ARM.Build.0 = Release|ARM {B16B81A9-BEF2-44C9-B603-1065183AE844}.Release|x86.ActiveCfg = Release|Win32 {B16B81A9-BEF2-44C9-B603-1065183AE844}.Release|x86.Build.0 = Release|Win32 {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Debug|ARM.ActiveCfg = Debug|ARM {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Debug|ARM.Build.0 = Debug|ARM {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Debug|x86.ActiveCfg = Debug|Win32 {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Debug|x86.Build.0 = Debug|Win32 {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Release|ARM.ActiveCfg = Release|ARM {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Release|ARM.Build.0 = Release|ARM {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Release|x86.ActiveCfg = Release|Win32 {A5A719E5-FDD6-4DFD-AAF6-68C9534B5562}.Release|x86.Build.0 = Release|Win32 {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Debug|ARM.ActiveCfg = Debug|ARM {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Debug|ARM.Build.0 = Debug|ARM {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Debug|x86.ActiveCfg = Debug|Win32 {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Debug|x86.Build.0 = Debug|Win32 {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Release|ARM.ActiveCfg = Release|ARM {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Release|ARM.Build.0 = Release|ARM {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Release|x86.ActiveCfg = Release|Win32 {1DB09AFE-FC9B-472E-A746-0E33F8EF8883}.Release|x86.Build.0 = Release|Win32 {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Debug|ARM.ActiveCfg = Debug|ARM {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Debug|ARM.Build.0 = Debug|ARM {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Debug|x86.ActiveCfg = Debug|Win32 {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Debug|x86.Build.0 = Debug|Win32 {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Release|ARM.ActiveCfg = Release|ARM {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Release|ARM.Build.0 = Release|ARM {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Release|x86.ActiveCfg = Release|Win32 {D450EC75-DF02-48B0-A4FB-ACA79BD894AB}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/LocalizedStrings.cs000066400000000000000000000006011313432737600251510ustar00rootroot00000000000000using LibLinphoneTester_wp8.Resources; namespace LibLinphoneTester_wp8 { /// /// Provides access to string resources. /// public class LocalizedStrings { private static AppResources _localizedResources = new AppResources(); public AppResources LocalizedResources { get { return _localizedResources; } } } }linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/MainPage.xaml000066400000000000000000000043371313432737600237200ustar00rootroot00000000000000 linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/MainPage.xaml.cs000066400000000000000000000034101313432737600243130ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; namespace LibLinphoneTester_wp8 { public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); var tester = (Application.Current as App).tester; List source = new List(); source.Add(new UnitTestSuiteName("ALL")); for (int i = 0; i < tester.nbTestSuites(); i++) { source.Add(new UnitTestSuiteName(tester.testSuiteName(i))); } Tests.ItemsSource = source; } private void Tests_Tap(object sender, System.Windows.Input.GestureEventArgs e) { UnitTestSuiteName test = (sender as LongListSelector).SelectedItem as UnitTestSuiteName; if (test == null) return; if (test.Name == "ALL") { NavigationService.Navigate(new Uri("/TestResultPage.xaml?SuiteName=" + test.Name + "&Verbose=" + Verbose.IsChecked.GetValueOrDefault(), UriKind.Relative)); } else { NavigationService.Navigate(new Uri("/TestCasePage.xaml?SuiteName=" + test.Name + "&Verbose=" + Verbose.IsChecked.GetValueOrDefault(), UriKind.Relative)); } } } public class UnitTestSuiteName { public string Name { get; set; } public UnitTestSuiteName(string name) { this.Name = name; } } }linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Properties/000077500000000000000000000000001313432737600235015ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Properties/AppManifest.xml000066400000000000000000000003171313432737600264330ustar00rootroot00000000000000 linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Properties/AssemblyInfo.cs000066400000000000000000000027261313432737600264320ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Resources; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("LibLinphoneTester_wp8")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("LibLinphoneTester_wp8")] [assembly: AssemblyCopyright("Copyright © 2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("f1aad7a9-2083-4726-ab28-f57b1dd5891e")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: NeutralResourcesLanguageAttribute("en-US")] linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Properties/WMAppManifest.xml000066400000000000000000000042661313432737600267060ustar00rootroot00000000000000 Assets\ApplicationIcon.png Assets\Tiles\FlipCycleTileSmall.png 0 Assets\Tiles\FlipCycleTileMedium.png LibLinphoneTester_wp8 linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Resources/000077500000000000000000000000001313432737600233175ustar00rootroot00000000000000linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Resources/AppResources.Designer.cs000066400000000000000000000106471313432737600300300ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.17626 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace LibLinphoneTester_wp8.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class AppResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal AppResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LibLinphoneTester_wp8.Resources.AppResources", typeof(AppResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to LeftToRight. /// public static string ResourceFlowDirection { get { return ResourceManager.GetString("ResourceFlowDirection", resourceCulture); } } /// /// Looks up a localized string similar to us-EN. /// public static string ResourceLanguage { get { return ResourceManager.GetString("ResourceLanguage", resourceCulture); } } /// /// Looks up a localized string similar to MY APPLICATION. /// public static string ApplicationTitle { get { return ResourceManager.GetString("ApplicationTitle", resourceCulture); } } /// /// Looks up a localized string similar to button. /// public static string AppBarButtonText { get { return ResourceManager.GetString("AppBarButtonText", resourceCulture); } } /// /// Looks up a localized string similar to menu item. /// public static string AppBarMenuItemText { get { return ResourceManager.GetString("AppBarMenuItemText", resourceCulture); } } } } linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/Resources/AppResources.resx000066400000000000000000000146651313432737600266510ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 LeftToRight Controls the FlowDirection for all elements in the RootFrame. Set to the traditional direction of this resource file's language en-US Controls the Language and ensures that the font for all elements in the RootFrame aligns with the app's language. Set to the language code of this resource file's language. MY APPLICATION add Menu Item linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/TestCasePage.xaml000066400000000000000000000041041313432737600245370ustar00rootroot00000000000000 linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/TestCasePage.xaml.cs000066400000000000000000000035771313432737600251600ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; namespace LibLinphoneTester_wp8 { public partial class TestCasePage : PhoneApplicationPage { public TestCasePage() { InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); suiteName = NavigationContext.QueryString["SuiteName"]; verbose = Convert.ToBoolean(NavigationContext.QueryString["Verbose"]); var tester = (Application.Current as App).tester; List source = new List(); source.Add(new UnitTestCaseName("ALL")); for (int i = 0; i < tester.nbTests(suiteName); i++) { source.Add(new UnitTestCaseName(tester.testName(suiteName, i))); } Tests.ItemsSource = source; } private void Tests_Tap(object sender, System.Windows.Input.GestureEventArgs e) { UnitTestCaseName test = (sender as LongListSelector).SelectedItem as UnitTestCaseName; if (test == null) return; if (!(Application.Current as App).suiteRunning()) { NavigationService.Navigate(new Uri("/TestResultPage.xaml?SuiteName=" + suiteName + "&CaseName=" + test.Name + "&Verbose=" + verbose, UriKind.Relative)); } } private string suiteName; private bool verbose; } public class UnitTestCaseName { public string Name { get; set; } public UnitTestCaseName(string name) { this.Name = name; } } }linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/TestResultPage.xaml000066400000000000000000000035411313432737600251460ustar00rootroot00000000000000 linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/TestResultPage.xaml.cs000066400000000000000000000111111313432737600255420ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using System.Threading.Tasks; using linphone_tester_native; namespace LibLinphoneTester_wp8 { public delegate void OutputDisplayDelegate(int level, String msg); public partial class TestResultPage : PhoneApplicationPage { public TestResultPage() { InitializeComponent(); Browser.Navigate(new Uri("log.html", UriKind.Relative)); } private void Browser_LoadCompleted(object sender, NavigationEventArgs e) { string suiteName = NavigationContext.QueryString["SuiteName"]; string caseName; if (NavigationContext.QueryString.ContainsKey("CaseName")) { caseName = NavigationContext.QueryString["CaseName"]; } else { caseName = "ALL"; } bool verbose = Convert.ToBoolean(NavigationContext.QueryString["Verbose"]); var app = (Application.Current as App); app.suite = new UnitTestSuite(suiteName, caseName, verbose, new OutputDisplayDelegate(OutputDisplay)); app.suite.run(); } public void OutputDisplay(int level, String msg) { this.Dispatcher.BeginInvoke(() => { msg = msg.Replace("\r\n", "\n"); string[] lines = msg.Split('\n'); bool insertNewLine = false; foreach (string line in lines) { if (line.Length == 0) { insertNewLine = false; Browser.InvokeScript("append_nl"); } else { if (insertNewLine == true) { Browser.InvokeScript("append_nl"); } if (level == 0) { Browser.InvokeScript("append_trace", line, "debug"); } else if (level == 1) { Browser.InvokeScript("append_trace", line, "message"); } else if (level == 2) { Browser.InvokeScript("append_trace", line, "warning"); } else if (level == 3) { Browser.InvokeScript("append_trace", line, "error"); } else { Browser.InvokeScript("append_text", line); } insertNewLine = true; } } }); } } public class UnitTestSuite : OutputTraceListener { public UnitTestSuite(string SuiteName, string CaseName, bool Verbose, OutputDisplayDelegate OutputDisplay) { this.SuiteName = SuiteName; this.CaseName = CaseName; this.Verbose = Verbose; this.Running = false; this.OutputDisplay = OutputDisplay; } async public void run() { Running = true; var tup = new Tuple(SuiteName, CaseName, Verbose); var t = Task.Factory.StartNew((object parameters) => { var tester = (Application.Current as App).tester; tester.setOutputTraceListener(this); var p = parameters as Tuple; tester.run(p.Item1, p.Item2, p.Item3); }, tup); await t; Running = false; } public void outputTrace(int level, String msg) { if (OutputDisplay != null) { OutputDisplay(level, msg); } System.Diagnostics.Debug.WriteLine(msg); } public bool running { get { return Running; } protected set { Running = value; } } private string SuiteName; private string CaseName; private bool Verbose; private bool Running; private OutputDisplayDelegate OutputDisplay; } }linphone-3.12.0/build/wp8/LibLinphoneTester-wp8/log.html000066400000000000000000000020761313432737600230210ustar00rootroot00000000000000

linphone-3.12.0/build/wp8/LibLinphone_no_tunnel.vcxproj000066400000000000000000000273641313432737600231540ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {08dd0d38-d9b5-4626-b60d-b4d76b571142} LibLinphone en-US 11.0 DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ false Level4 $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\zlib;$(ProjectDir)..\..\..\sqlite\;$(ProjectDir);%(AdditionalIncludeDirectories) __STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="\\linphone\\plugins";UNICODE;_XKEYCHECK_H;HAVE_ZLIB;HAVE_CONFIG_H;%(PreprocessorDefinitions) Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) Console false false belle-sip_no_tunnel.lib;mediastreamer2.lib;ws2_32.lib;ortp.lib;gsm.lib;speex.lib;speexdsp.lib;libxml2.lib;sqlite.lib;zlib.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib version.bat Batch script to get the git version _DEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions) true false NDEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions) MaxSpeed true true true false true true false {1db09afe-fc9b-472e-a746-0e33f8ef8883} {4c225a82-800b-427b-ba7b-61686a9b347f} {9924ac72-f96c-4e56-94d9-2b025da43c6b} {072fad20-7007-4da2-b2e7-16ce2b219f67} {36b528f9-fb79-4078-a16b-0a7442581bb7} {d22bd217-d0f8-4274-9b3a-f3f35f46482c} {b16b81a9-bef2-44c9-b603-1065183ae844} {0565952a-ea62-46a2-8261-f5b4b490da42} {a45d63b9-60de-476c-8836-f8eedbe139d0} {027bad0e-9179-48c1-9733-7aa7e2c2ec70} {ffc7b532-0502-4d88-ac98-9e89071cbc97} {5dfa07b4-0be9-46a9-ba32-fdf5a55c580b} {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f} linphone-3.12.0/build/wp8/libxml2/000077500000000000000000000000001313432737600166105ustar00rootroot00000000000000linphone-3.12.0/build/wp8/libxml2/install_headers.bat000066400000000000000000000002461313432737600224430ustar00rootroot00000000000000SET curdir=%CD% SET incdir=..\..\..\..\libxml2\include\libxml SET installdir=%1\libxml Xcopy /I /Y %incdir%\*.h %installdir%\ Xcopy /I /Y xmlversion.h %installdir%\ linphone-3.12.0/build/wp8/libxml2/libxml2.sln000066400000000000000000000023221313432737600206760ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2012 for Windows Phone Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxml2", "libxml2.vcxproj", "{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|Win32 = Debug|Win32 Release|ARM = Release|ARM Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|ARM.ActiveCfg = Debug|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|ARM.Build.0 = Debug|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|Win32.ActiveCfg = Debug|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|Win32.Build.0 = Debug|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|ARM.ActiveCfg = Release|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|ARM.Build.0 = Release|ARM {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Win32.ActiveCfg = Release|Win32 {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal linphone-3.12.0/build/wp8/libxml2/libxml2.vcxproj000066400000000000000000000175501313432737600216060ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {5dfa07b4-0be9-46a9-ba32-fdf5a55c580b} libxml2 en-US 11.0 DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ false Level4 $(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\..\libxml2\win32\VC10;%(AdditionalIncludeDirectories) _WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;HAVE_WIN32_THREADS;HAVE_COMPILER_TLS;UNICODE;%(PreprocessorDefinitions) LIBXML_MODULES_ENABLED Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) $(ProjectDir)libxml2_port.h Console false false $(TargetDir)$(TargetName).lib Ws2_32.lib;%(AdditionalDependencies) install_headers.bat $(SolutionDir)$(Platform)\$(Configuration)\include _DEBUG;%(PreprocessorDefinitions) true NDEBUG;%(PreprocessorDefinitions) MaxSpeed true true true false true true false linphone-3.12.0/build/wp8/libxml2/libxml2_port.h000066400000000000000000000011431313432737600213750ustar00rootroot00000000000000 #ifndef LIBXML2_PORT_H #define LIBXML2_PORT_H #define CreateMutex(a, b, c) CreateMutexExW(a, c, ((b) ? CREATE_MUTEX_INITIAL_OWNER : 0), 0) #define GetVersionEx(osvi) (((osvi)->dwPlatformId = 0) != 0) #define InitializeCriticalSection(cs) InitializeCriticalSectionEx(cs, 0, 0) #define WaitForSingleObject(hHandle, dwMilliseconds) WaitForSingleObjectEx(hHandle, dwMilliseconds, 0) #define Sleep(ms) { \ HANDLE sleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); \ if (!sleepEvent) return; \ WaitForSingleObjectEx(sleepEvent, ms, FALSE); \ } #endif /* LIBXML2_PORT_H */ linphone-3.12.0/build/wp8/libxml2/xmlversion.h000066400000000000000000000172741313432737600212020ustar00rootroot00000000000000/* * Summary: compile-time version informations * Description: compile-time version informations for the XML library * * Copy: See Copyright for the status of this software. * * Author: Daniel Veillard */ #ifndef __XML_VERSION_H__ #define __XML_VERSION_H__ #include #ifdef __cplusplus extern "C" { #endif /* * use those to be sure nothing nasty will happen if * your library and includes mismatch */ #ifndef LIBXML2_COMPILING_MSCCDEF XMLPUBFUN void XMLCALL xmlCheckVersion(int version); #endif /* LIBXML2_COMPILING_MSCCDEF */ /** * LIBXML_DOTTED_VERSION: * * the version string like "1.2.3" */ #define LIBXML_DOTTED_VERSION "2.8.0" /** * LIBXML_VERSION: * * the version number: 1.2.3 value is 10203 */ #define LIBXML_VERSION 20800 /** * LIBXML_VERSION_STRING: * * the version number string, 1.2.3 value is "10203" */ #define LIBXML_VERSION_STRING "20800" /** * LIBXML_VERSION_EXTRA: * * extra version information, used to show a CVS compilation */ #define LIBXML_VERSION_EXTRA "" /** * LIBXML_TEST_VERSION: * * Macro to check that the libxml version in use is compatible with * the version the software has been compiled against */ #define LIBXML_TEST_VERSION xmlCheckVersion(20800); #ifndef VMS #if 0 /** * WITH_TRIO: * * defined if the trio support need to be configured in */ #define WITH_TRIO #else /** * WITHOUT_TRIO: * * defined if the trio support should not be configured in */ #define WITHOUT_TRIO #endif #else /* VMS */ /** * WITH_TRIO: * * defined if the trio support need to be configured in */ #define WITH_TRIO 1 #endif /* VMS */ /** * LIBXML_THREAD_ENABLED: * * Whether the thread support is configured in */ #if 1 #if defined(_REENTRANT) || defined(__MT__) || \ (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE - 0 >= 199506L)) #define LIBXML_THREAD_ENABLED #endif #endif /** * LIBXML_TREE_ENABLED: * * Whether the DOM like tree manipulation API support is configured in */ #if 1 #define LIBXML_TREE_ENABLED #endif /** * LIBXML_OUTPUT_ENABLED: * * Whether the serialization/saving support is configured in */ #if 1 #define LIBXML_OUTPUT_ENABLED #endif /** * LIBXML_PUSH_ENABLED: * * Whether the push parsing interfaces are configured in */ #if 1 #define LIBXML_PUSH_ENABLED #endif /** * LIBXML_READER_ENABLED: * * Whether the xmlReader parsing interface is configured in */ #if 1 #define LIBXML_READER_ENABLED #endif /** * LIBXML_PATTERN_ENABLED: * * Whether the xmlPattern node selection interface is configured in */ #if 1 #define LIBXML_PATTERN_ENABLED #endif /** * LIBXML_WRITER_ENABLED: * * Whether the xmlWriter saving interface is configured in */ #if 1 #define LIBXML_WRITER_ENABLED #endif /** * LIBXML_SAX1_ENABLED: * * Whether the older SAX1 interface is configured in */ #if 1 #define LIBXML_SAX1_ENABLED #endif /** * LIBXML_FTP_ENABLED: * * Whether the FTP support is configured in */ #if 1 #define LIBXML_FTP_ENABLED #endif /** * LIBXML_HTTP_ENABLED: * * Whether the HTTP support is configured in */ #if 1 #define LIBXML_HTTP_ENABLED #endif /** * LIBXML_VALID_ENABLED: * * Whether the DTD validation support is configured in */ #if 1 #define LIBXML_VALID_ENABLED #endif /** * LIBXML_HTML_ENABLED: * * Whether the HTML support is configured in */ #if 1 #define LIBXML_HTML_ENABLED #endif /** * LIBXML_LEGACY_ENABLED: * * Whether the deprecated APIs are compiled in for compatibility */ #if 1 #define LIBXML_LEGACY_ENABLED #endif /** * LIBXML_C14N_ENABLED: * * Whether the Canonicalization support is configured in */ #if 1 #define LIBXML_C14N_ENABLED #endif /** * LIBXML_CATALOG_ENABLED: * * Whether the Catalog support is configured in */ #if 0 #define LIBXML_CATALOG_ENABLED #endif /** * LIBXML_DOCB_ENABLED: * * Whether the SGML Docbook support is configured in */ #if 1 #define LIBXML_DOCB_ENABLED #endif /** * LIBXML_XPATH_ENABLED: * * Whether XPath is configured in */ #if 1 #define LIBXML_XPATH_ENABLED #endif /** * LIBXML_XPTR_ENABLED: * * Whether XPointer is configured in */ #if 1 #define LIBXML_XPTR_ENABLED #endif /** * LIBXML_XINCLUDE_ENABLED: * * Whether XInclude is configured in */ #if 1 #define LIBXML_XINCLUDE_ENABLED #endif /** * LIBXML_ICONV_ENABLED: * * Whether iconv support is available */ #if 0 #define LIBXML_ICONV_ENABLED #endif /** * LIBXML_ICU_ENABLED: * * Whether icu support is available */ #if 0 #define LIBXML_ICU_ENABLED #endif /** * LIBXML_ISO8859X_ENABLED: * * Whether ISO-8859-* support is made available in case iconv is not */ #if 0 #define LIBXML_ISO8859X_ENABLED #endif /** * LIBXML_DEBUG_ENABLED: * * Whether Debugging module is configured in */ #if 1 #define LIBXML_DEBUG_ENABLED #endif /** * DEBUG_MEMORY_LOCATION: * * Whether the memory debugging is configured in */ #if 0 #define DEBUG_MEMORY_LOCATION #endif /** * LIBXML_DEBUG_RUNTIME: * * Whether the runtime debugging is configured in */ #if 0 #define LIBXML_DEBUG_RUNTIME #endif /** * LIBXML_UNICODE_ENABLED: * * Whether the Unicode related interfaces are compiled in */ #if 1 #define LIBXML_UNICODE_ENABLED #endif /** * LIBXML_REGEXP_ENABLED: * * Whether the regular expressions interfaces are compiled in */ #if 1 #define LIBXML_REGEXP_ENABLED #endif /** * LIBXML_AUTOMATA_ENABLED: * * Whether the automata interfaces are compiled in */ #if 1 #define LIBXML_AUTOMATA_ENABLED #endif /** * LIBXML_EXPR_ENABLED: * * Whether the formal expressions interfaces are compiled in */ #if 1 #define LIBXML_EXPR_ENABLED #endif /** * LIBXML_SCHEMAS_ENABLED: * * Whether the Schemas validation interfaces are compiled in */ #if 1 #define LIBXML_SCHEMAS_ENABLED #endif /** * LIBXML_SCHEMATRON_ENABLED: * * Whether the Schematron validation interfaces are compiled in */ #if 1 #define LIBXML_SCHEMATRON_ENABLED #endif /** * LIBXML_MODULES_ENABLED: * * Whether the module interfaces are compiled in */ #if 0 #define LIBXML_MODULES_ENABLED /** * LIBXML_MODULE_EXTENSION: * * the string suffix used by dynamic modules (usually shared libraries) */ #define LIBXML_MODULE_EXTENSION ".dll" #endif /** * LIBXML_ZLIB_ENABLED: * * Whether the Zlib support is compiled in */ #if 0 #define LIBXML_ZLIB_ENABLED #endif /** * LIBXML_LZMA_ENABLED: * * Whether the Lzma support is compiled in */ #if 0 #define LIBXML_LZMA_ENABLED #endif #ifdef __GNUC__ #ifdef HAVE_ANSIDECL_H #include #endif /** * ATTRIBUTE_UNUSED: * * Macro used to signal to GCC unused function parameters */ #ifndef ATTRIBUTE_UNUSED #define ATTRIBUTE_UNUSED __attribute__((unused)) #endif /** * LIBXML_ATTR_ALLOC_SIZE: * * Macro used to indicate to GCC this is an allocator function */ #ifndef LIBXML_ATTR_ALLOC_SIZE # if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))) # define LIBXML_ATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x))) # else # define LIBXML_ATTR_ALLOC_SIZE(x) # endif #else # define LIBXML_ATTR_ALLOC_SIZE(x) #endif /** * LIBXML_ATTR_FORMAT: * * Macro used to indicate to GCC the parameter are printf like */ #ifndef LIBXML_ATTR_FORMAT # if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) # define LIBXML_ATTR_FORMAT(fmt,args) __attribute__((__format__(__printf__,fmt,args))) # else # define LIBXML_ATTR_FORMAT(fmt,args) # endif #else # define LIBXML_ATTR_FORMAT(fmt,args) #endif #else /* ! __GNUC__ */ /** * ATTRIBUTE_UNUSED: * * Macro used to signal to GCC unused function parameters */ #define ATTRIBUTE_UNUSED /** * LIBXML_ATTR_ALLOC_SIZE: * * Macro used to indicate to GCC this is an allocator function */ #define LIBXML_ATTR_ALLOC_SIZE(x) /** * LIBXML_ATTR_FORMAT: * * Macro used to indicate to GCC the parameter are printf like */ #define LIBXML_ATTR_FORMAT(fmt,args) #endif /* __GNUC__ */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif linphone-3.12.0/build/wp8/version.bat000066400000000000000000000010621313432737600174130ustar00rootroot00000000000000@ECHO off SET gitlog= FOR /f "delims=" %%a IN ('git log -1 "--pretty=format:%%H" ../../configure.ac') DO SET gitlog=%%a IF [%gitlog%] == [] GOTO UnknownGitVersion FOR /f "delims=" %%a IN ('git describe --always') DO SET gitdescribe=%%a GOTO End :UnknownGitVersion SET gitdescribe=unknown :End ECHO #define LIBLINPHONE_GIT_VERSION "%gitdescribe%" > liblinphone_gitversion.h FOR /F "delims=" %%a IN ('findstr /B AC_INIT ..\..\configure.ac') DO ( FOR /F "tokens=1,2,3 delims=[,]" %%1 IN ("%%a") DO ( ECHO #define LIBLINPHONE_VERSION "%%3" > config.h ) ) linphone-3.12.0/build/wp8/zlib/000077500000000000000000000000001313432737600161775ustar00rootroot00000000000000linphone-3.12.0/build/wp8/zlib/zconf.h000066400000000000000000000363021313432737600174730ustar00rootroot00000000000000/* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2013 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* #undef Z_PREFIX */ #define Z_HAVE_UNISTD_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzvprintf z_gzvprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit_ z_inflateBackInit_ # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetHeader z_inflateGetHeader # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateSetDictionary z_inflateSetDictionary # define inflateGetDictionary z_inflateGetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateResetKeep z_inflateResetKeep # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif /* Some Mac compilers merge all .h files incorrectly: */ #if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) # define NO_DUMMY_DECL #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif #ifndef Z_ARG /* function prototypes for stdarg */ # if defined(STDC) || defined(Z_HAVE_STDARG_H) # define Z_ARG(args) args # else # define Z_ARG(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) # define Z_HAVE_UNISTD_H #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ linphone-3.12.0/build/wp8/zlib/zlib.sln000066400000000000000000000023141313432737600176550ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2012 for Windows Phone Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|Win32 = Debug|Win32 Release|ARM = Release|ARM Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|ARM.ActiveCfg = Debug|ARM {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|ARM.Build.0 = Debug|ARM {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|Win32.ActiveCfg = Debug|Win32 {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|Win32.Build.0 = Debug|Win32 {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|ARM.ActiveCfg = Release|ARM {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|ARM.Build.0 = Release|ARM {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|Win32.ActiveCfg = Release|Win32 {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal linphone-3.12.0/build/wp8/zlib/zlib.vcxproj000066400000000000000000000143231313432737600205570ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f} zlib en-US 11.0 DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ false Level4 $(ProjectDir);$(ProjectDir)..\..\..\..\zlib;%(AdditionalIncludeDirectories) _WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;UNICODE;%(PreprocessorDefinitions) Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) Console false false $(TargetDir)$(TargetName).lib Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\..\zlib\win32\zlib.def _DEBUG;%(PreprocessorDefinitions) true NDEBUG;%(PreprocessorDefinitions) MaxSpeed true true true false true true false linphone-3.12.0/cmake/000077500000000000000000000000001313432737600145025ustar00rootroot00000000000000linphone-3.12.0/cmake/FindCpuFeatures.cmake000066400000000000000000000026331313432737600205370ustar00rootroot00000000000000############################################################################ # FindCpuFeatures.cmake # Copyright (C) 2017 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the Android cpufeatures include file and library # # CPUFEATURES_FOUND - system has libcpufeatures # CPUFEATURES_LIBRARIES - The libraries needed to use libcpufeatures find_library(CPUFEATURES_LIBRARIES NAMES cpufeatures ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CpuFeatures DEFAULT_MSG CPUFEATURES_LIBRARIES ) mark_as_advanced(CPUFEATURES_LIBRARIES) linphone-3.12.0/cmake/FindGtkMacIntegration.cmake000066400000000000000000000041441313432737600216620ustar00rootroot00000000000000############################################################################ # FindGtkMacIntegration.txt # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the libgtkmacintegration include file and library # # GTKMACINTEGRATION_FOUND - system has libgtkmacintegration # GTKMACINTEGRATION_INCLUDE_DIRS - the libgtkmacintegration include directory # GTKMACINTEGRATION_LIBRARIES - The libraries needed to use libgtkmacintegration # GTKMACINTEGRATION_CPPFLAGS - The cflags needed to use libgtkmacintegration set(_GTKMACINTEGRATION_ROOT_PATHS ${CMAKE_INSTALL_PREFIX} ) find_path(GTKMACINTEGRATION_INCLUDE_DIRS NAMES gtkosxapplication.h HINTS _GTKMACINTEGRATION_ROOT_PATHS PATH_SUFFIXES include/gtkmacintegration-gtk2 include/gtkmacintegration ) find_library(GTKMACINTEGRATION_LIBRARIES NAMES gtkmacintegration-gtk2 gtkmacintegration HINTS ${_GTKMACINTEGRATION_ROOT_PATHS} PATH_SUFFIXES bin lib ) set(GTKMACINTEGRATION_CPPFLAGS "-DMAC_INTEGRATION") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GTKMACINTEGRATION DEFAULT_MSG GTKMACINTEGRATION_INCLUDE_DIRS GTKMACINTEGRATION_LIBRARIES GTKMACINTEGRATION_CPPFLAGS ) mark_as_advanced(GTKMACINTEGRATION_INCLUDE_DIRS GTKMACINTEGRATION_LIBRARIES GTKMACINTEGRATION_CPPFLAGS) linphone-3.12.0/cmake/FindIconv.cmake000066400000000000000000000034311313432737600173640ustar00rootroot00000000000000############################################################################ # FindIconv.cmake # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the iconv include file and library # # ICONV_FOUND - system has libiconv # ICONV_INCLUDE_DIRS - the libiconv include directory # ICONV_LIBRARIES - The libraries needed to use libiconv if(APPLE AND NOT IOS) set(ICONV_HINTS "${CMAKE_OSX_SYSROOT}/usr" "/usr") endif() if(ICONV_HINTS) set(ICONV_LIBRARIES_HINTS "${ICONV_HINTS}/lib") endif() find_path(ICONV_INCLUDE_DIRS NAMES iconv.h HINTS "${ICONV_HINTS}" PATH_SUFFIXES include ) if(ICONV_INCLUDE_DIRS) set(HAVE_ICONV_H 1) endif() find_library(ICONV_LIBRARIES NAMES iconv HINTS "${ICONV_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Iconv DEFAULT_MSG ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H ) mark_as_advanced(ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H) linphone-3.12.0/cmake/FindIntl.cmake000066400000000000000000000033511313432737600172150ustar00rootroot00000000000000############################################################################ # FindIntl.cmake # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the libintl include file and library # # INTL_FOUND - system has libintl # INTL_INCLUDE_DIRS - the libintl include directory # INTL_LIBRARIES - The libraries needed to use libintl set(_INTL_ROOT_PATHS ${CMAKE_INSTALL_PREFIX} ) find_path(INTL_INCLUDE_DIRS NAMES libintl.h HINTS _INTL_ROOT_PATHS PATH_SUFFIXES include ) if(INTL_INCLUDE_DIRS) set(HAVE_LIBINTL_H 1) endif() set(INTL_ARGS INTL_INCLUDE_DIRS HAVE_LIBINTL_H) if(NOT UNIX OR APPLE) find_library(INTL_LIBRARIES NAMES intl HINTS ${_INTL_ROOT_PATHS} PATH_SUFFIXES bin lib ) list(APPEND INTL_ARGS INTL_LIBRARIES) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Intl DEFAULT_MSG ${INTL_ARGS}) mark_as_advanced(${INTL_ARGS}) linphone-3.12.0/cmake/FindNotify.cmake000066400000000000000000000034011313432737600175530ustar00rootroot00000000000000############################################################################ # FindNotify.cmake # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the notify include file and library # # NOTIFY_FOUND - system has libnotify # NOTIFY_INCLUDE_DIRS - the libnotify include directory # NOTIFY_LIBRARIES - The libraries needed to use libnotify set(_NOTIFY_ROOT_PATHS ${CMAKE_INSTALL_PREFIX} ) find_path(NOTIFY_INCLUDE_DIRS NAMES libnotify/notify.h HINTS _NOTIFY_ROOT_PATHS PATH_SUFFIXES include ) if(NOTIFY_INCLUDE_DIRS) set(HAVE_LIBNOTIFY_NOTIFY_H 1) endif() find_library(NOTIFY_LIBRARIES NAMES notify HINTS ${_NOTIFY_ROOT_PATHS} PATH_SUFFIXES bin lib ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Notify DEFAULT_MSG NOTIFY_INCLUDE_DIRS NOTIFY_LIBRARIES HAVE_LIBNOTIFY_NOTIFY_H ) mark_as_advanced(NOTIFY_INCLUDE_DIRS NOTIFY_LIBRARIES HAVE_LIBNOTIFY_NOTIFY_H) linphone-3.12.0/cmake/FindSqlite3.cmake000066400000000000000000000034531313432737600176360ustar00rootroot00000000000000############################################################################ # FindSqlite3.cmake # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the sqlite3 include file and library # # SQLITE3_FOUND - system has sqlite3 # SQLITE3_INCLUDE_DIRS - the sqlite3 include directory # SQLITE3_LIBRARIES - The libraries needed to use sqlite3 if(APPLE AND NOT IOS) set(SQLITE3_HINTS "/usr") endif() if(SQLITE3_HINTS) set(SQLITE3_LIBRARIES_HINTS "${SQLITE3_HINTS}/lib") endif() find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h HINTS "${SQLITE3_HINTS}" PATH_SUFFIXES include ) if(SQLITE3_INCLUDE_DIRS) set(HAVE_SQLITE3_H 1) endif() find_library(SQLITE3_LIBRARIES NAMES sqlite3 HINTS "${SQLITE3_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Sqlite3 DEFAULT_MSG SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H ) mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H) linphone-3.12.0/cmake/FindSupport.cmake000066400000000000000000000025571313432737600177720ustar00rootroot00000000000000############################################################################ # FindSupport.cmake # Copyright (C) 2017 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the Android support include file and library # # SUPPORT_FOUND - system has libsupport # SUPPORT_LIBRARIES - The libraries needed to use libsupport find_library(SUPPORT_LIBRARIES NAMES support ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Support DEFAULT_MSG SUPPORT_LIBRARIES ) mark_as_advanced(SUPPORT_LIBRARIES) linphone-3.12.0/cmake/FindXML2.cmake000066400000000000000000000033541313432737600170340ustar00rootroot00000000000000############################################################################ # FindXML2.txt # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the libxml2 include file and library # # XML2_FOUND - system has libxml2 # XML2_INCLUDE_DIRS - the libxml2 include directory # XML2_LIBRARIES - The libraries needed to use libxml2 if(APPLE AND NOT IOS) set(XML2_HINTS "/usr") endif() if(XML2_HINTS) set(XML2_LIBRARIES_HINTS "${XML2_HINTS}/lib") endif() find_path(XML2_INCLUDE_DIRS NAMES libxml/xmlreader.h HINTS "${XML2_HINTS}" PATH_SUFFIXES include/libxml2 ) if(XML2_INCLUDE_DIRS) set(HAVE_LIBXML_XMLREADER_H 1) endif() find_library(XML2_LIBRARIES NAMES xml2 HINTS "${XML2_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(XML2 DEFAULT_MSG XML2_INCLUDE_DIRS XML2_LIBRARIES ) mark_as_advanced(XML2_INCLUDE_DIRS XML2_LIBRARIES) linphone-3.12.0/cmake/FindZlib.cmake000066400000000000000000000035601313432737600172110ustar00rootroot00000000000000############################################################################ # FindZlib.txt # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the zlib include file and library # # ZLIB_FOUND - system has zlib # ZLIB_INCLUDE_DIRS - the zlib include directory # ZLIB_LIBRARIES - The libraries needed to use zlib if(APPLE AND NOT IOS) set(ZLIB_HINTS "/usr") endif() if(ZLIB_HINTS) set(ZLIB_LIBRARIES_HINTS "${ZLIB_HINTS}/lib") endif() find_path(ZLIB_INCLUDE_DIRS NAMES zlib.h HINTS "${ZLIB_HINTS}" PATH_SUFFIXES include ) if(ZLIB_INCLUDE_DIRS) set(HAVE_ZLIB_H 1) endif() if(ENABLE_STATIC) find_library(ZLIB_LIBRARIES NAMES zstatic zlibstatic zlibstaticd z HINTS "${ZLIB_LIBRARIES_HINTS}" ) else() find_library(ZLIB_LIBRARIES NAMES z zlib zlibd HINTS "${ZLIB_LIBRARIES_HINTS}" ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Zlib DEFAULT_MSG ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H ) mark_as_advanced(ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H) linphone-3.12.0/cmake/LinphoneConfig.cmake.in000066400000000000000000000054761313432737600210270ustar00rootroot00000000000000############################################################################ # LinphoneConfig.cmake # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # Config file for the belle-sip package. # It defines the following variables: # # LINPHONE_FOUND - system has linphone # LINPHONE_INCLUDE_DIRS - the linphone include directory # LINPHONE_LIBRARIES - The libraries needed to use linphone # LINPHONE_CPPFLAGS - The compilation flags needed to use linphone # LINPHONE_LDFLAGS - The linking flags needed to use linphone if(NOT LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${CMAKE_CURRENT_LIST_DIR}/LinphoneTargets.cmake") endif() if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_ms2_CONFIG_DIR}/Mediastreamer2Config.cmake") include("${EP_bellesip_CONFIG_DIR}/BelleSIPConfig.cmake") else() find_package(Mediastreamer2 REQUIRED) find_package(BelleSIP REQUIRED) endif() if(@ENABLE_SHARED@) set(LINPHONE_TARGETNAME linphone) set(LINPHONE_LIBRARIES ${LINPHONE_TARGETNAME}) else() set(LINPHONE_TARGETNAME linphone-static) if(TARGET ${LINPHONE_TARGETNAME}) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(LINPHONE_LIBRARIES ${LINPHONE_TARGETNAME}) else() get_target_property(LINPHONE_LIBRARIES ${LINPHONE_TARGETNAME} LOCATION) endif() get_target_property(LINPHONE_LINK_LIBRARIES ${LINPHONE_TARGETNAME} INTERFACE_LINK_LIBRARIES) if(LINPHONE_LINK_LIBRARIES) list(APPEND LINPHONE_LIBRARIES ${LINPHONE_LINK_LIBRARIES}) endif() endif() endif() get_target_property(LINPHONE_INCLUDE_DIRS ${LINPHONE_TARGETNAME} INTERFACE_INCLUDE_DIRECTORIES) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) list(INSERT LINPHONE_INCLUDE_DIRS 0 "${EP_linphone_INCLUDE_DIR}") else() list(INSERT LINPHONE_INCLUDE_DIRS 0 "@CMAKE_INSTALL_FULL_INCLUDEDIR@") endif() list(REMOVE_DUPLICATES LINPHONE_INCLUDE_DIRS) set(LINPHONE_CPPFLAGS @LINPHONE_CPPFLAGS@) set(LINPHONE_LDFLAGS "@LINPHONE_LDFLAGS@") set(LINPHONE_FOUND 1) linphone-3.12.0/config.h.cmake000066400000000000000000000036741313432737600161310ustar00rootroot00000000000000/*************************************************************************** * config.h.cmake * Copyright (C) 2014 Belledonne Communications, Grenoble France * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #define LINPHONE_MAJOR_VERSION ${LINPHONE_MAJOR_VERSION} #define LINPHONE_MINOR_VERSION ${LINPHONE_MINOR_VERSION} #define LINPHONE_MICRO_VERSION ${LINPHONE_MICRO_VERSION} #define LINPHONE_VERSION "${LINPHONE_VERSION}" #define LIBLINPHONE_VERSION "${LINPHONE_VERSION}" #define LINPHONE_ALL_LANGS "${LINPHONE_ALL_LANGS}" #define LINPHONE_PLUGINS_DIR "${LINPHONE_PLUGINS_DIR}" #define LINPHONE_CONFIG_DIR "${LINPHONE_CONFIG_DIR}" #define GETTEXT_PACKAGE "${GETTEXT_PACKAGE}" #define PACKAGE_LOCALE_DIR "${PACKAGE_LOCALE_DIR}" #define PACKAGE_DATA_DIR "${PACKAGE_DATA_DIR}" #define PACKAGE_SOUND_DIR "${PACKAGE_SOUND_DIR}" #define PACKAGE_RING_DIR "${PACKAGE_RING_DIR}" #cmakedefine BUILD_WIZARD #cmakedefine HAVE_GTK_OSX 1 #cmakedefine HAVE_NOTIFY4 #cmakedefine HAVE_ZLIB 1 #cmakedefine HAVE_CU_GET_SUITE 1 #cmakedefine HAVE_CU_CURSES 1 #cmakedefine HAVE_LIBUDEV_H 0 #cmakedefine HAVE_LIME #cmakedefine ENABLE_NLS 1 #cmakedefine ENABLE_UPDATE_CHECK 1 linphone-3.12.0/config.rpath000077500000000000000000000374441313432737600157460ustar00rootroot00000000000000#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2006 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's _LT_CC_BASENAME. for cc_temp in $CC""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` # Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; darwin*) case $cc_basename in xlc*) wl='-Wl,' ;; esac ;; mingw* | pw32* | os2*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; newsos6) ;; linux*) case $cc_basename in icc* | ecc*) wl='-Wl,' ;; pgcc | pgf77 | pgf90) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) wl='-Wl,' ;; esac ;; esac ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; sco3.2v5*) ;; solaris*) wl='-Wl,' ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) wl='-Wl,' ;; sysv4*MP*) ;; unicos*) wl='-Wl,' ;; uts4*) ;; esac fi # Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' case "$host_os" in aix3* | aix4* | aix5*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # Samuel A. Falvo II reports # that the semantics of dynamic libraries on AmigaOS, at least up # to version 4, is to share data among multiple programs linked # with the same dynamic library. Since this doesn't match the # behavior of shared libraries on other platforms, we cannot use # them. ld_shlibs=no ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; interix3*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; linux*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris*) if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' else ld_shlibs=no fi ;; esac ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then hardcode_libdir_flag_spec= fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix4* | aix5*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix5*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 hardcode_direct=yes else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # see comment about different semantics on the GNU ld section ld_shlibs=no ;; bsdi[45]*) ;; cygwin* | mingw* | pw32*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) hardcode_direct=no if test "$GCC" = yes ; then : else case $cc_basename in xlc*) ;; *) ld_shlibs=no ;; esac fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd1*) ld_shlibs=no ;; freebsd2.2*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; freebsd2*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd* | kfreebsd*-gnu | dragonfly*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no ;; *) hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; openbsd*) hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) ;; sysv5* | sco3.2v5* | sco5v6*) hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' hardcode_libdir_separator=':' ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER. libname_spec='lib$name' case "$host_os" in aix3*) ;; aix4* | aix5*) ;; amigaos*) ;; beos*) ;; bsdi[45]*) ;; cygwin* | mingw* | pw32*) shrext=.dll ;; darwin* | rhapsody*) shrext=.dylib ;; dgux*) ;; freebsd1*) ;; kfreebsd*-gnu) ;; freebsd* | dragonfly*) ;; gnu*) ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac ;; interix3*) ;; irix5* | irix6* | nonstopux*) case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux*) ;; knetbsd*-gnu) ;; netbsd*) ;; newsos6) ;; nto-qnx*) ;; openbsd*) ;; os2*) libname_spec='$name' shrext=.dll ;; osf3* | osf4* | osf5*) ;; solaris*) ;; sunos4*) ;; sysv4 | sysv4.3*) ;; sysv4*MP*) ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) ;; uts4*) ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' < libtool.tmp cp -f ./libtool.tmp ./libtool rm -f ./libtool.tmp fi], [mingw_found=$mingw_found] ) dnl Add the languages which your application supports here. PKG_PROG_PKG_CONFIG ALL_LINGUAS=$(cd $srcdir/po && echo *.po | sed 's/\.po//g') AC_SUBST(ALL_LINGUAS) AC_DEFINE_UNQUOTED(LINPHONE_ALL_LANGS, "$ALL_LINGUAS", [All supported languages]) if test "$mingw_found" != "yes" ; then dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK. dnl AM_GNU_GETTEXT pollutes CPPFLAGS: workaround this. CPPFLAGS_save=$CPPFLAGS AM_GNU_GETTEXT([external]) AC_SUBST(INTLLIBS) CPPFLAGS=$CPPFLAGS_save LIBS="$LIBS $LIBINTL" else if test "$USE_NLS" = "yes" ; then AC_DEFINE(HAVE_INTL,1,[Tells wheter localisation is possible]) LIBS="$LIBS -lintl" fi fi GETTEXT_PACKAGE=linphone AC_SUBST([GETTEXT_PACKAGE]) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[The name of the gettext package name]) dnl AC_CHECK_LIB(intl,libintl_gettext) PKG_CHECK_MODULES(BCTOOLBOX, bctoolbox, [found_bctoolbox=yes],[found_bctoolbox=no]) if test "x$found_bctoolbox" != "xyes" ; then AC_MSG_ERROR(["Could not find bctoolbox (required dependency)"]) fi AC_CHECK_FUNCS([get_current_dir_name strndup stpcpy] ) AC_ARG_ENABLE(x11, [AS_HELP_STRING([--disable-x11], [Disable X11 support (default=yes for MacOSX, no otherwise)])], [case "${enableval}" in yes) enable_x11=true ;; no) enable_x11=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-x11) ;; esac], [case "$target_os" in *darwin*) enable_x11=false ;; #disable x11 on MacOS by default *) enable_x11=true ;; esac] ) dnl conditional build of LDAP support AC_ARG_ENABLE(ldap, [AS_HELP_STRING([--enable-ldap], [Enables LDAP support (default=no)])], [case "${enableval}" in yes) enable_ldap=true ;; no) enable_ldap=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-ldap) ;; esac], [enable_ldap=false] ) if test "$enable_ldap" = "true"; then PKG_CHECK_MODULES(LDAP, [openldap],[found_ldap=yes], [found_ldap=no]) if test "$found_ldap" = "no"; then AC_CHECK_LIB(ldap,ldap_initialize, [LDAP_LIBS="-lldap -llber"], [AC_MSG_ERROR([You need libldap for LDAP support])] ) AC_CHECK_HEADERS(ldap.h, [foo=bar], [AC_MSG_ERROR( [ldap.h not found] ) ] ) found_ldap=yes fi PKG_CHECK_MODULES(SASL, [libsasl2],[found_sasl=yes],[found_sasl=no] ) if test "$found_sasl" = "no"; then AC_CHECK_LIB(sasl2, sasl_client_init , [SASL_LIBS="-lsasl2"], [AC_MSG_ERROR([You need SASL for LDAP support] ) ] ) AC_CHECK_HEADERS(sasl/sasl.h,foo=bar, [AC_MSG_ERROR([sasl/sasl.h not found])]) found_sasl=yes fi AC_SUBST(LDAP_CFLAGS) AC_SUBST(LDAP_LIBS) AC_SUBST(SASL_CFLAGS) AC_SUBST(SASL_LIBS) if test "$found_ldap$found_sasl" = "yesyes"; then AC_DEFINE(BUILD_LDAP,1,[Defined if LDAP build option enabled]) else AC_MSG_ERROR([Cannot use LDAP due to previous errors]) fi fi AM_CONDITIONAL(BUILD_LDAP, test x$enable_ldap != xfalse) dnl conditionnal build of console interface. AC_ARG_ENABLE(console_ui, [AS_HELP_STRING([--enable-console_ui=[yes/no]], [Turn on or off compilation of console interface (default=yes)])], [case "${enableval}" in yes) console_ui=true ;; no) console_ui=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-console_ui) ;; esac], [console_ui=true] ) dnl conditionnal build of tools. AC_ARG_ENABLE(tools, [AS_HELP_STRING([--enable-tools=[yes/no]], [Turn on or off compilation of console interface (default=yes)])], [case "${enableval}" in yes) build_tools=true ;; no) build_tools=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;; esac], [build_tools=check] ) dnl check for installed version of libupnp AC_ARG_ENABLE(upnp, [AS_HELP_STRING([--disable-upnp], [Disable uPnP support])], [case "${enableval}" in yes) build_upnp=true ;; no) build_upnp=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-upnp) ;; esac], [build_upnp=auto] ) if test "$build_upnp" != "false" ; then PKG_CHECK_MODULES([LIBUPNP], [libupnp], [if pkg-config --atleast-version=1.6 "libupnp < 1.7"; then build_upnp=true else AC_MSG_ERROR([libupnp >= 1.6 < 1.5 required.]) fi], [if test "$build_upnp" == "true" ; then AC_MSG_ERROR([libupnp not found.]) else build_upnp=false fi] ) fi AM_CONDITIONAL(BUILD_UPNP, test x$build_upnp != xfalse) if test "$build_upnp" != "false" ; then AC_DEFINE(BUILD_UPNP, 1, [Define if upnp enabled]) fi dnl check zlib AC_ARG_ENABLE(zlib, [AS_HELP_STRING([--disable-zlib], [Disable ZLib support])], [case "${enableval}" in yes) build_zlib=true ;; no) build_zlib=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-zlib) ;; esac], [build_zlib=auto] ) if test "$build_zlib" != "false" ; then PKG_CHECK_MODULES(ZLIB, [zlib], [found_zlib=yes], [found_zlib=no]) if test "x$found_zlib" = "xno" ; then AC_CHECK_LIB(z, inflate, [AC_CHECK_HEADER([zlib.h], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1230) // compile error #endif ]],[])], [found_zlib=yes])])]) if test "x$found_zlib" = "xno" ; then AC_MSG_NOTICE([zlib library and headers not found]) else AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] ) ZLIB_LIBS='-lz' AC_SUBST(ZLIB_LIBS) fi else AC_MSG_NOTICE([ZLIB found]) AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] ) fi fi dnl check libxml2 PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[libxml2_found=yes],foo=bar) if test "$libxml2_found" != "yes" ; then AC_MSG_ERROR([libxml2 not found. Install it and try again (the package is usually named libxml2-dev in the Linux distributions)]) fi AM_CONDITIONAL(BUILD_TOOLS, test x$build_tools != xfalse) if test "$build_tools" != "false" ; then build_tools=true AC_DEFINE(BUILD_TOOLS, 1, [Define if tools enabled] ) fi dnl conditionnal build of gtk interface. AC_ARG_ENABLE(gtk_ui, [AS_HELP_STRING([--enable-gtk_ui=[yes/no]], [Turn on or off compilation of gtk interface (default=yes)])], [case "${enableval}" in yes) gtk_ui=true ;; no) gtk_ui=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-gtk_ui) ;; esac], [gtk_ui=true] ) if test "$gtk_ui" = "true" ; then PKG_CHECK_MODULES(LIBGTK, gtk+-2.0 >= 2.18.0 gthread-2.0) if test "$enable_x11" = "false" ; then PKG_CHECK_MODULES(LIBGTKMAC,[gtk-mac-integration >= 2.0.1], [found_gtkmac=true], [found_gtkmac=false]) if test "$found_gtkmac" != "true" ; then dnl for newest macports, the name changed. PKG_CHECK_MODULES(LIBGTKMAC,[gtk-mac-integration-gtk2 >= 2.0.1], [found_gtkmac=true], [found_gtkmac=false]) fi if test "$found_gtkmac" != "true" ; then AC_MSG_ERROR([gtk-mac-integration not found. Please install gtk-osx-application package.]) fi AC_DEFINE([HAVE_GTK_OSX],[1],[Defined when gtk osx is used]) fi PKG_CHECK_MODULES(LIBGLIB, [glib-2.0 >= 2.26.0], [build_status_notifier=yes], [build_status_notifier=no]) else echo "GTK interface compilation is disabled." fi AM_CONDITIONAL([BUILD_STATUS_NOTIFIER], [test "$build_status_notifier" = "yes"]) AC_ARG_ENABLE(notify, [AS_HELP_STRING([--enable-notify=[yes/no]], [Enable libnotify support (default=yes)])], [case "${enableval}" in yes) notify=true ;; no) notify=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-notify) ;; esac], [notify=true] ) dnl conditionnal build of the notify library if test "$gtk_ui" = "true" ; then if test "$notify" = "true"; then PKG_CHECK_MODULES([NOTIFY4], [libnotify >= 0.7.0 ], [found_notify4=yes], foo=bar) case "$found_notify4" in yes) AC_SUBST(NOTIFY4_CFLAGS) AC_SUBST(NOTIFY4_LIBS) AC_DEFINE([HAVE_NOTIFY4],[1],[NOTIFY4 support]) esac PKG_CHECK_MODULES([NOTIFY1], [libnotify < 0.7.0], [found_notify1=yes], foo=bar) case "$found_notify1" in yes) AC_SUBST(NOTIFY1_CFLAGS) AC_SUBST(NOTIFY1_LIBS) AC_DEFINE([HAVE_NOTIFY1],[1],[NOTIFY1 support]) esac else echo "Libnotify support is disabled." fi fi dnl os-specific problems not handled by existing macros. case "$host_os" in *freebsd*) LDFLAGS="$LDFLAGS -pthread" ;; esac case "$host_cpu" in *arm*) AC_DEFINE(__ARM__,1,[Defined if we are compiling for arm processor]) use_arm_toolchain=yes ;; esac AC_ARG_WITH(configdir, [AS_HELP_STRING([--with-configdir], [Set a APPDATA subdir where linphone is supposed to find its config (windows only)])], [ configdir=${withval}],[ configdir="Linphone" ]) AC_DEFINE_UNQUOTED(LINPHONE_CONFIG_DIR,"$configdir",[Windows appdata subdir where linphonerc can be found]) AC_ARG_ENABLE(relativeprefix, [AS_HELP_STRING([--enable-relativeprefix], [Build a linphone that finds its resources relatively to the directory where it is installed])], [case "${enableval}" in yes) relativeprefix=yes ;; no) relativeprefix=no ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-relativeprefix) ;; esac], [relativeprefix=guess] ) AC_ARG_ENABLE(date, [AS_HELP_STRING([--enable-date], [Use build date in internal version number])], [case "${enableval}" in yes) use_date=yes ;; no) use_date=no ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-date) ;; esac], [use_date=no] ) if test x$use_date = xyes ; then AC_DEFINE(USE_BUILDDATE_VERSION,1,[Tell whether date_version.h must be used]) fi dnl enable ipv6 support AC_ARG_ENABLE(ipv6, [AS_HELP_STRING([--enable-ipv6], [Turn on ipv6 support])], [case "${enableval}" in yes) ipv6=true;; no) ipv6=false;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-ipv6) ;; esac], [ipv6=true] ) IPV6_CFLAGS= if test x$ipv6 = xtrue ; then IPV6_CFLAGS=-DINET6 fi AC_SUBST(IPV6_CFLAGS) dnl enable timestamp support AC_ARG_ENABLE(ntp-timestamp, [AS_HELP_STRING([--enable-ntp-timestamp], [Turn on NTP timestamping on received packet])], [case "${enableval}" in yes) ntptimestamp=true;; no) ntptimestamp=false;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-ntp-timestamp) ;; esac], [ntptimestamp=false] ) AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug=[yes/no]], [Enables the display of traces showing the execution of the library. (default=yes)])], [case "${enableval}" in yes) debug_enabled=yes;; no) debug_enabled=no;; *) AC_MSG_ERROR("Bad value for --enable-debug");; esac], [debug_enabled=no] ) AS_CASE([$debug_enabled], [yes],[ CFLAGS="$CFLAGS -g -O0 -DDEBUG" CXXFLAGS="$CXXFLAGS -g -O0 -DDEBUG" OBJCFLAGS="$OBJCFLAGS -g -O0 -DDEBUG" ], [no], [ case "$CFLAGS" in *-O*) ;; *) CFLAGS="$CFLAGS -O2 -g" CXXFLAGS="$CXXFLAGS -O2 -g" OBJCFLAGS="$OBJCFLAGS -O2 -g" ;; esac ], [AC_MSG_ERROR([Bad value ($debug_enabled) for --enable-debug. Valid values are yes or no.])]) dnl enable truespeech codec support AC_ARG_ENABLE(truespeech, [AS_HELP_STRING([--enable-truespeech], [Turn on TrueSpeech support (x86 only)])], [case "${enableval}" in yes) truespeech=true;; no) truespeech=false;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-truespeech) ;; esac], [truespeech=false] ) TRUESPEECH_CFLAGS= if test x$truespeech = xtrue ; then TRUESPEECH_CFLAGS=-DTRUESPEECH fi AC_SUBST(TRUESPEECH_CFLAGS) AM_CONDITIONAL([BUILD_TRUESPEECH], [test x$truespeech = xtrue]) AC_ARG_ENABLE(nonstandard-gsm, [AS_HELP_STRING([--enable-nonstandard-gsm], [Enable GSM codec at nonstandard rates (11025hz, 16000hz)])], [case "${enableval}" in yes) exotic_gsm=yes AC_DEFINE(ENABLE_NONSTANDARD_GSM,1,[Defined when using gsm at nonstandard rates]) ;; no) exotic_gsm=no ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-nonstandard-gsm) ;; esac], [exotic_gsm=no] ) if test "x${prefix}" = "xNONE"; then package_prefix=${ac_default_prefix} else package_prefix=${prefix} fi if test "$relativeprefix" = "guess" ; then if test "$mingw_found" = "yes" ; then relativeprefix="yes" fi fi if test "$relativeprefix" = "yes" ; then dnl allow binaries to install everywhere package_prefix="." fi dnl Set PACKAGE_LOCALE_DIR in config.h. case "$target_os" in *qnx*) DATADIRNAME=app/native/assets ;; *) DATADIRNAME=share ;; esac AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, "${package_prefix}/${DATADIRNAME}/locale",[Defines the place where locales can be found]) AC_DEFINE_UNQUOTED(PACKAGE_DATA_DIR, "${package_prefix}/${DATADIRNAME}",[Defines the place where data are found]) dnl Set PACKAGE_SOUND_DIR in config.h. AC_DEFINE_UNQUOTED(PACKAGE_SOUND_DIR, "${package_prefix}/${DATADIRNAME}/sounds/linphone",[Defines the place where linphone sounds are found]) dnl Set PACKAGE_RING_DIR in config.h. AC_DEFINE_UNQUOTED(PACKAGE_RING_DIR, "${package_prefix}/${DATADIRNAME}/sounds/linphone/rings",[Defines the place where linphone rings are found]) dnl check if we have the getifaddrs() sytem call AC_CHECK_FUNCS(getifaddrs) if test "$console_ui" = "true" ; then dnl check gnu readline LP_CHECK_READLINE else echo "Console interface compilation is disabled." fi AC_WORDS_BIGENDIAN AC_ARG_ENABLE([speex], AS_HELP_STRING([--disable-speex], [Disable speex support]), [], [enable_speex=yes] ) if test "x$enable_speex" = "xyes"; then dnl normaly this should only by done by mediastreamer2/configure.ac dnl but to workaround bugs when cross-compiling for arm-linux, dnl we need to have SPEEX_LIBS defined dnl Furthermore it is good to repeat here all mediastreamer2 toggles dnl since top-level configure --help will not print them. PKG_CHECK_MODULES(SPEEX, speex >= 1.1.6, build_speex=yes) fi dnl conditionnal build of video support AC_ARG_ENABLE(video, [AS_HELP_STRING([--enable-video], [Turn on video support compiling (default=yes)])], [case "${enableval}" in yes) video=true ;; no) video=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-video) ;; esac], [video=true] ) AC_ARG_WITH(ffmpeg, [AS_HELP_STRING([--with-ffmpeg], [Sets the installation prefix of ffmpeg, needed for video support. (default=/usr)])], [ ffmpegdir=${withval}], [ ffmpegdir=/usr ] ) AM_CONDITIONAL([BUILD_MACOS], [test "x$build_macos" = "xyes"]) if test "$video" = "true"; then if test "$enable_x11" = "true"; then AC_CHECK_HEADERS(X11/Xlib.h) if test "$build_macos" = "yes"; then X11_LIBS="-L/usr/X11/lib -lX11" else AC_CHECK_LIB(X11,XUnmapWindow, X11_LIBS="-lX11") fi AC_SUBST(X11_LIBS) fi AC_DEFINE(VIDEO_ENABLED,1,[defined if video support is available]) fi AC_ARG_ENABLE(alsa, [AS_HELP_STRING([--enable-alsa], [Turn on alsa native support compiling])], [case "${enableval}" in yes) alsa=true ;; no) alsa=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-alsa) ;; esac], [alsa=true] ) AC_ARG_ENABLE(zrtp, [AS_HELP_STRING([--enable-zrtp], [Turn on zrtp support])], [case "${enableval}" in yes) zrtp=true ;; no) zrtp=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-zrtp) ;; esac], [zrtp=auto] ) if test "$zrtp" != "false" ; then PKG_CHECK_MODULES(LIBBZRTP, libbzrtp >= 1.0.0, found_zrtp=true, found_zrtp=false) if test "$zrtp$found_zrtp" = "truefalse" ; then AC_MSG_ERROR("Cound not find bZRTP library.") fi if test "$found_zrtp" = "true" ; then zrtp=true AC_DEFINE(HAVE_ZRTP, 1, [Defined if bzrtp is available]) else zrtp=false fi fi dnl this options are just for passing to mediastreamer2 subproject AC_ARG_ENABLE(dtls, [AS_HELP_STRING([--enable-dtls], [Turn on srtp-dtls support - requires polarssl > 1.4])], [case "${enableval}" in yes) dtls=true ;; no) dtls=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-dtls) ;; esac], [dtls=false] ) AC_ARG_ENABLE(g729bCN, [AS_HELP_STRING([--enable-g729bCN], [Turn on or off usage of G729AnnexB in RFC3389 implementation of Comfort Noise Payload (default=no)])], [case "${enableval}" in yes) g729bCN=true ;; no) g729bCN=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-g729bCN) ;; esac], [g729bCN=false] ) dnl Polarssl lib is requested for Lime AC_ARG_WITH( polarssl, [ --with-polarssl Set prefix where polarssl can be found (ex:/usr, /usr/local)[[default=PREFIX or /usr if NONE]] ], [ polarssl_prefix=${withval}],[ if test "$prefix" != "NONE"; then polarssl_prefix=${prefix} else polarssl_prefix="/usr" fi ]) found_polarssl=no if test "$polarssl_prefix" != "none" ; then if test "$polarssl_prefix" != "/usr" ; then POLARSSL_CFLAGS="-I${polarssl_prefix}/include" POLARSSL_LIBS="-L${polarssl_prefix}/lib" fi POLARSSL_LIBS="$POLARSSL_LIBS -lpolarssl" CPPFLAGS_save=$CPPFLAGS LIBS_save=$LIBS CPPFLAGS="$CPPFLAGS $POLARSSL_CFLAGS" LIBS="$LIBS $POLARSSL_LIBS" AC_CHECK_HEADERS(polarssl/gcm.h, [found_polarssl=yes; AC_MSG_NOTICE([polarssl usable])], [POLARSSL_CFLAGS="" POLARSSL_LIBS=""]) CPPFLAGS=$CPPFLAGS_save LIBS=$LIBS_save fi dnl check for Lime support, need polarssl version >= 1.3 (with gcm.h) AC_ARG_ENABLE(lime, [AS_HELP_STRING([--enable-lime], [Turn on or off compilation of Instant Messaging Encryption (default=auto)])], [case "${enableval}" in yes) lime=true ;; no) lime=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-lime) ;; esac], [lime=auto] ) if test "$lime" = "true" ; then if test "$zrtp" = "false" ; then AC_MSG_ERROR([LIME requires zrtp]) fi fi if test "$lime" != "false" ; then if test "$found_zrtp" = "true" ; then AC_DEFINE(HAVE_LIME, 1, [Defined when LIME support is compiled]) lime=true else lime = false fi fi dnl build console if required AM_CONDITIONAL(BUILD_CONSOLE, test x$console_ui = xtrue) dnl special things for arm-linux cross compilation toolchain AM_CONDITIONAL(ARMBUILD, test x$use_arm_toolchain = xyes) dnl compilation of gtk user interface AM_CONDITIONAL(BUILD_GTK_UI, [test x$gtk_ui = xtrue ] ) AM_CONDITIONAL(BUILD_WIN32, test x$mingw_found = xyes ) dnl check getenv AH_TEMPLATE([HAVE_GETENV]) AC_CHECK_FUNC([getenv], AC_DEFINE([HAVE_GETENV], [1], [If present, the getenv function allows fim to read environment variables.])) dnl AC_MSG_CHECKING([for sighandler_t]) AC_TRY_COMPILE([#include ],[sighandler_t *f;], has_sighandler_t=yes,has_sighandler_t=no) AC_MSG_RESULT($has_sighandler_t) if test "$has_sighandler_t" = "yes" ; then AC_DEFINE( HAVE_SIGHANDLER_T, 1, [Define if sighandler_t available] ) fi AC_ARG_ENABLE(assistant, [AS_HELP_STRING([--enable-assistant], [Turn on assistant compiling])], [case "${enableval}" in yes) build_wizard=true ;; no) build_wizard=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-assistant) ;; esac], [build_wizard=check] ) if test "$build_wizard" != "false" ; then PKG_CHECK_MODULES(LIBGTKWIZARD, [gtk+-2.0 >= 2.22.0],[], [if test "$build_wizard" = "true" ; then AC_MSG_ERROR([gtk+-2.0 < 2.22.0, assistant cannot be compiled.]) else build_wizard=false fi] ) fi AM_CONDITIONAL(BUILD_WIZARD, test x$build_wizard != xfalse) if test "$build_wizard" != "false" ; then build_wizard=true AC_DEFINE(BUILD_WIZARD, 1, [Define if wizard enabled] ) fi AC_CHECK_HEADERS(libudev.h) AC_CHECK_LIB(udev,udev_new) ################################################## # Stricter build options (after external packages) ################################################## AC_ARG_ENABLE(strict, AC_HELP_STRING([--enable-strict], [Build with stricter options @<:@yes@:>@]), [strictness="${enableval}"], [strictness=yes] ) STRICT_OPTIONS="-Wall -Wuninitialized" STRICT_OPTIONS_CC="-Wstrict-prototypes" STRICT_OPTIONS_CXX="" #for clang case $CC in *gcc*) STRICT_OPTIONS="$STRICT_OPTIONS -fno-inline-small-functions" ;; *clang*) STRICT_OPTIONS="$STRICT_OPTIONS -Qunused-arguments " #disabled due to wrong optimization false positive with small string #(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-array-bounds " ;; esac # because Darwin's gcc is actually clang, we need to check it... case "$target_os" in *darwin*) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-error=unknown-warning-option -Qunused-arguments -Wno-tautological-compare -Wno-unused-function " #disabled due to wrong optimization false positive with small string #(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-array-bounds " ;; esac if test "$strictness" = "yes" ; then STRICT_OPTIONS="$STRICT_OPTIONS -Werror -Wextra -Wno-unused-parameter -Wno-error=deprecated-declarations -Wno-missing-field-initializers" CFLAGS="$CFLAGS -fno-strict-aliasing" fi AC_SUBST(STRICT_OPTIONS) AC_SUBST(STRICT_OPTIONS_CC) AC_SUBST(STRICT_OPTIONS_CXX) top_srcdir=`dirname $0` AC_ARG_ENABLE(external-ortp, [AS_HELP_STRING([--enable-external-ortp], [Use external oRTP library])], [case "${enableval}" in yes) external_ortp=true ;; no) external_ortp=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-external-ortp) ;; esac], [external_ortp=$(if test -d oRTP; then printf false; else printf true; fi)] ) if test "$external_ortp" = 'true'; then PKG_CHECK_MODULES([ORTP], [ortp >= 0.24.0]) ORTP_VERSION=`$PKG_CONFIG --modversion ortp` else AC_CONFIG_SUBDIRS( oRTP ) ORTP_CFLAGS="-I\$(top_srcdir)/oRTP/include" ORTP_LIBS="\$(top_builddir)/oRTP/src/libortp.la" if test x$ac_cv_c_bigendian = xyes ; then ORTP_CFLAGS="$ORTP_CFLAGS -DORTP_BIGENDIAN" fi if test x$ntptimestamp = xtrue ; then ORTP_CFLAGS="$ORTP_CFLAGS -DORTP_TIMESTAMP" fi ORTP_DIR=oRTP changequote(<<, >>) ORTP_VERSION=`grep -E ^[AC]+_INIT ${top_srcdir}/oRTP/configure.ac | sed -e 's:^.*_INIT(.*,\[\(.*\)\]):\1:g'` changequote([, ]) fi AC_SUBST(ORTP_CFLAGS) AC_SUBST(ORTP_LIBS) AC_SUBST([ORTP_VERSION]) AC_SUBST([ORTP_DIR]) AC_ARG_ENABLE([external-mediastreamer], [AS_HELP_STRING([--enable-external-mediastreamer],[Use external mediastreamer library])],, [enable_external_mediastreamer=$(if test -d mediastreamer2; then printf no; else printf yes; fi)] ) AS_CASE($enable_external_mediastreamer, [yes], [PKG_CHECK_MODULES([MEDIASTREAMER], [mediastreamer >= 2.11.0]) MS2_VERSION=`$PKG_CONFIG --modversion mediastreamer`], [no], [AC_CONFIG_SUBDIRS( mediastreamer2 ) MEDIASTREAMER_DIR=${top_srcdir}/mediastreamer2 MEDIASTREAMER_CFLAGS="-I\$(top_srcdir)/mediastreamer2/include" MEDIASTREAMER_LIBS="\$(top_builddir)/mediastreamer2/src/libmediastreamer_base.la \$(top_builddir)/mediastreamer2/src/libmediastreamer_voip.la" dnl need to temporary change quotes to allow square brackets changequote(<<, >>) MS2_VERSION=`grep -e '^.C_INIT(' $MEDIASTREAMER_DIR/configure.ac | sed -e 's:\([^(]\+\)(\[mediastreamer\],\[\(.*\)\]):\2:g'` changequote([, ]) MS2_DIR=mediastreamer2], [AC_MSG_ERROR([bad value '${enable_external_mediastreamer}' for --enable-external-mediastreamer])] ) AC_SUBST(MEDIASTREAMER_CFLAGS) AC_SUBST(MEDIASTREAMER_LIBS) AC_SUBST([MS2_VERSION]) AC_SUBST([MS2_DIR]) AC_ARG_ENABLE(tunnel, [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])], [case "${enableval}" in yes) enable_tunnel=true ;; no) enable_tunnel=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-tunnel) ;; esac], [enable_tunnel=false] ) AM_CONDITIONAL(BUILD_TUNNEL, test x$enable_tunnel = xtrue) if test x$enable_tunnel = xtrue; then PKG_CHECK_MODULES(TUNNEL, tunnel >= 0.6.0) AC_DEFINE(TUNNEL_ENABLED,1,[Tells tunnel extension is built-in]) fi AC_ARG_ENABLE(vcard, [AS_HELP_STRING([--enable-vcard=[yes/no]], [Turn on compilation of vcard (default=auto)])], [case "${enableval}" in yes) enable_vcard=true ;; no) enable_vcard=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-vcard) ;; esac], [enable_vcard=auto] ) if test x$enable_vcard != xfalse; then PKG_CHECK_MODULES(BELCARD, belcard, [found_vcard=yes],[found_vcard=no]) if test "$found_vcard" = "no"; then dnl Check the lib presence in case the PKG-CONFIG version is not found AC_LANG_CPLUSPLUS AC_CHECK_LIB(belcard, main, [BELCARD_LIBS+=" -lbelr -lbelcard"; found_vcard=yes], [foo=bar]) AC_LANG_C fi if test "$found_vcard" = "yes"; then BELCARD_CFLAGS+=" -DVCARD_ENABLED" enable_vcard=true else if test x$enable_vcard = xtrue; then AC_MSG_ERROR([belcard, required for vcard support, not found]) fi enable_vcard=false fi AC_SUBST(BELCARD_CFLAGS) AC_SUBST(BELCARD_LIBS) fi AM_CONDITIONAL(BUILD_VCARD, test x$enable_vcard = xtrue) AC_ARG_ENABLE(sqlite-storage, [AS_HELP_STRING([--enable-sqlite-storage=[yes/no]], [Turn on compilation of sqlite storage for call history, messages, friends (default=auto)])], [case "${enableval}" in yes) enable_sqlite_storage=true ;; no) enable_sqlite_storage=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-sqlite-storage) ;; esac], [enable_sqlite_storage=auto] ) if test x$enable_sqlite_storage != xfalse; then PKG_CHECK_MODULES(SQLITE3,[sqlite3 >= 3.6.0],[found_sqlite=yes],[found_sqlite=no]) if test "$found_sqlite" = "no"; then dnl Check the lib presence in case the PKG-CONFIG version is not found AC_CHECK_LIB(sqlite3, sqlite3_open, [SQLITE3_LIBS+=" -lsqlite3 "; found_sqlite=yes], [foo=bar]) fi if test "$found_sqlite" = "yes"; then SQLITE3_CFLAGS+=" -DSQLITE_STORAGE_ENABLED" if test "$build_macos" = "yes" -o "$ios_found" = "yes"; then SQLITE3_LIBS+=" -liconv" fi enable_sqlite_storage=true else if test x$enable_sqlite_storage = xtrue; then AC_MSG_ERROR([sqlite3, required for storage, not found]) fi enable_sqlite_storage=false fi AC_SUBST(SQLITE3_CFLAGS) AC_SUBST(SQLITE3_LIBS) fi AM_CONDITIONAL(BUILD_SQLITE_STORAGE, test x$enable_sqlite_storage = xtrue) PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.4.2]) SIPSTACK_CFLAGS="$BELLESIP_CFLAGS" SIPSTACK_LIBS="$BELLESIP_LIBS" AC_SUBST(SIPSTACK_CFLAGS) AC_SUBST(SIPSTACK_LIBS) dnl check for db2html (docbook) to generate html user manual AC_CHECK_PROG(have_sgmltools, sgmltools, yes, no) AM_CONDITIONAL(ENABLE_MANUAL, test x$have_sgmltools$build_manual = xyesyes ) dnl for external use of linphone libs LINPHONE_CFLAGS="-I${includedir} -I${includedir}/linphone" LINPHONE_LIBS="-L${libdir} -llinphone" AC_SUBST(LINPHONE_CFLAGS) AC_SUBST(LINPHONE_LIBS) AC_DEFINE_UNQUOTED(LINPHONE_VERSION, "$PACKAGE_VERSION", [Linphone\'s version number]) AC_DEFINE_UNQUOTED(LINPHONE_PLUGINS_DIR, "${package_prefix}/lib/liblinphone/plugins" ,[path of liblinphone plugins, not mediastreamer2 plugins]) LINPHONE_PLUGINS_DIR="${package_prefix}/lib/liblinphone/plugins" AC_SUBST(LINPHONE_PLUGINS_DIR) AC_ARG_ENABLE(tutorials, [AS_HELP_STRING([--disable-tutorials], [Disable compilation of tutorials])], [case "${enableval}" in yes) tutorials_enabled=true ;; no) tutorials_enabled=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-tutorials) ;; esac], [tutorials_enabled=yes] ) AM_CONDITIONAL(ENABLE_TUTORIALS, test x$tutorials_enabled = xyes) AC_ARG_ENABLE(tests, [AS_HELP_STRING([--disable-tests], [Disable compilation of tests])], [case "${enableval}" in yes) tests_enabled=true ;; no) tests_enabled=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-tests) ;; esac], [tests_enabled=yes] ) AM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xyes) PKG_CHECK_MODULES(BCTOOLBOXTESTER, bctoolbox-tester, [found_pkg_config_bctoolboxtester=yes],[found_pkg_config_bctoolboxtester=no]) AM_CONDITIONAL([ENABLE_TESTS], [test x$found_pkg_config_bctoolboxtester = xyes && test x$tests_enabled != xfalse]) if test "$found_pkg_config_bctoolboxtester" = "no" ; then AC_MSG_WARN([Could not find bctoolbox-tester wrapper, tests are not compiled.]) fi dnl ################################################## dnl # Check for doxygen dnl ################################################## AC_ARG_ENABLE(documentation, [AS_HELP_STRING([--enable-documentation], [Documentation generation using doxygen (default=yes)])], [case "${enableval}" in yes) documentation_enabled=yes;; no) documentation_enabled=no;; *) AC_MSG_ERROR("Bad value for --enable-documentation");; esac], [documentation_enabled=yes] ) if test "$documentation_enabled" = "yes" ; then AC_CHECK_PROG(DOXYGEN,doxygen,doxygen,false) else DOXYGEN=false fi AM_CONDITIONAL(HAVE_DOXYGEN, test "$DOXYGEN" != "false") AC_PATH_PROG([SIPP],[sipp],[false]) if test "x$SIPP" != "xfalse" ; then AC_DEFINE(HAVE_SIPP,1,[defined when SIPP is available]) AC_DEFINE_UNQUOTED(SIPP_COMMAND,"$SIPP",[defined when SIPP is available]) fi AC_CONFIG_FILES([ Makefile build/Makefile build/macos/Makefile build/macos/Info-linphone.plist build/macos/pkg-distribution.xml m4/Makefile po/Makefile.in pixmaps/Makefile include/Makefile include/linphone/Makefile coreapi/Makefile tester/Makefile gtk/Makefile console/Makefile daemon/Makefile share/Makefile share/C/Makefile share/fr/Makefile share/it/Makefile share/ja/Makefile share/cs/Makefile share/xml/Makefile share/linphone.pc share/linphone.desktop share/audio-assistant.desktop scripts/Makefile tools/Makefile linphone.spec linphone.iss ]) AC_OUTPUT echo "Linphone build configuration ended." echo "Summary of build options:" printf "* %-30s %s\n" "Video support" $video printf "* %-30s %s\n" "GTK interface" $gtk_ui printf "* %-30s %s\n" "Account assistant" $build_wizard printf "* %-30s %s\n" "Console interface" $console_ui printf "* %-30s %s\n" "Tools" $build_tools printf "* %-30s %s\n" "Sqlite storage" $enable_sqlite_storage printf "* %-30s %s\n" "VCard support" $enable_vcard printf "* %-30s %s\n" "ZRTP" $zrtp printf "* %-30s %s\n" "IM encryption" $lime printf "* %-30s %s\n" "uPnP support" $build_upnp printf "* %-30s %s\n" "LDAP support" $enable_ldap printf "* %-30s %s\n" "ZLIB support" $found_zlib printf "* %-30s %s\n" "Documentation" $documentation_enabled if test "$enable_tunnel" = "true" ; then printf "* %-30s %s\n" "Tunnel support" "true" fi echo "Now type 'make' to compile, and then 'make install' as root to install it." linphone-3.12.0/console/000077500000000000000000000000001313432737600150645ustar00rootroot00000000000000linphone-3.12.0/console/.gitignore000066400000000000000000000001111313432737600170450ustar00rootroot00000000000000.deps .libs Makefile Makefile.in linphonec sipomatic wav2raw linphonecsh linphone-3.12.0/console/CMakeLists.txt000066400000000000000000000052651313432737600176340ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ set(LINPHONEC_SOURCE_FILES linphonec.c linphonec.h commands.c ) set(LINPHONECSH_SOURCE_FILES shell.c ) bc_apply_compile_flags(LINPHONEC_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C) if(MSVC) get_source_file_property(COMMANDS_C_COMPILE_FLAGS commands.c COMPILE_FLAGS) set(COMMANDS_C_COMPILE_FLAGS "${COMMANDS_C_COMPILE_FLAGS} /wd4996") # Disable "was declared deprecated" warnings set_source_files_properties(commands.c PROPERTY COMPILE_FLAGS "${COMMANDS_C_COMPILE_FLAGS}") endif() add_executable(linphonec ${LINPHONEC_SOURCE_FILES}) target_link_libraries(linphonec ${LINPHONE_LIBS_FOR_TOOLS} ${BCTOOLBOX_CORE_LIBRARIES} ${ORTP_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES}) set_target_properties(linphonec PROPERTIES LINK_FLAGS "${LINPHONE_LDFLAGS}") if(INTL_FOUND) target_link_libraries(linphonec ${INTL_LIBRARIES}) endif() if(WIN32) add_executable(linphoned WIN32 ${LINPHONEC_SOURCE_FILES}) target_link_libraries(linphoned ${LINPHONE_LIBS_FOR_TOOLS} ${BCTOOLBOX_CORE_LIBRARIES} ${ORTP_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES}) if(INTL_FOUND) target_link_libraries(linphoned ${INTL_LIBRARIES}) endif() endif() add_executable(linphonecsh ${LINPHONECSH_SOURCE_FILES}) target_link_libraries(linphonecsh ${LINPHONE_LIBS_FOR_TOOLS} ${ORTP_LIBRARIES}) set_target_properties(linphonecsh PROPERTIES LINK_FLAGS "${LINPHONE_LDFLAGS}") set(INSTALL_TARGETS linphonec linphonecsh) if(WIN32) list(APPEND INSTALL_TARGETS linphoned) endif() install(TARGETS ${INSTALL_TARGETS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) linphone-3.12.0/console/Makefile.am000066400000000000000000000021161313432737600171200ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in AM_CPPFLAGS=\ -I$(top_srcdir) \ -I$(top_srcdir)/coreapi \ -I$(top_srcdir)/include COMMON_CFLAGS=\ -D_ORTP_SOURCE \ $(STRICT_OPTIONS) \ $(STRICT_OPTIONS_CC) \ $(ORTP_CFLAGS) \ $(MEDIASTREAMER_CFLAGS) \ $(VIDEO_CFLAGS) \ $(READLINE_CFLAGS) \ $(SQLITE3_CFLAGS) \ $(LIBXML2_CFLAGS) \ $(BCTOOLBOX_CFLAGS) if BUILD_CONSOLE bin_PROGRAMS=linphonec linphonecsh if BUILD_WIN32 bin_PROGRAMS+=linphoned endif linphonec_SOURCES=linphonec.c linphonec.h commands.c linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) $(BELLESIP_CFLAGS) linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \ $(READLINE_LIBS) \ $(SQLITE3_LIBS) \ $(X11_LIBS) \ $(BELLESIP_LIBS) \ $(LIBXML2_LIBS) \ $(BCTOOLBOX_LIBS) if BUILD_WIN32 #special build of linphonec to detach from the windows console linphoned_SOURCES=$(linphonec_SOURCES) linphoned_CFLAGS=$(COMMON_CFLAGS) $(GUI_FLAGS) linphoned_LDADD=$(linphonec_LDADD) endif linphonecsh_SOURCES=shell.c linphonecsh_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) linphonecsh_LDADD=$(ORTP_LIBS) endif linphone-3.12.0/console/TODO000066400000000000000000000011041313432737600155500ustar00rootroot00000000000000 In pseudo-order of priority: --------------------------- - Exctract presence info in friends list - Allow friend call arg to be a pattern - hide input during password insertions [ could use ncurses noecho() ] - Allow for single-shot mode (call somebody and quit after the fact) maybe "linphonec [OPTS] " would do.. Unfortunately the -s switch would confuse people, I'd rather change its semantic to "source file" where file wold contain a list of commands, so ./linephone -s myfriend.sip would call yourfriend... - implement "smart" command completion linphone-3.12.0/console/commands.c000066400000000000000000002304611313432737600170370ustar00rootroot00000000000000/**************************************************************************** * * $Id: commands.c,v 1.39 2008/07/03 15:08:34 smorlat Exp $ * * Copyright (C) 2006-2009 Sandro Santilli * Copyright (C) 2004 Simon MORLAT * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #include #include #include #ifndef _WIN32_WCE #include #endif /*_WIN32_WCE*/ #include #include #include #include "linphonec.h" #include #ifndef _WIN32 #include #include #endif #define AUDIO 0 #define VIDEO 1 /*************************************************************************** * * Forward declarations * ***************************************************************************/ extern char *lpc_strip_blanks(char *input); /* Command handlers */ static int lpc_cmd_help(LinphoneCore *, char *); static int lpc_cmd_proxy(LinphoneCore *, char *); static int lpc_cmd_call(LinphoneCore *, char *); static int lpc_cmd_calls(LinphoneCore *, char *); static int lpc_cmd_chat(LinphoneCore *, char *); static int lpc_cmd_answer(LinphoneCore *, char *); static int lpc_cmd_autoanswer(LinphoneCore *, char *); static int lpc_cmd_terminate(LinphoneCore *, char *); static int lpc_cmd_redirect(LinphoneCore *, char *); static int lpc_cmd_call_logs(LinphoneCore *, char *); static int lpc_cmd_ipv6(LinphoneCore *, char *); static int lpc_cmd_transfer(LinphoneCore *, char *); static int lpc_cmd_quit(LinphoneCore *, char *); static int lpc_cmd_nat(LinphoneCore *, char *); static int lpc_cmd_stun(LinphoneCore *, char *); static int lpc_cmd_firewall(LinphoneCore *, char *); static int lpc_cmd_friend(LinphoneCore *, char*); static int lpc_cmd_soundcard(LinphoneCore *, char *); static int lpc_cmd_webcam(LinphoneCore *, char *); static int lpc_cmd_staticpic(LinphoneCore *, char *); static int lpc_cmd_play(LinphoneCore *, char *); static int lpc_cmd_record(LinphoneCore *, char *); static int lpc_cmd_register(LinphoneCore *, char *); static int lpc_cmd_unregister(LinphoneCore *, char *); static int lpc_cmd_duration(LinphoneCore *lc, char *args); static int lpc_cmd_status(LinphoneCore *lc, char *args); static int lpc_cmd_ports(LinphoneCore *lc, char *args); static int lpc_cmd_param(LinphoneCore *lc, char *args); static int lpc_cmd_speak(LinphoneCore *lc, char *args); static int lpc_cmd_acodec(LinphoneCore *lc, char *args); static int lpc_cmd_vcodec(LinphoneCore *lc, char *args); static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args); static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args); static int lpc_cmd_echolimiter(LinphoneCore *lc, char *args); static int lpc_cmd_pause(LinphoneCore *lc, char *args); static int lpc_cmd_resume(LinphoneCore *lc, char *args); static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_playback_gain(LinphoneCore *lc, char *args); static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args); #ifdef VIDEO_ENABLED static int lpc_cmd_camera(LinphoneCore *lc, char *args); static int lpc_cmd_video_window(LinphoneCore *lc, char *args); static int lpc_cmd_preview_window(LinphoneCore *lc, char *args); static int lpc_cmd_snapshot(LinphoneCore *lc, char *args); static int lpc_cmd_preview_snapshot(LinphoneCore *lc, char *args); static int lpc_cmd_vfureq(LinphoneCore *lc, char *arg); #endif static int lpc_cmd_states(LinphoneCore *lc, char *args); static int lpc_cmd_identify(LinphoneCore *lc, char *args); static int lpc_cmd_ringback(LinphoneCore *lc, char *args); static int lpc_cmd_conference(LinphoneCore *lc, char *args); static int lpc_cmd_zrtp_verified(LinphoneCore *lc, char *args); static int lpc_cmd_zrtp_unverified(LinphoneCore *lc, char *args); /* Command handler helpers */ static void linphonec_proxy_add(LinphoneCore *lc); static void linphonec_proxy_display(LinphoneProxyConfig *lc); static void linphonec_proxy_list(LinphoneCore *lc); static void linphonec_proxy_remove(LinphoneCore *lc, int index); static int linphonec_proxy_use(LinphoneCore *lc, int index); static void linphonec_proxy_show(LinphoneCore *lc,int index); static void linphonec_friend_display(LinphoneFriend *fr); static int linphonec_friend_list(LinphoneCore *lc, char *arg); static void linphonec_display_command_help(LPC_COMMAND *cmd); static int linphonec_friend_call(LinphoneCore *lc, unsigned int num); #ifndef _WIN32 static int linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr); #endif static int linphonec_friend_delete(LinphoneCore *lc, int num); static int linphonec_friend_delete(LinphoneCore *lc, int num); static void linphonec_codec_list(int type, LinphoneCore *lc); static void linphonec_codec_enable(int type, LinphoneCore *lc, int index); static void linphonec_codec_disable(int type, LinphoneCore *lc, int index); static void lpc_display_call_states(LinphoneCore *lc); /* Command table management */ static LPC_COMMAND *lpc_find_command(const char *name); void linphonec_out(const char *fmt,...); VideoParams lpc_video_params={-1,-1,-1,-1,NULL,TRUE,FALSE}; VideoParams lpc_preview_params={-1,-1,-1,-1,NULL,TRUE,FALSE}; /*************************************************************************** * * Global variables * ***************************************************************************/ /* * Commands table. */ static LPC_COMMAND commands[] = { { "help", lpc_cmd_help, "Print commands help.", "'help '\t: displays specific help for command.\n" "'help advanced'\t: shows advanced commands.\n" }, { "answer", lpc_cmd_answer, "Answer a call", "'answer' : Answer the current incoming call\n" "'answer ' : Answer the call with given id\n" }, { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", "'autoanswer' \t: show current autoanswer mode\n" "'autoanswer enable'\t: enable autoanswer mode\n" "'autoanswer disable'\t: disable autoanswer mode\n" }, { "call", lpc_cmd_call, "Call a SIP uri or number", #ifdef VIDEO_ENABLED "'call [options]' \t: initiate a call to the specified destination.\n" "Options can be:\n" "--audio-only : initiate the call without video.\n" "--early-media : sends audio and video stream immediately when remote proposes early media.\n" #else "'call ' \t: initiate a call to the specified destination.\n" #endif }, { "calls", lpc_cmd_calls, "Show all the current calls with their id and status.", NULL }, { "call-logs", lpc_cmd_call_logs, "Calls history", NULL }, #ifdef VIDEO_ENABLED { "camera", lpc_cmd_camera, "Send camera output for current call.", "'camera on'\t: allow sending of local camera video to remote end.\n" "'camera off'\t: disable sending of local camera's video to remote end.\n" }, #endif { "chat", lpc_cmd_chat, "Chat with a SIP uri", "'chat \"message\"' " ": send a chat message \"message\" to the specified destination." }, { "conference", lpc_cmd_conference, "Create and manage an audio conference.", "'conference add : join the call with id 'call id' into the audio conference." "'conference rm : remove the call with id 'call id' from the audio conference." }, { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, { "firewall", lpc_cmd_firewall, "Set firewall policy", "'firewall' : show current firewall policy.\n" "'firewall none' : use direct connection.\n" "'firewall nat' : use nat address given with the 'nat' command.\n" "'firewall stun' : use stun server given with the 'stun' command.\n" "'firewall ice' : use ice.\n" "'firewall upnp' : use uPnP IGD.\n" }, { "friend", lpc_cmd_friend, "Manage friends", "'friend list []' : list friends.\n" "'friend call ' : call a friend.\n" "'friend add ' : add friend, must be quoted to include\n" " spaces, has \"sip:\" added if it isn't\n" " there. Don't use '<' '>' around .\n" "'friend delete ' : remove friend, 'all' removes all\n" }, { "ipv6", lpc_cmd_ipv6, "Use IPV6", "'ipv6 status' : show ipv6 usage status.\n" "'ipv6 enable' : enable the use of the ipv6 network.\n" "'ipv6 disable' : do not use ipv6 network." }, { "mute", lpc_cmd_mute_mic, "Mute microphone and suspend voice transmission.", NULL }, { "nat", lpc_cmd_nat, "Set nat address", "'nat' : show nat settings.\n" "'nat ' : set nat address.\n" }, { "pause", lpc_cmd_pause, "pause a call", "'pause' : pause the current call\n" }, { "play", lpc_cmd_play, "play a wav file", "This command has two roles:\n" "Plays a file instead of capturing from soundcard - only available in file mode (see 'help soundcard')\n" "Specifies a wav file to be played to play music to far end when putting it on hold (pause)\n" "'play ' : play a wav file." }, { "playbackgain", lpc_cmd_playback_gain, "Adjust playback gain.", NULL }, { "proxy", lpc_cmd_proxy, "Manage proxies", "'proxy list' : list all proxy setups.\n" "'proxy add' : add a new proxy setup.\n" "'proxy remove ' : remove proxy setup with number index.\n" "'proxy use ' : use proxy with number index as default proxy.\n" "'proxy unuse' : don't use a default proxy.\n" "'proxy show ' : show configuration and status of the proxy numbered by index.\n" "'proxy show default' : show configuration and status of the default proxy.\n" }, { "record", lpc_cmd_record, "record to a wav file", "This feature is available only in file mode (see 'help soundcard')\n" "'record ' : record into wav file." }, { "resume", lpc_cmd_resume, "resume a call", "'resume' : resume the unique call\n" "'resume ' : hold off the call with given id\n" }, { "soundcard", lpc_cmd_soundcard, "Manage soundcards", "'soundcard list' : list all sound devices.\n" "'soundcard show' : show current sound devices configuration.\n" "'soundcard use ' : select a sound device.\n" "'soundcard use files' : use .wav files instead of soundcard\n" }, { "stun", lpc_cmd_stun, "Set stun server address", "'stun' : show stun settings.\n" "'stun ' : set stun server address.\n" }, { "terminate", lpc_cmd_terminate, "Terminate a call", "'terminate' : Terminate the current call\n" "'terminate ' : Terminate the call with supplied id\n" "'terminate ' : Terminate all the current calls\n" }, { "transfer", lpc_cmd_transfer, "Transfer a call to a specified destination.", "'transfer ' : transfers the current active call to the destination sip-uri\n" "'transfer ': transfers the call with 'id' to the destination sip-uri\n" "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" }, { "unmute", lpc_cmd_unmute_mic, "Unmute microphone and resume voice transmission.", NULL }, { "webcam", lpc_cmd_webcam, "Manage webcams", "'webcam list' : list all known devices.\n" "'webcam use ' : select a video device.\n" }, { "quit", lpc_cmd_quit, "Exit linphonec", NULL }, { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } }; static LPC_COMMAND advanced_commands[] = { { "codec", lpc_cmd_acodec, "Audio codec configuration", "'codec list' : list audio codecs\n" "'codec enable ' : enable available audio codec\n" "'codec disable ' : disable audio codec" }, { "vcodec", lpc_cmd_vcodec, "Video codec configuration", "'vcodec list' : list video codecs\n" "'vcodec enable ' : enable available video codec\n" "'vcodec disable ' : disable video codec" }, { "ec", lpc_cmd_echocancellation, "Echo cancellation", "'ec on [] [] []' : turn EC on with given delay, tail length and framesize\n" "'ec off' : turn echo cancellation (EC) off\n" "'ec show' : show EC status" }, { "el", lpc_cmd_echolimiter, "Echo limiter", "'el on turns on echo limiter (automatic half duplex, for cases where echo canceller cannot work)\n" "'el off' : turn echo limiter off\n" "'el show' : show echo limiter status" }, { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, "Set the rtp_no_xmit_on_audio_mute configuration parameter", " If set to 1 then rtp transmission will be muted when\n" " audio is muted , otherwise rtp is always sent." }, #ifdef VIDEO_ENABLED { "vwindow", lpc_cmd_video_window, "Control video display window", "'vwindow show': shows video window\n" "'vwindow hide': hides video window\n" "'vwindow pos ': Moves video window to x,y pixel coordinates\n" "'vwindow size ': Resizes video window\n" "'vwindow id ': embeds video display into supplied window id." }, { "pwindow", lpc_cmd_preview_window, "Control local camera video display (preview window)", "'pwindow show': shows the local camera video display\n" "'pwindow hide': hides the local camera video display\n" "'pwindow pos ': Moves preview window to x,y pixel coordinates\n" "'pwindow size ': Resizes preview window\n" "'pwindow id ': embeds preview display into supplied window id.\n" "'pwindow integrated': integrate preview display within the video window of current call.\n" "'pwindow standalone': use standalone window for preview display." }, { "snapshot", lpc_cmd_snapshot, "Take a snapshot of currently received video stream", "'snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" }, { "preview-snapshot", lpc_cmd_preview_snapshot, "Take a snapshot of currently captured video stream", "'preview-snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" }, { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call", NULL }, #endif { "states", lpc_cmd_states, "Show internal states of liblinphone, registrations and calls, according to linphonecore.h definitions", "'states global': shows global state of liblinphone \n" "'states calls': shows state of calls\n" "'states proxies': shows state of proxy configurations" }, { "register", lpc_cmd_register, "Register in one line to a proxy" , "register " }, { "unregister", lpc_cmd_unregister, "Unregister from default proxy", NULL }, { "status", lpc_cmd_status, "Print various status information", "'status register' \t: print status concerning registration\n" "'status autoanswer'\t: tell whether autoanswer mode is enabled\n" "'status hook' \t: print hook status\n" }, { "ports", lpc_cmd_ports, "Network ports configuration", "'ports' \t: prints current used ports.\n" "'ports sip '\t: Sets the sip port.\n" }, { "param", lpc_cmd_param, "parameter set or read as normally given in .linphonerc", "'param
[]' \t: reads [sets] given parameter.\n" "NOTES: - changes may become effective after (re)establishing a sip connection.\n" " - upon exit, .linphonerc will reflect the updated state.\n" }, { "speak", lpc_cmd_speak, "Speak a sentence using espeak TTS engine", "This feature is available only in file mode. (see 'help soundcard')\n" "'speak ' : speak a text using the specified espeak voice.\n" "Example for english voice: 'speak default Hello my friend !'" }, { "staticpic", lpc_cmd_staticpic, "Manage static pictures when nowebcam", "'staticpic set' : Set path to picture that should be used.\n" "'staticpic fps' : Get/set frames per seconds for picture emission.\n" }, { "identify", lpc_cmd_identify, "Returns the user-agent string of far end", "'identify' \t: returns remote user-agent string for current call.\n" "'identify ' \t: returns remote user-agent string for call with supplied id.\n" }, { "ringback", lpc_cmd_ringback, "Specifies a ringback tone to be played to remote end during incoming calls", "'ringback '\t: Specifies a ringback tone to be played to remote end during incoming calls\n" "'ringback disable'\t: Disable playing of ringback tone to callers\n" }, { "redirect", lpc_cmd_redirect, "Redirect an incoming call", "'redirect '\t: Redirect the specified call to the \n" "'redirect all '\t: Redirect all pending incoming calls to the \n" }, { "zrtp-set-verified", lpc_cmd_zrtp_verified,"Set ZRTP SAS verified.", "'Set ZRTP SAS verified'\n" }, { "zrtp-set-unverified", lpc_cmd_zrtp_unverified,"Set ZRTP SAS not verified.", "'Set ZRTP SAS not verified'\n" }, { NULL,NULL,NULL,NULL} }; /*************************************************************************** * * Public interface * ***************************************************************************/ /* * Main command dispatcher. * WARNING: modifies second argument! * * Always return 1 currently. */ int linphonec_parse_command_line(LinphoneCore *lc, char *cl) { char *ptr=cl; char *args=NULL; LPC_COMMAND *cmd; /* Isolate first word and args */ while(*ptr && !isspace(*ptr)) ++ptr; if (*ptr) { *ptr='\0'; /* set args to first nonblank */ args=ptr+1; while(*args && isspace(*args)) ++args; } /* Handle DTMF */ if ( isdigit(*cl) || *cl == '#' || *cl == '*' ) { while ( isdigit(*cl) || *cl == '#' || *cl == '*' ) { if (linphone_core_get_current_call(lc)) linphone_call_send_dtmf(linphone_core_get_current_call(lc), *cl); linphone_core_play_dtmf (lc,*cl,100); ms_sleep(1); // be nice ++cl; } // discard spurious trailing chars return 1; } /* Handle other kind of commands */ cmd=lpc_find_command(cl); if ( !cmd ) { linphonec_out("'%s': Cannot understand this.\n", cl); return 1; } if ( ! cmd->func(lc, args) ) { linphonec_out("Syntax error.\n"); linphonec_display_command_help(cmd); } return 1; } /* * Generator function for command completion. * STATE let us know whether to start from scratch; * without any state (STATE==0), then we start at the * top of the list. */ char * linphonec_command_generator(const char *text, int state) { static int index, len, adv; char *name; if ( ! state ) { index=0; adv=0; len=(int)strlen(text); } /* * Return the next name which partially matches * from the commands list */ if (adv==0){ while ((name=commands[index].name)) { ++index; /* so next call get next command */ if (strncmp(name, text, len) == 0) { return ortp_strdup(name); } } adv=1; index=0; } if (adv==1){ while ((name=advanced_commands[index].name)) { ++index; /* so next call get next command */ if (strncmp(name, text, len) == 0) { return ortp_strdup(name); } } } return NULL; } /*************************************************************************** * * Command handlers * ***************************************************************************/ static int lpc_cmd_help(LinphoneCore *lc, char *arg) { int i=0; LPC_COMMAND *cmd; if (!arg || !*arg) { linphonec_out("Commands are:\n"); linphonec_out("---------------------------\n"); while (commands[i].help) { linphonec_out("%10.10s\t%s\n", commands[i].name, commands[i].help); i++; } linphonec_out("---------------------------\n"); linphonec_out("Type 'help ' for more details or\n"); linphonec_out(" 'help advanced' to list additional commands.\n"); return 1; } if (strcmp(arg,"advanced")==0){ linphonec_out("Advanced commands are:\n"); linphonec_out("---------------------------\n"); i=0; while (advanced_commands[i].help) { linphonec_out("%20.20s\t%s\n", advanced_commands[i].name, advanced_commands[i].help); i++; } linphonec_out("---------------------------\n"); linphonec_out("Type 'help ' for more details.\n"); return 1; } cmd=lpc_find_command(arg); if ( !cmd ) { linphonec_out("No such command.\n"); return 1; } linphonec_display_command_help(cmd); return 1; } static char callee_name[256]={0}; static char caller_name[256]={0}; static int lpc_cmd_call(LinphoneCore *lc, char *args) { if ( ! args || ! *args ) { return 0; } { LinphoneCall *call; LinphoneCallParams *cp=linphone_core_create_call_params (lc, NULL); char *opt1,*opt2; if ( linphone_core_in_call(lc) ) { linphonec_out("Terminate or hold on the current call first.\n"); return 1; } opt1=strstr(args,"--audio-only"); opt2=strstr(args,"--early-media"); if (opt1){ opt1[0]='\0'; while(--opt1 > args && opt1[0]==' ') opt1[0]='\0'; linphone_call_params_enable_video (cp,FALSE); } if (opt2){ opt2[0]='\0'; while(--opt2 > args && opt2[0]==' ') opt2[0]='\0'; linphone_call_params_enable_early_media_sending(cp,TRUE); } if ( NULL == (call=linphone_core_invite_with_params(lc, args,cp)) ) { linphonec_out("Error from linphone_core_invite.\n"); } else { snprintf(callee_name,sizeof(callee_name),"%s",args); } linphone_call_params_unref(cp); } return 1; } static int lpc_cmd_calls(LinphoneCore *lc, char *args){ const bctbx_list_t *calls = linphone_core_get_calls(lc); if(calls) { lpc_display_call_states(lc); }else { linphonec_out("No active call.\n"); } return 1; } static int lpc_cmd_chat(LinphoneCore *lc, char *args) { char *arg1 = args; char *arg2 = NULL; char *ptr = args; LinphoneChatRoom *cr; if (!args) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } else { /* missing one parameter */ return 0; } cr = linphone_core_get_chat_room_from_uri(lc,arg1); linphone_chat_room_send_message(cr,arg2); return 1; } const char *linphonec_get_callee(void){ return callee_name; } const char *linphonec_get_caller(void){ return caller_name; } void linphonec_set_caller(const char *caller){ snprintf(caller_name,sizeof(caller_name)-1,"%s",caller); } static int lpc_cmd_transfer(LinphoneCore *lc, char *args) { if (args){ LinphoneCall *call; LinphoneCall *call2; const char *refer_to=NULL; char arg1[256]={0}; char arg2[266]={0}; long id2=0; int n=sscanf(args,"%255s %265s %li",arg1,arg2,&id2); if (n==1 || isalpha(*arg1)){ call=linphone_core_get_current_call(lc); if (call==NULL && bctbx_list_size(linphone_core_get_calls(lc))==1){ call=(LinphoneCall*)linphone_core_get_calls(lc)->data; } refer_to=args; if (call==NULL){ linphonec_out("No active call, please specify a call id among the ones listed by 'calls' command.\n"); return 0; } linphone_call_transfer(call, refer_to); }else if (n==2){ long id=atoi(arg1); refer_to=args+strlen(arg1)+1; call=linphonec_get_call(id); if (call==NULL) return 0; linphone_call_transfer(call, refer_to); }else if (n==3){ long id=atoi(arg1); call=linphonec_get_call(id); call2=linphonec_get_call(id2); if (call==NULL || call2==NULL) return 0; if (strcmp(arg2,"--to-call")!=0){ return 0; } linphonec_out("Performing attended transfer of call %i to call %i",id,id2); linphone_call_transfer_to_another(call,call2); }else return 0; }else{ linphonec_out("Transfer command requires at least one argument\n"); return 0; } return 1; } static int lpc_cmd_terminate(LinphoneCore *lc, char *args) { if (linphone_core_get_calls(lc)==NULL){ linphonec_out("No active calls\n"); return 1; } if (!args) { if ( -1 == linphone_core_terminate_call(lc, NULL) ){ linphonec_out("Could not stop the active call.\n"); } return 1; } if(strcmp(args,"all")==0){ linphonec_out("We are going to stop all the calls.\n"); linphone_core_terminate_all_calls(lc); return 1; }else{ /*the argument is a linphonec call id */ long id=atoi(args); LinphoneCall *call=linphonec_get_call(id); if (call){ if (linphone_call_terminate(call)==-1){ linphonec_out("Could not stop the call with id %li\n",id); } }else return 0; return 1; } return 0; } static int lpc_cmd_redirect(LinphoneCore *lc, char *args){ const bctbx_list_t *elem; int didit=0; if (!args) return 0; if ((elem=linphone_core_get_calls(lc))==NULL){ linphonec_out("No active calls.\n"); return 1; } if (strncmp(args, "all ", 4) == 0) { while(elem!=NULL){ LinphoneCall *call=(LinphoneCall*)elem->data; if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){ if (linphone_call_redirect(call,args+4) != 0) { linphonec_out("Could not redirect call.\n"); elem=elem->next; } else { didit=1; /*as the redirection closes the call, we need to re-check the call list that is invalidated.*/ elem=linphone_core_get_calls(lc); } }else elem=elem->next; } if (didit==0){ linphonec_out("There is no pending incoming call to redirect.\n"); } } else { char space; long id; int charRead; if ( sscanf(args, "%li%c%n", &id, &space, &charRead) == 2 && space == ' ') { LinphoneCall * call = linphonec_get_call(id); if ( call != NULL ) { if (linphone_call_get_state(call)!=LinphoneCallIncomingReceived) { linphonec_out("The state of the call is not incoming, can't be redirected.\n"); } else if (linphone_call_redirect(call,args+charRead) != 0) { linphonec_out("Could not redirect call.\n"); } } } else return 0; } return 1; } static int lpc_cmd_answer(LinphoneCore *lc, char *args){ if (!args) { int nb=(int)bctbx_list_size(linphone_core_get_calls(lc)); if (nb==1){ //if just one call is present answer the only one in passing NULL to the linphone_core_accept_call ... if ( -1 == linphone_core_accept_call(lc, NULL) ) { linphonec_out("Fail to accept incoming call\n"); } }else if (nb==0){ linphonec_out("There are no calls to answer.\n"); }else{ linphonec_out("Multiple calls in progress, please specify call id.\n"); return 0; } return 1; }else{ long id; if (sscanf(args,"%li",&id)==1){ LinphoneCall *call=linphonec_get_call (id); if (linphone_call_accept(call)==-1){ linphonec_out("Fail to accept call %i\n",id); } }else return 0; return 1; } return 0; } static int lpc_cmd_autoanswer(LinphoneCore *lc, char *args) { if ( ! args ) { if ( linphonec_get_autoanswer() ) { linphonec_out("Auto answer is enabled. Use 'autoanswer disable' to disable.\n"); } else { linphonec_out("Auto answer is disabled. Use 'autoanswer enable' to enable.\n"); } return 1; } if (strstr(args,"enable")){ linphonec_set_autoanswer(TRUE); linphonec_out("Auto answer enabled.\n"); }else if (strstr(args,"disable")){ linphonec_set_autoanswer(FALSE); linphonec_out("Auto answer disabled.\n"); }else return 0; return 1; } static int lpc_cmd_quit(LinphoneCore *lc, char *args) { linphonec_main_loop_exit(); return 1; } static int lpc_cmd_nat(LinphoneCore *lc, char *args) { bool_t use; const char *nat; if ( args ) args=lpc_strip_blanks(args); if ( args && *args ) { linphone_core_set_nat_address(lc, args); /* linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_NAT_ADDRESS); */ } nat = linphone_core_get_nat_address(lc); use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress; linphonec_out("Nat address: %s%s\n", nat ? nat : "unspecified" , use ? "" : " (disabled - use 'firewall nat' to enable)"); return 1; } static int lpc_cmd_stun(LinphoneCore *lc, char *args) { bool_t use; const char *stun; if ( args ) args=lpc_strip_blanks(args); if ( args && *args ) { linphone_core_set_stun_server(lc, args); /* linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_STUN); */ } stun = linphone_core_get_stun_server(lc); use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseStun; linphonec_out("Stun server: %s%s\n", stun ? stun : "unspecified" , use? "" : " (disabled - use 'firewall stun' to enable)"); return 1; } static int lpc_cmd_firewall(LinphoneCore *lc, char *args) { const char* setting=NULL; if ( args ) args=lpc_strip_blanks(args); if ( args && *args ) { if (strcmp(args,"none")==0) { linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall); } else if (strcmp(args,"upnp")==0) { linphone_core_set_firewall_policy(lc,LinphonePolicyUseUpnp); } else if (strcmp(args,"ice")==0) { setting = linphone_core_get_stun_server(lc); if ( ! setting ) { linphonec_out("No stun server address is defined, use 'stun
' first\n"); return 1; } linphone_core_set_firewall_policy(lc,LinphonePolicyUseIce); } else if (strcmp(args,"stun")==0) { setting = linphone_core_get_stun_server(lc); if ( ! setting ) { linphonec_out("No stun server address is defined, use 'stun
' first\n"); return 1; } linphone_core_set_firewall_policy(lc,LinphonePolicyUseStun); } else if (strcmp(args,"nat")==0) { setting = linphone_core_get_nat_address(lc); if ( ! setting ) { linphonec_out("No nat address is defined, use 'nat
' first"); return 1; } linphone_core_set_firewall_policy(lc,LinphonePolicyUseNatAddress); } } switch(linphone_core_get_firewall_policy(lc)) { case LinphonePolicyNoFirewall: linphonec_out("No firewall\n"); break; case LinphonePolicyUseStun: linphonec_out("Using stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; case LinphonePolicyUseNatAddress: linphonec_out("Using supplied nat address %s.\n", setting ? setting : linphone_core_get_nat_address(lc)); break; case LinphonePolicyUseIce: linphonec_out("Using ice with stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; case LinphonePolicyUseUpnp: linphonec_out("Using uPnP IGD protocol\n"); break; } return 1; } #ifndef _WIN32 /* Helper function for processing freind names */ static int lpc_friend_name(char **args, char **name) { /* Use space as a terminator unless quoted */ if (('"' == **args) || ('\'' == **args)){ char *end; char delim = **args; (*args)++; end = (*args); while ((delim != *end) && ('\0' != *end)) end++; if ('\0' == *end) { fprintf(stderr, "Mismatched quotes\n"); return 0; } *name = *args; *end = '\0'; *args = ++end; } else { *name = strsep(args, " "); if (NULL == *args) { /* Means there was no separator */ fprintf(stderr, "Either name or address is missing\n"); return 0; } if (NULL == *name) return 0; } return 1; } #endif static int lpc_cmd_friend(LinphoneCore *lc, char *args) { int friend_num; if ( args ) args=lpc_strip_blanks(args); if ( ! args || ! *args ) return 0; if ( !strncmp(args, "list", 4) ) { return linphonec_friend_list(lc, args+4); return 1; } else if ( !strncmp(args, "call", 4) ) { args+=4; if ( ! *args ) return 0; friend_num = strtol(args, NULL, 10); #ifndef _WIN32_WCE if ( errno == ERANGE ) { linphonec_out("Invalid friend number\n"); return 0; } #endif /*_WIN32_WCE*/ linphonec_friend_call(lc, friend_num); return 1; } else if ( !strncmp(args, "delete", 6) ) { args+=6; if ( ! *args ) return 0; while (*args == ' ') args++; if ( ! *args ) return 0; if (!strncmp(args, "all", 3)) { friend_num = -1; } else { friend_num = strtol(args, NULL, 10); #ifndef _WIN32_WCE if ( errno == ERANGE ) { linphonec_out("Invalid friend number\n"); return 0; } #endif /*_WIN32_WCE*/ } linphonec_friend_delete(lc, friend_num); return 1; } else if ( !strncmp(args, "add", 3) ) { #ifndef _WIN32 char *name; char addr[80]; char *addr_p = addr; char *addr_orig; args+=3; if ( ! *args ) return 0; while (*args == ' ') args++; if ( ! *args ) return 0; if (!lpc_friend_name(&args, &name)) return 0; while (*args == ' ') args++; if ( ! *args ) return 0; if (isdigit(*args)) { strcpy (addr, "sip:"); addr_p = addr + strlen("sip:"); } addr_orig = strsep(&args, " "); if (1 >= strlen(addr_orig)) { fprintf(stderr, "A single-digit address is not valid\n"); return 0; } strcpy(addr_p, addr_orig); linphonec_friend_add(lc, name, addr); #else LinphoneFriend *new_friend; new_friend = linphone_core_create_friend_with_address(lc, args); linphone_core_add_friend(lc, new_friend); #endif return 1; } return 0; } static int lpc_cmd_play(LinphoneCore *lc, char *args){ if ( args ) args=lpc_strip_blanks(args); if ( ! args || ! *args ) return 0; linphone_core_set_play_file(lc,args); return 1; } static int lpc_cmd_record(LinphoneCore *lc, char *args){ if ( args ) args=lpc_strip_blanks(args); if ( ! args || ! *args ) return 0; linphone_core_set_record_file(lc,args); return 1; } /* * Modified input */ static int lpc_cmd_proxy(LinphoneCore *lc, char *args) { char *arg1 = args; char *arg2 = NULL; char *ptr = args; int proxynum; if ( ! arg1 ) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1,"add")==0) { #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif linphonec_proxy_add(lc); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } else if (strcmp(arg1,"list")==0) { linphonec_proxy_list(lc); } else if (strcmp(arg1,"remove")==0) { if (arg2==NULL) return 0; linphonec_proxy_remove(lc,atoi(arg2)); } else if (strcmp(arg1,"use")==0) { if ( arg2 && *arg2 ) { proxynum=atoi(arg2); if ( linphonec_proxy_use(lc, proxynum) ) linphonec_out("Default proxy set to %d.\n", proxynum); } else { proxynum=linphone_core_get_default_proxy(lc, NULL); if ( proxynum == -1 ) linphonec_out("No default proxy.\n"); else linphonec_out("Current default proxy is %d.\n", proxynum); } }else if (strcmp(arg1, "unuse")==0){ linphone_core_set_default_proxy(lc, NULL); linphonec_out("Use no proxy.\n"); } else if (strcmp(arg1, "show")==0) { if (arg2 && *arg2) { if (strstr(arg2,"default")) { proxynum=linphone_core_get_default_proxy(lc, NULL); if ( proxynum < 0 ) { linphonec_out("No default proxy defined\n"); return 1; } linphonec_proxy_show(lc,proxynum); } else { linphonec_proxy_show(lc, atoi(arg2)); } } else return 0; /* syntax error */ } else { return 0; /* syntax error */ } return 1; } static int lpc_cmd_call_logs(LinphoneCore *lc, char *args) { const bctbx_list_t *elem=linphone_core_get_call_logs(lc); for (;elem!=NULL;elem=bctbx_list_next(elem)) { LinphoneCallLog *cl=(LinphoneCallLog*)elem->data; char *str=linphone_call_log_to_str(cl); linphonec_out("%s\n",str); ms_free(str); } return 1; } static int lpc_cmd_ipv6(LinphoneCore *lc, char *arg1) { if ( ! arg1 ) { return 0; /* syntax error */ } if (strcmp(arg1,"status")==0) { linphonec_out("ipv6 use enabled: %s\n",linphone_core_ipv6_enabled(lc) ? "true":"false"); } else if (strcmp(arg1,"enable")==0) { linphone_core_enable_ipv6(lc,TRUE); linphonec_out("ipv6 use enabled.\n"); } else if (strcmp(arg1,"disable")==0) { linphone_core_enable_ipv6(lc,FALSE); linphonec_out("ipv6 use disabled.\n"); } else { return 0; /* syntax error */ } return 1; } static int devname_to_index(LinphoneCore *lc, const char *devname){ const char **p; int i; for(i=0,p=linphone_core_get_sound_devices(lc);*p!=NULL;++p,++i){ if (strcmp(devname,*p)==0) return i; } return -1; } static const char *index_to_devname(LinphoneCore *lc, int index){ const char **p; int i; for(i=0,p=linphone_core_get_sound_devices(lc);*p!=NULL;++p,++i){ if (i==index) return *p; } return NULL; } static int lpc_cmd_soundcard(LinphoneCore *lc, char *args) { int i, index; const char **dev; char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* syntax error */ /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1, "list")==0) { dev=linphone_core_get_sound_devices(lc); for(i=0; dev[i]!=NULL; ++i){ linphonec_out("%i: %s\n",i,dev[i]); } return 1; } if (strcmp(arg1, "show")==0) { if (linphone_core_get_use_files(lc)) { linphonec_out("Using files.\n"); } else { linphonec_out("Ringer device: %s\n", linphone_core_get_ringer_device(lc)); linphonec_out("Playback device: %s\n", linphone_core_get_playback_device(lc)); linphonec_out("Capture device: %s\n", linphone_core_get_capture_device(lc)); } return 1; } if (strcmp(arg1, "use")==0 && arg2) { if (strcmp(arg2, "files")==0) { linphonec_out("Using wav files instead of soundcard.\n"); linphone_core_use_files(lc,TRUE); return 1; } linphone_core_use_files(lc,FALSE); dev=linphone_core_get_sound_devices(lc); index=atoi(arg2); /* FIXME: handle not-a-number */ for(i=0;dev[i]!=NULL;i++) { if (i!=index) continue; linphone_core_set_ringer_device(lc,dev[i]); linphone_core_set_playback_device(lc,dev[i]); linphone_core_set_capture_device(lc,dev[i]); linphonec_out("Using sound device %s\n",dev[i]); return 1; } linphonec_out("No such sound device\n"); return 1; } if (strcmp(arg1, "capture")==0) { const char *devname=linphone_core_get_capture_device(lc); if (!arg2){ linphonec_out("Using capture device #%i (%s)\n", devname_to_index(lc,devname),devname); }else{ index=atoi(arg2); /* FIXME: handle not-a-number */ devname=index_to_devname(lc,index); if (devname!=NULL){ linphone_core_set_capture_device(lc,devname); linphonec_out("Using capture sound device %s\n",devname); return 1; } linphonec_out("No such sound device\n"); } return 1; } if (strcmp(arg1, "playback")==0) { const char *devname=linphone_core_get_playback_device(lc); if (!arg2){ linphonec_out("Using playback device #%i (%s)\n", devname_to_index(lc,devname),devname); }else{ index=atoi(arg2); /* FIXME: handle not-a-number */ devname=index_to_devname(lc,index); if (devname!=NULL){ linphone_core_set_playback_device(lc,devname); linphonec_out("Using playback sound device %s\n",devname); return 1; } linphonec_out("No such sound device\n"); } return 1; } if (strcmp(arg1, "ring")==0) { const char *devname=linphone_core_get_ringer_device(lc); if (!arg2){ linphonec_out("Using ring device #%i (%s)\n", devname_to_index(lc,devname),devname); }else{ index=atoi(arg2); /* FIXME: handle not-a-number */ devname=index_to_devname(lc,index); if (devname!=NULL){ linphone_core_set_ringer_device(lc,devname); linphonec_out("Using ring sound device %s\n",devname); return 1; } linphonec_out("No such sound device\n"); } return 1; } return 0; /* syntax error */ } static int lpc_cmd_webcam(LinphoneCore *lc, char *args) { int i, index; const char **dev; char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* syntax error */ /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1, "list")==0) { dev=linphone_core_get_video_devices(lc); for(i=0; dev[i]!=NULL; ++i){ linphonec_out("%i: %s\n",i,dev[i]); } return 1; } if (strcmp(arg1, "use")==0 && arg2) { dev=linphone_core_get_video_devices(lc); index=atoi(arg2); /* FIXME: handle not-a-number */ for(i=0;dev[i]!=NULL;i++) { if (i!=index) continue; linphone_core_set_video_device(lc, dev[i]); linphonec_out("Using video device %s\n",dev[i]); return 1; } linphonec_out("No such video device\n"); return 1; } return 0; /* syntax error */ } static int lpc_cmd_staticpic(LinphoneCore *lc, char *args) { char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* Syntax error */ /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1, "set")==0 && arg2) { linphone_core_set_static_picture(lc, arg2); return 1; } if (strcmp(arg1, "fps")==0) { if (arg2) { float fps = (float)atof(arg2); /* FIXME: Handle not-a-float */ linphone_core_set_static_picture_fps(lc, fps); return 1; } else { float fps; fps = linphone_core_get_static_picture_fps(lc); linphonec_out("Current FPS %f\n", fps); return 1; } } return 0; /* Syntax error */ } static int lpc_cmd_pause(LinphoneCore *lc, char *args){ if(linphone_core_in_call(lc)) { linphone_call_pause(linphone_core_get_current_call(lc)); return 1; } linphonec_out("you can only pause when a call is in process\n"); return 0; } static int lpc_cmd_resume(LinphoneCore *lc, char *args){ if(linphone_core_in_call(lc)) { linphonec_out("There is already a call in process pause or stop it first"); return 1; } if (args) { long id; int n = sscanf(args, "%li", &id); if (n == 1){ LinphoneCall *call=linphonec_get_call (id); if (call){ if(linphone_call_resume(call)==-1){ linphonec_out("There was a problem to resume the call check the remote address you gave %s\n",args); } } return 1; }else return 0; } else { const bctbx_list_t *calls = linphone_core_get_calls(lc); int nbcalls=(int)bctbx_list_size(calls); if( nbcalls == 1) { if(linphone_call_resume(calls->data) < 0) { linphonec_out("There was a problem to resume the unique call.\n"); } return 1; }else if (nbcalls==0){ linphonec_out("There is no calls at this time.\n"); return 1; }else{ linphonec_out("There are %i calls at this time, please specify call id as given with 'calls' command.\n"); } } return 0; } static int lpc_cmd_conference(LinphoneCore *lc, char *args){ long id; char subcommand[32]={0}; int n; if (args==NULL) return 0; n=sscanf(args, "%31s %li", subcommand,&id); if (n == 2){ LinphoneCall *call=linphonec_get_call(id); if (call==NULL) return 1; if (strcmp(subcommand,"add")==0){ linphone_core_add_to_conference(lc,call); return 1; }else if (strcmp(subcommand,"rm")==0){ linphone_core_remove_from_conference(lc,call); return 1; }else if (strcmp(subcommand,"enter")==0){ linphone_core_enter_conference(lc); return 1; }else if (strcmp(subcommand,"leave")==0){ linphone_core_leave_conference(lc); return 1; } } return 0; } /*************************************************************************** * * Commands helper functions * ***************************************************************************/ static void linphonec_proxy_add(LinphoneCore *lc) { bool_t enable_register=FALSE; LinphoneProxyConfig *cfg; linphonec_out("Adding new proxy setup. Hit ^D to abort.\n"); /* * SIP Proxy address */ while (1) { char *input=linphonec_readline("Enter proxy sip address: "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } cfg=linphone_core_create_proxy_config(lc); if (linphone_proxy_config_set_server_addr(cfg,clean)<0) { linphonec_out("Invalid sip address (sip:sip.domain.tld).\n"); free(input); linphone_proxy_config_destroy(cfg); continue; } free(input); break; } /* * SIP Proxy identity */ while (1) { char *input=linphonec_readline("Your identity for this proxy: "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } linphone_proxy_config_set_identity(cfg, clean); if ( ! linphone_proxy_config_get_identity (cfg)) { linphonec_out("Invalid identity (sip:name@sip.domain.tld).\n"); free(input); continue; } free(input); break; } /* * SIP Proxy enable register */ while (1) { char *input=linphonec_readline("Do you want to register on this proxy (yes/no): "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } if ( ! strcmp(clean, "yes") ) enable_register=TRUE; else if ( ! strcmp(clean, "no") ) enable_register=FALSE; else { linphonec_out("Please answer with 'yes' or 'no'\n"); free(input); continue; } linphone_proxy_config_enableregister(cfg, enable_register); free(input); break; } /* * SIP Proxy registration expiration */ if ( enable_register==TRUE ) { int expires=0; while (1) { char *input=linphonec_readline("Specify register expiration time" " in seconds (default is 600): "); if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } expires=atoi(input); if (expires==0) expires=600; linphone_proxy_config_set_expires(cfg, expires); linphonec_out("Expiration: %d seconds\n", linphone_proxy_config_get_expires (cfg)); free(input); break; } } /* * SIP proxy route */ while (1) { char *input=linphonec_readline("Specify route if needed: "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); linphonec_out("No route specified.\n"); break; } linphone_proxy_config_set_route(cfg, clean); if ( ! linphone_proxy_config_get_route(cfg) ) { linphonec_out("Invalid route.\n"); free(input); continue; } free(input); break; } /* * Final confirmation */ while (1) { char *input; char *clean; linphonec_out("--------------------------------------------\n"); linphonec_proxy_display(cfg); linphonec_out("--------------------------------------------\n"); input=linphonec_readline("Accept the above proxy configuration (yes/no) ?: "); if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } if ( ! strcmp(clean, "yes") ) break; else if ( ! strcmp(clean, "no") ) { linphonec_out("Declined.\n"); linphone_proxy_config_destroy(cfg); free(input); return; } linphonec_out("Please answer with 'yes' or 'no'\n"); free(input); continue; } linphone_core_add_proxy_config(lc,cfg); /* automatically set the last entered proxy as the default one */ linphone_core_set_default_proxy(lc,cfg); linphonec_out("Proxy added.\n"); } static void linphonec_proxy_display(LinphoneProxyConfig *cfg) { const char *route=linphone_proxy_config_get_route(cfg); const char *identity=linphone_proxy_config_get_identity(cfg); linphonec_out("sip address: %s\nroute: %s\nidentity: %s\nregister: %s\nexpires: %i\nregistered: %s\n", linphone_proxy_config_get_addr(cfg), (route!=NULL)? route:"", (identity!=NULL)?identity:"", linphone_proxy_config_register_enabled (cfg)?"yes":"no", linphone_proxy_config_get_expires (cfg), linphone_proxy_config_is_registered(cfg) ? "yes" : "no"); } static void linphonec_proxy_show(LinphoneCore *lc, int index) { const bctbx_list_t *elem; int i; for(elem=linphone_core_get_proxy_config_list(lc),i=0;elem!=NULL;elem=elem->next,++i){ if (index==i){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig *)elem->data; linphonec_proxy_display(cfg); return; } } linphonec_out("No proxy with index %i\n", index); } static void linphonec_proxy_list(LinphoneCore *lc) { const bctbx_list_t *proxies; int n; int def=linphone_core_get_default_proxy(lc,NULL); proxies=linphone_core_get_proxy_config_list(lc); for(n=0;proxies!=NULL;proxies=bctbx_list_next(proxies),n++){ if (n==def) linphonec_out("****** Proxy %i - this is the default one - *******\n",n); else linphonec_out("****** Proxy %i *******\n",n); linphonec_proxy_display((LinphoneProxyConfig*)proxies->data); } if ( ! n ) linphonec_out("No proxies defined\n"); } static void linphonec_proxy_remove(LinphoneCore *lc, int index) { const bctbx_list_t *proxies; LinphoneProxyConfig *cfg; proxies=linphone_core_get_proxy_config_list(lc); cfg=(LinphoneProxyConfig*)bctbx_list_nth_data(proxies,index); if (cfg==NULL){ linphonec_out("No such proxy.\n"); return; } linphone_core_remove_proxy_config(lc,cfg); linphonec_out("Proxy %s removed.\n", linphone_proxy_config_get_addr(cfg)); } static int linphonec_proxy_use(LinphoneCore *lc, int index) { const bctbx_list_t *proxies; LinphoneProxyConfig *cfg; proxies=linphone_core_get_proxy_config_list(lc); cfg=(LinphoneProxyConfig*)bctbx_list_nth_data(proxies,index); if (cfg==NULL){ linphonec_out("No such proxy (try 'proxy list')."); return 0; } linphone_core_set_default_proxy(lc,cfg); return 1; } static void linphonec_friend_display(LinphoneFriend *fr) { const LinphoneAddress *addr = linphone_friend_get_address(fr); char *str = NULL; linphonec_out("name: %s\n", linphone_friend_get_name(fr)); if (addr) str = linphone_address_as_string_uri_only(addr); linphonec_out("address: %s\n", str); if (str) ms_free(str); } static int linphonec_friend_list(LinphoneCore *lc, char *pat) { const bctbx_list_t *friend; int n; if (pat) { pat=lpc_strip_blanks(pat); if (!*pat) pat = NULL; } friend = linphone_core_get_friend_list(lc); for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n ) { if ( pat ) { const char *name = linphone_friend_get_name((LinphoneFriend *)friend->data); if (name && !strstr(name, pat)) continue; } linphonec_out("****** Friend %i *******\n",n); linphonec_friend_display((LinphoneFriend*)friend->data); } return 1; } static int linphonec_friend_call(LinphoneCore *lc, unsigned int num) { const bctbx_list_t *friend = linphone_core_get_friend_list(lc); unsigned int n; char *addr_str; for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n ) { if ( n == num ) { int ret; const LinphoneAddress *addr = linphone_friend_get_address((LinphoneFriend*)friend->data); if (addr) { addr_str = linphone_address_as_string(addr); ret=lpc_cmd_call(lc, addr_str); ms_free(addr_str); return ret; } else { linphonec_out("Friend %u does not have an address\n", num); } } } linphonec_out("No such friend %u\n", num); return 1; } #ifndef _WIN32 static int linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr) { LinphoneFriend *newFriend; char url[PATH_MAX]; snprintf(url, PATH_MAX, "%s <%s>", name, addr); newFriend = linphone_core_create_friend_with_address(lc, url); linphone_core_add_friend(lc, newFriend); return 0; } #endif static int linphonec_friend_delete(LinphoneCore *lc, int num) { const bctbx_list_t *friend = linphone_core_get_friend_list(lc); int n; for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n ) { if ( n == num ) { linphone_core_remove_friend(lc, friend->data); return 0; } } if (-1 == num) { int i; for (i = 0 ; i < n ; i++) linphonec_friend_delete(lc, 0); return 0; } linphonec_out("No such friend %i\n", num); return 1; } static void linphonec_display_command_help(LPC_COMMAND *cmd) { if ( cmd->doc ) linphonec_out ("%s\n", cmd->doc); else linphonec_out("%s\n", cmd->help); } static int lpc_cmd_register(LinphoneCore *lc, char *args){ char identity[512]; char proxy[512]; char passwd[512]; LinphoneProxyConfig *cfg; const bctbx_list_t *elem; if (!args) { /* it means that you want to register the default proxy */ LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc); if (cfg) { if(!linphone_proxy_config_is_registered(cfg)) { linphone_proxy_config_enable_register(cfg,TRUE); linphone_proxy_config_done(cfg); }else{ linphonec_out("default proxy already registered\n"); } }else{ linphonec_out("we do not have a default proxy\n"); return 0; } return 1; } passwd[0]=proxy[0]=identity[0]='\0'; sscanf(args,"%511s %511s %511s",identity,proxy,passwd); if (proxy[0]=='\0' || identity[0]=='\0'){ linphonec_out("Missing parameters, see help register\n"); return 1; } if (passwd[0]!='\0'){ LinphoneAddress *from; LinphoneAuthInfo *info; if ((from=linphone_address_new(identity))!=NULL){ info=linphone_auth_info_new(linphone_address_get_username(from),NULL,passwd,NULL,NULL,linphone_address_get_username(from)); linphone_core_add_auth_info(lc,info); linphone_address_unref(from); linphone_auth_info_unref(info); } } elem=linphone_core_get_proxy_config_list(lc); if (elem) { cfg=(LinphoneProxyConfig*)elem->data; linphone_proxy_config_edit(cfg); } else cfg=linphone_core_create_proxy_config(lc); linphone_proxy_config_set_identity(cfg,identity); linphone_proxy_config_set_server_addr(cfg,proxy); linphone_proxy_config_enable_register(cfg,TRUE); if (elem) linphone_proxy_config_done(cfg); else linphone_core_add_proxy_config(lc,cfg); linphone_core_set_default_proxy(lc,cfg); return 1; } static int lpc_cmd_unregister(LinphoneCore *lc, char *args){ LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc); if (cfg && linphone_proxy_config_is_registered(cfg)) { linphone_proxy_config_edit(cfg); linphone_proxy_config_enable_register(cfg,FALSE); linphone_proxy_config_done(cfg); }else{ linphonec_out("unregistered\n"); } return 1; } static int lpc_cmd_duration(LinphoneCore *lc, char *args){ LinphoneCallLog *cl; const bctbx_list_t *elem=linphone_core_get_call_logs(lc); for(;elem!=NULL;elem=elem->next){ if (elem->next==NULL){ cl=(LinphoneCallLog*)elem->data; linphonec_out("%i seconds\n",linphone_call_log_get_duration(cl)); } } return 1; } static int lpc_cmd_status(LinphoneCore *lc, char *args) { LinphoneProxyConfig *cfg; if ( ! args ) return 0; cfg = linphone_core_get_default_proxy_config(lc); if (strstr(args,"register")) { if (cfg) { if (linphone_proxy_config_is_registered(cfg)){ linphonec_out("registered, identity=%s duration=%i\n", linphone_proxy_config_get_identity(cfg), linphone_proxy_config_get_expires(cfg)); }else if (linphone_proxy_config_register_enabled(cfg)){ linphonec_out("registered=-1\n"); }else linphonec_out("registered=0\n"); } else linphonec_out("registered=0\n"); } else if (strstr(args,"autoanswer")) { if (cfg && linphone_proxy_config_is_registered(cfg)) linphonec_out("autoanswer=%i\n",linphonec_get_autoanswer()); else linphonec_out("unregistered\n"); } else if (strstr(args,"hook")) { LinphoneCall *call=linphone_core_get_current_call (lc); LinphoneCallState call_state=LinphoneCallIdle; if (call) call_state=linphone_call_get_state(call); switch(call_state){ case LinphoneCallOutgoingInit: linphonec_out("hook=outgoing_init sip:%s\n",linphonec_get_callee()); break; case LinphoneCallOutgoingProgress: linphonec_out("hook=dialing sip:%s\n",linphonec_get_callee()); break; case LinphoneCallOutgoingRinging: linphonec_out("hook=ringing sip:%s\n",linphonec_get_callee()); break; case LinphoneCallPaused: linphonec_out("hook=paused sip:%s\n",linphonec_get_callee()); break; case LinphoneCallIdle: linphonec_out("hook=on-hook\n"); break; case LinphoneCallStreamsRunning: case LinphoneCallConnected: if (linphone_call_get_dir(call)==LinphoneCallOutgoing){ linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(), linphone_core_get_current_call_duration(lc), linphone_core_mic_enabled(lc) ? "no" : "yes", linphone_core_is_rtp_muted(lc) ? "yes" : "no"); }else{ linphonec_out("hook=answered duration=%i %s\n" , linphone_core_get_current_call_duration(lc), linphonec_get_caller()); } break; case LinphoneCallIncomingReceived: linphonec_out("Incoming call from %s\n",linphonec_get_caller()); break; default: break; } } else return 0; return 1; } static int lpc_cmd_ports(LinphoneCore *lc, char *args) { int port; if ( ! args ){ linphonec_out("sip port = %i\naudio rtp port = %i\nvideo rtp port = %i\n", linphone_core_get_sip_port(lc), linphone_core_get_audio_port(lc), linphone_core_get_video_port(lc)); return 1; } if (sscanf(args,"sip %i",&port)==1){ linphonec_out("Setting sip port to %i\n",port); linphone_core_set_sip_port(lc,port); }else return 0; return 1; } static int lpc_cmd_param(LinphoneCore *lc, char *args) { char section[20], param[20], value[50]; const char *string; if (args == NULL) { return 0; } switch (sscanf(args,"%19s %19s %49s",section,param,value)) { // case 1 might show all current settings under a section case 2: string = lp_config_get_string(linphone_core_get_config(lc), section, param, "(undef)"); linphonec_out("current value: %s\n", string); break; case 3: if (lp_config_get_string(linphone_core_get_config(lc), section, param, NULL) != NULL) { lp_config_set_string(linphone_core_get_config(lc), section, param, value); // no indication of existence linphonec_out("updated value: %s\n", value); } else { linphonec_out("only update of existing variables are allowed\n"); } break; default: return 0; } return 1; } static int lpc_cmd_speak(LinphoneCore *lc, char *args){ #ifndef _WIN32 char voice[64]; char *sentence; char cl[128]; char wavfile[128]="/tmp/linphonec-espeak-XXXXXX"; int status; FILE *file; if (!args) return 0; memset(voice,0,sizeof(voice)); sscanf(args,"%63s",voice); sentence=args+strlen(voice); #ifdef __APPLE__ mktemp(wavfile); #else if (mkstemp(wavfile)==-1){ ms_error("Could not create temporary filename: %s", strerror(errno)); linphonec_out("An error occured, please consult logs for details."); return 1; } #endif snprintf(cl,sizeof(cl),"espeak -v %s -s 100 -w %s --stdin",voice,wavfile); file=popen(cl,"w"); if (file==NULL){ ms_error("Could not open pipe to espeak !"); return 1; } fprintf(file,"%s",sentence); status=pclose(file); if (WEXITSTATUS(status)==0){ linphone_core_set_play_file(lc,wavfile); }else{ linphonec_out("espeak command failed."); } #else linphonec_out("Sorry, this command is not implemented in windows version."); #endif return 1; } static int lpc_cmd_acodec(LinphoneCore *lc, char *args){ return lpc_cmd_codec(AUDIO, lc, args); } static int lpc_cmd_vcodec(LinphoneCore *lc, char *args){ return lpc_cmd_codec(VIDEO, lc, args); } static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args){ char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1,"enable")==0) { #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif if (!strcmp(arg2,"all")) linphonec_codec_enable(type,lc,-1); else linphonec_codec_enable(type,lc,atoi(arg2)); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } else if (strcmp(arg1,"list")==0) { linphonec_codec_list(type,lc); } else if (strcmp(arg1,"disable")==0) { if (!strcmp(arg2,"all")) linphonec_codec_disable(type,lc,-1); else linphonec_codec_disable(type,lc,atoi(arg2)); } else { return 0; /* syntax error */ } return 1; } static void linphonec_codec_list(int type, LinphoneCore *lc){ PayloadType *pt; int index=0; const bctbx_list_t *node=NULL; if (type == AUDIO) { node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=bctbx_list_next(node)){ pt=(PayloadType*)(node->data); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, linphone_core_payload_type_enabled(lc,pt) ? "enabled" : "disabled"); index++; } } static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){ PayloadType *pt; int index=0; const bctbx_list_t *node=NULL; if (type == AUDIO) { node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=bctbx_list_next(node)){ if (index == sel_index || sel_index == -1) { pt=(PayloadType*)(node->data); linphone_core_enable_payload_type (lc,pt,TRUE); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "enabled"); } index++; } } static void linphonec_codec_disable(int type, LinphoneCore *lc, int sel_index){ PayloadType *pt; int index=0; const bctbx_list_t *node=NULL; if (type == AUDIO) { node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=bctbx_list_next(node)){ if (index == sel_index || sel_index == -1) { pt=(PayloadType*)(node->data); linphone_core_enable_payload_type (lc,pt,FALSE); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "disabled"); } index++; } } static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ char *arg1 = args; char *arg2 = NULL; char *ptr = args; LpConfig *config=linphone_core_get_config(lc); if (!args) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1,"on")==0){ int delay, tail_len, frame_size; int n; linphone_core_enable_echo_cancellation(lc,1); if (arg2 != 0) { n = sscanf(arg2, "%d %d %d", &delay, &tail_len, &frame_size); if (n == 1) { lp_config_set_int(config,"sound","ec_delay",delay); } else if (n == 2) { lp_config_set_int(config,"sound","ec_delay",delay); lp_config_set_int(config,"sound","ec_tail_len",tail_len); } else if (n == 3) { lp_config_set_int(config,"sound","ec_delay",delay); lp_config_set_int(config,"sound","ec_tail_len",tail_len); lp_config_set_int(config,"sound","ec_framesize",frame_size); } } } else if (strcmp(arg1,"off")==0){ linphone_core_enable_echo_cancellation(lc,0); } else if (strcmp(arg1,"show")==0){ linphonec_out("echo cancellation is %s; delay %d, tail length %d, frame size %d\n", linphone_core_echo_cancellation_enabled(lc) ? "on" : "off", lp_config_get_int(config,"sound","ec_delay",0), lp_config_get_int(config,"sound","ec_tail_len",0), lp_config_get_int(config,"sound","ec_framesize",0)); } else { return 0; } return 1; } static int lpc_cmd_echolimiter(LinphoneCore *lc, char *args){ if (args){ if (strcmp(args,"on")==0){ linphone_core_enable_echo_limiter (lc,TRUE); }else if (strcmp(args,"off")==0){ linphone_core_enable_echo_limiter (lc,FALSE); } } linphonec_out("Echo limiter is now %s.\n",linphone_core_echo_limiter_enabled (lc) ? "on":"off"); return 1; } static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args) { linphone_core_enable_mic(lc, 0); return 1; } static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args){ linphone_core_enable_mic(lc, 1); return 1; } static int lpc_cmd_playback_gain(LinphoneCore *lc, char *args) { if (args){ linphone_core_set_playback_gain_db(lc, (float)atof(args)); return 1; } return 0; } static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args) { bool_t rtp_xmit_off=FALSE; char *status; if(args){ if(strstr(args,"1"))rtp_xmit_off=TRUE; if(linphone_core_get_current_call (lc)==NULL) linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off); else linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n"); } rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc); if (rtp_xmit_off) status="off"; else status="on"; linphonec_out("rtp transmit %s when audio muted\n",status); return 1; } #ifdef VIDEO_ENABLED static int _lpc_cmd_video_window(LinphoneCore *lc, char *args, bool_t is_preview){ char subcommand[64]; long a,b; int err; VideoParams *params=is_preview ? &lpc_preview_params : &lpc_video_params; if (!args) return 0; err=sscanf(args,"%63s %ld %ld",subcommand,&a,&b); if (err>=1){ if (strcmp(subcommand,"pos")==0){ if (err<3) return 0; params->x=a; params->y=b; params->refresh=TRUE; }else if (strcmp(subcommand,"size")==0){ if (err<3) return 0; params->w=a; params->h=b; params->refresh=TRUE; }else if (strcmp(subcommand,"show")==0){ params->show=TRUE; params->refresh=TRUE; if (is_preview) linphone_core_enable_video_preview (lc,TRUE); }else if (strcmp(subcommand,"hide")==0){ params->show=FALSE; params->refresh=TRUE; if (is_preview) linphone_core_enable_video_preview (lc,FALSE); }else if (strcmp(subcommand,"id")==0){ if (err == 1){ linphonec_out("vwindow id: 0x%p\n",is_preview ? linphone_core_get_native_preview_window_id (lc) : linphone_core_get_native_video_window_id (lc)); return 1; } else if (err != 2) return 0; params->wid=(void *)a; if (is_preview) linphone_core_set_native_preview_window_id(lc, (void *)a); else linphone_core_set_native_video_window_id(lc, (void *)a); }else if (is_preview==TRUE){ if (strcmp(subcommand,"integrated")==0){ linphone_core_use_preview_window (lc,FALSE); }else if (strcmp(subcommand,"standalone")==0){ linphone_core_use_preview_window(lc,TRUE); }else return 0; }else return 0; } return 1; } static int lpc_cmd_video_window(LinphoneCore *lc, char *args){ return _lpc_cmd_video_window(lc, args, FALSE); } static int lpc_cmd_preview_window(LinphoneCore *lc, char *args){ return _lpc_cmd_video_window(lc, args, TRUE); } #endif static void lpc_display_global_state(LinphoneCore *lc){ linphonec_out("Global liblinphone state\n%s\n", linphone_global_state_to_string(linphone_core_get_global_state(lc))); } static void lpc_display_call_states(LinphoneCore *lc){ LinphoneCall *call; const bctbx_list_t *elem; char *tmp; linphonec_out("Call states\n" "Id | Destination | State | Flags |\n" "------------------------------------------------------------------------\n"); elem=linphone_core_get_calls(lc); if (elem==NULL){ linphonec_out("(empty)\n"); }else{ for(;elem!=NULL;elem=elem->next){ const char *flag; bool_t in_conference; call=(LinphoneCall*)elem->data; in_conference=(linphone_call_get_conference(call) != NULL); tmp=linphone_call_get_remote_address_as_string (call); flag=in_conference ? "conferencing" : ""; flag=linphone_call_has_transfer_pending(call) ? "transfer pending" : flag; linphonec_out("%-2i | %-35s | %-15s | %s\n",VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)), tmp,linphone_call_state_to_string(linphone_call_get_state(call))+strlen("LinphoneCall"),flag); ms_free(tmp); } } } static void lpc_display_proxy_states(LinphoneCore *lc){ const bctbx_list_t *elem; linphonec_out("Proxy registration states\n" " Identity | State\n" "------------------------------------------------------------\n"); elem=linphone_core_get_proxy_config_list (lc); if (elem==NULL) linphonec_out("(empty)\n"); else { for(;elem!=NULL;elem=elem->next){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; linphonec_out("%-40s | %s\n",linphone_proxy_config_get_identity (cfg), linphone_registration_state_to_string(linphone_proxy_config_get_state(cfg))); } } } static int lpc_cmd_states(LinphoneCore *lc, char *args){ if (args==NULL) { lpc_display_global_state(lc); lpc_display_call_states(lc); lpc_display_proxy_states(lc); return 1; } if (strcmp(args,"global")==0){ lpc_display_global_state(lc); return 1; } if (strcmp(args,"proxies")==0){ lpc_display_proxy_states(lc); return 1; } if (strcmp(args,"calls")==0){ lpc_display_call_states(lc); return 1; } return 0; } #ifdef VIDEO_ENABLED static int lpc_cmd_camera(LinphoneCore *lc, char *args){ LinphoneCall *call=linphone_core_get_current_call(lc); bool_t activated=FALSE; if (linphone_core_video_enabled (lc)==FALSE){ linphonec_out("Video is disabled, re-run linphonec with -V option."); return 1; } if (args){ if (strcmp(args,"on")==0) activated=TRUE; else if (strcmp(args,"off")==0) activated=FALSE; else return 0; } if (call==NULL){ if (args){ linphonec_camera_enabled=activated; } if (linphonec_camera_enabled){ linphonec_out("Camera is enabled. Video stream will be setup immediately for outgoing and incoming calls.\n"); }else{ linphonec_out("Camera is disabled. Calls will be established with audio-only, with the possibility to later add video using 'camera on'.\n"); } }else{ const LinphoneCallParams *cp=linphone_call_get_current_params (call); if (args){ linphone_call_enable_camera(call,activated); if (linphone_call_get_state(call)==LinphoneCallStreamsRunning){ if ((activated && !linphone_call_params_video_enabled (cp))){ /*update the call to add the video stream*/ LinphoneCallParams *ncp=linphone_call_params_copy(cp); linphone_call_params_enable_video(ncp,TRUE); linphone_call_update(call,ncp); linphone_call_params_unref (ncp); linphonec_out("Trying to bring up video stream...\n"); } } } if (linphone_call_camera_enabled (call)) linphonec_out("Camera is allowed for current call.\n"); else linphonec_out("Camera is dis-allowed for current call.\n"); } return 1; } static int lpc_cmd_snapshot(LinphoneCore *lc, char *args){ LinphoneCall *call; if (!args) return 0; call=linphone_core_get_current_call(lc); if (call!=NULL){ linphone_call_take_video_snapshot(call,args); linphonec_out("Taking video snapshot in file %s\n", args); }else linphonec_out("There is no active call.\n"); return 1; } static int lpc_cmd_preview_snapshot(LinphoneCore *lc, char *args){ LinphoneCall *call; if (!args) return 0; call=linphone_core_get_current_call(lc); if (call!=NULL){ linphone_call_take_preview_snapshot(call,args); linphonec_out("Taking video preview snapshot in file %s\n", args); }else linphonec_out("There is no active call.\n"); return 1; } static int lpc_cmd_vfureq(LinphoneCore *lc, char *arg){ LinphoneCall *call; call=linphone_core_get_current_call(lc); if (call!=NULL){ linphone_call_send_vfu_request(call); linphonec_out("VFU request sent\n"); }else linphonec_out("There is no active call.\n"); return 1; } #endif static int lpc_cmd_identify(LinphoneCore *lc, char *args){ LinphoneCall *call; const char *remote_ua; if (args==NULL){ call=linphone_core_get_current_call(lc); if (call==NULL) { linphonec_out("There is currently running call. Specify call id.\n"); return 0; } }else{ call=linphonec_get_call(atoi(args)); if (call==NULL){ return 0; } } remote_ua=linphone_call_get_remote_user_agent(call); if (remote_ua){ linphonec_out("Remote user agent string is: %s\n",remote_ua); } return 1; } static int lpc_cmd_ringback(LinphoneCore *lc, char *args){ if (!args) return 0; if (strcmp(args,"disable")==0){ linphone_core_set_remote_ringback_tone(lc,NULL); linphonec_out("Disabling ringback tone.\n"); return 1; } linphone_core_set_remote_ringback_tone (lc,args); linphonec_out("Using %s as ringback tone to be played to callers.",args); return 1; } static int zrtp_set_verified(LinphoneCore *lc, char *args, bool_t verified){ LinphoneCall *call=linphone_core_get_current_call(lc); if (linphone_call_params_get_media_encryption(linphone_call_get_current_params(call))==LinphoneMediaEncryptionZRTP){ linphone_call_set_authentication_token_verified(call,verified); } return 1; } static int lpc_cmd_zrtp_verified(LinphoneCore *lc, char *args){ return zrtp_set_verified(lc,args,TRUE); } static int lpc_cmd_zrtp_unverified(LinphoneCore *lc, char *args){ return zrtp_set_verified(lc,args,FALSE); } /*************************************************************************** * * Command table management funx * ***************************************************************************/ /* * Find a command given its name */ static LPC_COMMAND * lpc_find_command(const char *name) { int i; for (i=0; commands[i].name; ++i) { if (strcmp(name, commands[i].name) == 0) return &commands[i]; } for (i=0; advanced_commands[i].name; ++i) { if (strcmp(name, advanced_commands[i].name) == 0) return &advanced_commands[i]; } return (LPC_COMMAND *)NULL; } /**************************************************************************** * * $Log: commands.c,v $ * Revision 1.39 2008/07/03 15:08:34 smorlat * api cleanups, interface in progress. * * Revision 1.38 2008/06/17 20:38:59 smorlat * added missing file. * * Revision 1.37 2008/04/09 09:26:00 smorlat * merge various patches * H264 support. * * Revision 1.36 2007/08/01 14:47:53 strk * * console/commands.c: Clean up commands 'nat', 'stun' * and 'firewall' to be more intuitive. * * Revision 1.35 2007/06/27 09:01:25 smorlat * logging improvements. * * Revision 1.34 2007/02/20 10:17:13 smorlat * linphonec friends patch2 * * Revision 1.31 2006/09/22 07:22:47 smorlat * linphonecore api changes. * * Revision 1.30 2006/09/08 15:32:57 smorlat * support for using files instead of soundcard (used by linphonec only) * * Revision 1.29 2006/08/28 14:29:07 smorlat * fix bug. * * Revision 1.28 2006/08/21 12:49:59 smorlat * merged several little patches. * * Revision 1.27 2006/07/17 18:45:00 smorlat * support for several event queues in ortp. * glib dependency removed from coreapi/ and console/ * * Revision 1.26 2006/04/14 15:16:36 smorlat * soundcard use did nothing ! * * Revision 1.25 2006/04/06 20:09:33 smorlat * add linphonec command to see and select sound devices. * * Revision 1.24 2006/03/04 11:17:10 smorlat * mediastreamer2 in progress. * * Revision 1.23 2006/02/20 21:14:01 strk * Handled syntax errors with 'friend' command * * Revision 1.22 2006/02/20 10:20:29 strk * Added substring-based filter support for command 'friend list' * * Revision 1.21 2006/02/02 15:39:18 strk * - Added 'friend list' and 'friend call' commands * - Allowed for multiple DTFM send in a single line * - Added status-specific callback (bare version) * * Revision 1.20 2006/01/26 11:54:34 strk * More robust 'nat' command handler (strip blanks in args) * * Revision 1.19 2006/01/26 09:48:05 strk * Added limits.h include * * Revision 1.18 2006/01/26 02:18:05 strk * Added new commands 'nat use' and 'nat unuse'. * These will required a pending patch to linphonecore.c * in order to work. * * Revision 1.17 2006/01/20 14:12:33 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.16 2006/01/18 09:25:32 strk * Command completion inhibited in proxy addition and auth request prompts. * Avoided use of linphonec_readline's internal filename completion. * * Revision 1.15 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.14 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by linphonec_readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/ linphone-3.12.0/console/example/000077500000000000000000000000001313432737600165175ustar00rootroot00000000000000linphone-3.12.0/console/example/linphonec000066400000000000000000000013371313432737600204250ustar00rootroot00000000000000# this file is used to store user config values for linphonec # please modify and copy it to ~/.linphonec # # logfile=~/linphonec.log debuglevel=0 local_addr=192.168.1.10 if_name=eth0 # type of network connection 4 = ethernet con_type=4 # rtp audio_rtp_port=7078 jitt_comp=150 # audio driver_mode=0 rec_lev=100 play_lev=100 source=109 autokill=0 # sip sip_port=5060 use_registrar=0 username=1006 hostname=192.168.1.1 registrar=sip:192.168.1.1:5060 reg_passwd= addr_of_rec=sip:1006@192.168.1.1 reg_expires=900 as_proxy=1 as_redirect=0 as_outbound=1 # codecs audio_codecs=259 371 264 256 # short dial s0=sip:1002@130.83.176.121 s1=sip:1001@192.168.1.1 s2=sip:1002@192.168.1.1 s3=sip:1003@192.168.1.1 s4=sip:1004@192.168.1.1 linphone-3.12.0/console/linphonec.c000066400000000000000000001223211313432737600172100ustar00rootroot00000000000000/**************************************************************************** * * $Id: linphonec.c,v 1.57 2007/11/14 13:40:27 smorlat Exp $ * * Copyright (C) 2006 Sandro Santilli * Copyright (C) 2002 Florian Winterstein * Copyright (C) 2000 Simon MORLAT * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #include #ifndef _WIN32_WCE #include #include #include #include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */ #endif /*_WIN32_WCE*/ #include #include #include #include #include "linphonec.h" #include #ifdef _WIN32 #include #include #ifndef _WIN32_WCE #include #endif /*_WIN32_WCE*/ #else #include #include #include #include #include #include #endif #if !defined(PATH_MAX) #define PATH_MAX 256 #endif /*PATH_MAX*/ #if defined(_WIN32_WCE) #if !defined(strdup) #define strdup _strdup #endif /*strdup*/ #endif /*_WIN32_WCE*/ #ifndef PACKAGE_DIR #define PACKAGE_DIR "" #endif #ifdef HAVE_X11_XLIB_H #include #endif /*************************************************************************** * * Types * ***************************************************************************/ typedef struct { LinphoneAuthInfo *elem[MAX_PENDING_AUTH]; int nitems; } LPC_AUTH_STACK; /*************************************************************************** * * Forward declarations * ***************************************************************************/ char *lpc_strip_blanks(char *input); static int handle_configfile_migration(void); #if !defined(_WIN32_WCE) static int copy_file(const char *from, const char *to); #endif /*_WIN32_WCE*/ static int linphonec_parse_cmdline(int argc, char **argv); static int linphonec_init(int argc, char **argv); static int linphonec_main_loop (LinphoneCore * opm); static int linphonec_idle_call (void); #ifdef HAVE_READLINE static int linphonec_initialize_readline(void); static int linphonec_finish_readline(void); static char **linephonec_readline_completion(const char *text, int start, int end); #endif /* These are callback for linphone core */ static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain); static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to); static void linphonec_display_something (LinphoneCore * lc, const char *something); static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url); static void linphonec_display_warning (LinphoneCore * lc, const char *something); static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state); static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const LinphoneAddress *from, const char *msg); static void linphonec_display_status (LinphoneCore * lc, const char *something); static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf); static void print_prompt(LinphoneCore *opm); void linphonec_out(const char *fmt,...); /*************************************************************************** * * Global variables * ***************************************************************************/ LinphoneCore *linphonec; FILE *mylogfile; #ifdef HAVE_READLINE static char *histfile_name=NULL; static char last_in_history[256]; #endif //auto answer (-a) option static bool_t auto_answer=FALSE; static bool_t real_early_media_sending=FALSE; static bool_t answer_call=FALSE; static bool_t vcap_enabled=FALSE; static bool_t display_enabled=FALSE; static bool_t preview_enabled=FALSE; static bool_t show_general_state=FALSE; static bool_t unix_socket=FALSE; static bool_t linphonec_running=TRUE; LPC_AUTH_STACK auth_stack; static int trace_level = 0; static char *logfile_name = NULL; static char configfile_name[PATH_MAX]; static char zrtpsecrets[PATH_MAX]; static char usr_certificates_path[PATH_MAX]; static const char *factory_configfile_name=NULL; static char *sip_addr_to_call = NULL; /* for autocall */ static void *window_id = NULL; /* NULL=standalone window, or window id for embedding video */ #if !defined(_WIN32_WCE) static ortp_pipe_t client_sock=ORTP_PIPE_INVALID; #endif /*_WIN32_WCE*/ char prompt[PROMPT_MAX_LEN]; #if !defined(_WIN32_WCE) static ortp_thread_t pipe_reader_th; static bool_t pipe_reader_run=FALSE; #endif /*_WIN32_WCE*/ #if !defined(_WIN32_WCE) static ortp_pipe_t server_sock; #endif /*_WIN32_WCE*/ bool_t linphonec_camera_enabled=TRUE; void linphonec_call_identify(LinphoneCall* call){ static int callid=1; linphone_call_set_user_pointer (call,INT_TO_VOIDPTR(callid)); callid++; } LinphoneCall *linphonec_get_call(int id){ const MSList *elem=linphone_core_get_calls(linphonec); for (;elem!=NULL;elem=elem->next){ LinphoneCall *call=(LinphoneCall*)elem->data; if (VOIDPTR_TO_INT(linphone_call_get_user_pointer(call))==id){ return call; } } linphonec_out("Sorry, no call with id %i exists at this time.\n",id); return NULL; } /*************************************************************************** * * Linphone core callbacks * ***************************************************************************/ /* * Linphone core callback */ static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to) { linphonec_out("Receiving out of call refer to %s\n", refer_to); } /* * Linphone core callback */ static void linphonec_display_something (LinphoneCore * lc, const char *something) { fprintf (stdout, "%s\n%s", something,prompt); fflush(stdout); } /* * Linphone core callback */ static void linphonec_display_status (LinphoneCore * lc, const char *something) { fprintf (stdout, "%s\n%s", something,prompt); fflush(stdout); } /* * Linphone core callback */ static void linphonec_display_warning (LinphoneCore * lc, const char *something) { fprintf (stdout, "Warning: %s\n%s", something,prompt); fflush(stdout); } /* * Linphone core callback */ static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url) { fprintf (stdout, "%s : %s\n", something, url); } /* * Linphone core callback */ static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain) { /* no prompt possible when using pipes or tcp mode*/ if (unix_socket){ linphone_core_abort_authentication(lc,NULL); }else{ LinphoneAuthInfo *pending_auth; if ( auth_stack.nitems+1 > MAX_PENDING_AUTH ) { fprintf(stderr, "Can't accept another authentication request.\n" "Consider incrementing MAX_PENDING_AUTH macro.\n"); return; } pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm,domain); auth_stack.elem[auth_stack.nitems++]=pending_auth; } } /* * Linphone core callback */ static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state) { char *remote=linphone_call_get_remote_address_as_string(call); if (new_call_state==LinphoneCallConnected){ linphonec_out("The distant endpoint %s of call %i has been transfered, you can safely close the call.\n", remote,VOIDPTR_TO_INT(linphone_call_get_user_pointer(call))); } ms_free(remote); } /* * Linphone core callback */ static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid) { const LinphoneAddress *addr = linphone_friend_get_address(fid); if (addr) { char *tmp=linphone_address_as_string(addr); printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid))); ms_free(tmp); } // todo: update Friend list state (unimplemented) } /* * Linphone core callback */ static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url) { printf("Friend %s requested subscription " "(accept/deny is not implemented yet)\n", url); // This means that this person wishes to be notified // of your presence information (online, busy, away...). } static void linphonec_call_updated(LinphoneCall *call){ const LinphoneCallParams *cp=linphone_call_get_current_params(call); if (!linphone_call_camera_enabled (call) && linphone_call_params_video_enabled (cp)){ linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n"); } } static void linphonec_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t encrypted, const char *auth_token) { int id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); if (!encrypted) { linphonec_out("Call %i is not fully encrypted and auth token is %s.\n", id, (auth_token != NULL) ? auth_token : "absent"); } else { linphonec_out("Call %i is fully encrypted and auth token is %s.\n", id, (auth_token != NULL) ? auth_token : "absent"); } } static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){ char *from=linphone_call_get_remote_address_as_string(call); int id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); switch(st){ case LinphoneCallEnd: linphonec_out("Call %i with %s ended (%s).\n", id, from, linphone_reason_to_string(linphone_call_get_reason(call))); break; case LinphoneCallResuming: linphonec_out("Resuming call %i with %s.\n", id, from); break; case LinphoneCallStreamsRunning: linphonec_out("Media streams established with %s for call %i (%s).\n", from,id,( linphone_call_params_video_enabled( linphone_call_get_current_params(call)) ? "video":"audio")); break; case LinphoneCallPausing: linphonec_out("Pausing call %i with %s.\n", id, from); break; case LinphoneCallPaused: linphonec_out("Call %i with %s is now paused.\n", id, from); break; case LinphoneCallPausedByRemote: linphonec_out("Call %i has been paused by %s.\n",id,from); break; case LinphoneCallIncomingReceived: linphonec_call_identify(call); linphone_call_enable_camera (call,linphonec_camera_enabled); id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); linphonec_set_caller(from); linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id); if ( auto_answer) { answer_call=TRUE; } else if (real_early_media_sending) { LinphoneCallParams* callparams = linphone_core_create_call_params(lc, call); linphonec_out("Sending early media using real hardware\n"); linphone_call_params_enable_early_media_sending(callparams, TRUE); if (vcap_enabled) linphone_call_params_enable_video(callparams, TRUE); linphone_call_accept_early_media_with_params(call, callparams); linphone_call_params_unref(callparams); } break; case LinphoneCallOutgoingInit: linphonec_call_identify(call); id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); linphonec_out("Establishing call id to %s, assigned id %i\n", from,id); break; case LinphoneCallUpdatedByRemote: linphonec_call_updated(call); break; case LinphoneCallOutgoingProgress: linphonec_out("Call %i to %s in progress.\n", id, from); break; case LinphoneCallOutgoingRinging: linphonec_out("Call %i to %s ringing.\n", id, from); break; case LinphoneCallConnected: linphonec_out("Call %i with %s connected.\n", id, from); break; case LinphoneCallOutgoingEarlyMedia: linphonec_out("Call %i with %s early media.\n", id, from); break; case LinphoneCallError: linphonec_out("Call %i with %s error.\n", id, from); break; default: break; } ms_free(from); } /* * Linphone core callback */ static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const LinphoneAddress *from, const char *msg) { linphonec_out("Message received from %s: %s\n", linphone_address_as_string(from), msg); // TODO: provide mechanism for answering.. ('say' command?) } static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){ char *from=linphone_call_get_remote_address_as_string(call); fprintf(stdout,"Receiving tone %c from %s\n",dtmf,from); fflush(stdout); ms_free(from); } static char received_prompt[PROMPT_MAX_LEN]; static ms_mutex_t prompt_mutex; static bool_t have_prompt=FALSE; static void *prompt_reader_thread(void *arg){ char *ret; char tmp[PROMPT_MAX_LEN]; while ((ret=fgets(tmp,sizeof(tmp),stdin))!=NULL){ ms_mutex_lock(&prompt_mutex); strcpy(received_prompt,ret); have_prompt=TRUE; ms_mutex_unlock(&prompt_mutex); } return NULL; } static void start_prompt_reader(void){ ortp_thread_t th; ms_mutex_init(&prompt_mutex,NULL); ortp_thread_create(&th,NULL,prompt_reader_thread,NULL); } #if !defined(_WIN32_WCE) static ortp_pipe_t create_server_socket(void){ char path[128]; #ifndef _WIN32 snprintf(path,sizeof(path)-1,"linphonec-%i",getuid()); #else { TCHAR username[128]; DWORD size=sizeof(username)-1; GetUserName(username,&size); snprintf(path,sizeof(path)-1,"linphonec-%s",username); } #endif return ortp_server_pipe_create(path); } static void *pipe_thread(void*p){ char tmp[250]; server_sock=create_server_socket(); if (server_sock==ORTP_PIPE_INVALID) return NULL; while(pipe_reader_run){ while(client_sock!=ORTP_PIPE_INVALID){ /*sleep until the last command is finished*/ #ifndef _WIN32 usleep(20000); #else Sleep(20); #endif } client_sock=ortp_server_pipe_accept_client(server_sock); if (client_sock!=ORTP_PIPE_INVALID){ int len; /*now read from the client */ if ((len=ortp_pipe_read(client_sock,(uint8_t*)tmp,sizeof(tmp)-1))>0){ ortp_mutex_lock(&prompt_mutex); tmp[len]='\0'; strcpy(received_prompt,tmp); printf("Receiving command '%s'\n",received_prompt);fflush(stdout); have_prompt=TRUE; ortp_mutex_unlock(&prompt_mutex); }else{ printf("read nothing\n");fflush(stdout); ortp_server_pipe_close_client(client_sock); client_sock=ORTP_PIPE_INVALID; } }else{ if (pipe_reader_run) fprintf(stderr,"accept() failed: %s\n",strerror(errno)); } } ms_message("Exiting pipe_reader_thread."); fflush(stdout); return NULL; } static void start_pipe_reader(void){ pipe_reader_run=TRUE; ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL); } static void stop_pipe_reader(void){ pipe_reader_run=FALSE; linphonec_command_finished(); ortp_server_pipe_close(server_sock); ortp_thread_join(pipe_reader_th,NULL); } #endif /*_WIN32_WCE*/ #ifdef HAVE_READLINE #define BOOL_HAVE_READLINE 1 #else #define BOOL_HAVE_READLINE 0 #endif char *linphonec_readline(char *prompt){ if (unix_socket || !BOOL_HAVE_READLINE ){ static bool_t prompt_reader_started=FALSE; static bool_t pipe_reader_started=FALSE; if (!prompt_reader_started){ start_prompt_reader(); prompt_reader_started=TRUE; } if (unix_socket && !pipe_reader_started){ #if !defined(_WIN32_WCE) start_pipe_reader(); pipe_reader_started=TRUE; #endif /*_WIN32_WCE*/ } fprintf(stdout,"%s",prompt); fflush(stdout); while(1){ ms_mutex_lock(&prompt_mutex); if (have_prompt){ char *ret=strdup(received_prompt); have_prompt=FALSE; ms_mutex_unlock(&prompt_mutex); return ret; } ms_mutex_unlock(&prompt_mutex); linphonec_idle_call(); #ifdef _WIN32 { MSG msg; Sleep(20); /* Following is to get the video window going as it should. Maybe should we only have this on when the option -V or -D is on? */ if (PeekMessage(&msg, NULL, 0, 0,1)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #else usleep(20000); #endif } }else{ #ifdef HAVE_READLINE char* ret=readline(prompt); return ret; #endif } } void linphonec_out(const char *fmt,...){ char *res; va_list args; va_start (args, fmt); res=ortp_strdup_vprintf(fmt,args); va_end (args); printf("%s",res); fflush(stdout); #if !defined(_WIN32_WCE) if (client_sock!=ORTP_PIPE_INVALID){ if (ortp_pipe_write(client_sock,(uint8_t*)res,(int)strlen(res))==-1){ fprintf(stderr,"Fail to send output via pipe: %s",strerror(errno)); } } #endif /*_WIN32_WCE*/ ortp_free(res); } void linphonec_command_finished(void){ #if !defined(_WIN32_WCE) if (client_sock!=ORTP_PIPE_INVALID){ ortp_server_pipe_close_client(client_sock); client_sock=ORTP_PIPE_INVALID; } #endif /*_WIN32_WCE*/ } void linphonec_set_autoanswer(bool_t enabled){ auto_answer=enabled; } bool_t linphonec_get_autoanswer(void){ return auto_answer; } LinphoneCoreVTable linphonec_vtable={0}; /***************************************************************************/ /* * Main * * Use globals: * * - char *histfile_name * - FILE *mylogfile */ #if defined (_WIN32_WCE) char **convert_args_to_ascii(int argc, _TCHAR **wargv){ int i; char **result=malloc(argc*sizeof(char*)); char argtmp[128]; for(i=0;i 0) { if (logfile_name != NULL) mylogfile = fopen (logfile_name, "w+"); if (mylogfile == NULL) { mylogfile = stdout; fprintf (stderr, "INFO: no logfile, logging to stdout\n"); } linphone_core_enable_logs(mylogfile); } else { linphone_core_disable_logs(); } /* * Initialize auth stack */ auth_stack.nitems=0; /* * Initialize linphone core */ linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL); linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION); linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets); linphone_core_set_user_certificates_path(linphonec,usr_certificates_path); linphone_core_enable_video_capture(linphonec, vcap_enabled); linphone_core_enable_video_display(linphonec, display_enabled); if (display_enabled && (window_id != NULL)) { printf("Setting window_id: 0x%p\n", window_id); linphone_core_set_native_video_window_id(linphonec,window_id); } linphone_core_enable_video_preview(linphonec,preview_enabled); if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n"); #ifdef HAVE_READLINE /* * Initialize readline */ linphonec_initialize_readline(); #endif #if !defined(_WIN32_WCE) /* * Initialize signal handlers */ signal(SIGTERM, linphonec_finish); signal(SIGINT, linphonec_finish); #endif /*_WIN32_WCE*/ return 1; } void linphonec_main_loop_exit(void){ linphonec_running=FALSE; } /* * Close linphonec, cleanly terminating * any pending call */ void linphonec_finish(int exit_status) { // Do not allow concurrent destroying to prevent glibc errors static bool_t terminating=FALSE; if (terminating) return; terminating=TRUE; linphonec_out("Terminating...\n"); /* Terminate any pending call */ linphone_core_terminate_all_calls(linphonec); #ifdef HAVE_READLINE linphonec_finish_readline(); #endif #if !defined(_WIN32_WCE) if (pipe_reader_run) stop_pipe_reader(); #endif /*_WIN32_WCE*/ linphone_core_unref (linphonec); if (mylogfile != NULL && mylogfile != stdout) { fclose (mylogfile); mylogfile=stdout; } printf("\n"); exit(exit_status); } /* * This is called from idle_call() whenever * pending_auth != NULL. * * It prompts user for a password. * Hitting ^D (EOF) would make this function * return 0 (Cancel). * Any other input would try to set linphone core * auth_password for the pending_auth, add the auth_info * and return 1. */ int linphonec_prompt_for_auth_final(LinphoneCore *lc) { static int reentrancy=0; char *input, *iptr; char auth_prompt[256]; #ifdef HAVE_READLINE rl_hook_func_t *old_event_hook; #endif LinphoneAuthInfo *pending_auth; if (reentrancy!=0) return 0; reentrancy++; pending_auth=auth_stack.elem[auth_stack.nitems-1]; snprintf(auth_prompt, 256, "Password for %s on %s: ", pending_auth->username, pending_auth->realm); printf("\n"); #ifdef HAVE_READLINE /* * Disable event hook to avoid entering an * infinite loop. This would prevent idle_call * from being called during authentication reads. * Note that it might be undesiderable... */ old_event_hook=rl_event_hook; rl_event_hook=NULL; #endif while (1) { input=linphonec_readline(auth_prompt); /* * If EOF (^D) is sent you probably don't want * to provide an auth password... should give up * the operation, but there's no mechanism to * send this info back to caller currently... */ if ( ! input ) { printf("Cancel requested, but not implemented.\n"); continue; } /* Strip blanks */ iptr=lpc_strip_blanks(input); /* * Only blanks, continue asking */ if ( ! *iptr ) { free(input); continue; } /* Something typed, let's try */ break; } /* * No check is done here to ensure password is correct. * I guess password will be asked again later. */ linphone_auth_info_set_passwd(pending_auth, input); linphone_core_add_auth_info(lc, pending_auth); linphone_auth_info_unref(pending_auth); auth_stack.elem[auth_stack.nitems-1]=0; --(auth_stack.nitems); #ifdef HAVE_READLINE /* * Reset line_buffer, to avoid the password * to be used again from outer readline */ rl_line_buffer[0]='\0'; rl_event_hook=old_event_hook; #endif return 1; } void print_usage (int exit_status) { fprintf (stdout, "\n" "usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n" "linphonec -v\n" "\n" " -b file specify path of readonly factory configuration file.\n" " -c file specify path of configuration file.\n" " -d level be verbose. 0 is no output. 6 is all output\n" " -l logfile specify the log file for your SIP phone\n" " -s sipaddress specify the sip call to do at startup\n" " -a enable auto answering for incoming calls\n" " --real-early-media enable sending early media using real audio/video (beware of privacy issue)\n" " -V enable video features globally (disabled by default)\n" " -C enable video capture only (disabled by default)\n" " -D enable video display only (disabled by default)\n" " -S show general state messages (disabled by default)\n" " --wid windowid force embedding of video window into provided windowid (disabled by default)\n" " -v or --version display version and exits.\n" ); exit(exit_status); } #ifdef VIDEO_ENABLED #ifdef HAVE_X11_XLIB_H static void x11_apply_video_params(VideoParams *params, Window window){ XWindowChanges wc; unsigned int flags=0; static Display *display = NULL; const char *dname=getenv("DISPLAY"); if (display==NULL && dname!=NULL){ XInitThreads(); display=XOpenDisplay(dname); } if (display==NULL){ ms_error("Could not open display %s",dname); return; } memset(&wc,0,sizeof(wc)); wc.x=params->x; wc.y=params->y; wc.width=params->w; wc.height=params->h; if (params->x!=-1 ){ flags|=CWX|CWY; } if (params->w!=-1){ flags|=CWWidth|CWHeight; } /*printf("XConfigureWindow x=%i,y=%i,w=%i,h=%i\n", wc.x, wc.y ,wc.width, wc.height);*/ XConfigureWindow(display,window,flags,&wc); if (params->show) XMapWindow(display,window); else XUnmapWindow(display,window); XSync(display,FALSE); } #endif static void lpc_apply_video_params(void){ static void *old_wid=NULL; static void *old_pwid=NULL; void *wid=linphone_core_get_native_video_window_id(linphonec); void *pwid=linphone_core_get_native_preview_window_id(linphonec); if (wid!=NULL && (lpc_video_params.refresh || old_wid!=wid)){ lpc_video_params.refresh=FALSE; #ifdef HAVE_X11_XLIB_H if (lpc_video_params.wid==0){ // do not manage window if embedded x11_apply_video_params(&lpc_video_params,(Window)wid); } else { linphone_core_show_video(linphonec, lpc_video_params.show); } #endif } old_wid=wid; if (pwid!=NULL && (lpc_preview_params.refresh || old_pwid!=pwid)){ lpc_preview_params.refresh=FALSE; #ifdef HAVE_X11_XLIB_H /*printf("wid=%p pwid=%p\n",wid,pwid);*/ if (lpc_preview_params.wid==NULL){ // do not manage window if embedded printf("Refreshing\n"); x11_apply_video_params(&lpc_preview_params,(Window)pwid); } #endif } old_pwid=pwid; } #endif /* * * Called every second from main read loop. * * Will use the following globals: * * - LinphoneCore linphonec * - LPC_AUTH_STACK auth_stack; * */ static int linphonec_idle_call () { LinphoneCore *opm=linphonec; /* Uncomment the following to verify being called */ /* printf(".\n"); */ linphone_core_iterate(opm); if (answer_call){ fprintf (stdout, "-------auto answering to call-------\n" ); linphone_core_accept_call(opm,NULL); answer_call=FALSE; } /* auto call handling */ if (sip_addr_to_call != NULL ) { char buf[512]; snprintf (buf, sizeof(buf),"call %s", sip_addr_to_call); sip_addr_to_call=NULL; linphonec_parse_command_line(linphonec, buf); } if ( auth_stack.nitems ) { /* * Inhibit command completion * during password prompts */ #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif linphonec_prompt_for_auth_final(opm); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } #ifdef VIDEO_ENABLED lpc_apply_video_params(); #endif return 0; } #ifdef HAVE_READLINE /* * Use globals: * * - char *histfile_name (also sets this) * - char *last_in_history (allocates it) */ static int linphonec_initialize_readline() { /*rl_bind_key('\t', rl_insert);*/ /* Allow conditional parsing of ~/.inputrc */ rl_readline_name = "linphonec"; /* Call idle_call() every second */ rl_set_keyboard_input_timeout(LPC_READLINE_TIMEOUT); rl_event_hook=linphonec_idle_call; /* Set history file and read it */ histfile_name = ms_strdup_printf ("%s/.linphonec_history", getenv("HOME")); read_history(histfile_name); /* Initialized last_in_history cache*/ last_in_history[0] = '\0'; /* Register a completion function */ rl_attempted_completion_function = linephonec_readline_completion; /* printf("Readline initialized.\n"); */ setlinebuf(stdout); return 0; } /* * Uses globals: * * - char *histfile_name (writes history to file and frees it) * - char *last_in_history (frees it) * */ static int linphonec_finish_readline() { stifle_history(HISTSIZE); write_history(histfile_name); free(histfile_name); histfile_name=NULL; return 0; } #endif static void print_prompt(LinphoneCore *opm){ #ifdef IDENTITY_AS_PROMPT snprintf(prompt, PROMPT_MAX_LEN, "%s> ", linphone_core_get_primary_contact(opm)); #else snprintf(prompt, PROMPT_MAX_LEN, "linphonec> "); #endif } static int linphonec_main_loop (LinphoneCore * opm) { char *input; print_prompt(opm); while (linphonec_running && (input=linphonec_readline(prompt))) { char *iptr; /* input and input pointer */ size_t input_len; /* Strip blanks */ iptr=lpc_strip_blanks(input); input_len = strlen(iptr); /* * Do nothing but release memory * if only blanks are read */ if ( ! input_len ) { free(input); continue; } #ifdef HAVE_READLINE /* * Only add to history if not already * last item in it, and only if the command * doesn't start with a space (to allow for * hiding passwords) */ if ( iptr == input && strcmp(last_in_history, iptr) ) { strncpy(last_in_history,iptr,sizeof(last_in_history)); last_in_history[sizeof(last_in_history)-1]='\0'; add_history(iptr); } #endif linphonec_parse_command_line(linphonec, iptr); linphonec_command_finished(); free(input); } return 0; } /* * Parse command line switches * * Use globals: * * - int trace_level * - char *logfile_name * - char *configfile_name * - char *sipAddr */ static int linphonec_parse_cmdline(int argc, char **argv) { int arg_num=1; while (arg_num < argc) { int old_arg_num = arg_num; if (strncmp ("-d", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) trace_level = atoi (argv[arg_num]); else trace_level = 1; } else if (strncmp ("-l", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) logfile_name = argv[arg_num]; } else if (strncmp ("-c", argv[arg_num], 2) == 0) { if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); #ifdef _MSC_VER if (strcmp(argv[arg_num], "NUL") != 0) { #endif #if !defined(_WIN32_WCE) if (bctbx_file_exist(argv[arg_num]) != 0) { fprintf(stderr, "Cannot open config file %s.\n", argv[arg_num]); exit(EXIT_FAILURE); } #endif /*_WIN32_WCE*/ #ifdef _MSC_VER } #endif snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]); } else if (strncmp ("-b", argv[arg_num], 2) == 0) { if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); #if !defined(_WIN32_WCE) if (bctbx_file_exist(argv[arg_num])!=0 ) { fprintf (stderr, "Cannot open config file %s.\n", argv[arg_num]); exit(EXIT_FAILURE); } #endif /*_WIN32_WCE*/ factory_configfile_name = argv[arg_num]; } else if (strncmp ("-s", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) sip_addr_to_call = argv[arg_num]; } else if (strncmp ("-a", argv[arg_num], 2) == 0) { auto_answer = TRUE; } else if (strncmp ("--real-early-media", argv[arg_num], strlen("--real-early-media")) == 0) { real_early_media_sending = TRUE; } else if (strncmp ("-C", argv[arg_num], 2) == 0) { vcap_enabled = TRUE; } else if (strncmp ("-D", argv[arg_num], 2) == 0) { display_enabled = TRUE; } else if (strncmp ("-V", argv[arg_num], 2) == 0) { display_enabled = TRUE; vcap_enabled = TRUE; preview_enabled=TRUE; } else if ((strncmp ("-v", argv[arg_num], 2) == 0) || (strncmp ("--version", argv[arg_num], strlen ("--version")) == 0)) { #if !defined(_WIN32_WCE) printf ("version: " LINPHONE_VERSION "\n"); #endif exit (EXIT_SUCCESS); } else if (strncmp ("-S", argv[arg_num], 2) == 0) { show_general_state = TRUE; } else if (strncmp ("--pipe", argv[arg_num], 6) == 0) { unix_socket=1; } else if (strncmp ("--wid", argv[arg_num], 5) == 0) { arg_num++; if (arg_num < argc) { char *tmp; window_id = INT_TO_VOIDPTR((int)strtol( argv[arg_num], &tmp, 0 )); lpc_video_params.wid = window_id; } } else if (old_arg_num == arg_num) { fprintf (stderr, "ERROR: bad arguments\n"); print_usage (EXIT_FAILURE); } arg_num++; } return 1; } /* * Up to version 1.2.1 linphone used ~/.linphonec for * CLI and ~/.gnome2/linphone for GUI as configuration file. * In newer version both interfaces will use ~/.linphonerc. * * This function helps transparently migrating from one * to the other layout using the following heuristic: * * IF new_config EXISTS => do nothing * ELSE IF old_cli_config EXISTS => copy to new_config * ELSE IF old_gui_config EXISTS => copy to new_config * * Returns: * 0 if it did nothing * 1 if it migrated successfully * -1 on error */ static int handle_configfile_migration() { #if !defined(_WIN32_WCE) char *old_cfg_gui; char *old_cfg_cli; char *new_cfg; const char *home = getenv("HOME"); new_cfg = ms_strdup_printf("%s/.linphonerc", home); /* * If the *NEW* configuration already exists * do nothing. */ if (bctbx_file_exist(new_cfg)==0) { free(new_cfg); return 0; } old_cfg_cli = ms_strdup_printf("%s/.linphonec", home); /* * If the *OLD* CLI configurations exist copy it to * the new file and make it a symlink. */ if (bctbx_file_exist(old_cfg_cli)==0) { if ( ! copy_file(old_cfg_cli, new_cfg) ) { free(old_cfg_cli); free(new_cfg); return -1; } printf("%s copied to %s\n", old_cfg_cli, new_cfg); free(old_cfg_cli); free(new_cfg); return 1; } free(old_cfg_cli); old_cfg_gui = ms_strdup_printf("%s/.gnome2/linphone", home); /* * If the *OLD* GUI configurations exist copy it to * the new file and make it a symlink. */ if (bctbx_file_exist(old_cfg_gui)==0) { if ( ! copy_file(old_cfg_gui, new_cfg) ) { exit(EXIT_FAILURE); free(old_cfg_gui); free(new_cfg); return -1; } printf("%s copied to %s\n", old_cfg_gui, new_cfg); free(old_cfg_gui); free(new_cfg); return 1; } free(old_cfg_gui); free(new_cfg); #endif /*_WIN32_WCE*/ return 0; } #if !defined(_WIN32_WCE) /* * Copy file "from" to file "to". * Destination file is truncated if existing. * Return 1 on success, 0 on error (printing an error). */ static int copy_file(const char *from, const char *to) { char message[256]; FILE *in, *out; char buf[256]; size_t n; /* Open "from" file for reading */ in=fopen(from, "r"); if ( in == NULL ) { snprintf(message, 255, "Can't open %s for reading: %s\n", from, strerror(errno)); fprintf(stderr, "%s", message); return 0; } /* Open "to" file for writing (will truncate existing files) */ out=fopen(to, "w"); if ( out == NULL ) { snprintf(message, 255, "Can't open %s for writing: %s\n", to, strerror(errno)); fprintf(stderr, "%s", message); fclose(in); return 0; } /* Copy data from "in" to "out" */ while ( (n=fread(buf, 1, sizeof buf, in)) > 0 ) { if ( ! fwrite(buf, 1, n, out) ) { fclose(in); fclose(out); return 0; } } fclose(in); fclose(out); return 1; } #endif /*_WIN32_WCE*/ #ifdef HAVE_READLINE static char ** linephonec_readline_completion(const char *text, int start, int end) { char **matches = NULL; /* * Prevent readline from falling * back to filename-completion */ rl_attempted_completion_over=1; /* * If this is the start of line we complete with commands */ if ( ! start ) { return rl_completion_matches(text, linphonec_command_generator); } /* * Otherwise, we should peek at command name * or context to implement a smart completion. * For example: "call .." could return * friends' sip-uri as matches */ return matches; } #endif /* * Strip blanks from a string. * Return a pointer into the provided string. * Modifies input adding a NULL at first * of trailing blanks. */ char * lpc_strip_blanks(char *input) { char *iptr; /* Find first non-blank */ while(*input && isspace(*input)) ++input; /* Find last non-blank */ iptr=input+strlen(input); if (iptr > input) { while(isspace(*--iptr)); *(iptr+1)='\0'; } return input; } /**************************************************************************** * * $Log: linphonec.c,v $ * Revision 1.57 2007/11/14 13:40:27 smorlat * fix --disable-video build. * * Revision 1.56 2007/09/26 14:07:27 fixkowalski * - ANSI/C++ compilation issues with non-GCC compilers * - Faster epm-based packaging * - Ability to build & run on FC6's eXosip/osip * * Revision 1.55 2007/09/24 16:01:58 smorlat * fix bugs. * * Revision 1.54 2007/08/22 14:06:11 smorlat * authentication bugs fixed. * * Revision 1.53 2007/02/13 21:31:01 smorlat * added patch for general state. * new doxygen for oRTP * gtk-doc removed. * * Revision 1.52 2007/01/10 14:11:24 smorlat * add --video to linphonec. * * Revision 1.51 2006/08/21 12:49:59 smorlat * merged several little patches. * * Revision 1.50 2006/07/26 08:17:28 smorlat * fix bugs. * * Revision 1.49 2006/07/17 18:45:00 smorlat * support for several event queues in ortp. * glib dependency removed from coreapi/ and console/ * * Revision 1.48 2006/04/09 12:45:32 smorlat * linphonec improvements. * * Revision 1.47 2006/04/04 08:04:34 smorlat * switched to mediastreamer2, most bugs fixed. * * Revision 1.46 2006/03/16 17:17:40 smorlat * fix various bugs. * * Revision 1.45 2006/03/12 21:48:31 smorlat * gcc-2.95 compile error fixed. * mediastreamer2 in progress * * Revision 1.44 2006/03/04 11:17:10 smorlat * mediastreamer2 in progress. * * Revision 1.43 2006/02/13 09:50:50 strk * fixed unused variable warning. * * Revision 1.42 2006/02/02 15:39:18 strk * - Added 'friend list' and 'friend call' commands * - Allowed for multiple DTFM send in a single line * - Added status-specific callback (bare version) * * Revision 1.41 2006/02/02 13:30:05 strk * - Padded vtable with missing callbacks * (fixing a segfault on friends subscription) * - Handled friends notify (bare version) * - Handled text messages receive (bare version) * - Printed message on subscription request (bare version) * * Revision 1.40 2006/01/26 09:48:05 strk * Added limits.h include * * Revision 1.39 2006/01/26 02:11:01 strk * Removed unused variables, fixed copyright date * * Revision 1.38 2006/01/25 18:33:02 strk * Removed the -t swich, terminate_on_close made the default behaviour * * Revision 1.37 2006/01/20 14:12:34 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.36 2006/01/18 09:25:32 strk * Command completion inhibited in proxy addition and auth request prompts. * Avoided use of readline's internal filename completion. * * Revision 1.35 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.34 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/ linphone-3.12.0/console/linphonec.h000066400000000000000000000123741313432737600172230ustar00rootroot00000000000000/**************************************************************************** * * $Id: linphonec.h,v 1.3 2006/01/20 14:12:34 strk Exp $ * * Copyright (C) 2005 Sandro Santilli * **************************************************************************** * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ #ifndef LINPHONEC_H #define LINPHONEC_H 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_READLINE #ifdef HAVE_READLINE_H #include #else #ifdef HAVE_READLINE_READLINE_H #include #endif #endif #ifdef HAVE_HISTORY_H #include #else #ifdef HAVE_READLINE_HISTORY_H #include #endif #endif #endif #undef PARAMS #define INT_TO_VOIDPTR(i) ((void*)(intptr_t)(i)) #define VOIDPTR_TO_INT(p) ((int)(intptr_t)(p)) /************************************************************************** * * Compile-time defines * **************************************************************************/ #define HISTSIZE 500 /* how many lines of input history */ #define PROMPT_MAX_LEN 256 /* max len of prompt string */ #define LINE_MAX_LEN 256 /* really needed ? */ /* * Define this to have your primary contact * as the prompt string */ /* #define IDENTITY_AS_PROMPT 1 */ /* * Time between calls to linphonec_idle_call during main * input read loop in microseconds. */ #define LPC_READLINE_TIMEOUT 10000 /* * Filename of linphonec history */ #define LPC_HIST_FILE "~/.linphonec_history" /* * Maximum number of pending authentications */ #define MAX_PENDING_AUTH 8 /************************************************************************** * * Types * **************************************************************************/ /* * A structure which contains information on the commands this program * can understand. */ typedef int (*lpc_cmd_handler)(LinphoneCore *, char *); typedef struct { char *name; /* User printable name of the function. */ lpc_cmd_handler func; /* Function to call to do the job. */ char *help; /* Short help for this function. */ char *doc; /* Long description. */ } LPC_COMMAND; typedef struct { int x,y,w,h; void *wid; bool_t show; bool_t refresh; } VideoParams; extern VideoParams lpc_video_params; extern VideoParams lpc_preview_params; /*************************************************************************** * * Forward declarations * ***************************************************************************/ extern int linphonec_parse_command_line(LinphoneCore *lc, char *cl); extern char *linphonec_command_generator(const char *text, int state); void linphonec_main_loop_exit(void); extern void linphonec_finish(int exit_status); extern char *linphonec_readline(char *prompt); void linphonec_set_autoanswer(bool_t enabled); bool_t linphonec_get_autoanswer(void); void linphonec_command_finished(void); void linphonec_set_caller(const char *caller); LinphoneCall *linphonec_get_call(int id); void linphonec_call_identify(LinphoneCall* call); extern bool_t linphonec_camera_enabled; #endif /* def LINPHONEC_H */ /**************************************************************************** * * $Log: linphonec.h,v $ * Revision 1.3 2006/01/20 14:12:34 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.2 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.1 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/ linphone-3.12.0/console/shell.c000066400000000000000000000240321313432737600163400ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009 Simon MORLAT * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #include #include #include #include #ifdef _WIN32 #include #include #include #include #include #else #include #include #include #endif #include "ortp/ortp.h" #include #define DEFAULT_REPLY_SIZE 4096 #define STATUS_REGISTERED (1<<0) #define STATUS_REGISTERING (1<<1) #define STATUS_DIALING (1<<2) #define STATUS_AUTOANSWER (1<<3) #define STATUS_IN_CONNECTED (1<<4) /* incoming call accepted */ #define STATUS_OUT_CONNECTED (1<<5) /*outgoing call accepted */ #define STATUS_IN_COMING (1<<6) /*incoming call pending */ static int make_status_value(const char *status_string){ int ret=0; if (strstr(status_string,"registered, identity=")){ ret|=STATUS_REGISTERED; } if (strstr(status_string,"registered=-1")){ ret|=STATUS_REGISTERING; } if (strstr(status_string,"autoanswer=1")){ ret|=STATUS_AUTOANSWER; } if (strstr(status_string,"dialing")){ ret|=STATUS_DIALING; } if (strstr(status_string,"Call out")){ ret|=STATUS_OUT_CONNECTED; } if (strstr(status_string,"hook=answered")){ ret|=STATUS_IN_CONNECTED; } if (strstr(status_string,"Incoming call from ")){ ret|=STATUS_IN_COMING; } return ret; } static int send_command(const char *command, char *reply, int reply_len, int print_errors){ ortp_pipe_t pp; int i; int err; char path[128]; #ifndef _WIN32 snprintf(path,sizeof(path)-1,"linphonec-%i",getuid()); #else { char username[128]; DWORD size=sizeof(username)-1; GetUserName(username,&size); snprintf(path,sizeof(path)-1,"linphonec-%s",username); } #endif if ((pp=ortp_client_pipe_connect(path))==ORTP_PIPE_INVALID){ if (print_errors) fprintf(stderr,"ERROR: Failed to connect pipe: %s\n",strerror(errno)); return -1; } if (ortp_pipe_write(pp,(uint8_t*)command,strlen(command))==-1){ if (print_errors) fprintf(stderr,"ERROR: Fail to send command to remote linphonec\n"); ortp_client_pipe_close(pp); return -1; } /*wait for replies */ i=0; while ((err=ortp_pipe_read(pp,(uint8_t*)&reply[i],reply_len-i-1))>0){ i+=err; } reply[i]='\0'; ortp_client_pipe_close(pp); return 0; } static void print_usage(void){ fprintf(stderr,"Usage:\nlinphonecsh [arguments]\n" "where action is one of\n" "\tinit\t\t: spawn a linphonec daemon (first step to make other actions)\n" "\t\t\tfollowed by the arguments sent to linphonec\n" "\tgeneric\t\t: sends a generic command to the running linphonec daemon\n" "\t\t\tfollowed by the generic command surrounded by quotes,\n\t\t\t for example \"call sip:joe@example.net\"\n" "\tregister\t: register; arguments are \n\t\t\t--host \n\t\t\t--username \n\t\t\t--password \n" "\tunregister\t: unregister\n" "\tdial\t\t: dial \n" "\tstatus\t\t: can be 'status register', 'status autoanswer' or 'status hook'\n" "\tsoundcard\t: can be 'soundcard capture', 'soundcard playback', 'soundcard ring',\n" "\t\t\t followed by an optional number representing the index of the soundcard,\n" "\t\t\t in which case the soundcard is set instead of just read.\n" "\texit\t\t: make the linphonec daemon to exit.\n" ); exit(-1); } #ifdef _WIN32 static char *argv_to_line(int argc, char *argv[]) { int i; int line_length; char *line; assert( argc>=0 ); if(argc == 0) return NULL; line_length = strlen(argv[0]); for(i=1; i #include "sipomatic.h" #include int run_cond=1; Sipomatic sipomatic; int sipomatic_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload); int sipomatic_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload); sdp_handler_t sipomatic_sdp_handler={ sipomatic_accept_audio_offer, /*from remote sdp */ sipomatic_accept_video_offer, NULL, NULL, NULL, NULL, }; void stop_handler(int signum) { run_cond=0; } void sipomatic_process_event(Sipomatic *obj,eXosip_event_t *ev) { Call *call; switch(ev->type){ case EXOSIP_CALL_INVITE: call_new(obj,ev); break; case EXOSIP_CALL_CLOSED: case EXOSIP_CALL_CANCELLED: call=sipomatic_find_call(obj,ev->did); if (call==NULL){ ms_warning("Could not find call with did %i !",ev->did); } call_release(call); call_destroy(call); break; default: break; } eXosip_event_free(ev); } void endoffile_cb(void *ud, MSFilter *f, unsigned int ev,void * arg){ Call*call=(Call*)ud; call->eof=1; } void call_accept(Call *call) { sdp_context_t *ctx; PayloadType *payload; char *hellofile; static int call_count=0; char record_file[250]; osip_message_t *msg=NULL; sprintf(record_file,"/tmp/sipomatic%i.wav",call_count); ctx=call->sdpc; payload=rtp_profile_get_payload(call->profile,call->audio.pt); if (strcmp(payload->mime_type,"telephone-event")==0){ /* telephone-event is not enough to accept a call */ ms_message("Cannot accept call with only telephone-event.\n"); eXosip_call_send_answer(call->did,415,NULL); call->state=CALL_STATE_FINISHED; return; } if (payload->clock_rate==16000){ hellofile=call->root->file_path16000hz; }else hellofile=call->root->file_path8000hz; eXosip_call_build_answer(call->tid,200,&msg); osip_message_set_content_type(msg,"application/sdp"); osip_message_set_body(msg,call->sdpc->answerstr,strlen(call->sdpc->answerstr)); eXosip_call_send_answer(call->tid,200,msg); call->audio_stream=audio_stream_new(call->audio.localport,call->audio.localport+1,call->root->ipv6); audio_stream_start_with_files(call->audio_stream, call->profile, call->audio.remaddr,call->audio.remoteport,call->audio.remoteport+1, call->audio.pt,20,hellofile,record_file); call_count++; #ifdef VIDEO_ENABLED if (call->video.remoteport!=0){ video_stream_send_only_start(call->video_stream,call->profile, call->video.remaddr,call->video.remoteport,call->video.remoteport+1,call->video.pt, 60, ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get())); } #endif call->time=time(NULL); call->state=CALL_STATE_RUNNING; ms_filter_set_notify_callback(call->audio_stream->soundread,endoffile_cb,(void*)call); } PayloadType * sipomatic_payload_is_supported(sdp_payload_t *payload,RtpProfile *local_profile,RtpProfile *dialog_profile) { int localpt; if (payload->a_rtpmap!=NULL){ localpt=rtp_profile_get_payload_number_from_rtpmap(local_profile,payload->a_rtpmap); }else{ localpt=payload->pt; ms_warning("payload has no rtpmap."); } if (localpt>=0){ /* this payload is supported in our local rtp profile, so add it to the dialog rtp profile */ PayloadType *rtppayload; rtppayload=rtp_profile_get_payload(local_profile,localpt); if (rtppayload==NULL) return NULL; /*check if we have the appropriate coder/decoder for this payload */ if (strcmp(rtppayload->mime_type,"telephone-event")!=0) { if (!ms_filter_codec_supported(rtppayload->mime_type)) { ms_message("Codec %s is not supported.", rtppayload->mime_type); return NULL; } } rtppayload=payload_type_clone(rtppayload); rtp_profile_set_payload(dialog_profile,payload->pt,rtppayload); /* add to the rtp payload type some other parameters (bandwidth) */ if (payload->b_as_bandwidth!=0) rtppayload->normal_bitrate=payload->b_as_bandwidth*1000; if (payload->a_fmtp!=NULL) payload_type_set_send_fmtp(rtppayload,payload->a_fmtp); if (strcasecmp(rtppayload->mime_type,"iLBC")==0){ /*default to 30 ms mode */ payload->a_fmtp="ptime=30"; payload_type_set_recv_fmtp(rtppayload,payload->a_fmtp); } return rtppayload; } return NULL; } int sipomatic_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload) { static int audioport=8000; Call *call=(Call*)sdp_context_get_user_pointer(ctx); PayloadType *supported; struct stream_params *params=&call->audio; /* see if this codec is supported in our local rtp profile*/ supported=sipomatic_payload_is_supported(payload,&av_profile,call->profile); if (supported==NULL) { ms_message("Refusing codec %i (%s)",payload->pt,payload->a_rtpmap); return -1; } if (strcmp(supported->mime_type,"telephone-event")==0) return 0; if (params->ncodecs==0 ){ /* this is the first codec we may accept*/ params->localport=payload->localport=audioport; params->remoteport=payload->remoteport; params->line=payload->line; params->pt=payload->pt; /* remember the first payload accepted */ params->remaddr=payload->c_addr; params->ncodecs++; audioport+=4; }else{ /* refuse all other audio lines*/ if(params->line!=payload->line) return -1; } return 0; } int sipomatic_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload) { #ifdef VIDEO_ENABLED static int videoport=80000; Call *call=(Call*)sdp_context_get_user_pointer(ctx); PayloadType *supported; struct stream_params *params=&call->video; /* see if this codec is supported in our local rtp profile*/ supported=sipomatic_payload_is_supported(payload,&av_profile,call->profile); if (supported==NULL) { ms_message("Refusing video codec %i (%s)",payload->pt,payload->a_rtpmap); return -1; } if (params->ncodecs==0 ){ /* this is the first codec we may accept*/ params->localport=payload->localport=videoport; params->remoteport=payload->remoteport; params->line=payload->line; params->pt=payload->pt; /* remember the first payload accepted */ params->remaddr=payload->c_addr; params->ncodecs++; videoport+=4; }else{ /* refuse all other video lines*/ if(params->line!=payload->line) return -1; } return 0; #else return -1; #endif } void sipomatic_init(Sipomatic *obj, char *url, bool_t ipv6) { osip_uri_t *uri=NULL; int port=5064; obj->ipv6=ipv6; if (url==NULL){ url=getenv("SIPOMATIC_URL"); if (url==NULL){ if (ipv6) url="sip:robot@[::1]:5064"; else url="sip:robot@127.0.0.1:5064"; } } if (url!=NULL) { osip_uri_init(&uri); if (osip_uri_parse(uri,url)==0){ if (uri->port!=NULL) port=atoi(uri->port); }else{ ms_warning("Invalid identity uri:%s",url); } } ms_message("Starting using url %s",url); ms_mutex_init(&obj->lock,NULL); obj->calls=NULL; obj->acceptance_time=5; obj->max_call_time=300; obj->file_path8000hz=ms_strdup_printf("%s/%s",PACKAGE_SOUND_DIR,ANNOUCE_FILE8000HZ); obj->file_path16000hz=ms_strdup_printf("%s/%s",PACKAGE_SOUND_DIR,ANNOUCE_FILE16000HZ); osip_trace_initialize(OSIP_INFO1,stdout); osip_trace_initialize(OSIP_INFO2,stdout); osip_trace_initialize(OSIP_WARNING,stdout); osip_trace_initialize(OSIP_ERROR,stdout); osip_trace_initialize(OSIP_BUG,stdout); osip_trace_initialize(OSIP_FATAL,stdout); osip_trace_enable_level(OSIP_INFO1); osip_trace_enable_level(OSIP_INFO2); osip_trace_enable_level(OSIP_WARNING); osip_trace_enable_level(OSIP_ERROR); osip_trace_enable_level(OSIP_BUG); osip_trace_enable_level(OSIP_FATAL); eXosip_init(); eXosip_set_user_agent("sipomatic-" LINPHONE_VERSION "/eXosip"); eXosip_listen_addr(IPPROTO_UDP,NULL,port,ipv6 ? AF_INET6 : AF_INET,0); } void sipomatic_uninit(Sipomatic *obj) { ms_mutex_destroy(&obj->lock); eXosip_quit(); } void sipomatic_iterate(Sipomatic *obj) { bctbx_list_t *elem; bctbx_list_t *to_be_destroyed=NULL; Call *call; double elapsed; eXosip_event_t *ev; while((ev=eXosip_event_wait(0,0))!=NULL){ sipomatic_process_event(obj,ev); } elem=obj->calls; while(elem!=NULL){ call=(Call*)elem->data; elapsed=time(NULL)-call->time; switch(call->state){ case CALL_STATE_INIT: if (elapsed>obj->acceptance_time){ call_accept(call); } break; case CALL_STATE_RUNNING: if (elapsed>obj->max_call_time || call->eof){ call_release(call); to_be_destroyed=bctbx_list_append(to_be_destroyed,call); } break; } elem=bctbx_list_next(elem); } for(;to_be_destroyed!=NULL; to_be_destroyed=bctbx_list_next(to_be_destroyed)){ call_destroy((Call*)to_be_destroyed->data); } } Call* sipomatic_find_call(Sipomatic *obj,int did) { bctbx_list_t *it; Call *call=NULL; for (it=obj->calls;it!=NULL;it=bctbx_list_next(it)){ call=(Call*)it->data; if ( call->did==did) return call; } return call; } Call * call_new(Sipomatic *root, eXosip_event_t *ev) { Call *obj; char *sdpans; int status; sdp_message_t *sdp; sdp_context_t *sdpc; sdp=eXosip_get_sdp_info(ev->request); sdpc=sdp_handler_create_context(&sipomatic_sdp_handler,NULL,"sipomatic",NULL); obj=ms_new0(Call,1); obj->profile=rtp_profile_new("remote"); eXosip_call_send_answer(ev->tid,100,NULL); sdp_context_set_user_pointer(sdpc,obj); sdpans=sdp_context_get_answer(sdpc,sdp); if (sdpans!=NULL){ eXosip_call_send_answer(ev->tid,180,NULL); }else{ status=sdp_context_get_status(sdpc); eXosip_call_send_answer(ev->tid,status,NULL); sdp_context_free(sdpc); rtp_profile_destroy(obj->profile); ms_free(obj); return NULL; } obj->sdpc=sdpc; obj->did=ev->did; obj->tid=ev->tid; obj->time=time(NULL); obj->audio_stream=NULL; obj->state=CALL_STATE_INIT; obj->eof=0; obj->root=root; root->calls=bctbx_list_append(root->calls,obj); return obj; } void call_release(Call *call) { eXosip_call_terminate(0,call->did); if (call->audio_stream!=NULL) audio_stream_stop(call->audio_stream); #ifdef VIDEO_ENABLED if (call->video_stream!=NULL) video_stream_send_only_stop(call->video_stream); #endif call->state=CALL_STATE_FINISHED; } void call_destroy(Call *obj) { obj->root->calls=bctbx_list_remove(obj->root->calls,obj); rtp_profile_destroy(obj->profile); sdp_context_free(obj->sdpc); ms_free(obj); } void sipomatic_set_annouce_file(Sipomatic *obj, char *file) { if (obj->file_path8000hz!=NULL){ ms_free(obj->file_path8000hz); } obj->file_path8000hz=ms_strdup(file); } void display_help() { printf("sipomatic [-u sip-url] [-f annouce-file ] [-s port]\n" "sipomatic -h or --help: display this help.\n" "sipomatic -v or --version: display version information.\n" " -u sip-url : specify the sip url sipomatic listens and answers.\n" " -f annouce-file : set the annouce file (16 bit raw format,8000Hz)\n" " -6 enable ipv6 network usage\n"); exit(0); } char *getarg(int argc, char*argv[], int i) { if (i #undef PACKAGE #undef VERSION #include "mediastreamer2/mediastream.h" #include #include #define ANNOUCE_FILE8000HZ "hello8000.wav" #define ANNOUCE_FILE16000HZ "hello16000.wav" struct _Sipomatic { ms_mutex_t lock; MSList *calls; double acceptance_time; double max_call_time; char *file_path8000hz; char *file_path16000hz; bool_t ipv6; }; typedef struct _Sipomatic Sipomatic; void sipomatic_init(Sipomatic *obj, char *url, bool_t ipv6); void sipomatic_uninit(Sipomatic *obj); void sipomatic_iterate(Sipomatic *obj); #define sipomatic_lock(obj) ms_mutex_lock(&(obj)->lock); #define sipomatic_unlock(obj) ms_mutex_unlock(&(obj)->lock); void sipomatic_set_annouce_file(Sipomatic *obj, char *file); struct stream_params{ int ncodecs; int line; int localport; int remoteport; int pt; char *remaddr; }; struct _Call { Sipomatic *root; sdp_context_t *sdpc; int time; int did; int tid; AudioStream *audio_stream; #ifdef VIDEO_ENABLED VideoStream *video_stream; #endif int state; struct _CallParams *params; int eof; RtpProfile *profile; struct stream_params audio; struct stream_params video; }; #define CALL_STATE_INIT 0 #define CALL_STATE_RUNNING 1 #define CALL_STATE_FINISHED 2 typedef struct _Call Call; Call * call_new(Sipomatic *obj, eXosip_event_t *ev); void call_accept(Call *call); void call_release(Call *call); void call_destroy(Call *call); Call* sipomatic_find_call(Sipomatic *obj,int cid); linphone-3.12.0/console/wav2raw.c000066400000000000000000000022171313432737600166230ustar00rootroot00000000000000 #include "../config.h" #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int ifd,ofd; char *name,*p; char buf[200]; int len; if (argc<2) return -1; name=malloc(strlen(argv[1])+10); sprintf(name,"%s",argv[1]); p=strstr(name,".raw"); if (p!=NULL){ sprintf(p,"%s",".wav\0"); }else{ sprintf(name,"%s%s",argv[1],".raw"); } ifd=open(name,O_RDONLY); if (ifd<0) { perror("Could not open input file"); return -1; } ofd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP); if (ofd<0) { perror("Could not open output file"); return -1; } len=read(ifd,buf,20); printf("len=%i\n",len); /* erase the wav header */ if (len>0){ memset(buf,0,20); write(ofd,buf,20); }else{ printf("Error while processing %s: %s\n",argv[1],strerror(errno)); return -1; }; while ( (len=read(ifd,buf,200))>0){ #ifdef WORDS_BIGENDIAN for (i=0;i $(builddir)/$(GITVERSION_FILE_TMP) ; \ elif test "$(GITREVISION)" != "" ; then \ printf "#define LIBLINPHONE_GIT_VERSION \"$(LINPHONE_VERSION)_$(GITREVISION)\"\n" > $(builddir)/$(GITVERSION_FILE_TMP) ; \ else \ printf "" > $(builddir)/$(GITVERSION_FILE_TMP) ; \ fi ; \ if ! test -f $(builddir)/$(GITVERSION_FILE) ; then \ cp -f $(builddir)/$(GITVERSION_FILE_TMP) $(builddir)/$(GITVERSION_FILE) ; \ fi ; \ if test "`cat $(builddir)/$(GITVERSION_FILE_TMP)`" != "`cat $(builddir)/$(GITVERSION_FILE)`" ; then \ cp -f $(builddir)/$(GITVERSION_FILE_TMP) $(builddir)/$(GITVERSION_FILE) ; \ fi ; \ rm -f $(builddir)/$(GITVERSION_FILE_TMP) ; \ else \ touch $(GITVERSION_FILE) ; \ fi $(GITVERSION_FILE): make_gitversion_h linphone-3.12.0/coreapi/TunnelManager.cc000066400000000000000000000443431313432737600201230ustar00rootroot00000000000000/* * C Implementation: tunnel * * Description: * * * Author: Simon Morlat , (C) 2009 * * Copyright (C) 2010 Belledonne Comunications, Grenoble, France * */ #include "TunnelManager.hh" #include "ortp/rtpsession.h" #include "linphone/core.h" #include "linphone/core_utils.h" #include "private.h" #ifdef __ANDROID__ #include #endif belledonnecomm::TunnelManager *bcTunnel(const LinphoneTunnel *tunnel); using namespace belledonnecomm; using namespace ::std; void TunnelManager::addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay) { if (ip == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } addServer(ip,port); mUdpMirrorClients.push_back(UdpMirrorClient(ServerAddr(ip,udpMirrorPort),delay)); } void TunnelManager::addServer(const char *ip, int port) { if (ip == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } if (mUseDualClient) { ms_warning("TunnelManager is configured in dual mode, use addServerPair instead"); return; } mServerAddrs.push_back(ServerAddr(ip,port)); if (mTunnelClient && !mUseDualClient) { static_cast(mTunnelClient)->addServer(ip,port); } } void TunnelManager::addServerPair(const char *ip1, int port1, const char *ip2, int port2, unsigned int udpMirrorPort, unsigned int delay) { if (ip1 == NULL || ip2 == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } addServerPair(ip1, port1, ip2, port2); mUdpMirrorClients.push_back(UdpMirrorClient(ServerAddr(ip1, udpMirrorPort), delay)); } void TunnelManager::addServerPair(const char *ip1, int port1, const char *ip2, int port2) { if (ip1 == NULL || ip2 == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } if (!mUseDualClient) { ms_warning("TunnelManager is configured in single mode, use addServer instead"); return; } mDualServerAddrs.push_back(DualServerAddr(ip1, port1, ip2, port2)); if (mTunnelClient && mUseDualClient) { static_cast(mTunnelClient)->addServerPair(ip1, port1, ip2, port2); } } void TunnelManager::cleanServers() { mServerAddrs.clear(); mDualServerAddrs.clear(); if (mLongRunningTaskId > 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; } UdpMirrorClientList::iterator it; for (it = mUdpMirrorClients.begin(); it != mUdpMirrorClients.end();) { UdpMirrorClient& s=*it++; s.stop(); } mUdpMirrorClients.clear(); mCurrentUdpMirrorClient = mUdpMirrorClients.end(); if (mTunnelClient) mTunnelClient->cleanServers(); } void TunnelManager::enableDualMode(bool enable) { mUseDualClient = enable; } bool TunnelManager::isDualModeEnabled() { return mUseDualClient; } void TunnelManager::reconnect(){ if (mTunnelClient) mTunnelClient->reconnect(); } static void sCloseRtpTransport(RtpTransport *t){ DualSocket *ds = (DualSocket *)t->data; TunnelSocket *sendSocket = ds->sendSocket; TunnelSocket *recvSocket = ds->recvSocket; TunnelManager *manager=(TunnelManager*)sendSocket->getUserPointer(); manager->closeRtpTransport(t, sendSocket); manager->closeRtpTransport(t, recvSocket); ms_free(ds); } void TunnelManager::closeRtpTransport(RtpTransport *t, TunnelSocket *s){ mTunnelClient->closeSocket(s); } static RtpTransport *sCreateRtpTransport(void* userData, int port){ return ((TunnelManager *) userData)->createRtpTransport(port); } void sDestroyRtpTransport(RtpTransport *t){ ms_free(t); } RtpTransport *TunnelManager::createRtpTransport(int port){ DualSocket *dualSocket = ms_new0(DualSocket, 1); if (!mUseDualClient) { TunnelSocket *socket = ((TunnelClient *)mTunnelClient)->createSocket(port); socket->setUserPointer(this); dualSocket->sendSocket = socket; dualSocket->recvSocket = socket; } else { dualSocket->sendSocket = ((DualTunnelClient *)mTunnelClient)->createSocket(TunnelSendOnly, port); dualSocket->sendSocket->setUserPointer(this); dualSocket->recvSocket = ((DualTunnelClient *)mTunnelClient)->createSocket(TunnelRecvOnly, port); dualSocket->recvSocket->setUserPointer(this); } RtpTransport *t = ms_new0(RtpTransport,1); t->t_getsocket=NULL; t->t_recvfrom=customRecvfrom; t->t_sendto=customSendto; t->t_close=sCloseRtpTransport; t->t_destroy=sDestroyRtpTransport; t->data=dualSocket; ms_message("Creating tunnel RTP transport for local virtual port %i", port); return t; } void TunnelManager::startClient() { ms_message("TunnelManager: Starting tunnel client"); if (!mTunnelClient) { if (mUseDualClient) { mTunnelClient = DualTunnelClient::create(TRUE); } else { mTunnelClient = TunnelClient::create(TRUE); } sal_set_tunnel(mCore->sal, mTunnelClient); if (!mUseDualClient) { static_cast(mTunnelClient)->setCallback(tunnelCallback,this); } else { static_cast(mTunnelClient)->setCallback(tunnelCallback2,this); } } if (mVerifyServerCertificate) { const char *rootCertificatePath = linphone_core_get_root_ca(mCore); if (rootCertificatePath != NULL) { ms_message("TunnelManager: Load root certificate from %s", rootCertificatePath); mTunnelClient->setRootCertificate(rootCertificatePath); /* give the path to root certificate to the tunnel client in order to be able to verify the server certificate */ } else { ms_warning("TunnelManager is set to verify server certificate but no root certificate is available in linphoneCore"); } } mTunnelClient->cleanServers(); if (mUseDualClient) { list::iterator it; for(it=mDualServerAddrs.begin();it!=mDualServerAddrs.end();++it){ const DualServerAddr &addr=*it; static_cast(mTunnelClient)->addServerPair(addr.mAddr1.c_str(), addr.mPort1, addr.mAddr2.c_str(), addr.mPort2); } } else { list::iterator it; for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ const ServerAddr &addr=*it; static_cast(mTunnelClient)->addServer(addr.mAddr.c_str(), addr.mPort); } } mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str()); if (!mTunnelClient->isStarted()) { ms_message("Starting tunnel client"); mTunnelClient->start(); } else { ms_message("Reconnecting tunnel client"); mTunnelClient->reconnect(); /*force a reconnection to take into account new parameters*/ } } void TunnelManager::stopClient(){ if (linphone_core_get_calls_nb(mCore) == 0){ /*if no calls are running, we can decide to stop the client completely, so that the connection to the tunnel server is terminated.*/ if (mTunnelClient) { ms_message("TunnelManager: stoppping tunnel client"); mTunnelClient->stop(); } /*otherwise, it doesn't really matter if the tunnel connection is kept alive even if it is not used anymore by the liblinphone.*/ } } bool TunnelManager::isConnected() const { return mTunnelClient != NULL && mTunnelClient->isReady(); } int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen){ int size; DualSocket *ds = (DualSocket *)t->data; msgpullup(msg,-1); size=msgdsize(msg); ds->sendSocket->sendto(msg->b_rptr,size,to,tolen); return size; } int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen){ DualSocket *ds = (DualSocket *)t->data; memset(&msg->recv_addr,0,sizeof(msg->recv_addr)); int err=ds->recvSocket->recvfrom(msg->b_wptr,dblk_lim(msg->b_datap)-dblk_base(msg->b_datap),from,*fromlen); //to make ice happy inet_aton(((TunnelManager*)(ds->recvSocket)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr); msg->recv_addr.family = AF_INET; msg->recv_addr.port = htons((unsigned short)(ds->recvSocket)->getPort()); if (err>0) return err; return 0; } TunnelManager::TunnelManager(LinphoneCore* lc) : mCore(lc), mMode(LinphoneTunnelModeDisable), mTunnelClient(NULL), mHttpProxyPort(0), mVTable(NULL), mLongRunningTaskId(0), mSimulateUdpLoss(false), mUseDualClient(false) { linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this); mTransportFactories.audio_rtcp_func=sCreateRtpTransport; mTransportFactories.audio_rtcp_func_data=this; mTransportFactories.audio_rtp_func=sCreateRtpTransport; mTransportFactories.audio_rtp_func_data=this; mTransportFactories.video_rtcp_func=sCreateRtpTransport; mTransportFactories.video_rtcp_func_data=this; mTransportFactories.video_rtp_func=sCreateRtpTransport; mTransportFactories.video_rtp_func_data=this; mVTable = linphone_core_v_table_new(); mVTable->network_reachable = networkReachableCb; linphone_core_add_listener(mCore, mVTable); linphone_core_get_local_ip_for(AF_INET, NULL, mLocalAddr); mAutodetectionRunning = false; mState = Off; mTargetState = Off; mStarted = false; mTunnelizeSipPackets = true; } TunnelManager::~TunnelManager(){ if (mLongRunningTaskId > 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; } for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) { udpMirror->stop(); } stopClient(); if (mTunnelClient) { mTunnelClient->stop(); delete mTunnelClient; } sal_set_tunnel(mCore->sal,NULL); linphone_core_remove_listener(mCore, mVTable); linphone_core_v_table_destroy(mVTable); } void TunnelManager::doRegistration(){ LinphoneProxyConfig* lProxy; lProxy = linphone_core_get_default_proxy_config(mCore); if (lProxy) { ms_message("TunnelManager: New registration"); lProxy->commit = TRUE; } } void TunnelManager::doUnregistration() { LinphoneProxyConfig *lProxy; lProxy = linphone_core_get_default_proxy_config(mCore); if(lProxy) { _linphone_proxy_config_unregister(lProxy); } } void TunnelManager::tunnelizeLiblinphone(){ ms_message("LinphoneCore goes into tunneled mode."); mState = On; /*do this first because _linphone_core_apply_transports() will use it to know if tunnel listening point is to be used*/ linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories); if (mTunnelizeSipPackets) { doUnregistration(); _linphone_core_apply_transports(mCore); doRegistration(); } } void TunnelManager::untunnelizeLiblinphone(){ ms_message("LinphoneCore leaves tunneled mode."); mState = Off; linphone_core_set_rtp_transport_factories(mCore, NULL); if (mTunnelizeSipPackets) { doUnregistration(); _linphone_core_apply_transports(mCore); doRegistration(); } } void TunnelManager::applyState() { if (!linphone_core_is_network_reachable(mCore)) return; if (mTargetState == On && mState == Off){ if (!mTunnelClient || !mTunnelClient->isStarted()){ startClient(); } if (mTunnelClient->isReady()) tunnelizeLiblinphone(); }else if (mTargetState == Off && mState == On){ untunnelizeLiblinphone(); stopClient(); } } void TunnelManager::setState ( TunnelManager::State state ) { mTargetState = state; applyState(); } void TunnelManager::processTunnelEvent(const Event &ev){ if (ev.mData.mConnected){ ms_message("TunnelManager: tunnel is connected"); applyState(); } else { ms_error("TunnelManager: tunnel has been disconnected"); } } void TunnelManager::applyMode() { switch(mMode) { case LinphoneTunnelModeEnable: stopAutoDetection(); setState(On); break; case LinphoneTunnelModeDisable: stopAutoDetection(); setState(Off); break; case LinphoneTunnelModeAuto: if (linphone_core_is_network_reachable(mCore)) startAutoDetection(); break; default: ms_error("TunnelManager::setMode(): invalid mode (%d)", (int)mMode); } } void TunnelManager::setMode(LinphoneTunnelMode mode) { if(mMode == mode) return; ms_message("TunnelManager: switching mode from %s to %s", linphone_tunnel_mode_to_string(mMode), linphone_tunnel_mode_to_string(mode)); mMode = mode; applyMode(); } void TunnelManager::stopLongRunningTask() { if (mLongRunningTaskId != 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; } } void TunnelManager::tunnelCallback(bool connected, void *user_pointer){ TunnelManager *zis = static_cast(user_pointer); Event ev; ev.mType=TunnelEvent; ev.mData.mConnected=connected; zis->postEvent(ev); } void TunnelManager::tunnelCallback2(TunnelDirection direction, bool connected, void *user_pointer){ TunnelManager *zis = static_cast(user_pointer); Event ev; ev.mType=TunnelEvent; ev.mData.mConnected=connected; zis->postEvent(ev); } void TunnelManager::onIterate(){ mMutex.lock(); while(!mEvq.empty()){ Event ev=mEvq.front(); mEvq.pop(); mMutex.unlock(); if (ev.mType==TunnelEvent) processTunnelEvent(ev); else if (ev.mType==UdpMirrorClientEvent){ processUdpMirrorEvent(ev); } mMutex.lock(); } mMutex.unlock(); } /*invoked from linphone_core_iterate() */ void TunnelManager::sOnIterate(TunnelManager *zis){ zis->onIterate(); } #ifdef __ANDROID__ extern void linphone_android_log_handler(int prio, char *str); static void linphone_android_tunnel_log_handler(int lev, const char *fmt, va_list args) { char str[4096]; vsnprintf(str, sizeof(str) - 1, fmt, args); str[sizeof(str) - 1] = '\0'; int prio; switch(lev){ case TUNNEL_DEBUG: prio = ANDROID_LOG_DEBUG; break; case TUNNEL_INFO: prio = ANDROID_LOG_INFO; break; case TUNNEL_NOTICE: prio = ANDROID_LOG_INFO; break; case TUNNEL_WARN: prio = ANDROID_LOG_WARN; break; case TUNNEL_ERROR: prio = ANDROID_LOG_ERROR; break; default: prio = ANDROID_LOG_DEFAULT; break; } linphone_android_log_handler(prio, str); } #endif /* __ANDROID__ */ void TunnelManager::enableLogs(bool value) { enableLogs(value,NULL); } void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) { if (logHandler != NULL) SetLogHandler(logHandler); #ifdef __ANDROID__ else SetLogHandler(linphone_android_tunnel_log_handler); #else else SetLogHandler(tunnel_default_log_handler); #endif if (isEnabled) { SetLogLevel(TUNNEL_ERROR|TUNNEL_WARN|TUNNEL_INFO); } else { SetLogLevel(TUNNEL_ERROR|TUNNEL_WARN); } } LinphoneTunnelMode TunnelManager::getMode() const { return mMode; } void TunnelManager::processUdpMirrorEvent(const Event &ev){ if (mAutodetectionRunning == false) return; /*auto detection was cancelled, for example by switching to disabled state*/ if (mSimulateUdpLoss || !ev.mData.mHaveUdp) { if (mSimulateUdpLoss) { ms_message("TunnelManager: simulate UDP lost on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); } else { ms_message("TunnelManager: UDP mirror test failed on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); } mCurrentUdpMirrorClient++; if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) { ms_message("TunnelManager: trying another UDP mirror on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); mAutodetectionRunning = true; return; } else { ms_message("TunnelManager: all UDP mirror tests failed"); setState(On); } } else { ms_message("TunnelManager: UDP mirror test success on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); setState(Off); } mAutodetectionRunning = false; stopLongRunningTask(); } void TunnelManager::postEvent(const Event &ev){ mMutex.lock(); mEvq.push(ev); mMutex.unlock(); } void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) { TunnelManager* thiz = (TunnelManager*)data; Event ev; ev.mType=UdpMirrorClientEvent; ev.mData.mHaveUdp=isUdpAvailable; thiz->postEvent(ev); } void TunnelManager::networkReachableCb(LinphoneCore *lc, bool_t reachable) { TunnelManager *tunnel = bcTunnel(linphone_core_get_tunnel(lc)); if (reachable) { linphone_core_get_local_ip_for(AF_INET, NULL,tunnel->mLocalAddr); if (tunnel->getMode() == LinphoneTunnelModeAuto){ tunnel->startAutoDetection(); /*autodetection will call applyState() when finished*/ }else{ tunnel->applyState(); } } else if (!reachable) { // if network is no more reachable, cancel autodetection if any tunnel->stopAutoDetection(); //turn off the tunnel connection tunnel->stopClient(); tunnel->untunnelizeLiblinphone(); } } void TunnelManager::stopAutoDetection(){ if (mAutodetectionRunning){ for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) { udpMirror->stop(); } mAutodetectionRunning = false; stopLongRunningTask(); } } bool TunnelManager::startAutoDetection() { if (mUdpMirrorClients.empty()) { ms_error("TunnelManager: No UDP mirror server configured aborting auto detection"); return false; } ms_message("TunnelManager: Starting auto-detection"); mCurrentUdpMirrorClient = mUdpMirrorClients.begin(); if (mLongRunningTaskId == 0) mLongRunningTaskId = sal_begin_background_task("Tunnel auto detect", NULL, NULL); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; mAutodetectionRunning = true; lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); return true; } bool TunnelManager::isActivated() const{ return mState == On; } void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd) { mHttpUserName=username?username:""; mHttpPasswd=passwd?passwd:""; if (mTunnelClient) mTunnelClient->setHttpProxyAuthInfo(username,passwd); } void TunnelManager::tunnelizeSipPackets(bool enable){ mTunnelizeSipPackets = enable; } bool TunnelManager::tunnelizeSipPacketsEnabled() const { return mTunnelizeSipPackets; } void TunnelManager::verifyServerCertificate(bool enable){ mVerifyServerCertificate = enable; } bool TunnelManager::verifyServerCertificateEnabled() const { return mVerifyServerCertificate; } void TunnelManager::setHttpProxy(const char *host,int port, const char *username, const char *passwd){ mHttpUserName=username?username:""; mHttpPasswd=passwd?passwd:""; mHttpProxyPort=(port>0) ? port : 0; mHttpProxyHost=host ? host : ""; if (mTunnelClient) mTunnelClient->setHttpProxy(host, port, username, passwd); } LinphoneCore *TunnelManager::getLinphoneCore() const{ return mCore; } void TunnelManager::simulateUdpLoss(bool enabled) { mSimulateUdpLoss = enabled; } linphone-3.12.0/coreapi/TunnelManager.hh000066400000000000000000000225311313432737600201300ustar00rootroot00000000000000/* * C Implementation: tunnel * * Description: * * * *Copyright (C) 2011 Belledonne Comunications, Grenoble, France */ #ifndef __TUNNEL_CLIENT_MANAGER_H__ #define __TUNNEL_CLIENT_MANAGER_H__ #include #include #include #include #include "linphone/core.h" #include "linphone/tunnel.h" #ifndef USE_BELLESIP extern "C" { #include } #endif namespace belledonnecomm { /** * @addtogroup tunnel_client * @{ **/ struct DualSocket { TunnelSocket *sendSocket; TunnelSocket *recvSocket; }; /** * The TunnelManager class extends the LinphoneCore functionnality in order to provide an easy to use API to * - provision tunnel servers ip addresses and ports * - start/stop the tunneling service * - be informed of connection and disconnection events to the tunnel server * - perform auto-detection whether tunneling is required, based on a test of sending/receiving a flow of UDP packets. * * It takes in charge automatically the SIP registration procedure when connecting or disconnecting to a tunnel server. * No other action on LinphoneCore is required to enable full operation in tunnel mode. **/ class TunnelManager { public: /** * Add a tunnel server. At least one should be provided to be able to connect. * When several addresses are provided, the tunnel client may try each of them until it gets connected. * * @param ip tunnMethod definition for '-isInitialStateOn' not foundel server ip address * @param port tunnel server tls port, recommended value is 443 */ void addServer(const char *ip, int port); /** *Add tunnel server with auto detection capabilities * * @param ip tunnel server ip address * @param port tunnel server tls port, recommended value is 443 * @param udpMirrorPort remote port on the tunnel server side used to test udp reachability * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms. */ void addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay); /** * Add a tunnel server couple. At least one should be provided to be able to connect. * This is used when using the dual socket mode where one client will connect to one ip and the other client to the other ip. * * @param ip1 server ip address n°1 * @param port1 tunnel server tls port, recommended value is 443 * @param ip2 server ip address n°2 * @param port2 tunnel server tls port, recommended value is 443 * @param udpMirrorPort remote port on the tunnel server 1 side used to test udp reachability * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms. */ void addServerPair(const char *ip1, int port1, const char *ip2, int port2, unsigned int udpMirrorPort, unsigned int delay); /** * Add a tunnel server couple. At least one should be provided to be able to connect. * This is used when using the dual socket mode where one client will connect to one ip and the other client to the other ip. * * @param ip1 server ip address n°1 * @param port1 tunnel server tls port, recommended value is 443 * @param ip2 server ip address n°2 * @param port2 tunnel server tls port, recommended value is 443 */ void addServerPair(const char *ip1, int port1, const char *ip2, int port2); /** * Removes all tunnel server address previously entered with addServer() **/ void cleanServers(); /** * Enables the dual socket mode. In this mode, we have to configure pairs or ServerAddr * 2 TunneClient will be used, one for each IP and each one will only either send or receive the data stream. * @param enable true to enable the DualMode, false otherwise */ void enableDualMode(bool enable); /** * Returns whether or not the DualMode is enabled * @return true if it is enabled, false otherwise */ bool isDualModeEnabled(); /** * Forces reconnection to the tunnel server. * This method is useful when the device switches from wifi to Edge/3G or vice versa. In most cases the tunnel client socket * won't be notified promptly that its connection is now zombie, so it is recommended to call this method that will cause * the lost connection to be closed and new connection to be issued. **/ void reconnect(); /** * @brief setMode * @param mode */ void setMode(LinphoneTunnelMode mode); /** * @brief Return the tunnel mode * @return #LinphoneTunnelMode */ LinphoneTunnelMode getMode() const; /** * Enables debug logs of the Tunnel subsystem. **/ void enableLogs(bool isEnabled); /** * Enables debugs logs of the Tunnel subsystem and specify a callback where to receive the debug messages. **/ void enableLogs(bool isEnabled,LogHandler logHandler); /** * iOS only feature: specify http proxy credentials. * When the iOS device has an http proxy configured in the iOS settings, the tunnel client will connect to the server * through this http proxy. Credentials might be needed depending on the proxy configuration. * @param username The username. * @param passwd The password. **/ void setHttpProxyAuthInfo(const char* username,const char* passwd); void setHttpProxy(const char *host,int port, const char *username, const char *passwd); /** * Indicate to the tunnel manager whether SIP packets must pass * through the tunnel. That featurte is automatically enabled at * the creation of the TunnelManager instance. * @param enable If set to TRUE, SIP packets will pass through the tunnel. * If set to FALSE, SIP packets will pass by the configured proxies. */ void tunnelizeSipPackets(bool enable); /** * @brief Check whether the tunnel manager is set to tunnelize SIP packets * @return True, SIP packets pass through the tunnel */ bool tunnelizeSipPacketsEnabled() const; /** * Indicate to the tunnel manager wether server certificate * must be verified during TLS handshake. Default: disabled * @param enable If set to TRUE, SIP packets will pass through the tunnel. * If set to FALSE, SIP packets will pass by the configured proxies. */ void verifyServerCertificate(bool enable); /** * Check wether the tunnel manager is set to verify server certificate during TLS handshake * @return True, server certificate is verified(using the linphonecore root certificate) */ bool verifyServerCertificateEnabled() const; /** * @brief Constructor * @param lc The LinphoneCore instance of which the TunnelManager will be associated to. */ TunnelManager(LinphoneCore* lc); /** * @brief Destructor */ ~TunnelManager(); /** * @brief Create an RtpTransport * @param port * @return */ RtpTransport *createRtpTransport(int port); /** * @brief Destroy the given RtpTransport * @param t * @param s */ void closeRtpTransport(RtpTransport *t, TunnelSocket *s); /** * @brief Get associated Linphone Core * @return pointer on the associated LinphoneCore */ LinphoneCore *getLinphoneCore() const; /** * @brief Check wehter the tunnel is connected * @return True whether the tunnel is connected */ bool isConnected() const; bool isActivated() const; void simulateUdpLoss(bool enabled); private: enum EventType{ UdpMirrorClientEvent, TunnelEvent, }; struct Event{ EventType mType; union EventData{ bool mConnected; bool mHaveUdp; }mData; }; typedef std::list UdpMirrorClientList; static int customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen); static int customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen); static void tunnelCallback(bool connected, void *zis); static void tunnelCallback2(TunnelDirection direction, bool connected, void *zis); static void sOnIterate(TunnelManager *zis); static void sUdpMirrorClientCallback(bool result, void* data); static void networkReachableCb(LinphoneCore *lc, bool_t reachable); private: enum State{ Off, /*no tunneling */ On /*tunneling activated*/ }; void onIterate(); void doRegistration(); void doUnregistration(); void startClient(); bool startAutoDetection(); void processTunnelEvent(const Event &ev); void processUdpMirrorEvent(const Event &ev); void postEvent(const Event &ev); void stopClient(); void stopAutoDetection(); void stopLongRunningTask(); void applyMode(); void setState(State state); void applyState(); void tunnelizeLiblinphone(); void untunnelizeLiblinphone(); private: LinphoneCore* mCore; LinphoneTunnelMode mMode; TunnelClientI* mTunnelClient; std::string mHttpUserName; std::string mHttpPasswd; std::string mHttpProxyHost; int mHttpProxyPort; LinphoneCoreVTable *mVTable; std::list mServerAddrs; std::list mDualServerAddrs; UdpMirrorClientList mUdpMirrorClients; UdpMirrorClientList::iterator mCurrentUdpMirrorClient; LinphoneRtpTransportFactories mTransportFactories; Mutex mMutex; std::queue mEvq; char mLocalAddr[64]; unsigned long mLongRunningTaskId; State mTargetState; State mState; bool mVerifyServerCertificate; bool mStarted; bool mAutodetectionRunning; bool mTunnelizeSipPackets; bool mSimulateUdpLoss; bool mUseDualClient; }; /** * @} **/ } #endif /*__TUNNEL_CLIENT_MANAGER_H__*/ linphone-3.12.0/coreapi/account_creator.c000066400000000000000000001466271313432737600204030ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2017 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/account_creator.h" #include "linphone/core.h" #include "linphone/lpconfig.h" #include "private.h" #if !_WIN32 #include "regex.h" #endif #include BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorCbs); BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorCbs, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // marshal FALSE ); /************************** Start Misc **************************/ static const char* ha1_for_passwd(const char* username, const char* realm, const char* passwd) { static char ha1[33]; sal_auth_compute_ha1(username, realm, passwd, ha1); return ha1; } static unsigned int validate_uri(const char* username, const char* domain, const char* display_name) { LinphoneAddress* addr; int status = 0; LinphoneProxyConfig* proxy = linphone_proxy_config_new(); linphone_proxy_config_set_identity(proxy, "sip:?@domain.com"); if (username) { addr = linphone_proxy_config_normalize_sip_uri(proxy, username); } else { addr = linphone_address_clone(linphone_proxy_config_get_identity_address(proxy)); } if (addr == NULL) { status = 1; goto end; } if (domain && linphone_address_set_domain(addr, domain) != 0) { status = 1; } if (display_name && (!strlen(display_name) || linphone_address_set_display_name(addr, display_name) != 0)) { status = 1; } linphone_address_unref(addr); end: linphone_proxy_config_destroy(proxy); return status; } static char* _get_identity(const LinphoneAccountCreator *creator) { char *identity = NULL; if ((creator->username || creator->phone_number)) { //we must escape username LinphoneProxyConfig* proxy = linphone_core_create_proxy_config(creator->core); LinphoneAddress* addr; addr = linphone_proxy_config_normalize_sip_uri(proxy, creator->username ? creator->username : creator->phone_number); if (addr == NULL) goto end; identity = linphone_address_as_string(addr); linphone_address_unref(addr); end: linphone_proxy_config_destroy(proxy); } return identity; } static bool_t is_matching_regex(const char *entry, const char* regex) { #if _WIN32 return TRUE; #else regex_t regex_pattern; char err_msg[256]; int res; res = regcomp(®ex_pattern, regex, REG_EXTENDED | REG_NOSUB); if(res != 0) { regerror(res, ®ex_pattern, err_msg, sizeof(err_msg)); ms_error("Could not compile regex '%s: %s", regex, err_msg); return FALSE; } res = regexec(®ex_pattern, entry, 0, NULL, 0); regfree(®ex_pattern); return (res != REG_NOMATCH); #endif } LinphoneProxyConfig * linphone_account_creator_create_proxy_config(const LinphoneAccountCreator *creator) { LinphoneAuthInfo *info; LinphoneProxyConfig *cfg = linphone_core_create_proxy_config(creator->core); char *identity_str = _get_identity(creator); LinphoneAddress *identity = linphone_address_new(identity_str); ms_free(identity_str); if (creator->display_name) { linphone_address_set_display_name(identity, creator->display_name); } linphone_proxy_config_set_identity_address(cfg, identity); if (creator->phone_country_code) { linphone_proxy_config_set_dial_prefix(cfg, creator->phone_country_code); } else if (creator->phone_number) { int dial_prefix_number = linphone_dial_plan_lookup_ccc_from_e164(creator->phone_number); char buff[4]; snprintf(buff, sizeof(buff), "%d", dial_prefix_number); linphone_proxy_config_set_dial_prefix(cfg, buff); } linphone_proxy_config_enable_register(cfg, TRUE); info = linphone_auth_info_new(linphone_address_get_username(identity), // username NULL, //user id creator->password, // passwd creator->password ? NULL : creator->ha1, // ha1 !creator->password && creator->ha1 ? linphone_address_get_domain(identity) : NULL, // realm - assumed to be domain linphone_address_get_domain(identity) // domain ); linphone_core_add_auth_info(creator->core, info); linphone_address_unref(identity); if (linphone_core_add_proxy_config(creator->core, cfg) != -1) { linphone_core_set_default_proxy(creator->core, cfg); return cfg; } linphone_core_remove_auth_info(creator->core, info); return NULL; } LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCreator *creator) { return linphone_account_creator_create_proxy_config(creator); } /************************** End Misc **************************/ /************************** Start Account Creator Cbs **************************/ static LinphoneAccountCreatorCbs * linphone_account_creator_cbs_new(void) { return belle_sip_object_new(LinphoneAccountCreatorCbs); } LinphoneAccountCreatorCbs * linphone_account_creator_cbs_ref(LinphoneAccountCreatorCbs *cbs) { belle_sip_object_ref(cbs); return cbs; } void linphone_account_creator_cbs_unref(LinphoneAccountCreatorCbs *cbs) { belle_sip_object_unref(cbs); } void *linphone_account_creator_cbs_get_user_data(const LinphoneAccountCreatorCbs *cbs) { return cbs->user_data; } void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs, void *ud) { cbs->user_data = ud; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_create_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->create_account_response_cb; } void linphone_account_creator_cbs_set_create_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->create_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_exist(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_account_exist_response_cb; } void linphone_account_creator_cbs_set_is_account_exist(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_account_exist_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->activate_account_response_cb; } void linphone_account_creator_cbs_set_activate_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->activate_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_activated(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_account_activated_response_cb; } void linphone_account_creator_cbs_set_is_account_activated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_account_activated_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_link_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->link_account_response_cb; } void linphone_account_creator_cbs_set_link_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->link_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_alias(const LinphoneAccountCreatorCbs *cbs) { return cbs->activate_alias_response_cb; } void linphone_account_creator_cbs_set_activate_alias(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->activate_alias_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_alias_used(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_alias_used_response_cb; } void linphone_account_creator_cbs_set_is_alias_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_alias_used_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_linked(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_account_linked_response_cb; } void linphone_account_creator_cbs_set_is_account_linked(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_account_linked_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_recover_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->recover_account_response_cb; } void linphone_account_creator_cbs_set_recover_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->recover_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_update_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->update_account_response_cb; } void linphone_account_creator_cbs_set_update_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->update_account_response_cb = cb; } /************************** End Account Creator Cbs **************************/ /************************** Start Account Creator data **************************/ static void _linphone_account_creator_destroy(LinphoneAccountCreator *creator) { /*this will drop all pending requests if any*/ if (creator->xmlrpc_session) linphone_xml_rpc_session_release(creator->xmlrpc_session); if (creator->service != NULL && linphone_account_creator_service_get_destructor_cb(creator->service) != NULL) linphone_account_creator_service_get_destructor_cb(creator->service)(creator); linphone_account_creator_cbs_unref(creator->cbs); linphone_proxy_config_unref(creator->proxy_cfg); linphone_account_creator_reset(creator); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreator); BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreator, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_account_creator_destroy, NULL, // clone NULL, // marshal FALSE ); LinphoneAccountCreator * _linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url) { LinphoneAccountCreator *creator; const char* domain = lp_config_get_string(core->config, "assistant", "domain", NULL); creator = belle_sip_object_new(LinphoneAccountCreator); creator->service = linphone_core_get_account_creator_service(core); creator->cbs = linphone_account_creator_cbs_new(); creator->core = core; creator->transport = LinphoneTransportTcp; creator->xmlrpc_session = (xmlrpc_url) ? linphone_xml_rpc_session_new(core, xmlrpc_url) : NULL; if (domain) { linphone_account_creator_set_domain(creator, domain); } creator->proxy_cfg = linphone_core_create_proxy_config(core); if (creator->service != NULL && linphone_account_creator_service_get_constructor_cb(creator->service) != NULL) linphone_account_creator_service_get_constructor_cb(creator->service)(creator); return creator; } LinphoneAccountCreator * linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url) { return _linphone_account_creator_new(core, xmlrpc_url); } #define _reset_field(field) \ if (field) { \ ms_free(field); \ field = NULL; \ } void linphone_account_creator_reset(LinphoneAccountCreator *creator) { _reset_field(creator->username); _reset_field(creator->display_name); _reset_field(creator->password); _reset_field(creator->ha1); _reset_field(creator->phone_number); _reset_field(creator->phone_country_code); _reset_field(creator->email); _reset_field(creator->language); _reset_field(creator->activation_code); _reset_field(creator->domain); _reset_field(creator->route); } #undef _reset_field LinphoneAccountCreator * linphone_core_create_account_creator(LinphoneCore *core, const char *xmlrpc_url) { return _linphone_account_creator_new(core, xmlrpc_url); } LinphoneAccountCreator * linphone_account_creator_ref(LinphoneAccountCreator *creator) { belle_sip_object_ref(creator); return creator; } void linphone_account_creator_unref(LinphoneAccountCreator *creator) { belle_sip_object_unref(creator); } void *linphone_account_creator_get_user_data(const LinphoneAccountCreator *creator) { return creator->user_data; } void linphone_account_creator_set_user_data(LinphoneAccountCreator *creator, void *ud) { creator->user_data = ud; } LinphoneAccountCreatorUsernameStatus linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username) { int min_length = lp_config_get_int(creator->core->config, "assistant", "username_min_length", -1); int max_length = lp_config_get_int(creator->core->config, "assistant", "username_max_length", -1); bool_t use_phone_number = lp_config_get_int(creator->core->config, "assistant", "use_phone_number", 0); const char* regex = lp_config_get_string(creator->core->config, "assistant", "username_regex", 0); if (!username) { creator->username = NULL; return LinphoneAccountCreatorUsernameStatusOk; } else if (min_length > 0 && strlen(username) < (size_t)min_length) { return LinphoneAccountCreatorUsernameStatusTooShort; } else if (max_length > 0 && strlen(username) > (size_t)max_length) { return LinphoneAccountCreatorUsernameStatusTooLong; } else if (use_phone_number && !linphone_proxy_config_is_phone_number(NULL, username)) { return LinphoneAccountCreatorUsernameStatusInvalid; } else if (regex && !is_matching_regex(username, regex)) { return LinphoneAccountCreatorUsernameStatusInvalidCharacters; } else if (validate_uri(username, NULL, NULL) != 0) { return LinphoneAccountCreatorUsernameStatusInvalid; } set_string(&creator->username, username, TRUE); return LinphoneAccountCreatorUsernameStatusOk; } const char * linphone_account_creator_get_username(const LinphoneAccountCreator *creator) { return creator->username; } LinphoneAccountCreatorPhoneNumberStatusMask linphone_account_creator_set_phone_number(LinphoneAccountCreator *creator, const char *phone_number, const char *country_code) { char *normalized_phone_number; LinphoneAccountCreatorPhoneNumberStatusMask return_status = 0; if (!phone_number || !country_code) { if (!phone_number && !country_code) { creator->phone_number = NULL; creator->phone_country_code = NULL; return (LinphoneAccountCreatorPhoneNumberStatusMask)LinphoneAccountCreatorPhoneNumberStatusOk; } else { return (LinphoneAccountCreatorPhoneNumberStatusMask)LinphoneAccountCreatorPhoneNumberStatusInvalid; } } else { LinphoneProxyConfig *numCfg = creator->proxy_cfg; creator->phone_country_code = ms_strdup(country_code[0] == '+' ? &country_code[1] : country_code); linphone_proxy_config_set_dial_prefix(numCfg, creator->phone_country_code); normalized_phone_number = linphone_proxy_config_normalize_phone_number(numCfg, phone_number); if (!normalized_phone_number) { return LinphoneAccountCreatorPhoneNumberStatusInvalid; } // if phone is valid, we lastly want to check that length is OK { const LinphoneDialPlan* plan = linphone_dial_plan_by_ccc(creator->phone_country_code); int size = (int)strlen(phone_number); if (linphone_dial_plan_is_generic(plan)) { return_status = LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode; } if (size < plan->nnl - 1) { return_status += LinphoneAccountCreatorPhoneNumberStatusTooShort; goto end; } else if (size > plan->nnl + 1) { return_status += LinphoneAccountCreatorPhoneNumberStatusTooLong; goto end; } else if (return_status & LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode) { goto end; } } } set_string(&creator->phone_number, normalized_phone_number, TRUE); return_status = LinphoneAccountCreatorPhoneNumberStatusOk; end: ms_free(normalized_phone_number); return return_status; } const char * linphone_account_creator_get_phone_number(const LinphoneAccountCreator *creator) { return creator->phone_number; } LinphoneAccountCreatorPasswordStatus linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password) { int min_length = lp_config_get_int(creator->core->config, "assistant", "password_min_length", -1); int max_length = lp_config_get_int(creator->core->config, "assistant", "password_max_length", -1); if (!password) { creator->password = NULL; return LinphoneAccountCreatorPasswordStatusTooShort; } if (min_length > 0 && strlen(password) < (size_t)min_length) { return LinphoneAccountCreatorPasswordStatusTooShort; } else if (max_length > 0 && strlen(password) > (size_t)max_length) { return LinphoneAccountCreatorPasswordStatusTooLong; } set_string(&creator->password, password, FALSE); return LinphoneAccountCreatorPasswordStatusOk; } const char * linphone_account_creator_get_password(const LinphoneAccountCreator *creator) { return creator->password; } LinphoneAccountCreatorPasswordStatus linphone_account_creator_set_ha1(LinphoneAccountCreator *creator, const char *ha1){ set_string(&creator->ha1, ha1, FALSE); return LinphoneAccountCreatorPasswordStatusOk; } const char * linphone_account_creator_get_ha1(const LinphoneAccountCreator *creator) { return creator->ha1; } LinphoneAccountCreatorActivationCodeStatus linphone_account_creator_set_activation_code(LinphoneAccountCreator *creator, const char *activation_code){ set_string(&creator->activation_code, activation_code, FALSE); return LinphoneAccountCreatorActivationCodeStatusOk; } const char * linphone_account_creator_get_activation_code(const LinphoneAccountCreator *creator) { return creator->activation_code; } LinphoneAccountCreatorLanguageStatus linphone_account_creator_set_language(LinphoneAccountCreator *creator, const char *lang) { set_string(&creator->language, lang, FALSE); return LinphoneAccountCreatorLanguageStatusOk; } const char * linphone_account_creator_get_language(const LinphoneAccountCreator *creator) { return creator->language; } LinphoneAccountCreatorUsernameStatus linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name) { if (validate_uri(NULL, display_name, NULL) != 0) { return LinphoneAccountCreatorUsernameStatusInvalid; } set_string(&creator->display_name, display_name, FALSE); return LinphoneAccountCreatorUsernameStatusOk; } const char * linphone_account_creator_get_display_name(const LinphoneAccountCreator *creator) { return creator->display_name; } LinphoneAccountCreatorEmailStatus linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email) { if (!email || !is_matching_regex(email, "^.+@.+\\..*$")) { return LinphoneAccountCreatorEmailStatusMalformed; } if (!is_matching_regex(email, "^.+@.+\\.[A-Za-z]{2}[A-Za-z]*$")) { return LinphoneAccountCreatorEmailStatusInvalidCharacters; } set_string(&creator->email, email, TRUE); return LinphoneAccountCreatorEmailStatusOk; } const char * linphone_account_creator_get_email(const LinphoneAccountCreator *creator) { return creator->email; } LinphoneAccountCreatorDomainStatus linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain) { if (domain && validate_uri(NULL, domain, NULL) != 0) { return LinphoneAccountCreatorDomainInvalid; } set_string(&creator->domain, domain, TRUE); return LinphoneAccountCreatorDomainOk; } const char * linphone_account_creator_get_domain(const LinphoneAccountCreator *creator) { return creator->domain; } LinphoneAccountCreatorTransportStatus linphone_account_creator_set_transport(LinphoneAccountCreator *creator, LinphoneTransportType transport) { if (!linphone_core_sip_transport_supported(creator->core, transport)) { return LinphoneAccountCreatorTransportUnsupported; } creator->transport = transport; return LinphoneAccountCreatorTransportOk; } LinphoneTransportType linphone_account_creator_get_transport(const LinphoneAccountCreator *creator) { return creator->transport; } LinphoneAccountCreatorStatus linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route) { if (!route || linphone_proxy_config_set_route(creator->proxy_cfg, route) != 0) return LinphoneAccountCreatorStatusRequestFailed; set_string(&creator->route, route, TRUE); return LinphoneAccountCreatorStatusRequestOk; } const char * linphone_account_creator_get_route(const LinphoneAccountCreator *creator) { return creator->route; } LinphoneAccountCreatorCbs * linphone_account_creator_get_callbacks(const LinphoneAccountCreator *creator) { return creator->cbs; } LinphoneAccountCreatorService * linphone_account_creator_get_service(const LinphoneAccountCreator *creator) { return creator->service; } LinphoneAccountCreatorStatus linphone_account_creator_is_account_exist(LinphoneAccountCreator *creator) { if (creator->service->is_account_exist_request_cb == NULL || creator->cbs->is_account_exist_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->is_account_exist_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_create_account(LinphoneAccountCreator *creator) { if (creator->service->create_account_request_cb == NULL || creator->cbs->create_account_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->create_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated(LinphoneAccountCreator *creator) { if (creator->service->is_account_activated_request_cb == NULL || creator->cbs->is_account_activated_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->is_account_activated_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_activate_account(LinphoneAccountCreator *creator) { if (creator->service->activate_account_request_cb == NULL || creator->cbs->activate_account_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->activate_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_link_account(LinphoneAccountCreator *creator) { if (creator->service->link_account_request_cb == NULL || creator->cbs->link_account_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->link_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_activate_alias(LinphoneAccountCreator *creator) { if (creator->service->activate_alias_request_cb == NULL || creator->cbs->activate_alias_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->activate_alias_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_is_alias_used(LinphoneAccountCreator *creator) { if (creator->service->is_alias_used_request_cb == NULL || creator->cbs->is_alias_used_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->is_alias_used_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked(LinphoneAccountCreator *creator) { if (creator->service->is_account_linked_request_cb == NULL || creator->cbs->is_account_linked_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->is_account_linked_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_recover_account(LinphoneAccountCreator *creator) { if (creator->service->recover_account_request_cb == NULL || creator->cbs->recover_account_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->recover_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_update_account(LinphoneAccountCreator *creator) { if (creator->service->update_account_request_cb == NULL || creator->cbs->update_account_response_cb == NULL) { return LinphoneAccountCreatorStatusMissingCallbacks; } return creator->service->update_account_request_cb(creator); } /************************** End Account Creator data **************************/ /************************** Start Account Creator Linphone **************************/ LinphoneAccountCreatorStatus linphone_account_creator_constructor_linphone(LinphoneAccountCreator *creator) { LinphoneAddress *addr; const char *identity = lp_config_get_default_string(creator->core->config, "proxy", "reg_identity", NULL); const char *proxy = lp_config_get_default_string(creator->core->config, "proxy", "reg_proxy", NULL); const char *route = lp_config_get_default_string(creator->core->config, "proxy", "reg_route", NULL); const char *realm = lp_config_get_default_string(creator->core->config, "proxy", "realm", NULL); linphone_proxy_config_set_realm(creator->proxy_cfg, realm ? realm : "sip.linphone.org"); linphone_proxy_config_set_route(creator->proxy_cfg, route ? route : "sip.linphone.org"); linphone_proxy_config_set_server_addr(creator->proxy_cfg, proxy ? proxy : "sip.linphone.org"); addr = linphone_address_new(identity ? identity : "sip:username@sip.linphone.org"); linphone_proxy_config_set_identity_address(creator->proxy_cfg, addr); linphone_address_unref(addr); return LinphoneAccountCreatorStatusRequestOk; } /****************** START OF ACCOUNT USED SECTION *****************************/ static void _is_account_exist_response_cb(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->is_account_exist_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist : ( (strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountExist : LinphoneAccountCreatorStatusAccountExistWithAlias); if (status == LinphoneAccountCreatorStatusAccountExistWithAlias) { set_string(&creator->phone_number, resp, FALSE); } } creator->cbs->is_account_exist_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_is_account_exist_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->username && !creator->phone_number) { if (creator->cbs->is_account_exist_response_cb != NULL) { creator->cbs->is_account_exist_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: is_account_exist (%s=%s, domain=%s)", (creator->username) ? "username" : "phone number", (creator->username) ? creator->username : creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "get_phone_number_for_account", LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_account_exist_response_cb); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF CREATE ACCOUNT USED SECTION ************************/ /****************** START OF CREATE ACCOUNT SECTION ***************************/ static void _create_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->create_account_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountCreated : (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusServerError : (strcmp(resp, "ERROR_ACCOUNT_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorStatusAccountExist : (strcmp(resp, "ERROR_ALIAS_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorStatusAccountExistWithAlias :LinphoneAccountCreatorStatusAccountNotCreated; } creator->cbs->create_account_response_cb(creator, status, resp); } } static LinphoneXmlRpcRequest * _create_account_with_phone_custom(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->phone_number) { return NULL; } ms_debug("Account creator: create_account_with_phone (phone number=%s, username=%s, domain=%s, language=%s)", creator->phone_number, (creator->username) ? creator->username : creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->language); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "create_phone_account", LinphoneXmlRpcArgString, creator->phone_number, LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number, LinphoneXmlRpcArgString, creator->password ? ha1_for_passwd(creator->username ? creator->username : creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->password) : "", LinphoneXmlRpcArgString, linphone_core_get_user_agent(creator->core), LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgString, creator->language, LinphoneXmlRpcArgNone); return request; } static LinphoneXmlRpcRequest * _create_account_with_email_custom(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->username || !creator->email || !creator->password) { return NULL; } ms_debug("Account creator: create_account_with_email (username=%s, email=%s, domain=%s)", creator->username, creator->email, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "create_email_account", LinphoneXmlRpcArgString, creator->username, LinphoneXmlRpcArgString, creator->email, LinphoneXmlRpcArgString, ha1_for_passwd(creator->username ? creator->username : creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->password), LinphoneXmlRpcArgString, linphone_core_get_user_agent(creator->core), LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); return request; } LinphoneAccountCreatorStatus linphone_account_creator_create_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; char *identity = _get_identity(creator); if (!identity || (!(request = _create_account_with_phone_custom(creator)) && !(request = _create_account_with_email_custom(creator)))) { if (creator->cbs->create_account_response_cb != NULL) { creator->cbs->create_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } if (identity) ms_free(identity); return LinphoneAccountCreatorStatusMissingArguments; } linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _create_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); ms_free(identity); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF CREATE ACCOUNT SECTION *****************************/ /****************** START OF VALIDATE ACCOUNT SECTION *************************/ static void _activate_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->activate_account_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strcmp(resp, "ERROR_ACCOUNT_ALREADY_ACTIVATED") == 0) { status = LinphoneAccountCreatorStatusAccountAlreadyActivated; } else if (strstr(resp, "ERROR_") == resp) { status = LinphoneAccountCreatorStatusAccountNotActivated; } else { status = LinphoneAccountCreatorStatusAccountActivated; set_string(&creator->ha1, resp, FALSE); } } creator->cbs->activate_account_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_activate_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->phone_number || !creator->activation_code) { if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: activate_account_phone (phone number=%s, username=%s, activation code=%s, domain=%s)", creator->phone_number, creator->username ? creator->username : creator->phone_number, creator->activation_code, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "activate_phone_account", LinphoneXmlRpcArgString, creator->phone_number, LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number, LinphoneXmlRpcArgString, creator->activation_code, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } LinphoneAccountCreatorStatus linphone_account_creator_activate_email_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->activation_code || !creator->username) { if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: activate_account_email (username=%s, activation code=%s, domain=%s)", creator->username, creator->activation_code, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "activate_email_account", LinphoneXmlRpcArgString, creator->username, LinphoneXmlRpcArgString, creator->activation_code, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF CREATE VALIDATE ACCOUNT SECTION ********************/ /****************** START OF ACCOUNT VALIDATED SECTION ************************/ static void _is_account_activated_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->is_account_activated_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountActivated : LinphoneAccountCreatorStatusAccountNotActivated; } creator->cbs->is_account_activated_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; char *identity = _get_identity(creator); if (!identity) { if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: is_account_activated (username=%s, domain=%s)", creator->username ? creator->username : creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "is_account_activated", LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_account_activated_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); ms_free(identity); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF CREATE ACCOUNT VALIDATED SECTION********************/ /****************** START OF PHONE NUMBER VALIDATED SECTION *******************/ static void _is_phone_number_used_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->is_alias_used_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK_ACCOUNT") == 0) ? LinphoneAccountCreatorStatusAliasIsAccount : (strcmp(resp, "OK_ALIAS") == 0) ? LinphoneAccountCreatorStatusAliasExist : LinphoneAccountCreatorStatusAliasNotExist; } creator->cbs->is_alias_used_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_is_phone_number_used_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; char *identity = _get_identity(creator); if (!identity) { if (creator->cbs->is_alias_used_response_cb != NULL) { creator->cbs->is_alias_used_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: is_phone_number_used (phone number=%s, domain=%s)", creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "is_phone_number_used", LinphoneXmlRpcArgString, creator->phone_number, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_phone_number_used_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); ms_free(identity); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF PHONE NUMBER VALIDATED SECTION *********************/ /****************** START OF LINK PHONE NUMBER WITH ACCOUNT SECTION ***********/ static void _link_phone_number_with_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->link_account_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusRequestOk : LinphoneAccountCreatorStatusAccountNotLinked; } creator->cbs->link_account_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_link_phone_number_with_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->phone_number || !creator->username) { if (creator->cbs->link_account_response_cb != NULL) { creator->cbs->link_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: link_phone_number_with_account (phone number=%s, username=%s, domain=%s, language=%s)", creator->phone_number, creator->username, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->language); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "link_phone_number_with_account", LinphoneXmlRpcArgString, creator->phone_number, LinphoneXmlRpcArgString, creator->username, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgString, creator->language, LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _link_phone_number_with_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } static void _get_phone_number_for_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->is_account_linked_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0 || strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0 || strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotLinked : LinphoneAccountCreatorStatusAccountLinked; } creator->cbs->is_account_linked_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->username || !linphone_proxy_config_get_domain(creator->proxy_cfg)) { return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: is_account_linked (username=%s, domain=%s)", creator->username, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "get_phone_number_for_account", LinphoneXmlRpcArgString, creator->username, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _get_phone_number_for_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF LINK PHONE NUMBER WITH ACCOUNT SECTION *************/ /****************** START OF ACTIVE PHONE NUMBER LINK **************************/ static void _activate_phone_number_link_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->activate_alias_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strstr(resp, "ERROR_") == resp) ? LinphoneAccountCreatorStatusAccountNotActivated : LinphoneAccountCreatorStatusAccountActivated; } creator->cbs->activate_alias_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_number_link_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->phone_number || !creator->username || !creator->activation_code || (!creator->password && !creator->ha1) || !linphone_proxy_config_get_domain(creator->proxy_cfg)) { if (creator->cbs->activate_alias_response_cb != NULL) { creator->cbs->activate_alias_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: activate_phone_number_link (phone number=%s, username=%s, activation code=%s, domain=%s)", creator->phone_number, creator->username, creator->activation_code, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "activate_phone_number_link", LinphoneXmlRpcArgString, creator->phone_number, LinphoneXmlRpcArgString, creator->username, LinphoneXmlRpcArgString, creator->activation_code, LinphoneXmlRpcArgString, creator->ha1 ? creator->ha1 : ha1_for_passwd(creator->username, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->password), LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_phone_number_link_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF ACTIVE PHONE NUMBER LINK **************************/ /****************** START OF ACTIVE PHONE NUMBER LINK **************************/ static void _recover_phone_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->recover_account_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strstr(resp, "ERROR_") == resp) { status = (strstr(resp, "ERROR_CANNOT_SEND_SMS") == resp) ? LinphoneAccountCreatorStatusServerError : (strstr(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == resp) ? LinphoneAccountCreatorStatusAccountNotExist : LinphoneAccountCreatorStatusRequestFailed; } else { status = LinphoneAccountCreatorStatusRequestOk; set_string(&creator->username, resp, FALSE); } } creator->cbs->recover_account_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_recover_phone_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->phone_number) { if (creator->cbs->recover_account_response_cb != NULL) { creator->cbs->recover_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } ms_debug("Account creator: recover_phone_account (phone number=%s, domain=%s, language=%s)", creator->phone_number, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->language); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "recover_phone_account", LinphoneXmlRpcArgString, creator->phone_number, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgString, creator->language, LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _recover_phone_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF ACTIVE PHONE NUMBER LINK **************************/ /****************** START OF UPDATE ACCOUNT **************************/ static void _password_updated_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); if (creator->cbs->update_account_response_cb != NULL) { LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strcmp(resp, "OK") == 0) { status = LinphoneAccountCreatorStatusRequestOk; } else if (strcmp(resp, "ERROR_PASSWORD_DOESNT_MATCH") == 0) { status = LinphoneAccountCreatorStatusAccountNotExist; } else { status = LinphoneAccountCreatorStatusServerError; } } creator->cbs->update_account_response_cb(creator, status, resp); } } LinphoneAccountCreatorStatus linphone_account_creator_update_password_linphone(LinphoneAccountCreator *creator){ LinphoneXmlRpcRequest *request; char *identity = _get_identity(creator); const char* new_pwd = (const char*)linphone_account_creator_get_user_data(creator); if (!identity || ((!creator->username && !creator->phone_number) || !linphone_proxy_config_get_domain(creator->proxy_cfg) || (!creator->password && !creator->ha1) || !new_pwd ) ) { if (creator->cbs->update_account_response_cb != NULL) { creator->cbs->update_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } return LinphoneAccountCreatorStatusMissingArguments; } const char * username = creator->username ? creator->username : creator->phone_number; const char * ha1 = ms_strdup(creator->ha1 ? creator->ha1 : ha1_for_passwd(username, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->password) ); const char * new_ha1 = ms_strdup(ha1_for_passwd(username, linphone_proxy_config_get_domain(creator->proxy_cfg), new_pwd)); ms_debug("Account creator: update_password (username=%s, domain=%s)", creator->username, linphone_proxy_config_get_domain(creator->proxy_cfg)); request = linphone_xml_rpc_request_new_with_args(LinphoneXmlRpcArgString, "update_hash", LinphoneXmlRpcArgString, username, LinphoneXmlRpcArgString, ha1, LinphoneXmlRpcArgString, new_ha1, LinphoneXmlRpcArgString, linphone_proxy_config_get_domain(creator->proxy_cfg), LinphoneXmlRpcArgNone); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _password_updated_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF UPDATE ACCOUNT **************************/ /************************** End Account Creator Linphone **************************/ linphone-3.12.0/coreapi/account_creator_service.c000066400000000000000000000152521313432737600221100ustar00rootroot00000000000000/* account_creator_service.c Copyright (C) 2017 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/account_creator_service.h" #include "linphone/core.h" #include "private.h" BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorService); BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorService, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // marshal FALSE ); /************************** Start Account Creator service **************************/ LinphoneAccountCreatorService * linphone_account_creator_service_new(void) { return belle_sip_object_new(LinphoneAccountCreatorService); } LinphoneAccountCreatorService * linphone_account_creator_service_ref(LinphoneAccountCreatorService *service) { belle_sip_object_ref(service); return service; } void linphone_account_creator_service_unref(LinphoneAccountCreatorService *service) { belle_sip_object_unref(service); } void *linphone_account_creator_service_get_user_data(const LinphoneAccountCreatorService *service) { return service->user_data; } void linphone_account_creator_service_set_user_data(LinphoneAccountCreatorService *service, void *ud) { service->user_data = ud; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_constructor_cb(const LinphoneAccountCreatorService *service) { return service->account_creator_service_constructor_cb; } void linphone_account_creator_service_set_constructor_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->account_creator_service_constructor_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_destructor_cb(const LinphoneAccountCreatorService *service) { return service->account_creator_service_destructor_cb; } void linphone_account_creator_service_set_destructor_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->account_creator_service_destructor_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_create_account_cb(const LinphoneAccountCreatorService *service) { return service->create_account_request_cb; } void linphone_account_creator_service_set_create_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->create_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_account_exist_cb(const LinphoneAccountCreatorService *service) { return service->is_account_exist_request_cb; } void linphone_account_creator_service_set_is_account_exist_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_account_exist_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_activate_account_cb(const LinphoneAccountCreatorService *service) { return service->activate_account_request_cb; } void linphone_account_creator_service_set_activate_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->activate_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_account_activated_cb(const LinphoneAccountCreatorService *service) { return service->is_account_activated_request_cb; } void linphone_account_creator_service_set_is_account_activated_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_account_activated_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_link_account_cb(const LinphoneAccountCreatorService *service) { return service->link_account_request_cb; } void linphone_account_creator_service_set_link_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->link_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_activate_alias_cb(const LinphoneAccountCreatorService *service) { return service->activate_alias_request_cb; } void linphone_account_creator_service_set_activate_alias_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->activate_alias_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_alias_used_cb(const LinphoneAccountCreatorService *service) { return service->is_alias_used_request_cb; } void linphone_account_creator_service_set_is_alias_used_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_alias_used_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_account_linked_cb(const LinphoneAccountCreatorService *service) { return service->is_account_linked_request_cb; } void linphone_account_creator_service_set_is_account_linked_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_account_linked_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_recover_account_cb(const LinphoneAccountCreatorService *service) { return service->is_account_linked_request_cb; } void linphone_account_creator_service_set_recover_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->recover_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_update_account_cb(const LinphoneAccountCreatorService *service) { return service->update_account_request_cb; } void linphone_account_creator_service_set_update_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->update_account_request_cb = cb; } /************************** End Account Creator service **************************/ void linphone_core_set_account_creator_service(LinphoneCore *lc, LinphoneAccountCreatorService *service) { if (lc->default_ac_service) linphone_account_creator_service_unref(lc->default_ac_service); lc->default_ac_service = service; } LinphoneAccountCreatorService * linphone_core_get_account_creator_service(LinphoneCore *lc) { return lc->default_ac_service; } linphone-3.12.0/coreapi/address.c000066400000000000000000000147361313432737600166500ustar00rootroot00000000000000/* linphone Copyright (C) 2009 Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "linphone/lpconfig.h" #include "private.h" LinphoneAddress * _linphone_address_new(const char *addr){ SalAddress *saddr=sal_address_new(addr); if (saddr==NULL) ms_error("Cannot create LinphoneAddress, bad uri [%s]",addr); return saddr; } LinphoneAddress * linphone_address_new(const char *addr) { return _linphone_address_new(addr); } LinphoneAddress * linphone_address_clone(const LinphoneAddress *addr){ return sal_address_clone(addr); } LinphoneAddress * linphone_address_ref(LinphoneAddress *addr){ return sal_address_ref(addr); } void linphone_address_unref(LinphoneAddress *addr){ sal_address_unref(addr); } const char *linphone_address_get_scheme(const LinphoneAddress *u){ return sal_address_get_scheme(u); } const char *linphone_address_get_display_name(const LinphoneAddress* u){ return sal_address_get_display_name(u); } const char *linphone_address_get_username(const LinphoneAddress *u){ return sal_address_get_username(u); } const char *linphone_address_get_domain(const LinphoneAddress *u){ return sal_address_get_domain(u); } int linphone_address_get_port(const LinphoneAddress *u) { return sal_address_get_port(u); } LinphoneStatus linphone_address_set_display_name(LinphoneAddress *u, const char *display_name){ sal_address_set_display_name(u,display_name); return 0; } LinphoneStatus linphone_address_set_username(LinphoneAddress *uri, const char *username) { sal_address_set_username(uri,username); return 0; } LinphoneStatus linphone_address_set_domain(LinphoneAddress *uri, const char *host){ sal_address_set_domain(uri,host); return 0; } LinphoneStatus linphone_address_set_port(LinphoneAddress *uri, int port){ sal_address_set_port(uri,port); return 0; } LinphoneStatus linphone_address_set_transport(LinphoneAddress *uri, LinphoneTransportType tp){ sal_address_set_transport(uri,(SalTransport)tp); return 0; } LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri){ return (LinphoneTransportType)sal_address_get_transport(uri); } void linphone_address_set_method_param(LinphoneAddress *addr, const char *method) { sal_address_set_method_param(addr, method); } const char *linphone_address_get_method_param(const LinphoneAddress *addr) { return sal_address_get_method_param(addr); } void linphone_address_clean(LinphoneAddress *uri){ sal_address_clean(uri); } char *linphone_address_as_string(const LinphoneAddress *u){ return sal_address_as_string(u); } char *linphone_address_as_string_uri_only(const LinphoneAddress *u){ return sal_address_as_string_uri_only(u); } bool_t linphone_address_is_secure(const LinphoneAddress *uri){ return sal_address_is_secure(uri); } bool_t linphone_address_get_secure(const LinphoneAddress *uri){ return sal_address_is_secure(uri); } void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled){ sal_address_set_secure(addr, enabled); } bool_t linphone_address_is_sip(const LinphoneAddress *uri){ return sal_address_is_sip(uri); } static bool_t strings_equals(const char *s1, const char *s2){ if (s1==NULL && s2==NULL) return TRUE; if (s1!=NULL && s2!=NULL && strcmp(s1,s2)==0) return TRUE; return FALSE; } bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2){ const char *u1,*u2; const char *h1,*h2; int p1,p2; u1=linphone_address_get_username(a1); u2=linphone_address_get_username(a2); p1=linphone_address_get_port(a1); p2=linphone_address_get_port(a2); h1=linphone_address_get_domain(a1); h2=linphone_address_get_domain(a2); return strings_equals(u1,u2) && strings_equals(h1,h2) && p1==p2; } bool_t linphone_address_equal(const LinphoneAddress *a1, const LinphoneAddress *a2) { char *s1; char *s2; bool_t res; if ((a1 == NULL) && (a2 == NULL)) return TRUE; if ((a1 == NULL) || (a2 == NULL)) return FALSE; s1 = linphone_address_as_string(a1); s2 = linphone_address_as_string(a2); res = (strcmp(s1, s2) == 0) ? TRUE : FALSE; ms_free(s1); ms_free(s2); return res; } void linphone_address_destroy(LinphoneAddress *u){ linphone_address_unref(u); } void linphone_address_set_password(LinphoneAddress *addr, const char *passwd){ sal_address_set_password(addr,passwd); } const char *linphone_address_get_password(const LinphoneAddress *addr){ return sal_address_get_password(addr); } void linphone_address_set_header(LinphoneAddress *addr, const char *header_name, const char *header_value){ sal_address_set_header(addr,header_name,header_value); } const char *linphone_address_get_header(const LinphoneAddress *addr, const char *name){ return sal_address_get_header(addr,name); } bool_t linphone_address_has_param(const LinphoneAddress *addr, const char *name) { return sal_address_has_param(addr, name); } const char * linphone_address_get_param(const LinphoneAddress *addr, const char *name) { return sal_address_get_param(addr, name); } void linphone_address_set_param(LinphoneAddress *addr, const char *name, const char *value) { sal_address_set_param(addr, name, value); } void linphone_address_set_params(LinphoneAddress *addr, const char *params) { sal_address_set_params(addr, params); } void linphone_address_set_uri_param(LinphoneAddress *addr, const char *name, const char *value) { sal_address_set_uri_param(addr, name, value); } void linphone_address_set_uri_params(LinphoneAddress *addr, const char *params) { sal_address_set_uri_params(addr, params); } bool_t linphone_address_has_uri_param(const LinphoneAddress *addr, const char *name) { return sal_address_has_uri_param(addr, name); } const char * linphone_address_get_uri_param(const LinphoneAddress *addr, const char *name) { return sal_address_get_uri_param(addr, name); } LinphoneAddress * linphone_core_create_address(LinphoneCore *lc, const char *address) { return linphone_address_new(address); } /** @} */ linphone-3.12.0/coreapi/authentication.c000066400000000000000000000422571313432737600202410ustar00rootroot00000000000000/*************************************************************************** * authentication.c * * Fri Jul 16 12:08:34 2004 * Copyright 2004-2009 Simon MORLAT * simon.morlat@linphone.org ****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "private.h" #include "linphone/lpconfig.h" static void _linphone_auth_info_uninit(LinphoneAuthInfo *obj); static void _linphone_auth_info_copy(LinphoneAuthInfo *dst, const LinphoneAuthInfo *src); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAuthInfo); BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneAuthInfo); BELLE_SIP_INSTANCIATE_VPTR( LinphoneAuthInfo, belle_sip_object_t, _linphone_auth_info_uninit, // destroy _linphone_auth_info_copy, // clone NULL, // marshal FALSE ); LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain){ LinphoneAuthInfo *obj=belle_sip_object_new(LinphoneAuthInfo); if (username!=NULL && (strlen(username)>0) ) obj->username=ms_strdup(username); if (userid!=NULL && (strlen(userid)>0)) obj->userid=ms_strdup(userid); if (passwd!=NULL && (strlen(passwd)>0)) obj->passwd=ms_strdup(passwd); if (ha1!=NULL && (strlen(ha1)>0)) obj->ha1=ms_strdup(ha1); if (realm!=NULL && (strlen(realm)>0)) obj->realm=ms_strdup(realm); if (domain!=NULL && (strlen(domain)>0)) obj->domain=ms_strdup(domain); return obj; } static void _linphone_auth_info_copy(LinphoneAuthInfo *dst, const LinphoneAuthInfo *src) { if (src->username) dst->username = ms_strdup(src->username); if (src->userid) dst->userid = ms_strdup(src->userid); if (src->passwd) dst->passwd = ms_strdup(src->passwd); if (src->ha1) dst->ha1 = ms_strdup(src->ha1); if (src->realm) dst->realm = ms_strdup(src->realm); if (src->domain) dst->domain = ms_strdup(src->domain); if (src->tls_cert) dst->tls_cert = ms_strdup(src->tls_cert); if (src->tls_key) dst->tls_key = ms_strdup(src->tls_key); if (src->tls_cert_path) dst->tls_cert_path = ms_strdup(src->tls_cert_path); if (src->tls_key_path) dst->tls_key_path = ms_strdup(src->tls_key_path); } LinphoneAuthInfo *linphone_auth_info_clone(const LinphoneAuthInfo *ai){ return LINPHONE_AUTH_INFO(belle_sip_object_clone(BELLE_SIP_OBJECT(ai))); } LinphoneAuthInfo *linphone_auth_info_ref(LinphoneAuthInfo *obj) { return LINPHONE_AUTH_INFO(belle_sip_object_ref(obj)); } void linphone_auth_info_unref(LinphoneAuthInfo *obj) { belle_sip_object_unref(obj); } const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i) { return i->username; } const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i) { return i->passwd; } const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i) { return i->userid; } const char *linphone_auth_info_get_realm(const LinphoneAuthInfo *i) { return i->realm; } const char *linphone_auth_info_get_domain(const LinphoneAuthInfo *i) { return i->domain; } const char *linphone_auth_info_get_ha1(const LinphoneAuthInfo *i) { return i->ha1; } const char *linphone_auth_info_get_tls_cert(const LinphoneAuthInfo *i) { return i->tls_cert; } const char *linphone_auth_info_get_tls_key(const LinphoneAuthInfo *i) { return i->tls_key; } const char *linphone_auth_info_get_tls_cert_path(const LinphoneAuthInfo *i) { return i->tls_cert_path; } const char *linphone_auth_info_get_tls_key_path(const LinphoneAuthInfo *i) { return i->tls_key_path; } void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd) { if (info->passwd) { ms_free(info->passwd); info->passwd = NULL; } if (passwd && strlen(passwd) > 0) info->passwd = ms_strdup(passwd); } void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username) { if (info->username) { ms_free(info->username); info->username = NULL; } if (username && strlen(username) > 0) info->username = ms_strdup(username); } void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid) { if (info->userid) { ms_free(info->userid); info->userid = NULL; } if (userid && strlen(userid) > 0) info->userid = ms_strdup(userid); } void linphone_auth_info_set_realm(LinphoneAuthInfo *info, const char *realm) { if (info->realm) { ms_free(info->realm); info->realm = NULL; } if (realm && strlen(realm) > 0) info->realm = ms_strdup(realm); } void linphone_auth_info_set_domain(LinphoneAuthInfo *info, const char *domain) { if (info->domain) { ms_free(info->domain); info->domain = NULL; } if (domain && strlen(domain) > 0) info->domain = ms_strdup(domain); } void linphone_auth_info_set_ha1(LinphoneAuthInfo *info, const char *ha1) { if (info->ha1) { ms_free(info->ha1); info->ha1 = NULL; } if (ha1 && strlen(ha1) > 0) info->ha1 = ms_strdup(ha1); } void linphone_auth_info_set_tls_cert(LinphoneAuthInfo *info, const char *tls_cert) { if (info->tls_cert) { ms_free(info->tls_cert); info->tls_cert = NULL; } if (tls_cert && strlen(tls_cert) > 0) info->tls_cert = ms_strdup(tls_cert); } void linphone_auth_info_set_tls_key(LinphoneAuthInfo *info, const char *tls_key) { if (info->tls_key) { ms_free(info->tls_key); info->tls_key = NULL; } if (tls_key && strlen(tls_key) > 0) info->tls_key = ms_strdup(tls_key); } void linphone_auth_info_set_tls_cert_path(LinphoneAuthInfo *info, const char *tls_cert_path) { if (info->tls_cert_path) { ms_free(info->tls_cert_path); info->tls_cert_path = NULL; } if (tls_cert_path && strlen(tls_cert_path) > 0) info->tls_cert_path = ms_strdup(tls_cert_path); } void linphone_auth_info_set_tls_key_path(LinphoneAuthInfo *info, const char *tls_key_path) { if (info->tls_key_path) { ms_free(info->tls_key_path); info->tls_key_path = NULL; } if (tls_key_path && strlen(tls_key_path) > 0) info->tls_key_path = ms_strdup(tls_key_path); } static void _linphone_auth_info_uninit(LinphoneAuthInfo *obj) { if (obj->username != NULL) ms_free(obj->username); if (obj->userid != NULL) ms_free(obj->userid); if (obj->passwd != NULL) ms_free(obj->passwd); if (obj->ha1 != NULL) ms_free(obj->ha1); if (obj->realm != NULL) ms_free(obj->realm); if (obj->domain != NULL) ms_free(obj->domain); if (obj->tls_cert != NULL) ms_free(obj->tls_cert); if (obj->tls_key != NULL) ms_free(obj->tls_key); if (obj->tls_cert_path != NULL) ms_free(obj->tls_cert_path); if (obj->tls_key_path != NULL) ms_free(obj->tls_key_path); } /** * Destroys a LinphoneAuthInfo object. **/ void linphone_auth_info_destroy(LinphoneAuthInfo *obj){ belle_sip_object_unref(obj); } void linphone_auth_info_write_config(LpConfig *config, LinphoneAuthInfo *obj, int pos) { char key[50]; bool_t store_ha1_passwd = lp_config_get_int(config, "sip", "store_ha1_passwd", 1); sprintf(key, "auth_info_%i", pos); lp_config_clean_section(config, key); if (obj == NULL || lp_config_get_int(config, "sip", "store_auth_info", 1) == 0) { return; } if (!obj->ha1 && obj->realm && obj->passwd && (obj->username || obj->userid) && store_ha1_passwd) { /*compute ha1 to avoid storing clear text password*/ obj->ha1 = ms_malloc(33); sal_auth_compute_ha1(obj->userid ? obj->userid : obj->username, obj->realm, obj->passwd, obj->ha1); } if (obj->username != NULL) { lp_config_set_string(config, key, "username", obj->username); } if (obj->userid != NULL) { lp_config_set_string(config, key, "userid", obj->userid); } if (obj->ha1 != NULL) { lp_config_set_string(config, key, "ha1", obj->ha1); } if (obj->passwd != NULL) { if (store_ha1_passwd && obj->ha1) { /*if we have our ha1 and store_ha1_passwd set to TRUE, then drop the clear text password for security*/ linphone_auth_info_set_passwd(obj, NULL); } else { /*we store clear text password only if store_ha1_passwd is FALSE AND we have an ha1 to store. Otherwise, passwd would simply be removed, which might bring major auth issue*/ lp_config_set_string(config, key, "passwd", obj->passwd); } } if (obj->realm != NULL) { lp_config_set_string(config, key, "realm", obj->realm); } if (obj->domain != NULL) { lp_config_set_string(config, key, "domain", obj->domain); } if (obj->tls_cert_path != NULL) { lp_config_set_string(config, key, "client_cert_chain", obj->tls_cert_path); } if (obj->tls_key_path != NULL) { lp_config_set_string(config, key, "client_cert_key", obj->tls_key_path); } } LinphoneAuthInfo *linphone_auth_info_new_from_config_file(LpConfig * config, int pos) { char key[50]; const char *username,*userid,*passwd,*ha1,*realm,*domain,*tls_cert_path,*tls_key_path; LinphoneAuthInfo *ret; sprintf(key, "auth_info_%i", pos); if (!lp_config_has_section(config, key)) { return NULL; } username = lp_config_get_string(config, key, "username", NULL); userid = lp_config_get_string(config, key, "userid", NULL); passwd = lp_config_get_string(config, key, "passwd", NULL); ha1 = lp_config_get_string(config, key, "ha1", NULL); realm = lp_config_get_string(config, key, "realm", NULL); domain = lp_config_get_string(config, key, "domain", NULL); tls_cert_path = lp_config_get_string(config, key, "client_cert_chain", NULL); tls_key_path = lp_config_get_string(config, key, "client_cert_key", NULL); ret = linphone_auth_info_new(username, userid, passwd, ha1, realm, domain); linphone_auth_info_set_tls_cert_path(ret, tls_cert_path); linphone_auth_info_set_tls_key_path(ret, tls_key_path); return ret; } static char * remove_quotes(char * input){ char *tmp; if (*input=='"') input++; tmp=strchr(input,'"'); if (tmp) *tmp='\0'; return input; } static bool_t realm_match(const char *realm1, const char *realm2){ if (realm1==NULL && realm2==NULL) return TRUE; if (realm1!=NULL && realm2!=NULL){ if (strcmp(realm1,realm2)==0) return TRUE; else{ char tmp1[128]; char tmp2[128]; char *p1,*p2; strncpy(tmp1,realm1,sizeof(tmp1)-1); strncpy(tmp2,realm2,sizeof(tmp2)-1); p1=remove_quotes(tmp1); p2=remove_quotes(tmp2); return strcmp(p1,p2)==0; } } return FALSE; } static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain, bool_t ignore_realm){ bctbx_list_t *elem; const LinphoneAuthInfo *ret=NULL; for (elem=lc->auth_info;elem!=NULL;elem=elem->next) { LinphoneAuthInfo *pinfo = (LinphoneAuthInfo*)elem->data; if (username && pinfo->username && strcmp(username,pinfo->username)==0) { if (realm && domain){ if (pinfo->realm && realm_match(realm,pinfo->realm) && pinfo->domain && strcmp(domain,pinfo->domain)==0) { return pinfo; } } else if (realm) { if (pinfo->realm && realm_match(realm,pinfo->realm)) { if (ret!=NULL) { ms_warning("Non unique realm found for %s",username); return NULL; } ret=pinfo; } } else if (domain && pinfo->domain && strcmp(domain,pinfo->domain)==0 && (pinfo->ha1==NULL || ignore_realm)) { return pinfo; } else if (!domain && (pinfo->ha1==NULL || ignore_realm)) { return pinfo; } } } return ret; } const LinphoneAuthInfo *_linphone_core_find_tls_auth_info(LinphoneCore *lc) { bctbx_list_t *elem; for (elem=lc->auth_info;elem!=NULL;elem=elem->next) { LinphoneAuthInfo *pinfo = (LinphoneAuthInfo*)elem->data; if (pinfo->tls_cert && pinfo->tls_key) { return pinfo; } else if (pinfo->tls_cert_path && pinfo->tls_key_path) { return pinfo; } } return NULL; } const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, bool_t ignore_realm){ const LinphoneAuthInfo *ai=NULL; if (realm){ ai=find_auth_info(lc,username,realm,NULL, FALSE); if (ai==NULL && domain){ ai=find_auth_info(lc,username,realm,domain, FALSE); } } if (ai == NULL && domain != NULL) { ai=find_auth_info(lc,username,NULL,domain, ignore_realm); } if (ai==NULL){ ai=find_auth_info(lc,username,NULL,NULL, ignore_realm); } if (ai) ms_message("linphone_core_find_auth_info(): returning auth info username=%s, realm=%s", ai->username ? ai->username : "", ai->realm ? ai->realm : ""); return ai; } const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain){ return _linphone_core_find_auth_info(lc, realm, username, domain, TRUE); } /*the auth info is expected to be in the core's list*/ void linphone_core_write_auth_info(LinphoneCore *lc, LinphoneAuthInfo *ai){ int i; bctbx_list_t *elem = lc->auth_info; if (!lc->sip_conf.save_auth_info) return; for (i=0; elem != NULL; elem = elem->next, i++){ if (ai == elem->data){ linphone_auth_info_write_config(lc->config, ai, i); } } } static void write_auth_infos(LinphoneCore *lc){ bctbx_list_t *elem; int i; if (!linphone_core_ready(lc)) return; if (!lc->sip_conf.save_auth_info) return; for(elem=lc->auth_info,i=0;elem!=NULL;elem=bctbx_list_next(elem),i++){ LinphoneAuthInfo *ai=(LinphoneAuthInfo*)(elem->data); linphone_auth_info_write_config(lc->config,ai,i); } linphone_auth_info_write_config(lc->config,NULL,i); /* mark the end */ } LinphoneAuthInfo * linphone_core_create_auth_info(LinphoneCore *lc, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain) { return linphone_auth_info_new(username, userid, passwd, ha1, realm, domain); } void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){ LinphoneAuthInfo *ai; bctbx_list_t *elem; bctbx_list_t *l; int restarted_op_count=0; bool_t updating=FALSE; if (info->tls_key == NULL && info->tls_key_path == NULL && info->ha1==NULL && info->passwd==NULL){ ms_error("linphone_core_add_auth_info(): info supplied with empty password, ha1 or TLS client/key"); return; } /* find if we are attempting to modify an existing auth info */ ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain); if (ai!=NULL && ai->domain && info->domain && strcmp(ai->domain, info->domain)==0){ lc->auth_info=bctbx_list_remove(lc->auth_info,ai); linphone_auth_info_unref(ai); updating=TRUE; } lc->auth_info=bctbx_list_append(lc->auth_info,linphone_auth_info_clone(info)); /* retry pending authentication operations */ for(l=elem=sal_get_pending_auths(lc->sal);elem!=NULL;elem=elem->next){ SalOp *op=(SalOp*)elem->data; LinphoneAuthInfo *ai; const SalAuthInfo *req_sai=sal_op_get_auth_requested(op); ai=(LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,req_sai->realm,req_sai->username,req_sai->domain, FALSE); if (ai){ SalAuthInfo sai; bctbx_list_t* proxy; sai.username=ai->username; sai.userid=ai->userid; sai.realm=ai->realm; sai.password=ai->passwd; sai.ha1=ai->ha1; if (ai->tls_cert && ai->tls_key) { sal_certificates_chain_parse(&sai, ai->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse(&sai, ai->tls_key, ""); } else if (ai->tls_cert_path && ai->tls_key_path) { sal_certificates_chain_parse_file(&sai, ai->tls_cert_path, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse_file(&sai, ai->tls_key_path, ""); } /*proxy case*/ for (proxy=(bctbx_list_t*)linphone_core_get_proxy_config_list(lc);proxy!=NULL;proxy=proxy->next) { if (proxy->data == sal_op_get_user_pointer(op)) { linphone_proxy_config_set_state((LinphoneProxyConfig*)(proxy->data),LinphoneRegistrationProgress,"Authentication..."); break; } } sal_op_authenticate(op,&sai); restarted_op_count++; } } if (l){ ms_message("linphone_core_add_auth_info(): restarted [%i] operation(s) after %s auth info for\n" "\tusername: [%s]\n" "\trealm [%s]\n" "\tdomain [%s]\n", restarted_op_count, updating ? "updating" : "adding", info->username ? info->username : "", info->realm ? info->realm : "", info->domain ? info->domain : ""); } bctbx_list_free(l); write_auth_infos(lc); } void linphone_core_abort_authentication(LinphoneCore *lc, LinphoneAuthInfo *info){ } void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){ LinphoneAuthInfo *r; r=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain); if (r){ lc->auth_info=bctbx_list_remove(lc->auth_info,r); linphone_auth_info_unref(r); write_auth_infos(lc); } } const bctbx_list_t *linphone_core_get_auth_info_list(const LinphoneCore *lc){ return lc->auth_info; } void linphone_core_clear_all_auth_info(LinphoneCore *lc){ bctbx_list_t *elem; int i; for(i=0,elem=lc->auth_info;elem!=NULL;elem=bctbx_list_next(elem),i++){ LinphoneAuthInfo *info=(LinphoneAuthInfo*)elem->data; linphone_auth_info_unref(info); linphone_auth_info_write_config(lc->config,NULL,i); } bctbx_list_free(lc->auth_info); lc->auth_info=NULL; } linphone-3.12.0/coreapi/bellesip_sal/000077500000000000000000000000001313432737600175025ustar00rootroot00000000000000linphone-3.12.0/coreapi/bellesip_sal/sal_address_impl.c000066400000000000000000000233521313432737600231600ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" /**/ /* Address manipulation API*/ SalAddress * sal_address_new(const char *uri){ belle_sip_header_address_t* result; if (uri) { result=belle_sip_header_address_fast_parse (uri); /*may return NULL*/ } else { result = belle_sip_header_address_new(); belle_sip_header_address_set_uri(result,belle_sip_uri_new()); } if (result) belle_sip_object_ref(result); return (SalAddress *)result; } SalAddress * sal_address_clone(const SalAddress *addr){ return (SalAddress *) belle_sip_object_ref(belle_sip_object_clone(BELLE_SIP_OBJECT(addr))); } const char *sal_address_get_scheme(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); belle_generic_uri_t* generic_uri = belle_sip_header_address_get_absolute_uri(header_addr); if (uri) { if (belle_sip_uri_is_secure(uri)) return "sips"; else return "sip"; } else if (generic_uri) return belle_generic_uri_get_scheme(generic_uri); else return NULL; } void sal_address_set_secure(SalAddress *addr, bool_t enabled){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) belle_sip_uri_set_secure(uri,enabled); } bool_t sal_address_is_secure(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) return belle_sip_uri_is_secure(uri); return FALSE; } const char *sal_address_get_display_name(const SalAddress* addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); return belle_sip_header_address_get_displayname(header_addr); } const char *sal_address_get_display_name_unquoted(const SalAddress *addr){ return sal_address_get_display_name(addr); } #define SAL_ADDRESS_GET(addr,param) \ {belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);\ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);\ if (uri) {\ return belle_sip_uri_get_##param(uri);\ } else\ return NULL;} #define SAL_ADDRESS_SET(addr,param,value) {\ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);\ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);\ belle_sip_uri_set_##param(uri,value);} const char *sal_address_get_username(const SalAddress *addr){ SAL_ADDRESS_GET(addr,user) } const char *sal_address_get_domain(const SalAddress *addr){ SAL_ADDRESS_GET(addr,host) } int sal_address_get_port(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) { return belle_sip_uri_get_port(uri); } else return -1; } SalTransport sal_address_get_transport(const SalAddress* addr){ const char *transport=sal_address_get_transport_name(addr); if (transport) return sal_transport_parse(transport); else return SalTransportUDP; }; const char* sal_address_get_transport_name(const SalAddress* addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) { return belle_sip_uri_get_transport_param(uri); } return NULL; } const char *sal_address_get_method_param(const SalAddress *addr) { belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) { return belle_sip_uri_get_method_param(uri); } return NULL; } void sal_address_set_display_name(SalAddress *addr, const char *display_name){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_header_address_set_displayname(header_addr,display_name); } void sal_address_set_username(SalAddress *addr, const char *username){ SAL_ADDRESS_SET(addr,user,username); } void sal_address_set_password(SalAddress *addr, const char *passwd){ SAL_ADDRESS_SET(addr,user_password,passwd); } const char* sal_address_get_password(const SalAddress *addr){ SAL_ADDRESS_GET(addr,user_password); } void sal_address_set_domain(SalAddress *addr, const char *host){ SAL_ADDRESS_SET(addr,host,host); } void sal_address_set_port(SalAddress *addr, int port){ SAL_ADDRESS_SET(addr,port,port); } void sal_address_clean(SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri=belle_sip_header_address_get_uri(header_addr); if (uri) { belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(uri)); belle_sip_uri_headers_clean(uri); } belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(header_addr)); return ; } char *sal_address_as_string(const SalAddress *addr){ char tmp[1024]={0}; size_t off=0; belle_sip_object_marshal((belle_sip_object_t*)addr,tmp,sizeof(tmp),&off); return ms_strdup(tmp); } bool_t sal_address_is_sip(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); return belle_sip_header_address_get_uri(header_addr) != NULL; } char *sal_address_as_string_uri_only(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* sip_uri = belle_sip_header_address_get_uri(header_addr); belle_generic_uri_t* absolute_uri = belle_sip_header_address_get_absolute_uri(header_addr); char tmp[1024]={0}; size_t off=0; belle_sip_object_t* uri; if (sip_uri) { uri=(belle_sip_object_t*)sip_uri; } else if (absolute_uri) { uri=(belle_sip_object_t*)absolute_uri; } else { ms_error("Cannot generate string for addr [%p] with null uri",addr); return NULL; } belle_sip_object_marshal(uri,tmp,sizeof(tmp),&off); return ms_strdup(tmp); } void sal_address_set_param(SalAddress *addr,const char* name,const char* value){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); belle_sip_parameters_set_parameter(parameters,name,value); return ; } bool_t sal_address_has_param(const SalAddress *addr, const char *name){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); return belle_sip_parameters_has_parameter(parameters, name); } const char * sal_address_get_param(const SalAddress *addr, const char *name) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); return belle_sip_parameters_get_parameter(parameters, name); } void sal_address_set_params(SalAddress *addr, const char *params){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); belle_sip_parameters_set(parameters,params); } void sal_address_set_uri_param(SalAddress *addr, const char *name, const char *value) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); belle_sip_parameters_set_parameter(parameters, name, value); } void sal_address_set_uri_params(SalAddress *addr, const char *params){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); belle_sip_parameters_set(parameters,params); } bool_t sal_address_has_uri_param(const SalAddress *addr, const char *name){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); return belle_sip_parameters_has_parameter(parameters, name); } const char * sal_address_get_uri_param(const SalAddress *addr, const char *name) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); return belle_sip_parameters_get_parameter(parameters, name); } void sal_address_set_header(SalAddress *addr, const char *header_name, const char *header_value){ belle_sip_uri_set_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr)),header_name, header_value); } const char* sal_address_get_header(const SalAddress *addr, const char *name){ return belle_sip_uri_get_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr)),name); } void sal_address_set_transport(SalAddress* addr,SalTransport transport){ if (!sal_address_is_secure(addr)){ SAL_ADDRESS_SET(addr,transport_param,sal_transport_to_string(transport)); } } void sal_address_set_transport_name(SalAddress* addr,const char *transport){ SAL_ADDRESS_SET(addr,transport_param,transport); } void sal_address_set_method_param(SalAddress *addr, const char *method) { SAL_ADDRESS_SET(addr, method_param, method); } SalAddress *sal_address_ref(SalAddress *addr){ return (SalAddress*)belle_sip_object_ref(BELLE_SIP_HEADER_ADDRESS(addr)); } void sal_address_unref(SalAddress *addr){ belle_sip_object_unref(BELLE_SIP_HEADER_ADDRESS(addr)); } bool_t sal_address_is_ipv6(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri){ const char *host=belle_sip_uri_get_host(uri); if (host && strchr(host,':')!=NULL) return TRUE; } return FALSE; } void sal_address_destroy(SalAddress *addr){ sal_address_unref(addr); } linphone-3.12.0/coreapi/bellesip_sal/sal_impl.c000066400000000000000000001610211313432737600214470ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif typedef struct belle_sip_certificates_chain_t _SalCertificatesChain; typedef struct belle_sip_signing_key_t _SalSigningKey; /* rfc3323 4.2 Expressing Privacy Preferences When a Privacy header is constructed, it MUST consist of either the value 'none', or one or more of the values 'user', 'header' and 'session' (each of which MUST appear at most once) which MAY in turn be followed by the 'critical' indicator. */ void sal_op_set_privacy_from_message(SalOp* op,belle_sip_message_t* msg) { belle_sip_header_privacy_t* privacy = belle_sip_message_get_header_by_type(msg,belle_sip_header_privacy_t); if (!privacy) { sal_op_set_privacy(op,SalPrivacyNone); } else { belle_sip_list_t* privacy_list=belle_sip_header_privacy_get_privacy(privacy); sal_op_set_privacy(op,0); for (;privacy_list!=NULL;privacy_list=privacy_list->next) { char* privacy_value=(char*)privacy_list->data; if(strcmp(sal_privacy_to_string(SalPrivacyCritical),privacy_value) == 0) sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyCritical); if(strcmp(sal_privacy_to_string(SalPrivacyHeader),privacy_value) == 0) sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyHeader); if(strcmp(sal_privacy_to_string(SalPrivacyId),privacy_value) == 0) sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyId); if(strcmp(sal_privacy_to_string(SalPrivacyNone),privacy_value) == 0) { sal_op_set_privacy(op,SalPrivacyNone); break; } if(strcmp(sal_privacy_to_string(SalPrivacySession),privacy_value) == 0) sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacySession); if(strcmp(sal_privacy_to_string(SalPrivacyUser),privacy_value) == 0) sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyUser); } } } static void set_tls_properties(Sal *ctx); void _belle_sip_log(const char *domain, belle_sip_log_level lev, const char *fmt, va_list args) { int ortp_level; switch(lev) { case BELLE_SIP_LOG_FATAL: ortp_level=ORTP_FATAL; break; case BELLE_SIP_LOG_ERROR: ortp_level=ORTP_ERROR; break; case BELLE_SIP_LOG_WARNING: ortp_level=ORTP_WARNING; break; case BELLE_SIP_LOG_MESSAGE: ortp_level=ORTP_MESSAGE; break; case BELLE_SIP_LOG_DEBUG: default: ortp_level=ORTP_DEBUG; break; } if (ortp_log_level_enabled("belle-sip", ortp_level)){ ortp_logv("belle-sip", ortp_level,fmt,args); } } void sal_enable_log(){ sal_set_log_level(ORTP_MESSAGE); } void sal_disable_log() { sal_set_log_level(ORTP_ERROR); } void sal_set_log_level(OrtpLogLevel level) { belle_sip_log_level belle_sip_level; if ((level&ORTP_FATAL) != 0) { belle_sip_level = BELLE_SIP_LOG_FATAL; } else if ((level&ORTP_ERROR) != 0) { belle_sip_level = BELLE_SIP_LOG_ERROR; } else if ((level&ORTP_WARNING) != 0) { belle_sip_level = BELLE_SIP_LOG_WARNING; } else if ((level&ORTP_MESSAGE) != 0) { belle_sip_level = BELLE_SIP_LOG_MESSAGE; } else if (((level&ORTP_DEBUG) != 0) || ((level&ORTP_TRACE) != 0)) { belle_sip_level = BELLE_SIP_LOG_DEBUG; } else { //well, this should never occurs but... belle_sip_level = BELLE_SIP_LOG_MESSAGE; } belle_sip_set_log_level(belle_sip_level); } void sal_add_pending_auth(Sal *sal, SalOp *op){ if (bctbx_list_find(sal->pending_auths,op)==NULL){ sal->pending_auths=bctbx_list_append(sal->pending_auths,op); op->has_auth_pending=TRUE; } } void sal_remove_pending_auth(Sal *sal, SalOp *op){ if (op->has_auth_pending){ op->has_auth_pending=FALSE; if (bctbx_list_find(sal->pending_auths,op)){ sal->pending_auths=bctbx_list_remove(sal->pending_auths,op); } } } void sal_process_authentication(SalOp *op) { belle_sip_request_t* initial_request=belle_sip_transaction_get_request((belle_sip_transaction_t*)op->pending_auth_transaction); belle_sip_request_t* new_request; bool_t is_within_dialog=FALSE; belle_sip_list_t* auth_list=NULL; belle_sip_auth_event_t* auth_event; belle_sip_response_t *response=belle_sip_transaction_get_response((belle_sip_transaction_t*)op->pending_auth_transaction); belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(initial_request,belle_sip_header_from_t); belle_sip_uri_t *from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from); if (strcasecmp(belle_sip_uri_get_host(from_uri),"anonymous.invalid")==0){ /*prefer using the from from the SalOp*/ from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)sal_op_get_from_address(op)); } if (op->dialog && belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_CONFIRMED) { new_request = belle_sip_dialog_create_request_from(op->dialog,initial_request); if (!new_request) new_request = belle_sip_dialog_create_queued_request_from(op->dialog,initial_request); is_within_dialog=TRUE; } else { new_request=initial_request; belle_sip_message_remove_header(BELLE_SIP_MESSAGE(new_request),BELLE_SIP_AUTHORIZATION); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(new_request),BELLE_SIP_PROXY_AUTHORIZATION); } if (new_request==NULL) { ms_error("sal_process_authentication() op=[%p] cannot obtain new request from dialog.",op); return; } if (belle_sip_provider_add_authorization(op->base.root->prov,new_request,response,from_uri,&auth_list,op->base.realm)) { if (is_within_dialog) { sal_op_send_request(op,new_request); } else { sal_op_resend_request(op,new_request); } sal_remove_pending_auth(op->base.root,op); }else { belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(response,belle_sip_header_from_t); char *tmp=belle_sip_object_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from))); ms_message("No auth info found for [%s]",tmp); belle_sip_free(tmp); sal_add_pending_auth(op->base.root,op); if (is_within_dialog) { belle_sip_object_unref(new_request); } } /*always store auth info, for case of wrong credential*/ if (op->auth_info) { sal_auth_info_delete(op->auth_info); op->auth_info=NULL; } if (auth_list){ auth_event=(belle_sip_auth_event_t*)(auth_list->data); op->auth_info=sal_auth_info_create(auth_event); belle_sip_list_free_with_data(auth_list,(void (*)(void*))belle_sip_auth_event_destroy); } } static void process_dialog_terminated(void *sal, const belle_sip_dialog_terminated_event_t *event){ belle_sip_dialog_t* dialog = belle_sip_dialog_terminated_event_get_dialog(event); SalOp* op = belle_sip_dialog_get_application_data(dialog); if (op && op->callbacks && op->callbacks->process_dialog_terminated) { op->callbacks->process_dialog_terminated(op,event); } else { ms_error("sal process_dialog_terminated no op found for this dialog [%p], ignoring",dialog); } } static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ belle_sip_client_transaction_t*client_transaction; SalOp* op; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(belle_sip_io_error_event_get_source(event),belle_sip_client_transaction_t)) { client_transaction=BELLE_SIP_CLIENT_TRANSACTION(belle_sip_io_error_event_get_source(event)); op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); /*also reset auth count on IO error*/ op->auth_requests=0; if (op->callbacks && op->callbacks->process_io_error) { op->callbacks->process_io_error(op,event); } } else { /*ms_error("sal process_io_error not implemented yet for non transaction");*/ /*nop, because already handle at transaction layer*/ } } static void process_request_event(void *ud, const belle_sip_request_event_t *event) { Sal *sal=(Sal*)ud; SalOp* op=NULL; belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_dialog_t* dialog=belle_sip_request_event_get_dialog(event); belle_sip_header_address_t* origin_address; belle_sip_header_address_t* address=NULL; belle_sip_header_from_t* from_header; belle_sip_header_to_t* to; belle_sip_header_diversion_t* diversion; belle_sip_response_t* resp; belle_sip_header_t *evh; const char *method=belle_sip_request_get_method(req); belle_sip_header_contact_t* remote_contact = belle_sip_message_get_header_by_type(req, belle_sip_header_contact_t); from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t); if (dialog) { op=(SalOp*)belle_sip_dialog_get_application_data(dialog); if (op == NULL && strcmp("NOTIFY",method) == 0) { /*special case for Dialog created by notify mathing subscribe*/ belle_sip_transaction_t * sub_trans = belle_sip_dialog_get_last_transaction(dialog); op = (SalOp*)belle_sip_transaction_get_application_data(sub_trans); } if (op==NULL || op->state==SalOpStateTerminated){ ms_warning("Receiving request for null or terminated op [%p], ignored",op); return; } }else{ /*handle the case where we are receiving a request with to tag but it is not belonging to any dialog*/ belle_sip_header_to_t *to = belle_sip_message_get_header_by_type(req, belle_sip_header_to_t); if ((strcmp("INVITE",method)==0 || strcmp("NOTIFY",method)==0) && (belle_sip_header_to_get_tag(to) != NULL)) { ms_warning("Receiving %s with to-tag but no know dialog here. Rejecting.", method); resp=belle_sip_response_create_from_request(req,481); belle_sip_provider_send_response(sal->prov,resp); return; /* by default (eg. when a to-tag is present), out of dialog ACK are automatically handled in lower layers (belle-sip) but in case it misses, it will be forwarded to us */ } else if (strcmp("ACK",method)==0 && (belle_sip_header_to_get_tag(to) == NULL)) { ms_warning("Receiving ACK without to-tag but no know dialog here. Ignoring"); return; } if (strcmp("INVITE",method)==0) { op=sal_op_new(sal); op->dir=SalOpDirIncoming; sal_op_call_fill_cbs(op); }else if ((strcmp("SUBSCRIBE",method)==0 || strcmp("NOTIFY",method)==0) && (evh=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event"))!=NULL) { op=sal_op_new(sal); op->dir=SalOpDirIncoming; if (strncmp(belle_sip_header_get_unparsed_value(evh),"presence",strlen("presence"))==0){ sal_op_presence_fill_cbs(op); }else sal_op_subscribe_fill_cbs(op); }else if (strcmp("MESSAGE",method)==0) { op=sal_op_new(sal); op->dir=SalOpDirIncoming; sal_op_message_fill_cbs(op); }else if (strcmp("OPTIONS",method)==0) { resp=belle_sip_response_create_from_request(req,200); belle_sip_provider_send_response(sal->prov,resp); return; }else if (strcmp("INFO",method)==0) { resp=belle_sip_response_create_from_request(req,481);/*INFO out of call dialogs are not allowed*/ belle_sip_provider_send_response(sal->prov,resp); return; }else if (strcmp("BYE",method)==0) { resp=belle_sip_response_create_from_request(req,481);/*out of dialog BYE */ belle_sip_provider_send_response(sal->prov,resp); return; }else if (strcmp("CANCEL",method)==0) { resp=belle_sip_response_create_from_request(req,481);/*out of dialog CANCEL */ belle_sip_provider_send_response(sal->prov,resp); return; }else if (sal->enable_test_features && strcmp("PUBLISH",method)==0) { resp=belle_sip_response_create_from_request(req,200);/*out of dialog PUBLISH */ belle_sip_message_add_header((belle_sip_message_t*)resp,belle_sip_header_create("SIP-Etag","4441929FFFZQOA")); belle_sip_provider_send_response(sal->prov,resp); return; }else { ms_error("sal process_request_event not implemented yet for method [%s]",belle_sip_request_get_method(req)); resp=belle_sip_response_create_from_request(req,405); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp) ,BELLE_SIP_HEADER(belle_sip_header_allow_create("INVITE, CANCEL, ACK, BYE, SUBSCRIBE, NOTIFY, MESSAGE, OPTIONS, INFO"))); belle_sip_provider_send_response(sal->prov,resp); return; } } if (!op->base.from_address) { if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))) address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(from_header)))) address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); else ms_error("Cannot not find from uri from request [%p]",req); sal_op_set_from_address(op,(SalAddress*)address); belle_sip_object_unref(address); } if( remote_contact ){ __sal_op_set_remote_contact(op, belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(remote_contact))); } if (!op->base.to_address) { to=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_to_t); if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to))) address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(to)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to))); else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(to)))) address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(to)) ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(to))); else ms_error("Cannot not find to uri from request [%p]",req); sal_op_set_to_address(op,(SalAddress*)address); belle_sip_object_unref(address); } if(!op->base.diversion_address){ diversion=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_diversion_t); if (diversion) { if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(diversion))) address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(diversion)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(diversion))); else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(diversion)))) address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(diversion)) ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(diversion))); else ms_warning("Cannot not find diversion header from request [%p]",req); if (address) { sal_op_set_diversion_address(op,(SalAddress*)address); belle_sip_object_unref(address); } } } if (!op->base.origin) { /*set origin uri*/ origin_address=belle_sip_header_address_create(NULL,belle_sip_request_extract_origin(req)); __sal_op_set_network_origin_address(op,(SalAddress*)origin_address); belle_sip_object_unref(origin_address); } if (!op->base.remote_ua) { sal_op_set_remote_ua(op,BELLE_SIP_MESSAGE(req)); } if (!op->base.call_id) { op->base.call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_call_id_t)))); } /*It is worth noting that proxies can (and will) remove this header field*/ sal_op_set_privacy_from_message(op,(belle_sip_message_t*)req); sal_op_assign_recv_headers(op,(belle_sip_message_t*)req); if (op->callbacks && op->callbacks->process_request_event) { op->callbacks->process_request_event(op,event); } else { ms_error("sal process_request_event not implemented yet"); } } static void process_response_event(void *user_ctx, const belle_sip_response_event_t *event){ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_response_t* response = belle_sip_response_event_get_response(event); int response_code = belle_sip_response_get_status_code(response); if (!client_transaction) { ms_warning("Discarding stateless response [%i]",response_code); return; } else { SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); belle_sip_header_contact_t* remote_contact = belle_sip_message_get_header_by_type(response, belle_sip_header_contact_t); if (op->state == SalOpStateTerminated) { belle_sip_message("Op [%p] is terminated, nothing to do with this [%i]", op, response_code); return; } /*do it all the time, since we can receive provisional responses from a different instance than the final one*/ sal_op_set_remote_ua(op,BELLE_SIP_MESSAGE(response)); if(remote_contact) { __sal_op_set_remote_contact(op, belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(remote_contact))); } if (!op->base.call_id) { op->base.call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(response), belle_sip_header_call_id_t)))); } sal_op_assign_recv_headers(op,(belle_sip_message_t*)response); if (op->callbacks && op->callbacks->process_response_event) { /*handle authorization*/ switch (response_code) { case 200: break; case 401: case 407: if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) { /*only bye are completed*/ belle_sip_message("Op is in state terminating, nothing else to do "); } else { if (op->pending_auth_transaction){ belle_sip_object_unref(op->pending_auth_transaction); op->pending_auth_transaction=NULL; } if (++op->auth_requests > 2) { ms_warning("Auth info cannot be found for op [%s/%s] after 2 attempts, giving up",sal_op_get_from(op) ,sal_op_get_to(op)); op->base.root->callbacks.auth_failure(op,op->auth_info); sal_remove_pending_auth(op->base.root,op); } else { op->pending_auth_transaction=(belle_sip_client_transaction_t*)belle_sip_object_ref(client_transaction); sal_process_authentication(op); return; } } break; case 403: if (op->auth_info) op->base.root->callbacks.auth_failure(op,op->auth_info); break; } if (response_code >= 180 && response_code !=401 && response_code !=407 && response_code !=403) { /*not an auth request*/ op->auth_requests=0; } op->callbacks->process_response_event(op,event); } else { ms_error("Unhandled event response [%p]",event); } } } static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event); SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); if (op && op->callbacks && op->callbacks->process_timeout) { op->callbacks->process_timeout(op,event); } else { ms_error("Unhandled event timeout [%p]",event); } } static void process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { belle_sip_client_transaction_t* client_transaction = belle_sip_transaction_terminated_event_get_client_transaction(event); belle_sip_server_transaction_t* server_transaction = belle_sip_transaction_terminated_event_get_server_transaction(event); belle_sip_transaction_t* trans; SalOp* op; if(client_transaction) trans=BELLE_SIP_TRANSACTION(client_transaction); else trans=BELLE_SIP_TRANSACTION(server_transaction); op = (SalOp*)belle_sip_transaction_get_application_data(trans); if (op && op->callbacks && op->callbacks->process_transaction_terminated) { op->callbacks->process_transaction_terminated(op,event); } else { ms_message("Unhandled transaction terminated [%p]",trans); } if (op) { sal_op_unref(op); /*because every transaction ref op*/ belle_sip_transaction_set_application_data(trans,NULL); /*no longuer reference something we do not ref to avoid futur access of a released op*/ } } static void process_auth_requested(void *sal, belle_sip_auth_event_t *event) { SalAuthInfo* auth_info = sal_auth_info_create(event); ((Sal*)sal)->callbacks.auth_requested(sal,auth_info); belle_sip_auth_event_set_passwd(event,(const char*)auth_info->password); belle_sip_auth_event_set_ha1(event,(const char*)auth_info->ha1); belle_sip_auth_event_set_userid(event,(const char*)auth_info->userid); belle_sip_auth_event_set_signing_key(event,(belle_sip_signing_key_t *)auth_info->key); belle_sip_auth_event_set_client_certificates_chain(event,(belle_sip_certificates_chain_t* )auth_info->certificates); sal_auth_info_delete(auth_info); } Sal * sal_init(MSFactory *factory){ belle_sip_listener_callbacks_t listener_callbacks; Sal * sal=ms_new0(Sal,1); /*belle_sip_object_enable_marshal_check(TRUE);*/ sal->auto_contacts=TRUE; sal->factory = factory; /*first create the stack, which initializes the belle-sip object's pool for this thread*/ belle_sip_set_log_handler(_belle_sip_log); sal->stack = belle_sip_stack_new(NULL); sal->user_agent=belle_sip_header_user_agent_new(); #if defined(PACKAGE_NAME) && defined(LIBLINPHONE_VERSION) belle_sip_header_user_agent_add_product(sal->user_agent, PACKAGE_NAME "/" LIBLINPHONE_VERSION); #else belle_sip_header_user_agent_add_product(sal->user_agent, "Unknown"); #endif sal_append_stack_string_to_user_agent(sal); belle_sip_object_ref(sal->user_agent); sal->prov = belle_sip_stack_create_provider(sal->stack,NULL); sal_nat_helper_enable(sal,TRUE); memset(&listener_callbacks,0,sizeof(listener_callbacks)); listener_callbacks.process_dialog_terminated=process_dialog_terminated; listener_callbacks.process_io_error=process_io_error; listener_callbacks.process_request_event=process_request_event; listener_callbacks.process_response_event=process_response_event; listener_callbacks.process_timeout=process_timeout; listener_callbacks.process_transaction_terminated=process_transaction_terminated; listener_callbacks.process_auth_requested=process_auth_requested; sal->listener=belle_sip_listener_create_from_callbacks(&listener_callbacks,sal); belle_sip_provider_add_sip_listener(sal->prov,sal->listener); sal->tls_verify=TRUE; sal->tls_verify_cn=TRUE; sal->refresher_retry_after=60000; /*default value in ms*/ sal->enable_sip_update=TRUE; sal->pending_trans_checking=TRUE; sal->ssl_config = NULL; return sal; } void sal_set_user_pointer(Sal *sal, void *user_data){ sal->up=user_data; } void *sal_get_user_pointer(const Sal *sal){ return sal->up; } static void unimplemented_stub(void){ ms_warning("Unimplemented SAL callback"); } void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ memcpy(&ctx->callbacks,cbs,sizeof(*cbs)); if (ctx->callbacks.call_received==NULL) ctx->callbacks.call_received=(SalOnCallReceived)unimplemented_stub; if (ctx->callbacks.call_ringing==NULL) ctx->callbacks.call_ringing=(SalOnCallRinging)unimplemented_stub; if (ctx->callbacks.call_accepted==NULL) ctx->callbacks.call_accepted=(SalOnCallAccepted)unimplemented_stub; if (ctx->callbacks.call_failure==NULL) ctx->callbacks.call_failure=(SalOnCallFailure)unimplemented_stub; if (ctx->callbacks.call_terminated==NULL) ctx->callbacks.call_terminated=(SalOnCallTerminated)unimplemented_stub; if (ctx->callbacks.call_released==NULL) ctx->callbacks.call_released=(SalOnCallReleased)unimplemented_stub; if (ctx->callbacks.call_updating==NULL) ctx->callbacks.call_updating=(SalOnCallUpdating)unimplemented_stub; if (ctx->callbacks.auth_failure==NULL) ctx->callbacks.auth_failure=(SalOnAuthFailure)unimplemented_stub; if (ctx->callbacks.register_success==NULL) ctx->callbacks.register_success=(SalOnRegisterSuccess)unimplemented_stub; if (ctx->callbacks.register_failure==NULL) ctx->callbacks.register_failure=(SalOnRegisterFailure)unimplemented_stub; if (ctx->callbacks.dtmf_received==NULL) ctx->callbacks.dtmf_received=(SalOnDtmfReceived)unimplemented_stub; if (ctx->callbacks.notify==NULL) ctx->callbacks.notify=(SalOnNotify)unimplemented_stub; if (ctx->callbacks.subscribe_received==NULL) ctx->callbacks.subscribe_received=(SalOnSubscribeReceived)unimplemented_stub; if (ctx->callbacks.incoming_subscribe_closed==NULL) ctx->callbacks.incoming_subscribe_closed=(SalOnIncomingSubscribeClosed)unimplemented_stub; if (ctx->callbacks.parse_presence_requested==NULL) ctx->callbacks.parse_presence_requested=(SalOnParsePresenceRequested)unimplemented_stub; if (ctx->callbacks.convert_presence_to_xml_requested==NULL) ctx->callbacks.convert_presence_to_xml_requested=(SalOnConvertPresenceToXMLRequested)unimplemented_stub; if (ctx->callbacks.notify_presence==NULL) ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub; if (ctx->callbacks.subscribe_presence_received==NULL) ctx->callbacks.subscribe_presence_received=(SalOnSubscribePresenceReceived)unimplemented_stub; if (ctx->callbacks.message_received==NULL) ctx->callbacks.message_received=(SalOnMessageReceived)unimplemented_stub; if (ctx->callbacks.ping_reply==NULL) ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub; if (ctx->callbacks.auth_requested==NULL) ctx->callbacks.auth_requested=(SalOnAuthRequested)unimplemented_stub; if (ctx->callbacks.info_received==NULL) ctx->callbacks.info_received=(SalOnInfoReceived)unimplemented_stub; if (ctx->callbacks.on_publish_response==NULL) ctx->callbacks.on_publish_response=(SalOnPublishResponse)unimplemented_stub; if (ctx->callbacks.on_expire==NULL) ctx->callbacks.on_expire=(SalOnExpire)unimplemented_stub; } void sal_uninit(Sal* sal){ belle_sip_object_unref(sal->user_agent); belle_sip_object_unref(sal->prov); belle_sip_object_unref(sal->stack); belle_sip_object_unref(sal->listener); if (sal->supported) belle_sip_object_unref(sal->supported); bctbx_list_free_with_data(sal->supported_tags,ms_free); bctbx_list_free_with_data(sal->supported_content_types, ms_free); if (sal->uuid) ms_free(sal->uuid); if (sal->root_ca) ms_free(sal->root_ca); if (sal->root_ca_data) ms_free(sal->root_ca_data); ms_free(sal); }; int sal_transport_available(Sal *sal, SalTransport t){ switch(t){ case SalTransportUDP: case SalTransportTCP: return TRUE; case SalTransportTLS: return belle_sip_stack_tls_available(sal->stack); case SalTransportDTLS: return FALSE; } return FALSE; } bool_t sal_content_encoding_available(Sal *sal, const char *content_encoding) { return (bool_t)belle_sip_stack_content_encoding_available(sal->stack, content_encoding); } static int sal_add_listen_port(Sal *ctx, SalAddress* addr, bool_t is_tunneled){ int result; belle_sip_listening_point_t* lp; if (is_tunneled){ #ifdef TUNNEL_ENABLED if (sal_address_get_transport(addr)!=SalTransportUDP){ ms_error("Tunneled mode is only available for UDP kind of transports."); return -1; } lp = belle_sip_tunnel_listening_point_new(ctx->stack, ctx->tunnel_client); if (!lp){ ms_error("Could not create tunnel listening point."); return -1; } #else ms_error("No tunnel support in library."); return -1; #endif }else{ lp = belle_sip_stack_create_listening_point(ctx->stack, sal_address_get_domain(addr), sal_address_get_port(addr), sal_transport_to_string(sal_address_get_transport(addr))); } if (lp) { belle_sip_listening_point_set_keep_alive(lp,ctx->keep_alive); result = belle_sip_provider_add_listening_point(ctx->prov,lp); if (sal_address_get_transport(addr)==SalTransportTLS) { set_tls_properties(ctx); } } else { return -1; } return result; } int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_tunneled) { SalAddress* sal_addr = sal_address_new(NULL); int result; sal_address_set_domain(sal_addr,addr); sal_address_set_port(sal_addr,port); sal_address_set_transport(sal_addr,tr); result = sal_add_listen_port(ctx, sal_addr, is_tunneled); sal_address_destroy(sal_addr); return result; } static void remove_listening_point(belle_sip_listening_point_t* lp,belle_sip_provider_t* prov) { belle_sip_provider_remove_listening_point(prov,lp); } int sal_get_listening_port(Sal *ctx, SalTransport tr){ const char *tpn=sal_transport_to_string(tr); belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(ctx->prov, tpn); if (lp){ return belle_sip_listening_point_get_port(lp); } return 0; } int sal_unlisten_ports(Sal *ctx){ const belle_sip_list_t * lps = belle_sip_provider_get_listening_points(ctx->prov); belle_sip_list_t * tmp_list = belle_sip_list_copy(lps); belle_sip_list_for_each2 (tmp_list,(void (*)(void*,void*))remove_listening_point,ctx->prov); belle_sip_list_free(tmp_list); ms_message("sal_unlisten_ports done"); return 0; } ortp_socket_t sal_get_socket(Sal *ctx){ ms_warning("sal_get_socket is deprecated"); return -1; } void sal_set_user_agent(Sal *ctx, const char *user_agent){ belle_sip_header_user_agent_set_products(ctx->user_agent,NULL); belle_sip_header_user_agent_add_product(ctx->user_agent,user_agent); return ; } const char* sal_get_user_agent(Sal *ctx){ static char user_agent[255]; belle_sip_header_user_agent_get_products_as_string(ctx->user_agent, user_agent, 254); return user_agent; } void sal_append_stack_string_to_user_agent(Sal *ctx) { char stack_string[64]; snprintf(stack_string, sizeof(stack_string) - 1, "(belle-sip/%s)", belle_sip_version_to_string()); belle_sip_header_user_agent_add_product(ctx->user_agent, stack_string); } /*keepalive period in ms*/ void sal_set_keepalive_period(Sal *ctx,unsigned int value){ const belle_sip_list_t* iterator; belle_sip_listening_point_t* lp; ctx->keep_alive=value; for (iterator=belle_sip_provider_get_listening_points(ctx->prov);iterator!=NULL;iterator=iterator->next) { lp=(belle_sip_listening_point_t*)iterator->data; if (ctx->use_tcp_tls_keep_alive || strcasecmp(belle_sip_listening_point_get_transport(lp),"udp")==0) { belle_sip_listening_point_set_keep_alive(lp,ctx->keep_alive); } } } int sal_set_tunnel(Sal *ctx, void *tunnelclient) { #ifdef TUNNEL_ENABLED ctx->tunnel_client=tunnelclient; return 0; #else return -1; #endif } /** * returns keepalive period in ms * 0 desactiaved * */ unsigned int sal_get_keepalive_period(Sal *ctx){ return ctx->keep_alive; } void sal_use_session_timers(Sal *ctx, int expires){ ctx->session_expires=expires; return ; } void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec){ ctx->one_matching_codec=one_matching_codec; } void sal_use_rport(Sal *ctx, bool_t use_rports){ belle_sip_provider_enable_rport(ctx->prov,use_rports); ms_message("Sal use rport [%s]",use_rports?"enabled":"disabled"); return ; } static void set_tls_properties(Sal *ctx){ belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(ctx->prov,"TLS"); if (lp){ belle_sip_tls_listening_point_t *tlp=BELLE_SIP_TLS_LISTENING_POINT(lp); belle_tls_crypto_config_t *crypto_config = belle_tls_crypto_config_new(); int verify_exceptions = BELLE_TLS_VERIFY_NONE; if (!ctx->tls_verify) verify_exceptions = BELLE_TLS_VERIFY_ANY_REASON; else if (!ctx->tls_verify_cn) verify_exceptions = BELLE_TLS_VERIFY_CN_MISMATCH; belle_tls_crypto_config_set_verify_exceptions(crypto_config, verify_exceptions); if (ctx->root_ca != NULL) belle_tls_crypto_config_set_root_ca(crypto_config, ctx->root_ca); if (ctx->root_ca_data != NULL) belle_tls_crypto_config_set_root_ca_data(crypto_config, ctx->root_ca_data); if (ctx->ssl_config != NULL) belle_tls_crypto_config_set_ssl_config(crypto_config, ctx->ssl_config); belle_sip_tls_listening_point_set_crypto_config(tlp, crypto_config); belle_sip_object_unref(crypto_config); } } void sal_set_root_ca(Sal* ctx, const char* rootCa) { if (ctx->root_ca) { ms_free(ctx->root_ca); ctx->root_ca = NULL; } if (rootCa) ctx->root_ca = ms_strdup(rootCa); set_tls_properties(ctx); return; } void sal_set_root_ca_data(Sal* ctx, const char* data) { if (ctx->root_ca_data) { ms_free(ctx->root_ca_data); ctx->root_ca_data = NULL; } if (data) ctx->root_ca_data = ms_strdup(data); set_tls_properties(ctx); return; } void sal_verify_server_certificates(Sal *ctx, bool_t verify){ ctx->tls_verify=verify; set_tls_properties(ctx); return ; } void sal_verify_server_cn(Sal *ctx, bool_t verify){ ctx->tls_verify_cn=verify; set_tls_properties(ctx); return ; } void sal_set_ssl_config(Sal *ctx, void *ssl_config) { ctx->ssl_config = ssl_config; set_tls_properties(ctx); return ; } void sal_use_tcp_tls_keepalive(Sal *ctx, bool_t enabled) { ctx->use_tcp_tls_keep_alive=enabled; } int sal_iterate(Sal *sal){ belle_sip_stack_sleep(sal->stack,0); return 0; } bctbx_list_t * sal_get_pending_auths(Sal *sal){ return bctbx_list_copy(sal->pending_auths); } /*misc*/ void sal_get_default_local_ip(Sal *sal, int address_family, char *ip, size_t iplen){ strncpy(ip,address_family==AF_INET6 ? "::1" : "127.0.0.1",iplen); ms_error("sal_get_default_local_ip() is deprecated."); } const char *sal_get_root_ca(Sal* ctx) { return ctx->root_ca; } int sal_reset_transports(Sal *ctx){ ms_message("Reseting transports"); belle_sip_provider_clean_channels(ctx->prov); return 0; } void sal_set_dscp(Sal *ctx, int dscp){ belle_sip_stack_set_default_dscp(ctx->stack,dscp); } void sal_set_send_error(Sal *sal,int value) { belle_sip_stack_set_send_error(sal->stack,value); } void sal_set_recv_error(Sal *sal,int value) { belle_sip_provider_set_recv_error(sal->prov,value); } void sal_nat_helper_enable(Sal *sal,bool_t enable) { sal->nat_helper_enabled=enable; belle_sip_provider_enable_nat_helper(sal->prov,enable); ms_message("Sal nat helper [%s]",enable?"enabled":"disabled"); } bool_t sal_nat_helper_enabled(Sal *sal) { return sal->nat_helper_enabled; } void sal_set_dns_timeout(Sal* sal,int timeout) { belle_sip_stack_set_dns_timeout(sal->stack, timeout); } int sal_get_dns_timeout(const Sal* sal) { return belle_sip_stack_get_dns_timeout(sal->stack); } void sal_set_transport_timeout(Sal* sal,int timeout) { belle_sip_stack_set_transport_timeout(sal->stack, timeout); } int sal_get_transport_timeout(const Sal* sal) { return belle_sip_stack_get_transport_timeout(sal->stack); } void sal_set_dns_servers(Sal *sal, const bctbx_list_t *servers){ belle_sip_list_t *l = NULL; /*we have to convert the bctbx_list_t into a belle_sip_list_t first*/ for (; servers != NULL; servers = servers->next){ l = belle_sip_list_append(l, servers->data); } belle_sip_stack_set_dns_servers(sal->stack, l); belle_sip_list_free(l); } void sal_enable_dns_srv(Sal *sal, bool_t enable) { belle_sip_stack_enable_dns_srv(sal->stack, (unsigned char)enable); } bool_t sal_dns_srv_enabled(const Sal *sal) { return (bool_t)belle_sip_stack_dns_srv_enabled(sal->stack); } void sal_enable_dns_search(Sal *sal, bool_t enable) { belle_sip_stack_enable_dns_search(sal->stack, (unsigned char)enable); } bool_t sal_dns_search_enabled(const Sal *sal) { return (bool_t)belle_sip_stack_dns_search_enabled(sal->stack); } void sal_set_dns_user_hosts_file(Sal *sal, const char *hosts_file) { belle_sip_stack_set_dns_user_hosts_file(sal->stack, hosts_file); } const char * sal_get_dns_user_hosts_file(const Sal *sal) { return belle_sip_stack_get_dns_user_hosts_file(sal->stack); } SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) { SalAuthInfo* auth_info = sal_auth_info_new(); auth_info->realm = ms_strdup(belle_sip_auth_event_get_realm(event)); auth_info->username = ms_strdup(belle_sip_auth_event_get_username(event)); auth_info->domain = ms_strdup(belle_sip_auth_event_get_domain(event)); auth_info->mode = (SalAuthMode)belle_sip_auth_event_get_mode(event); return auth_info; } SalAuthMode sal_auth_info_get_mode(const SalAuthInfo* auth_info) { return auth_info->mode; } SalSigningKey *sal_auth_info_get_signing_key(const SalAuthInfo* auth_info) { return auth_info->key; } SalCertificatesChain *sal_auth_info_get_certificates_chain(const SalAuthInfo* auth_info) { return auth_info->certificates; } void sal_auth_info_set_mode(SalAuthInfo* auth_info, SalAuthMode mode) { auth_info->mode = mode; } void sal_certificates_chain_delete(SalCertificatesChain *chain) { belle_sip_object_unref((belle_sip_object_t *)chain); } void sal_signing_key_delete(SalSigningKey *key) { belle_sip_object_unref((belle_sip_object_t *)key); } const char* sal_op_type_to_string(const SalOpType type) { switch(type) { case SalOpRegister: return "SalOpRegister"; case SalOpCall: return "SalOpCall"; case SalOpMessage: return "SalOpMessage"; case SalOpPresence: return "SalOpPresence"; default: return "SalOpUnknown"; } } void sal_use_dates(Sal *ctx, bool_t enabled){ ctx->use_dates=enabled; } int sal_auth_compute_ha1(const char* userid,const char* realm,const char* password, char ha1[33]) { return belle_sip_auth_helper_compute_ha1(userid, realm, password, ha1); } SalCustomHeader *sal_custom_header_ref(SalCustomHeader *ch){ if (ch == NULL) return NULL; belle_sip_object_ref(ch); return ch; } void sal_custom_header_unref(SalCustomHeader *ch){ if (ch == NULL) return; belle_sip_object_unref(ch); } SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value){ belle_sip_message_t *msg=(belle_sip_message_t*)ch; belle_sip_header_t *h; if (msg==NULL){ msg=(belle_sip_message_t*)belle_sip_request_new(); belle_sip_object_ref(msg); } h=belle_sip_header_create(name,value); if (h==NULL){ belle_sip_error("Fail to parse custom header."); return (SalCustomHeader*)msg; } belle_sip_message_add_header(msg,h); return (SalCustomHeader*)msg; } const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name){ if (ch){ belle_sip_header_t *h=belle_sip_message_get_header((belle_sip_message_t*)ch,name); if (h){ return belle_sip_header_get_unparsed_value(h); } } return NULL; } SalCustomHeader *sal_custom_header_remove(SalCustomHeader *ch, const char *name) { belle_sip_message_t *msg=(belle_sip_message_t*)ch; if (msg==NULL) return NULL; belle_sip_message_remove_header(msg, name); return (SalCustomHeader*)msg; } void sal_custom_header_free(SalCustomHeader *ch){ if (ch==NULL) return; belle_sip_object_unref((belle_sip_message_t*)ch); } SalCustomHeader *sal_custom_header_clone(const SalCustomHeader *ch){ if (ch==NULL) return NULL; return (SalCustomHeader*)belle_sip_object_ref((belle_sip_message_t*)ch); } const SalCustomHeader *sal_op_get_recv_custom_header(SalOp *op){ SalOpBase *b=(SalOpBase *)op; return b->recv_custom_headers; } SalCustomSdpAttribute * sal_custom_sdp_attribute_append(SalCustomSdpAttribute *csa, const char *name, const char *value) { belle_sdp_session_description_t *desc = (belle_sdp_session_description_t *)csa; belle_sdp_attribute_t *attr; if (desc == NULL) { desc = (belle_sdp_session_description_t *)belle_sdp_session_description_new(); belle_sip_object_ref(desc); } attr = BELLE_SDP_ATTRIBUTE(belle_sdp_raw_attribute_create(name, value)); if (attr == NULL) { belle_sip_error("Fail to create custom SDP attribute."); return (SalCustomSdpAttribute*)desc; } belle_sdp_session_description_add_attribute(desc, attr); return (SalCustomSdpAttribute *)desc; } const char * sal_custom_sdp_attribute_find(const SalCustomSdpAttribute *csa, const char *name) { if (csa) { return belle_sdp_session_description_get_attribute_value((belle_sdp_session_description_t *)csa, name); } return NULL; } void sal_custom_sdp_attribute_free(SalCustomSdpAttribute *csa) { if (csa == NULL) return; belle_sip_object_unref((belle_sdp_session_description_t *)csa); } SalCustomSdpAttribute * sal_custom_sdp_attribute_clone(const SalCustomSdpAttribute *csa) { if (csa == NULL) return NULL; return (SalCustomSdpAttribute *)belle_sip_object_ref((belle_sdp_session_description_t *)csa); } void sal_set_uuid(Sal *sal, const char *uuid){ if (sal->uuid){ ms_free(sal->uuid); sal->uuid=NULL; } if (uuid) sal->uuid=ms_strdup(uuid); } typedef struct { unsigned int time_low; unsigned short time_mid; unsigned short time_hi_and_version; unsigned char clock_seq_hi_and_reserved; unsigned char clock_seq_low; unsigned char node[6]; } sal_uuid_t; int sal_generate_uuid(char *uuid, size_t len) { sal_uuid_t uuid_struct; int i; int written; if (len==0) return -1; /*create an UUID as described in RFC4122, 4.4 */ belle_sip_random_bytes((unsigned char*)&uuid_struct, sizeof(sal_uuid_t)); uuid_struct.clock_seq_hi_and_reserved&=~(1<<6); uuid_struct.clock_seq_hi_and_reserved|=1<<7; uuid_struct.time_hi_and_version&=~(0xf<<12); uuid_struct.time_hi_and_version|=4<<12; written=snprintf(uuid,len,"%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", uuid_struct.time_low, uuid_struct.time_mid, uuid_struct.time_hi_and_version, uuid_struct.clock_seq_hi_and_reserved, uuid_struct.clock_seq_low); if ((written < 0) || ((size_t)written > (len +13))) { ms_error("sal_create_uuid(): buffer is too short !"); return -1; } for (i = 0; i < 6; i++) written+=snprintf(uuid+written,len-written,"%2.2x", uuid_struct.node[i]); uuid[len-1]='\0'; return 0; } int sal_create_uuid(Sal*ctx, char *uuid, size_t len) { if (sal_generate_uuid(uuid, len) == 0) { sal_set_uuid(ctx, uuid); return 0; } return -1; } static void make_supported_header(Sal *sal){ bctbx_list_t *it; char *alltags=NULL; size_t buflen=64; size_t written=0; if (sal->supported){ belle_sip_object_unref(sal->supported); sal->supported=NULL; } for(it=sal->supported_tags;it!=NULL;it=it->next){ const char *tag=(const char*)it->data; size_t taglen=strlen(tag); if (alltags==NULL || (written+taglen+1>=buflen)) alltags=ms_realloc(alltags,(buflen=buflen*2)); written+=snprintf(alltags+written,buflen-written,it->next ? "%s, " : "%s",tag); } if (alltags){ sal->supported=belle_sip_header_create("Supported",alltags); if (sal->supported){ belle_sip_object_ref(sal->supported); } ms_free(alltags); } } void sal_set_supported_tags(Sal *ctx, const char* tags){ ctx->supported_tags=bctbx_list_free_with_data(ctx->supported_tags,ms_free); if (tags){ char *iter; char *buffer=ms_strdup(tags); char *tag; char *context=NULL; iter=buffer; while((tag=strtok_r(iter,", ",&context))!=NULL){ iter=NULL; ctx->supported_tags=bctbx_list_append(ctx->supported_tags,ms_strdup(tag)); } ms_free(buffer); } make_supported_header(ctx); } const char *sal_get_supported_tags(Sal *ctx){ if (ctx->supported){ return belle_sip_header_get_unparsed_value(ctx->supported); } return NULL; } void sal_add_supported_tag(Sal *ctx, const char* tag){ bctbx_list_t *elem=bctbx_list_find_custom(ctx->supported_tags,(bctbx_compare_func)strcasecmp,tag); if (!elem){ ctx->supported_tags=bctbx_list_append(ctx->supported_tags,ms_strdup(tag)); make_supported_header(ctx); } } void sal_remove_supported_tag(Sal *ctx, const char* tag){ bctbx_list_t *elem=bctbx_list_find_custom(ctx->supported_tags,(bctbx_compare_func)strcasecmp,tag); if (elem){ ms_free(elem->data); ctx->supported_tags=bctbx_list_erase_link(ctx->supported_tags,elem); make_supported_header(ctx); } } belle_sip_response_t* sal_create_response_from_request ( Sal* sal, belle_sip_request_t* req, int code ) { belle_sip_response_t *resp=belle_sip_response_create_from_request(req,code); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(sal->user_agent)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),sal->supported); return resp; } void sal_set_refresher_retry_after(Sal *sal,int value) { sal->refresher_retry_after=value; } int sal_get_refresher_retry_after(const Sal *sal) { return sal->refresher_retry_after; } void sal_enable_auto_contacts(Sal *ctx, bool_t enabled){ ctx->auto_contacts=enabled; } void sal_enable_test_features(Sal*ctx, bool_t enabled){ ctx->enable_test_features=enabled; } void sal_use_no_initial_route(Sal *ctx, bool_t enabled){ ctx->no_initial_route=enabled; } belle_sip_resolver_context_t * sal_resolve_a(Sal* sal, const char *name, int port, int family, belle_sip_resolver_callback_t cb, void *data){ return belle_sip_stack_resolve_a(sal->stack,name,port,family,cb,data); } belle_sip_resolver_context_t * sal_resolve(Sal *sal, const char *service, const char *transport, const char *name, int port, int family, belle_sip_resolver_callback_t cb, void *data) { return belle_sip_stack_resolve(sal->stack, service, transport, name, port, family, cb, data); } void sal_enable_unconditional_answer(Sal *sal,int value) { belle_sip_provider_enable_unconditional_answer(sal->prov,value); } /** Parse a file containing either a certificate chain order in PEM format or a single DER cert * @param auth_info structure where to store the result of parsing * @param path path to certificate chain file * @param format either PEM or DER */ void sal_certificates_chain_parse_file(SalAuthInfo* auth_info, const char* path, SalCertificateRawFormat format) { auth_info->certificates = (SalCertificatesChain*) belle_sip_certificates_chain_parse_file(path, (belle_sip_certificate_raw_format_t)format); if (auth_info->certificates) belle_sip_object_ref((belle_sip_object_t *) auth_info->certificates); } /** * Parse a file containing either a private or public rsa key * @param auth_info structure where to store the result of parsing * @param passwd password (optionnal) */ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const char *passwd) { auth_info->key = (SalSigningKey *) belle_sip_signing_key_parse_file(path, passwd); if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key); } /** Parse a buffer containing either a certificate chain order in PEM format or a single DER cert * @param auth_info structure where to store the result of parsing * @param buffer the buffer to parse * @param format either PEM or DER */ void sal_certificates_chain_parse(SalAuthInfo* auth_info, const char* buffer, SalCertificateRawFormat format) { size_t len = buffer != NULL ? strlen(buffer) : 0; auth_info->certificates = (SalCertificatesChain*) belle_sip_certificates_chain_parse(buffer, len, (belle_sip_certificate_raw_format_t)format); if (auth_info->certificates) belle_sip_object_ref((belle_sip_object_t *) auth_info->certificates); } /** * Parse a buffer containing either a private or public rsa key * @param auth_info structure where to store the result of parsing * @param passwd password (optionnal) */ void sal_signing_key_parse(SalAuthInfo* auth_info, const char* buffer, const char *passwd) { size_t len = buffer != NULL ? strlen(buffer) : 0; auth_info->key = (SalSigningKey *) belle_sip_signing_key_parse(buffer, len, passwd); if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key); } /** * Parse a directory to get a certificate with the given subject common name * */ void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_pem, char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) { belle_sip_certificates_chain_t *certificate = NULL; belle_sip_signing_key_t *key = NULL; *certificate_pem = NULL; *key_pem = NULL; if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key, (belle_sip_certificate_raw_format_t)format) == 0) { *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); *key_pem = belle_sip_signing_key_get_pem(key); ms_message("Retrieve certificate with CN=%s successful\n", subject); } else { if (generate_certificate == TRUE) { if ( belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) { *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); *key_pem = belle_sip_signing_key_get_pem(key); ms_message("Generate self-signed certificate with CN=%s successful\n", subject); } } } /* generate the fingerprint as described in RFC4572 if needed */ if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) { if (*fingerprint != NULL) { ms_free(*fingerprint); } *fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate); } /* free key and certificate */ if ( certificate != NULL ) { belle_sip_object_unref(certificate); } if ( key != NULL ) { belle_sip_object_unref(key); } } unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){ return belle_sip_random_bytes(ret,size); } char *sal_get_random_token(int size){ return belle_sip_random_token(ms_malloc(size),size); } unsigned int sal_get_random(void){ unsigned int ret=0; belle_sip_random_bytes((unsigned char*)&ret,4); return ret; } belle_sip_source_t * sal_create_timer(Sal *sal, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms, const char* timer_name) { belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack); return belle_sip_main_loop_create_timeout(ml, func, data, timeout_value_ms, timer_name); } void sal_cancel_timer(Sal *sal, belle_sip_source_t *timer) { belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack); belle_sip_main_loop_remove_source(ml, timer); } unsigned long sal_begin_background_task(const char *name, void (*max_time_reached)(void *), void *data){ return belle_sip_begin_background_task(name, max_time_reached, data); } void sal_end_background_task(unsigned long id){ belle_sip_end_background_task(id); } void sal_enable_sip_update_method(Sal *ctx,bool_t value) { ctx->enable_sip_update=value; } void sal_default_set_sdp_handling(Sal *sal, SalOpSDPHandling sdp_handling_method) { if (sdp_handling_method != SalOpSDPNormal ) ms_message("Enabling special SDP handling for all new SalOp in Sal[%p]!", sal); sal->default_sdp_handling = sdp_handling_method; } bool_t sal_pending_trans_checking_enabled(const Sal *sal) { return sal->pending_trans_checking; } int sal_enable_pending_trans_checking(Sal *sal, bool_t value) { sal->pending_trans_checking = value; return 0; } void sal_set_http_proxy_host(Sal *sal, const char *host) { belle_sip_stack_set_http_proxy_host(sal->stack, host); } void sal_set_http_proxy_port(Sal *sal, int port) { belle_sip_stack_set_http_proxy_port(sal->stack, port); } const char *sal_get_http_proxy_host(const Sal *sal) { return belle_sip_stack_get_http_proxy_host(sal->stack); } int sal_get_http_proxy_port(const Sal *sal) { return belle_sip_stack_get_http_proxy_port(sal->stack); } static belle_sip_header_t * sal_body_handler_find_header(const SalBodyHandler *body_handler, const char *header_name) { belle_sip_body_handler_t *bsbh = BELLE_SIP_BODY_HANDLER(body_handler); const belle_sip_list_t *l = belle_sip_body_handler_get_headers(bsbh); for (; l != NULL; l = l->next) { belle_sip_header_t *header = BELLE_SIP_HEADER(l->data); if (strcmp(belle_sip_header_get_name(header), header_name) == 0) { return header; } } return NULL; } SalBodyHandler * sal_body_handler_new(void) { belle_sip_memory_body_handler_t *body_handler = belle_sip_memory_body_handler_new(NULL, NULL); return (SalBodyHandler *)BELLE_SIP_BODY_HANDLER(body_handler); } SalBodyHandler * sal_body_handler_ref(SalBodyHandler *body_handler) { return (SalBodyHandler *)belle_sip_object_ref(BELLE_SIP_OBJECT(body_handler)); } void sal_body_handler_unref(SalBodyHandler *body_handler) { belle_sip_object_unref(BELLE_SIP_OBJECT(body_handler)); } const char * sal_body_handler_get_type(const SalBodyHandler *body_handler) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { return belle_sip_header_content_type_get_type(content_type); } return NULL; } void sal_body_handler_set_type(SalBodyHandler *body_handler, const char *type) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type == NULL) { content_type = belle_sip_header_content_type_new(); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_type)); } belle_sip_header_content_type_set_type(content_type, type); } const char * sal_body_handler_get_subtype(const SalBodyHandler *body_handler) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { return belle_sip_header_content_type_get_subtype(content_type); } return NULL; } void sal_body_handler_set_subtype(SalBodyHandler *body_handler, const char *subtype) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type == NULL) { content_type = belle_sip_header_content_type_new(); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_type)); } belle_sip_header_content_type_set_subtype(content_type, subtype); } const char * sal_body_handler_get_encoding(const SalBodyHandler *body_handler) { belle_sip_header_t *content_encoding = sal_body_handler_find_header(body_handler, "Content-Encoding"); if (content_encoding != NULL) { return belle_sip_header_get_unparsed_value(content_encoding); } return NULL; } void sal_body_handler_set_encoding(SalBodyHandler *body_handler, const char *encoding) { belle_sip_header_t *content_encoding = sal_body_handler_find_header(body_handler, "Content-Encoding"); if (content_encoding != NULL) { belle_sip_body_handler_remove_header_from_ptr(BELLE_SIP_BODY_HANDLER(body_handler), content_encoding); } belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), belle_sip_header_create("Content-Encoding", encoding)); } void * sal_body_handler_get_data(const SalBodyHandler *body_handler) { return belle_sip_memory_body_handler_get_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(body_handler)); } void sal_body_handler_set_data(SalBodyHandler *body_handler, void *data) { belle_sip_memory_body_handler_set_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(body_handler), data); } size_t sal_body_handler_get_size(const SalBodyHandler *body_handler) { return belle_sip_body_handler_get_size(BELLE_SIP_BODY_HANDLER(body_handler)); } void sal_body_handler_set_size(SalBodyHandler *body_handler, size_t size) { belle_sip_header_content_length_t *content_length = BELLE_SIP_HEADER_CONTENT_LENGTH(sal_body_handler_find_header(body_handler, "Content-Length")); if (content_length == NULL) { content_length = belle_sip_header_content_length_new(); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_length)); } belle_sip_header_content_length_set_content_length(content_length, size); belle_sip_body_handler_set_size(BELLE_SIP_BODY_HANDLER(body_handler), size); } bool_t sal_body_handler_is_multipart(const SalBodyHandler *body_handler) { if (BELLE_SIP_IS_INSTANCE_OF(body_handler, belle_sip_multipart_body_handler_t)) return TRUE; return FALSE; } SalBodyHandler * sal_body_handler_get_part(const SalBodyHandler *body_handler, int idx) { const belle_sip_list_t *l = belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler)); return (SalBodyHandler *)belle_sip_list_nth_data(l, idx); } SalBodyHandler * sal_body_handler_find_part_by_header(const SalBodyHandler *body_handler, const char *header_name, const char *header_value) { const belle_sip_list_t *l = belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler)); for (; l != NULL; l = l->next) { belle_sip_body_handler_t *bsbh = BELLE_SIP_BODY_HANDLER(l->data); const belle_sip_list_t *headers = belle_sip_body_handler_get_headers(bsbh); for (; headers != NULL; headers = headers->next) { belle_sip_header_t *header = BELLE_SIP_HEADER(headers->data); if ((strcmp(belle_sip_header_get_name(header), header_name) == 0) && (strcmp(belle_sip_header_get_unparsed_value(header), header_value) == 0)) { return (SalBodyHandler *)bsbh; } } } return NULL; } const char * sal_body_handler_get_header(const SalBodyHandler *body_handler, const char *header_name) { belle_sip_header_t *header = sal_body_handler_find_header(body_handler, header_name); if (header != NULL) { return belle_sip_header_get_unparsed_value(header); } return NULL; } void *sal_get_stack_impl(Sal *sal) { return sal->stack; } bool_t sal_is_content_type_supported(const Sal *sal, const char *content_type) { bctbx_list_t *item; for (item = sal->supported_content_types; item != NULL; item = bctbx_list_next(item)) { const char *item_content_type = (const char *)bctbx_list_get_data(item); if (strcmp(item_content_type, content_type) == 0) return TRUE; } return FALSE; } void sal_add_content_type_support(Sal *sal, const char *content_type) { if ((content_type != NULL) && (sal_is_content_type_supported(sal, content_type) == FALSE)) { sal->supported_content_types = bctbx_list_append(sal->supported_content_types, ms_strdup(content_type)); } } linphone-3.12.0/coreapi/bellesip_sal/sal_impl.h000066400000000000000000000145071313432737600214620ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SAL_IMPL_H_ #define SAL_IMPL_H_ #include "sal/sal.h" #include "belle-sip/belle-sip.h" #include "belle-sip/belle-sdp.h" struct Sal{ MSFactory *factory; SalCallbacks callbacks; MSList *pending_auths;/*MSList of SalOp */ belle_sip_stack_t* stack; belle_sip_provider_t *prov; belle_sip_header_user_agent_t* user_agent; belle_sip_listener_t *listener; void *tunnel_client; void *up; /*user pointer*/ int session_expires; unsigned int keep_alive; char *root_ca; char *root_ca_data; char *uuid; int refresher_retry_after; /*retry after value for refresher*/ MSList *supported_tags;/*list of char * */ belle_sip_header_t *supported; bool_t one_matching_codec; bool_t use_tcp_tls_keep_alive; bool_t nat_helper_enabled; bool_t tls_verify; bool_t tls_verify_cn; bool_t use_dates; bool_t auto_contacts; bool_t enable_test_features; bool_t no_initial_route; bool_t enable_sip_update; /*true by default*/ SalOpSDPHandling default_sdp_handling; bool_t pending_trans_checking; /*testing purpose*/ void *ssl_config; bctbx_list_t *supported_content_types; /* list of char* */ }; typedef enum SalOpState { SalOpStateEarly=0 ,SalOpStateActive ,SalOpStateTerminating /*this state is used to wait until a proceeding state, so we can send the cancel*/ ,SalOpStateTerminated }SalOpState; const char* sal_op_state_to_string(SalOpState value); typedef enum SalOpDir { SalOpDirIncoming=0 ,SalOpDirOutgoing }SalOpDir; typedef enum SalOpType { SalOpUnknown, SalOpRegister, SalOpCall, SalOpMessage, SalOpPresence, SalOpPublish, SalOpSubscribe }SalOpType; const char* sal_op_type_to_string(SalOpType type); struct SalOp{ SalOpBase base; const belle_sip_listener_callbacks_t *callbacks; SalErrorInfo error_info; SalErrorInfo reason_error_info; belle_sip_client_transaction_t *pending_auth_transaction; belle_sip_server_transaction_t* pending_server_trans; belle_sip_server_transaction_t* pending_update_server_trans; belle_sip_client_transaction_t* pending_client_trans; SalAuthInfo* auth_info; belle_sip_dialog_t* dialog; belle_sip_header_replaces_t *replaces; belle_sip_header_referred_by_t *referred_by; SalMediaDescription *result; belle_sdp_session_description_t *sdp_answer; SalOpState state; SalOpDir dir; belle_sip_refresher_t* refresher; int ref; SalOpType type; SalPrivacyMask privacy; belle_sip_header_event_t *event; /*used by SalOpSubscribe kinds*/ SalOpSDPHandling sdp_handling; int auth_requests; /*number of auth requested for this op*/ bool_t cnx_ip_to_0000_if_sendonly_enabled; bool_t auto_answer_asked; bool_t sdp_offering; bool_t call_released; bool_t manual_refresher; bool_t has_auth_pending; bool_t supports_session_timers; bool_t op_released; }; belle_sdp_session_description_t * media_description_to_sdp(const SalMediaDescription *sal); int sdp_to_media_description(belle_sdp_session_description_t *sdp, SalMediaDescription *desc); belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method); void sal_op_call_fill_cbs(SalOp*op); void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog); /*return reffed op*/ SalOp* sal_op_ref(SalOp* op); /*return null, destroy op if ref count =0*/ void* sal_op_unref(SalOp* op); void sal_op_release_impl(SalOp *op); void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces); void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message); int sal_op_send_request(SalOp* op, belle_sip_request_t* request); int sal_op_send_request_with_expires(SalOp* op, belle_sip_request_t* request,int expires); void sal_op_resend_request(SalOp* op, belle_sip_request_t* request); int sal_op_send_and_create_refresher(SalOp* op,belle_sip_request_t* req, int expires,belle_sip_refresher_listener_t listener ); belle_sip_response_t *sal_op_create_response_from_request(SalOp *op, belle_sip_request_t *req, int code); /* * return true if both from and to uri are sips * */ bool_t sal_op_is_secure(const SalOp* op); void sal_process_authentication(SalOp *op); belle_sip_header_contact_t* sal_op_create_contact(SalOp *op) ; bool_t _sal_compute_sal_errors(belle_sip_response_t* response, SalReason* sal_reason, char* reason, size_t reason_size); SalReason _sal_reason_from_sip_code(int code); void sal_op_set_reason_error_info(SalOp *op, belle_sip_message_t *msg); void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *response); /*presence*/ void sal_op_presence_fill_cbs(SalOp*op); /*messaging*/ void sal_op_message_fill_cbs(SalOp*op); void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event); void sal_op_subscribe_fill_cbs(SalOp*op); /*call transfer*/ void sal_op_process_refer(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t *tr); void sal_op_call_process_notify(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t *tr); /*create SalAuthInfo by copying username and realm from suth event*/ SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) ; void sal_add_pending_auth(Sal *sal, SalOp *op); void sal_remove_pending_auth(Sal *sal, SalOp *op); void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence); belle_sip_response_t *sal_create_response_from_request(Sal *sal, belle_sip_request_t *req, int code); void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming); SalBodyHandler * sal_op_get_body_handler(SalOp *op, belle_sip_message_t *msg); int sal_reason_to_sip_code(SalReason r); void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg); SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg); #endif /* SAL_IMPL_H_ */ linphone-3.12.0/coreapi/bellesip_sal/sal_op_call.c000066400000000000000000001361571313432737600221330ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" #include "offeranswer.h" #include static int extract_sdp(SalOp* op,belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error); /*used for calls terminated before creation of a dialog*/ static void call_set_released(SalOp* op){ if (!op->call_released){ op->state=SalOpStateTerminated; op->base.root->callbacks.call_released(op); op->call_released=TRUE; /*be aware that the following line may destroy the op*/ set_or_update_dialog(op,NULL); } } static void call_set_error(SalOp* op,belle_sip_response_t* response, bool_t fatal){ sal_op_set_error_info_from_response(op,response); if (fatal) op->state = SalOpStateTerminating; op->base.root->callbacks.call_failure(op); } static void set_addr_to_0000(char value[], size_t sz) { if (ms_is_ipv6(value)) { strncpy(value,"::0", sz); } else { strncpy(value,"0.0.0.0", sz); } return; } static void sdp_process(SalOp *h){ ms_message("Doing SDP offer/answer process of type %s",h->sdp_offering ? "outgoing" : "incoming"); if (h->result){ sal_media_description_unref(h->result); h->result = NULL; } /* if SDP was invalid */ if (h->base.remote_media == NULL) return; h->result=sal_media_description_new(); if (h->sdp_offering){ offer_answer_initiate_outgoing(h->base.root->factory, h->base.local_media,h->base.remote_media,h->result); }else{ int i; if (h->sdp_answer){ belle_sip_object_unref(h->sdp_answer); } offer_answer_initiate_incoming(h->base.root->factory, h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec); /*for backward compatibility purpose*/ if(h->cnx_ip_to_0000_if_sendonly_enabled && sal_media_description_has_dir(h->result,SalStreamSendOnly)) { set_addr_to_0000(h->result->addr, sizeof(h->result->addr)); for(i=0;iresult->streams[i].dir == SalStreamSendOnly) { set_addr_to_0000(h->result->streams[i].rtp_addr, sizeof(h->result->streams[i].rtp_addr)); set_addr_to_0000(h->result->streams[i].rtcp_addr, sizeof(h->result->streams[i].rtcp_addr)); } } } h->sdp_answer=(belle_sdp_session_description_t *)belle_sip_object_ref(media_description_to_sdp(h->result)); /*once we have generated the SDP answer, we modify the result description for processing by the upper layer. It should contains media parameters constraint from the remote offer, not our response*/ strcpy(h->result->addr,h->base.remote_media->addr); h->result->bandwidth=h->base.remote_media->bandwidth; for(i=0;iresult->streams[i].rtp_port!=0){ /*if stream was accepted*/ strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime; h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth; h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port; strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr); h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port; if (sal_stream_description_has_srtp(&h->result->streams[i])) { h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; } } } } } static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* session_desc) { belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; belle_sip_error_code error = BELLE_SIP_BUFFER_OVERFLOW; size_t length = 0; if (session_desc) { size_t bufLen = 2048; size_t hardlimit = 16*1024; /* 16k SDP limit seems reasonable */ char* buff = belle_sip_malloc(bufLen); content_type = belle_sip_header_content_type_create("application","sdp"); /* try to marshal the description. This could go higher than 2k so we iterate */ while( error != BELLE_SIP_OK && bufLen <= hardlimit && buff != NULL){ error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); if( error != BELLE_SIP_OK ){ bufLen *= 2; length = 0; buff = belle_sip_realloc(buff,bufLen); } } /* give up if hard limit reached */ if (error != BELLE_SIP_OK || buff == NULL) { ms_error("Buffer too small (%d) or not enough memory, giving up SDP", (int)bufLen); return -1; } content_length = belle_sip_header_content_length_create(length); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_length)); belle_sip_message_assign_body(msg,buff,length); return 0; } else { return -1; } } static int set_sdp_from_desc(belle_sip_message_t *msg, const SalMediaDescription *desc){ int err; belle_sdp_session_description_t *sdp=media_description_to_sdp(desc); err=set_sdp(msg,sdp); belle_sip_object_unref(sdp); return err; } static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event) { SalOp *op = (SalOp *)user_ctx; if (op->state == SalOpStateTerminated) return; if (op->pending_client_trans && (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_INIT)) { sal_error_info_set(&op->error_info, SalReasonIOError, "SIP", 503, "IO error", NULL); op->base.root->callbacks.call_failure(op); if (!op->dialog || belle_sip_dialog_get_state(op->dialog) != BELLE_SIP_DIALOG_CONFIRMED){ /* Call terminated very very early, before INVITE is even sent, probably DNS resolution timeout. */ op->state = SalOpStateTerminating; call_set_released(op); } } else { /* Nothing to be done. If the error comes from a connectivity loss, * the call will be marked as broken, and an attempt to repair it will be done. */ } } static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { SalOp* op=(SalOp*)ctx; if (op->dialog && op->dialog==belle_sip_dialog_terminated_event_get_dialog(event)) { /*belle_sip_transaction_t* trans=belle_sip_dialog_get_last_transaction(op->dialog);*/ ms_message("Dialog [%p] terminated for op [%p]",belle_sip_dialog_terminated_event_get_dialog(event),op); switch(belle_sip_dialog_get_previous_state(op->dialog)) { case BELLE_SIP_DIALOG_EARLY: case BELLE_SIP_DIALOG_NULL: if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) { /*this is an early termination due to incorrect response received*/ op->base.root->callbacks.call_failure(op); op->state=SalOpStateTerminating; } break; case BELLE_SIP_DIALOG_CONFIRMED: if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) { /*this is probably a normal termination from a BYE*/ op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); op->state=SalOpStateTerminating; } break; default: break; } belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack) ,(belle_sip_callback_t) call_set_released , op); } else { ms_error("dialog unknown for op "); } } static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) { belle_sdp_session_description_t* sdp; SalReason reason; if (op->base.remote_media){ sal_media_description_unref(op->base.remote_media); op->base.remote_media=NULL; } if (extract_sdp(op,BELLE_SIP_MESSAGE(response),&sdp,&reason)==0) { if (sdp){ op->base.remote_media=sal_media_description_new(); sdp_to_media_description(sdp,op->base.remote_media); }/*if no sdp in response, what can we do ?*/ } /* process sdp in any case to reset result media description*/ if (op->base.local_media) sdp_process(op); } void sal_call_cancel_invite(SalOp *op) { sal_call_cancel_invite_with_info(op,NULL); } static void cancelling_invite(SalOp *op, const SalErrorInfo *info) { sal_call_cancel_invite_with_info(op, info); op->state=SalOpStateTerminating; } static int vfu_retry (void *user_data, unsigned int events) { SalOp *op=(SalOp *)user_data; sal_call_send_vfu_request(op); sal_op_unref(op); return BELLE_SIP_STOP; } static void call_process_response(void *op_base, const belle_sip_response_event_t *event){ SalOp* op = (SalOp*)op_base; belle_sip_request_t* ack; belle_sip_dialog_state_t dialog_state; belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_request_t* req; belle_sip_response_t* response=belle_sip_response_event_get_response(event); int code = belle_sip_response_get_status_code(response); belle_sip_header_content_type_t *header_content_type=NULL; belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event); const char *method; if (!client_transaction) { ms_warning("Discarding stateless response [%i] on op [%p]",code,op); return; } req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); set_or_update_dialog(op,dialog); dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL; method=belle_sip_request_get_method(req); ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state)); /*to make sure no cb will destroy op*/ sal_op_ref(op); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: case BELLE_SIP_DIALOG_EARLY: { if (strcmp("INVITE",method)==0 ) { if (op->state == SalOpStateTerminating) { /*check if CANCEL was sent before*/ if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) { /*it wasn't sent */ if (code<200) { cancelling_invite(op, NULL); }else{ /* no need to send the INVITE because the UAS rejected the INVITE*/ if (op->dialog==NULL) call_set_released(op); } } else { /*it was sent already, so just expect the 487 or any error response to send the call_released() notification*/ if (code>=300){ if (op->dialog==NULL) call_set_released(op); } } } else if (code >= 180 && code<200) { belle_sip_response_t *prev_response=belle_sip_object_data_get(BELLE_SIP_OBJECT(dialog),"early_response"); if (!prev_response || code>belle_sip_response_get_status_code(prev_response)){ handle_sdp_from_response(op,response); op->base.root->callbacks.call_ringing(op); } belle_sip_object_data_set(BELLE_SIP_OBJECT(dialog),"early_response",belle_sip_object_ref(response),belle_sip_object_unref); } else if (code>=300){ call_set_error(op, response, TRUE); if (op->dialog==NULL) call_set_released(op); } } else if (code >=200 && code<300) { if (strcmp("UPDATE",method)==0) { handle_sdp_from_response(op,response); op->base.root->callbacks.call_accepted(op); } else if (strcmp("CANCEL", method) == 0) { op->base.root->callbacks.call_cancel_done(op); } } } break; case BELLE_SIP_DIALOG_CONFIRMED: { switch (op->state) { case SalOpStateEarly:/*invite case*/ case SalOpStateActive: /*re-invite, INFO, UPDATE case*/ if (strcmp("INVITE",method)==0){ if (code >=200 && code<300) { handle_sdp_from_response(op,response); ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog)); if (ack == NULL) { ms_error("This call has been already terminated."); return ; } if (op->sdp_answer){ set_sdp(BELLE_SIP_MESSAGE(ack),op->sdp_answer); belle_sip_object_unref(op->sdp_answer); op->sdp_answer=NULL; } belle_sip_message_add_header(BELLE_SIP_MESSAGE(ack),BELLE_SIP_HEADER(op->base.root->user_agent)); op->base.root->callbacks.call_accepted(op); /*INVITE*/ op->base.root->callbacks.call_ack_being_sent(op, (SalCustomHeader*)ack); belle_sip_dialog_send_ack(op->dialog,ack); op->state=SalOpStateActive; }else if (code >= 300){ call_set_error(op,response, FALSE); } }else if (strcmp("INFO",method)==0){ if (code == 491 && (header_content_type = belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t)) && strcmp("application",belle_sip_header_content_type_get_type(header_content_type))==0 && strcmp("media_control+xml",belle_sip_header_content_type_get_subtype(header_content_type))==0) { unsigned int retry_in = (unsigned int)(1000*((float)rand()/RAND_MAX)); belle_sip_source_t *s=sal_create_timer(op->base.root,vfu_retry,sal_op_ref(op), retry_in, "vfu request retry"); ms_message("Rejected vfu request on op [%p], just retry in [%ui] ms",op,retry_in); belle_sip_object_unref(s); }else { /*ignoring*/ } }else if (strcmp("UPDATE",method)==0){ op->base.root->callbacks.call_accepted(op); /*INVITE*/ }else if (strcmp("CANCEL",method)==0){ op->base.root->callbacks.call_cancel_done(op); } break; case SalOpStateTerminating: sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE")); break; case SalOpStateTerminated: default: ms_error("Call op [%p] receives unexpected answer [%i] while in state [%s].",op,code, sal_op_state_to_string(op->state)); } } break; case BELLE_SIP_DIALOG_TERMINATED: { if (strcmp("INVITE",method)==0 && code >= 300){ call_set_error(op,response, TRUE); } } break; default: { ms_error("call op [%p] receive answer [%i] not implemented",op,code); } break; } sal_op_unref(op); } static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { SalOp* op=(SalOp*)user_ctx; if (op->state==SalOpStateTerminated) return; if (!op->dialog) { /*call terminated very early*/ sal_error_info_set(&op->error_info, SalReasonRequestTimeout, "SIP", 408, "Request timeout", NULL); op->base.root->callbacks.call_failure(op); op->state = SalOpStateTerminating; call_set_released(op); } else { /*dialog will terminated shortly, nothing to do*/ } } static void call_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { SalOp* op = (SalOp*)user_ctx; belle_sip_client_transaction_t *client_transaction=belle_sip_transaction_terminated_event_get_client_transaction(event); belle_sip_server_transaction_t *server_transaction=belle_sip_transaction_terminated_event_get_server_transaction(event); belle_sip_request_t* req; belle_sip_response_t* resp; int code = 0; bool_t release_call=FALSE; if (client_transaction) { req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(client_transaction)); } else { req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)); resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(server_transaction)); } if (resp) code = belle_sip_response_get_status_code(resp); if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(req))==0 && (!resp || (belle_sip_response_get_status_code(resp) != 401 && belle_sip_response_get_status_code(resp) != 407)) && op->dialog==NULL) { release_call=TRUE; }else if (op->state == SalOpStateEarly && code < 200){ /*call terminated early*/ sal_error_info_set(&op->error_info, SalReasonIOError, "SIP", 503, "I/O error", NULL); op->state = SalOpStateTerminating; op->base.root->callbacks.call_failure(op); release_call=TRUE; } if (server_transaction){ if (op->pending_server_trans==server_transaction){ belle_sip_object_unref(op->pending_server_trans); op->pending_server_trans=NULL; } if (op->pending_update_server_trans==server_transaction){ belle_sip_object_unref(op->pending_update_server_trans); op->pending_update_server_trans=NULL; } } if (release_call) call_set_released(op); } static void call_terminated(SalOp* op,belle_sip_server_transaction_t* server_transaction, int status_code, belle_sip_request_t* cancel_request) { belle_sip_response_t* resp; belle_sip_request_t* server_req = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)); op->state = SalOpStateTerminating; sal_op_set_reason_error_info(op, BELLE_SIP_MESSAGE(cancel_request ? cancel_request : server_req)); resp=sal_op_create_response_from_request(op,server_req,status_code); belle_sip_server_transaction_send_response(server_transaction,resp); op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); } static void unsupported_method(belle_sip_server_transaction_t* server_transaction,belle_sip_request_t* request) { belle_sip_response_t* resp; resp=belle_sip_response_create_from_request(request,501); belle_sip_server_transaction_send_response(server_transaction,resp); return; } /* * Extract the sdp from a sip message. * If there is no body in the message, the session_desc is set to null, 0 is returned. * If body was present is not a SDP or parsing of SDP failed, -1 is returned and SalReason is set appropriately. * **/ static int extract_sdp(SalOp *op, belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error) { const char *body; belle_sip_header_content_type_t* content_type; if (op&&op->sdp_handling == SalOpSDPSimulateError){ ms_error("Simulating SDP parsing error for op %p", op); *session_desc=NULL; *error=SalReasonNotAcceptable; return -1; } else if( op && op->sdp_handling == SalOpSDPSimulateRemove){ ms_error("Simulating no SDP for op %p", op); *session_desc = NULL; return 0; } body = belle_sip_message_get_body(message); if(body == NULL) { *session_desc = NULL; return 0; } content_type = belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t); if (content_type){ if (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0 && strcmp("sdp",belle_sip_header_content_type_get_subtype(content_type))==0) { *session_desc=belle_sdp_session_description_parse(body); if (*session_desc==NULL) { ms_error("Failed to parse SDP message."); *error=SalReasonNotAcceptable; return -1; } }else{ *error=SalReasonUnsupportedContent; return -1; } }else *session_desc=NULL; return 0; } static int is_media_description_acceptable(SalMediaDescription *md){ if (md->nb_streams==0){ ms_warning("Media description does not define any stream."); return FALSE; } return TRUE; } static SalReason process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) { belle_sdp_session_description_t* sdp; SalReason reason = SalReasonNone; SalErrorInfo sei = {0}; if (extract_sdp(op,BELLE_SIP_MESSAGE(invite),&sdp,&reason)==0) { if (sdp){ op->sdp_offering=FALSE; op->base.remote_media=sal_media_description_new(); sdp_to_media_description(sdp,op->base.remote_media); /*make some sanity check about the SDP received*/ if (!is_media_description_acceptable(op->base.remote_media)){ reason=SalReasonNotAcceptable; } belle_sip_object_unref(sdp); }else op->sdp_offering=TRUE; /*INVITE without SDP*/ } if (reason != SalReasonNone){ sal_error_info_set(&sei, reason,"SIP", 0, NULL, NULL); sal_call_decline_with_error_info(op, &sei,NULL); sal_error_info_reset(&sei); } return reason; } static void sal_op_reset_descriptions(SalOp *op) { if (op->base.remote_media){ sal_media_description_unref(op->base.remote_media); op->base.remote_media=NULL; } if (op->result){ sal_media_description_unref(op->result); op->result=NULL; } } static bool_t is_a_pending_invite_incoming_transaction(belle_sip_transaction_t *tr){ return BELLE_SIP_OBJECT_IS_INSTANCE_OF(tr, belle_sip_ist_t) && belle_sip_transaction_state_is_transient( belle_sip_transaction_get_state(tr)); } static void process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; SalReason reason; belle_sip_server_transaction_t* server_transaction=NULL; belle_sdp_session_description_t* sdp; belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_dialog_state_t dialog_state; belle_sip_response_t* resp; belle_sip_header_t* call_info; const char *method=belle_sip_request_get_method(req); bool_t is_update=FALSE; bool_t drop_op = FALSE; if (strcmp("ACK",method)!=0){ /*ACK doesn't create a server transaction*/ server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); belle_sip_object_ref(server_transaction); belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),sal_op_ref(op)); } if (strcmp("INVITE",method)==0) { if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); /*updating pending invite transaction*/ op->pending_server_trans=server_transaction; belle_sip_object_ref(op->pending_server_trans); } if (strcmp("UPDATE",method)==0) { if (op->pending_update_server_trans) belle_sip_object_unref(op->pending_update_server_trans); /*updating pending update transaction*/ op->pending_update_server_trans=server_transaction; belle_sip_object_ref(op->pending_update_server_trans); } if (!op->dialog) { set_or_update_dialog(op,belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(op->pending_server_trans))); ms_message("new incoming call from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op)); } dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { if (strcmp("INVITE",method)==0) { if (!op->replaces && (op->replaces=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_replaces_t))) { belle_sip_object_ref(op->replaces); } else if(op->replaces) { ms_warning("replace header already set"); } if ( (reason = process_sdp_for_invite(op,req)) == SalReasonNone) { if ((call_info=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Call-Info"))) { if( strstr(belle_sip_header_get_unparsed_value(call_info),"answer-after=") != NULL) { op->auto_answer_asked=TRUE; ms_message("The caller asked to automatically answer the call(Emergency?)\n"); } } op->base.root->callbacks.call_received(op); }else{ sal_error_info_set(&op->error_info, reason, "SIP", 0, NULL, NULL); op->base.root->callbacks.call_rejected(op); /*the INVITE was declined by process_sdp_for_invite(). As we are not inside an established dialog, we can drop the op immediately*/ drop_op = TRUE; } break; }BCTBX_NO_BREAK; /* else same behavior as for EARLY state, thus NO BREAK*/ } case BELLE_SIP_DIALOG_EARLY: { if (strcmp("CANCEL",method)==0) { if(belle_sip_request_event_get_server_transaction(event)) { /*first answer 200 ok to cancel*/ belle_sip_server_transaction_send_response(server_transaction ,sal_op_create_response_from_request(op,req,200)); /*terminate invite transaction*/ call_terminated(op,op->pending_server_trans,487,req); } else { /*call leg does not exist*/ belle_sip_server_transaction_send_response(server_transaction ,sal_op_create_response_from_request(op,req,481)); } } else if (strcmp("PRACK",method)==0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if (strcmp("UPDATE",method)==0) { sal_op_reset_descriptions(op); if (process_sdp_for_invite(op,req)==SalReasonNone) op->base.root->callbacks.call_updating(op,TRUE); } else { belle_sip_error("Unexpected method [%s] for dialog state BELLE_SIP_DIALOG_EARLY",belle_sip_request_get_method(req)); unsupported_method(server_transaction,req); } break; } case BELLE_SIP_DIALOG_CONFIRMED: /*great ACK received*/ if (strcmp("ACK",method)==0) { if (!op->pending_client_trans || !belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state((belle_sip_transaction_t*)op->pending_client_trans))){ if (op->sdp_offering){ SalReason reason; if (extract_sdp(op,BELLE_SIP_MESSAGE(req),&sdp,&reason)==0){ if (sdp){ if (op->base.remote_media) sal_media_description_unref(op->base.remote_media); op->base.remote_media=sal_media_description_new(); sdp_to_media_description(sdp,op->base.remote_media); sdp_process(op); belle_sip_object_unref(sdp); }else{ ms_warning("SDP expected in ACK but not found."); } } } op->base.root->callbacks.call_ack_received(op, (SalCustomHeader*)req); }else{ ms_message("Ignored received ack since a new client transaction has been started since."); } } else if(strcmp("BYE",method)==0) { call_terminated(op,server_transaction,200,req); /*call end not notified by dialog deletion because transaction can end before dialog*/ } else if(strcmp("INVITE",method)==0 || (is_update=(strcmp("UPDATE",method)==0)) ) { if (is_update && !belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))) { /*session timer case*/ /*session expire should be handled. to be done when real session timer (rfc4028) will be implemented*/ resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); belle_sip_object_unref(op->pending_update_server_trans); op->pending_update_server_trans=NULL; } else { /*re-invite*/ sal_op_reset_descriptions(op); if (process_sdp_for_invite(op,req)==SalReasonNone) op->base.root->callbacks.call_updating(op,is_update); } } else if (strcmp("INFO",method)==0){ if (belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)) && strstr(belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)),"picture_fast_update")) { /*vfu request*/ ms_message("Receiving VFU request on op [%p]",op); if (op->base.root->callbacks.vfu_request){ op->base.root->callbacks.vfu_request(op); } }else{ belle_sip_message_t *msg = BELLE_SIP_MESSAGE(req); belle_sip_body_handler_t *body_handler = BELLE_SIP_BODY_HANDLER(sal_op_get_body_handler(op, msg)); if (body_handler) { belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t); if (content_type && (strcmp(belle_sip_header_content_type_get_type(content_type), "application") == 0) && (strcmp(belle_sip_header_content_type_get_subtype(content_type), "dtmf-relay") == 0)) { char tmp[10]; if (sal_lines_get_value(belle_sip_message_get_body(msg), "Signal",tmp, sizeof(tmp))){ op->base.root->callbacks.dtmf_received(op,tmp[0]); } }else op->base.root->callbacks.info_received(op, (SalBodyHandler *)body_handler); } else { op->base.root->callbacks.info_received(op,NULL); } } resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); }else if (strcmp("REFER",method)==0) { sal_op_process_refer(op,event,server_transaction); } else if (strcmp("NOTIFY",method)==0) { sal_op_call_process_notify(op,event,server_transaction); } else if (strcmp("OPTIONS",method)==0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if (strcmp("CANCEL",method)==0) { belle_sip_transaction_t *last_transaction = belle_sip_dialog_get_last_transaction(op->dialog); if (last_transaction == NULL || !is_a_pending_invite_incoming_transaction(last_transaction) ) { /*call leg does not exist because 200ok already sent*/ belle_sip_server_transaction_send_response(server_transaction,sal_op_create_response_from_request(op,req,481)); } else { /* CANCEL on re-INVITE for which a 200ok has not been sent yet */ belle_sip_server_transaction_send_response(server_transaction, sal_op_create_response_from_request(op, req, 200)); belle_sip_server_transaction_send_response(BELLE_SIP_SERVER_TRANSACTION(last_transaction), sal_op_create_response_from_request(op, belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_transaction)), 487)); } } else if (strcmp("MESSAGE",method)==0){ sal_process_incoming_message(op,event); }else{ ms_error("unexpected method [%s] for dialog [%p]",belle_sip_request_get_method(req),op->dialog); unsupported_method(server_transaction,req); } break; default: ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); break; } if (server_transaction) belle_sip_object_unref(server_transaction); if (drop_op) sal_op_release(op); } /*Call API*/ int sal_call_set_local_media_description(SalOp *op, SalMediaDescription *desc){ if (desc) sal_media_description_ref(desc); if (op->base.local_media) sal_media_description_unref(op->base.local_media); op->base.local_media=desc; if (op->base.remote_media){ /*case of an incoming call where we modify the local capabilities between the time * the call is ringing and it is accepted (for example if you want to accept without video*/ /*reset the sdp answer so that it is computed again*/ if (op->sdp_answer){ belle_sip_object_unref(op->sdp_answer); op->sdp_answer=NULL; } } return 0; } static belle_sip_header_allow_t *create_allow(bool_t enable_update){ belle_sip_header_allow_t* header_allow; char allow [256]; snprintf(allow,sizeof(allow),"INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO%s",(enable_update?", UPDATE":"")); header_allow = belle_sip_header_allow_create(allow); return header_allow; } static void sal_op_fill_invite(SalOp *op, belle_sip_request_t* invite) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(create_allow(op->base.root->enable_sip_update))); if (op->base.root->session_expires!=0){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "600;refresher=uas")); belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Supported", "timer")); } if (op->base.local_media){ op->sdp_offering=TRUE; set_sdp_from_desc(BELLE_SIP_MESSAGE(invite),op->base.local_media); }else op->sdp_offering=FALSE; return; } int sal_call(SalOp *op, const char *from, const char *to){ belle_sip_request_t* invite; op->dir=SalOpDirOutgoing; sal_op_set_from(op,from); sal_op_set_to(op,to); ms_message("[%s] calling [%s] on op [%p]", from, to, op); invite=sal_op_build_request(op,"INVITE"); if( invite == NULL ){ /* can happen if the op has an invalid address */ return -1; } sal_op_fill_invite(op,invite); sal_op_call_fill_cbs(op); if (op->replaces){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(op->replaces)); } if (op->referred_by) belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(op->referred_by)); return sal_op_send_request(op,invite); } static belle_sip_listener_callbacks_t call_op_callbacks={0}; void sal_op_call_fill_cbs(SalOp*op) { if (call_op_callbacks.process_response_event==NULL){ call_op_callbacks.process_io_error=call_process_io_error; call_op_callbacks.process_response_event=call_process_response; call_op_callbacks.process_timeout=call_process_timeout; call_op_callbacks.process_transaction_terminated=call_process_transaction_terminated; call_op_callbacks.process_request_event=process_request_event; call_op_callbacks.process_dialog_terminated=process_dialog_terminated; } op->callbacks=&call_op_callbacks; op->type=SalOpCall; } static void handle_offer_answer_response(SalOp* op, belle_sip_response_t* response) { if (op->base.local_media){ /*this is the case where we received an invite without SDP*/ if (op->sdp_offering) { set_sdp_from_desc(BELLE_SIP_MESSAGE(response),op->base.local_media); }else{ if ( op->sdp_answer==NULL ) { if( op->sdp_handling == SalOpSDPSimulateRemove ){ ms_warning("Simulating SDP removal in answer for op %p", op); } else { sdp_process(op); } } if (op->sdp_answer){ set_sdp(BELLE_SIP_MESSAGE(response),op->sdp_answer); belle_sip_object_unref(op->sdp_answer); op->sdp_answer=NULL; } } }else{ ms_error("You are accepting a call but not defined any media capabilities !"); } } int sal_call_notify_ringing(SalOp *op, bool_t early_media){ int status_code =early_media?183:180; belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)); belle_sip_response_t* ringing_response = sal_op_create_response_from_request(op,req,status_code); belle_sip_header_t *require; const char *tags=NULL; if (early_media){ handle_offer_answer_response(op,ringing_response); } require=belle_sip_message_get_header((belle_sip_message_t*)req,"Require"); if (require) tags=belle_sip_header_get_unparsed_value(require); /* if client requires 100rel, then add necessary stuff*/ if (tags && strstr(tags,"100rel")!=0) { belle_sip_message_add_header((belle_sip_message_t*)ringing_response,belle_sip_header_create("Require","100rel")); belle_sip_message_add_header((belle_sip_message_t*)ringing_response,belle_sip_header_create("RSeq","1")); } #ifndef SAL_OP_CALL_FORCE_CONTACT_IN_RINGING if (tags && strstr(tags,"100rel")!=0) #endif { belle_sip_header_address_t* contact= (belle_sip_header_address_t*)sal_op_get_contact_address(op); belle_sip_header_contact_t* contact_header; if (contact && (contact_header=belle_sip_header_contact_create(contact))) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(ringing_response),BELLE_SIP_HEADER(contact_header)); } } belle_sip_server_transaction_send_response(op->pending_server_trans,ringing_response); return 0; } /*accept an incoming call or, during a call accept a reINVITE*/ int sal_call_accept(SalOp*h){ belle_sip_response_t *response; belle_sip_header_contact_t* contact_header; belle_sip_server_transaction_t* transaction; /*first check if an UPDATE transaction need to be accepted*/ if (h->pending_update_server_trans) { transaction=h->pending_update_server_trans; } else if (h->pending_server_trans) { /*so it must be an invite/re-invite*/ transaction=h->pending_server_trans; } else { ms_error("No transaction to accept for op [%p]",h); return -1; } ms_message("Accepting server transaction [%p] on op [%p]", transaction, h); /* sends a 200 OK */ response = sal_op_create_response_from_request(h,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(transaction)),200); if (response==NULL){ ms_error("Fail to build answer for call"); return -1; } belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(create_allow(h->base.root->enable_sip_update))); if (h->base.root->session_expires!=0){ /* if (h->supports_session_timers) {*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create("Supported", "timer")); belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create( "Session-expires", "600;refresher=uac")); /*}*/ } if ((contact_header=sal_op_create_contact(h))) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact_header)); } _sal_op_add_custom_headers(h, BELLE_SIP_MESSAGE(response)); handle_offer_answer_response(h,response); belle_sip_server_transaction_send_response(transaction,response); if (h->pending_update_server_trans) { belle_sip_object_unref(h->pending_update_server_trans); h->pending_update_server_trans=NULL; } if (h->state == SalOpStateEarly){ h->state = SalOpStateActive; } return 0; } static belle_sip_header_reason_t *sal_call_make_reason_header( const SalErrorInfo *info){ if (info != NULL){ belle_sip_header_reason_t* reason = BELLE_SIP_HEADER_REASON(belle_sip_header_reason_new()); belle_sip_header_reason_set_text(reason, info->status_string); belle_sip_header_reason_set_protocol(reason,info->protocol); belle_sip_header_reason_set_cause(reason,info->protocol_code); return reason; } return NULL; } void sal_call_cancel_invite_with_info(SalOp* op, const SalErrorInfo *info) { belle_sip_request_t* cancel; ms_message("Cancelling INVITE request from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op)); cancel = belle_sip_client_transaction_create_cancel(op->pending_client_trans); if (cancel){ if (info != NULL){ belle_sip_header_reason_t* reason = sal_call_make_reason_header(info); belle_sip_message_add_header(BELLE_SIP_MESSAGE(cancel),BELLE_SIP_HEADER(reason)); } sal_op_send_request(op,cancel); }else if (op->dialog){ belle_sip_dialog_state_t state = belle_sip_dialog_get_state(op->dialog);; /*case where the response received is invalid (could not establish a dialog), but the transaction is not cancellable * because already terminated*/ switch(state){ case BELLE_SIP_DIALOG_EARLY: case BELLE_SIP_DIALOG_NULL: /*force kill the dialog*/ ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog); belle_sip_dialog_delete(op->dialog); break; default: break; } } } int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*optional*/){ belle_sip_response_t* response; belle_sip_header_contact_t* contact=NULL; int status=sal_reason_to_sip_code(reason); belle_sip_transaction_t *trans; if (reason==SalReasonRedirect){ if (redirection!=NULL) { if (strstr(redirection,"sip:")!=0) status=302; else status=380; contact= belle_sip_header_contact_new(); belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact),belle_sip_uri_parse(redirection)); } else { ms_error("Cannot redirect to null"); } } trans=(belle_sip_transaction_t*)op->pending_server_trans; if (!trans) trans=(belle_sip_transaction_t*)op->pending_update_server_trans; if (!trans){ ms_error("sal_call_decline(): no pending transaction to decline."); return -1; } response = sal_op_create_response_from_request(op,belle_sip_transaction_get_request(trans),status); if (contact) belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact)); belle_sip_server_transaction_send_response(BELLE_SIP_SERVER_TRANSACTION(trans),response); return 0; } int sal_call_decline_with_error_info(SalOp *op, const SalErrorInfo *info, const char *redirection /*optional*/){ belle_sip_response_t* response; belle_sip_header_contact_t* contact=NULL; int status = info->protocol_code; belle_sip_transaction_t *trans; if (info->reason==SalReasonRedirect){ if (redirection!=NULL) { if (strstr(redirection,"sip:")!=0) status=302; else status=380; contact= belle_sip_header_contact_new(); belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact),belle_sip_uri_parse(redirection)); } else { ms_error("Cannot redirect to null"); } } trans=(belle_sip_transaction_t*)op->pending_server_trans; if (!trans) trans=(belle_sip_transaction_t*)op->pending_update_server_trans; if (!trans){ ms_error("sal_call_decline_with_error_info(): no pending transaction to decline."); return -1; } response = sal_op_create_response_from_request(op,belle_sip_transaction_get_request(trans),status); belle_sip_header_reason_t* reason_header = sal_call_make_reason_header(info->sub_sei); if (reason_header) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(reason_header)); } if (contact) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact)); } belle_sip_server_transaction_send_response(BELLE_SIP_SERVER_TRANSACTION(trans),response); return 0; } int sal_call_update(SalOp *op, const char *subject, bool_t no_user_consent){ belle_sip_request_t *update; belle_sip_dialog_state_t state; if (op->dialog == NULL) { /* If the dialog does not exist, this is that we are trying to recover from a connection loss during a very early state of outgoing call initiation (the dialog has not been created yet). */ const char *from = sal_op_get_from(op); const char *to = sal_op_get_to(op); return sal_call(op, from, to); } state = belle_sip_dialog_get_state(op->dialog); belle_sip_dialog_enable_pending_trans_checking(op->dialog,op->base.root->pending_trans_checking); /*check for dialog state*/ if ( state == BELLE_SIP_DIALOG_CONFIRMED) { if (no_user_consent) update=belle_sip_dialog_create_request(op->dialog,"UPDATE"); else update=belle_sip_dialog_create_request(op->dialog,"INVITE"); } else if (state == BELLE_SIP_DIALOG_EARLY) { update=belle_sip_dialog_create_request(op->dialog,"UPDATE"); } else { ms_error("Cannot update op [%p] with dialog [%p] in state [%s]",op, op->dialog,belle_sip_dialog_state_to_string(state)); return -1; } if (update){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(update),belle_sip_header_create( "Subject", subject)); sal_op_fill_invite(op, update); return sal_op_send_request(op,update); } /*it failed why ?*/ if (belle_sip_dialog_request_pending(op->dialog)) sal_error_info_set(&op->error_info,SalReasonRequestPending, "SIP", 491,NULL,NULL); else sal_error_info_set(&op->error_info,SalReasonUnknown, "SIP", 500,NULL,NULL); return -1; } SalMediaDescription * sal_call_get_remote_media_description(SalOp *h){ return h->base.remote_media; } SalMediaDescription * sal_call_get_final_media_description(SalOp *h){ if (h->base.local_media && h->base.remote_media && !h->result){ sdp_process(h); } return h->result; } int sal_call_send_dtmf(SalOp *h, char dtmf){ if (h->dialog && (belle_sip_dialog_get_state(h->dialog) == BELLE_SIP_DIALOG_CONFIRMED || belle_sip_dialog_get_state(h->dialog) == BELLE_SIP_DIALOG_EARLY)){ belle_sip_request_t *req=belle_sip_dialog_create_queued_request(h->dialog,"INFO"); if (req){ size_t bodylen; char dtmf_body[128]={0}; snprintf(dtmf_body, sizeof(dtmf_body)-1, "Signal=%c\r\nDuration=250\r\n", dtmf); bodylen=strlen(dtmf_body); belle_sip_message_set_body((belle_sip_message_t*)req,dtmf_body,bodylen); belle_sip_message_add_header((belle_sip_message_t*)req,(belle_sip_header_t*)belle_sip_header_content_length_create(bodylen)); belle_sip_message_add_header((belle_sip_message_t*)req,(belle_sip_header_t*)belle_sip_header_content_type_create("application", "dtmf-relay")); sal_op_send_request(h,req); }else ms_error("sal_call_send_dtmf(): could not build request"); }else ms_error("sal_call_send_dtmf(): no dialog"); return 0; } int sal_call_terminate_with_error(SalOp *op, const SalErrorInfo *info){ SalErrorInfo sei = { 0 }; const SalErrorInfo *p_sei; belle_sip_dialog_state_t dialog_state = op->dialog ? belle_sip_dialog_get_state(op->dialog) : BELLE_SIP_DIALOG_NULL; int ret = 0; if (info == NULL && dialog_state != BELLE_SIP_DIALOG_CONFIRMED && op->dir == SalOpDirIncoming){ /*the purpose of this line is to set a default SalErrorInfo for declining an incoming call (not yet established of course) */ sal_error_info_set(&sei,SalReasonDeclined, "SIP", 0, NULL, NULL); p_sei = &sei; } else{ p_sei = info; } if (op->state==SalOpStateTerminating || op->state==SalOpStateTerminated) { ms_error("Cannot terminate op [%p] in state [%s]",op,sal_op_state_to_string(op->state)); ret = -1; goto end; } switch(dialog_state) { case BELLE_SIP_DIALOG_CONFIRMED: { belle_sip_request_t * req = belle_sip_dialog_create_request(op->dialog,"BYE"); if (info != NULL){ belle_sip_header_reason_t* reason = sal_call_make_reason_header(info); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(reason)); } sal_op_send_request(op,req); op->state=SalOpStateTerminating; break; } case BELLE_SIP_DIALOG_NULL: { if (op->dir == SalOpDirIncoming) { sal_call_decline_with_error_info(op, p_sei, NULL); op->state=SalOpStateTerminated; } else if (op->pending_client_trans){ if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){ cancelling_invite(op, p_sei); }else{ /* Case where the CANCEL cannot be sent because no provisional response was received so far. * The Op must be kept for the time of the transaction in case a response is received later. * The state is passed to Terminating to remember to terminate later. */ op->state=SalOpStateTerminating; } } break; } case BELLE_SIP_DIALOG_EARLY: { if (op->dir == SalOpDirIncoming) { sal_call_decline_with_error_info(op, p_sei,NULL); op->state=SalOpStateTerminated; } else { cancelling_invite(op, p_sei); } break; } default: { ms_error("sal_call_terminate not implemented yet for dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); ret = -1; goto end; } } end: sal_error_info_reset(&sei); return ret; } int sal_call_terminate(SalOp *op){ return sal_call_terminate_with_error(op, NULL); } bool_t sal_call_autoanswer_asked(SalOp *op){ return op->auto_answer_asked; } void sal_call_send_vfu_request(SalOp *op){ char info_body[] = "" "" " " " " " " " " " " ""; size_t content_lenth = sizeof(info_body) - 1; belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; /*no dialog = dialog in NULL state*/ if (dialog_state == BELLE_SIP_DIALOG_CONFIRMED) { belle_sip_request_t* info = belle_sip_dialog_create_queued_request(op->dialog,"INFO"); int error=TRUE; if (info) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","media_control+xml"))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_lenth))); belle_sip_message_set_body(BELLE_SIP_MESSAGE(info),info_body,content_lenth); error=sal_op_send_request(op,info); } if (error) ms_warning("Cannot send vfu request to [%s] ", sal_op_get_to(op)); } else { ms_warning("Cannot send vfu request to [%s] because dialog [%p] in wrong state [%s]",sal_op_get_to(op) ,op->dialog ,belle_sip_dialog_state_to_string(dialog_state)); } return ; } int sal_call_is_offerer(const SalOp *h){ return h->sdp_offering; } bool_t sal_call_compare_op(const SalOp *op1, const SalOp *op2) { if (strcmp(op1->base.call_id, op2->base.call_id) == 0) return TRUE; return FALSE; } bool_t sal_call_dialog_request_pending(const SalOp *op) { return belle_sip_dialog_request_pending(op->dialog) ? TRUE : FALSE; } const char * sal_call_get_local_tag(SalOp *op) { return belle_sip_dialog_get_local_tag(op->dialog); } const char * sal_call_get_remote_tag(SalOp *op) { return belle_sip_dialog_get_remote_tag(op->dialog); } void sal_call_set_replaces(SalOp *op, const char *call_id, const char *from_tag, const char *to_tag) { belle_sip_header_replaces_t *replaces = belle_sip_header_replaces_create(call_id, from_tag, to_tag); sal_op_set_replaces(op, replaces); } linphone-3.12.0/coreapi/bellesip_sal/sal_op_call_transfer.c000066400000000000000000000270661313432737600240350ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" #include "offeranswer.h" /*call transfer*/ static void sal_op_set_referred_by(SalOp* op,belle_sip_header_referred_by_t* referred_by) { if (op->referred_by){ belle_sip_object_unref(op->referred_by); } op->referred_by=referred_by; belle_sip_object_ref(op->referred_by); } int sal_call_refer_to(SalOp *op, belle_sip_header_refer_to_t* refer_to, belle_sip_header_referred_by_t* referred_by){ char* tmp; belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"REFER"):sal_op_build_request(op, "REFER"); if (!req) { tmp=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to))); ms_error("Cannot refer to [%s] for op [%p]",tmp,op); belle_sip_free(tmp); return -1; } belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(refer_to)); if (referred_by) belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(referred_by)); return sal_op_send_request(op,req); } int sal_call_refer(SalOp *op, const char *refer_to){ belle_sip_header_address_t *referred_by; belle_sip_header_refer_to_t* refer_to_header; if (op->dialog) { referred_by=(belle_sip_header_address_t*)belle_sip_object_clone(BELLE_SIP_OBJECT(belle_sip_dialog_get_local_party(op->dialog))); }else{ referred_by=BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op)); } refer_to_header=belle_sip_header_refer_to_create(belle_sip_header_address_parse(refer_to)); return sal_call_refer_to(op,refer_to_header,belle_sip_header_referred_by_create(referred_by)); } int sal_call_refer_with_replaces(SalOp *op, SalOp *other_call_op){ belle_sip_dialog_state_t other_call_dialog_state=other_call_op->dialog?belle_sip_dialog_get_state(other_call_op->dialog):BELLE_SIP_DIALOG_NULL; belle_sip_dialog_state_t op_dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; belle_sip_header_replaces_t* replaces; belle_sip_header_refer_to_t* refer_to; belle_sip_header_referred_by_t* referred_by; const char* from_tag; const char* to_tag; char* escaped_replaces; /*first, build refer to*/ if ((other_call_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) && (other_call_dialog_state!=BELLE_SIP_DIALOG_EARLY)) { ms_error("wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED or BELE_SIP_DIALOG_EARLY", belle_sip_dialog_state_to_string(other_call_dialog_state), other_call_op); return -1; } if (op_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) { ms_error("wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED", belle_sip_dialog_state_to_string(op_dialog_state), op); return -1; } refer_to=belle_sip_header_refer_to_create(belle_sip_dialog_get_remote_party(other_call_op->dialog)); belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(refer_to)); /*rfc3891 ... 4. User Agent Client Behavior: Sending a Replaces Header A User Agent that wishes to replace a single existing early or confirmed dialog with a new dialog of its own, MAY send the target User Agent an INVITE request containing a Replaces header field. The User Agent Client (UAC) places the Call-ID, to-tag, and from-tag information for the target dialog in a single Replaces header field and sends the new INVITE to the target.*/ from_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog); to_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog); replaces=belle_sip_header_replaces_create(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(other_call_op->dialog)) ,from_tag,to_tag); escaped_replaces=belle_sip_header_replaces_value_to_escaped_string(replaces); belle_sip_uri_set_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)),"Replaces",escaped_replaces); belle_sip_free(escaped_replaces); referred_by=belle_sip_header_referred_by_create(belle_sip_dialog_get_local_party(op->dialog)); belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(referred_by)); return sal_call_refer_to(op,refer_to,referred_by); } /* int sal_call_accept_refer(SalOp *h){ ms_fatal("sal_call_accept_refer not implemented yet"); return -1; }*/ /*informs this call is consecutive to an incoming refer */ int sal_call_set_referer(SalOp *h, SalOp *refered_call){ if (refered_call->replaces) sal_op_set_replaces(h,refered_call->replaces); if (refered_call->referred_by) sal_op_set_referred_by(h,refered_call->referred_by); return 0; } /* returns the SalOp of a call that should be replaced by h, if any */ SalOp *sal_call_get_replaces(SalOp *op){ if (op && op->replaces){ /*rfc3891 3. User Agent Server Behavior: Receiving a Replaces Header The Replaces header contains information used to match an existing SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE with a Replaces header, the User Agent (UA) attempts to match this information with a confirmed or early dialog. The User Agent Server (UAS) matches the to-tag and from-tag parameters as if they were tags present in an incoming request. In other words, the to-tag parameter is compared to the local tag, and the from-tag parameter is compared to the remote tag. */ belle_sip_dialog_t* dialog=belle_sip_provider_find_dialog(op->base.root->prov ,belle_sip_header_replaces_get_call_id(op->replaces) ,belle_sip_header_replaces_get_to_tag(op->replaces) ,belle_sip_header_replaces_get_from_tag(op->replaces)); if (!dialog) { /*for backward compatibility with liblinphone <= 3.10.2-243 */ dialog=belle_sip_provider_find_dialog(op->base.root->prov ,belle_sip_header_replaces_get_call_id(op->replaces) ,belle_sip_header_replaces_get_from_tag(op->replaces) ,belle_sip_header_replaces_get_to_tag(op->replaces)); } if (dialog) { return (SalOp*)belle_sip_dialog_get_application_data(dialog); } } return NULL; } static int send_notify_for_refer(SalOp* op, int code, const char *reason){ belle_sip_request_t* notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"); char *sipfrag=belle_sip_strdup_printf("SIP/2.0 %i %s\r\n",code,reason); size_t content_length=strlen(sipfrag); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,-1))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),belle_sip_header_create("Event","refer")); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(belle_sip_header_content_type_create("message","sipfrag"))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length))); belle_sip_message_assign_body(BELLE_SIP_MESSAGE(notify),sipfrag,content_length); return sal_op_send_request(op,notify); } static void notify_last_response(SalOp *op, SalOp *newcall){ belle_sip_client_transaction_t *tr=newcall->pending_client_trans; belle_sip_response_t *resp=NULL; if (tr){ resp=belle_sip_transaction_get_response((belle_sip_transaction_t*)tr); } if (resp==NULL){ send_notify_for_refer(op, 100, "Trying"); }else{ send_notify_for_refer(op, belle_sip_response_get_status_code(resp), belle_sip_response_get_reason_phrase(resp)); } } int sal_call_notify_refer_state(SalOp *op, SalOp *newcall){ belle_sip_dialog_state_t state; if(belle_sip_dialog_get_state(op->dialog) == BELLE_SIP_DIALOG_TERMINATED){ return 0; } state = newcall->dialog?belle_sip_dialog_get_state(newcall->dialog):BELLE_SIP_DIALOG_NULL; switch(state) { case BELLE_SIP_DIALOG_EARLY: send_notify_for_refer(op, 100, "Trying"); break; case BELLE_SIP_DIALOG_CONFIRMED: send_notify_for_refer(op, 200, "Ok"); break; case BELLE_SIP_DIALOG_TERMINATED: case BELLE_SIP_DIALOG_NULL: notify_last_response(op,newcall); break; } return 0; } void sal_op_process_refer(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t *server_transaction){ belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_header_refer_to_t *refer_to= belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_refer_to_t); belle_sip_header_referred_by_t *referred_by= belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_referred_by_t); belle_sip_response_t* resp; belle_sip_uri_t* refer_to_uri; char* refer_to_uri_str; ms_message("Receiving REFER request on op [%p]",op); if (refer_to) { refer_to_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)); if (refer_to_uri && belle_sip_uri_get_header(refer_to_uri,"Replaces")) { sal_op_set_replaces(op,belle_sip_header_replaces_create2(belle_sip_uri_get_header(refer_to_uri,"Replaces"))); belle_sip_uri_remove_header(refer_to_uri,"Replaces"); } if (referred_by){ sal_op_set_referred_by(op,referred_by); } refer_to_uri_str=belle_sip_uri_to_string(refer_to_uri); resp = sal_op_create_response_from_request(op,req,202); belle_sip_server_transaction_send_response(server_transaction,resp); op->base.root->callbacks.refer_received(op->base.root,op,refer_to_uri_str); belle_sip_free(refer_to_uri_str); } else { ms_warning("cannot do anything with the refer without destination\n"); resp = sal_op_create_response_from_request(op,req,400); belle_sip_server_transaction_send_response(server_transaction,resp); } } void sal_op_call_process_notify(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t* server_transaction){ belle_sip_request_t* req = belle_sip_request_event_get_request(event); const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); belle_sip_header_t* header_event=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event"); belle_sip_header_content_type_t* content_type = belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t); belle_sip_response_t* resp; ms_message("Receiving NOTIFY request on op [%p]",op); if (header_event && strncasecmp(belle_sip_header_get_unparsed_value(header_event),"refer",strlen("refer"))==0 && content_type && strcmp(belle_sip_header_content_type_get_type(content_type),"message")==0 && strcmp(belle_sip_header_content_type_get_subtype(content_type),"sipfrag")==0 && body){ belle_sip_response_t* sipfrag=BELLE_SIP_RESPONSE(belle_sip_message_parse(body)); if (sipfrag){ int code=belle_sip_response_get_status_code(sipfrag); SalReferStatus status=SalReferFailed; if (code<200){ status=SalReferTrying; }else if (code<300){ status=SalReferSuccess; }else if (code>=400){ status=SalReferFailed; } belle_sip_object_unref(sipfrag); resp = sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); op->base.root->callbacks.notify_refer(op,status); } }else{ ms_error("Notify without sipfrag, trashing"); resp = sal_op_create_response_from_request(op,req,501); belle_sip_server_transaction_send_response(server_transaction,resp); } } linphone-3.12.0/coreapi/bellesip_sal/sal_op_events.c000066400000000000000000000377251313432737600225250ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg){ belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(msg,belle_sip_header_subscription_state_t); SalSubscribeStatus sss=SalSubscribeNone; if (subscription_state_header){ if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED)==0) sss=SalSubscribeTerminated; else if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_PENDING)==0) sss=SalSubscribePending; else if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE)==0) sss=SalSubscribeActive; } return sss; } static void subscribe_refresher_listener (belle_sip_refresher_t* refresher ,void* user_pointer ,unsigned int status_code ,const char* reason_phrase, int will_retry) { SalOp* op = (SalOp*)user_pointer; belle_sip_transaction_t *tr=BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)); /*belle_sip_response_t* response=belle_sip_transaction_get_response(tr);*/ SalSubscribeStatus sss=SalSubscribeTerminated; ms_message("Subscribe refresher [%i] reason [%s] ",status_code,reason_phrase?reason_phrase:"none"); if (status_code>=200 && status_code<300){ if (status_code==200) sss=SalSubscribeActive; else if (status_code==202) sss=SalSubscribePending; set_or_update_dialog(op,belle_sip_transaction_get_dialog(tr)); op->base.root->callbacks.subscribe_response(op,sss, will_retry); } else if (status_code >= 300) { SalReason reason = SalReasonUnknown; if (status_code == 503) { /*refresher returns 503 for IO error*/ reason = SalReasonIOError; } sal_error_info_set(&op->error_info, reason, "SIP", status_code,reason_phrase,NULL); op->base.root->callbacks.subscribe_response(op,sss, will_retry); }else if (status_code==0){ op->base.root->callbacks.on_expire(op); } } static void subscribe_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ SalOp *op = (SalOp*)user_ctx; belle_sip_object_t *src = belle_sip_io_error_event_get_source(event); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(src, belle_sip_client_transaction_t)){ belle_sip_client_transaction_t *tr = BELLE_SIP_CLIENT_TRANSACTION(src); belle_sip_request_t* req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr); const char *method=belle_sip_request_get_method(req); if (!op->dialog) { /*this is handling outgoing out-of-dialog notifies*/ if (strcmp(method,"NOTIFY")==0){ SalErrorInfo *ei=&op->error_info; sal_error_info_set(ei,SalReasonIOError, "SIP", 0,NULL,NULL); op->base.root->callbacks.on_notify_response(op); } } } } static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { belle_sip_dialog_t *dialog = belle_sip_dialog_terminated_event_get_dialog(event); SalOp* op= (SalOp*)ctx; if (op->dialog) { if (belle_sip_dialog_terminated_event_is_expired(event)){ if (!belle_sip_dialog_is_server(dialog)){ /*notify the app that our subscription is dead*/ const char *eventname = NULL; if (op->event){ eventname = belle_sip_header_event_get_package_name(op->event); } op->base.root->callbacks.notify(op, SalSubscribeTerminated, eventname, NULL); }else{ op->base.root->callbacks.incoming_subscribe_closed(op); } } set_or_update_dialog(op, NULL); } } static void subscribe_response_event(void *op_base, const belle_sip_response_event_t *event){ SalOp *op = (SalOp*)op_base; belle_sip_request_t * req; const char *method; belle_sip_client_transaction_t *tr = belle_sip_response_event_get_client_transaction(event); if (!tr) return; req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr); method = belle_sip_request_get_method(req); if (!op->dialog) { if (strcmp(method,"NOTIFY")==0){ sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event)); op->base.root->callbacks.on_notify_response(op); } } } static void subscribe_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { SalOp *op = (SalOp*)user_ctx; belle_sip_request_t * req; const char *method; belle_sip_client_transaction_t *tr = belle_sip_timeout_event_get_client_transaction(event); if (!tr) return; req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr); method = belle_sip_request_get_method(req); if (!op->dialog) { /*this is handling outgoing out-of-dialog notifies*/ if (strcmp(method,"NOTIFY")==0){ SalErrorInfo *ei=&op->error_info; sal_error_info_set(ei,SalReasonRequestTimeout, "SIP", 0,NULL,NULL); op->base.root->callbacks.on_notify_response(op); } } } static void subscribe_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { } static void handle_notify(SalOp *op, belle_sip_request_t *req, const char *eventname, SalBodyHandler* body_handler){ SalSubscribeStatus sub_state; belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t); belle_sip_response_t* resp; belle_sip_server_transaction_t* server_transaction = op->pending_server_trans; if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) { sub_state=SalSubscribeTerminated; ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); } else sub_state=SalSubscribeActive; sal_op_ref(op); op->base.root->callbacks.notify(op,sub_state,eventname,body_handler); resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); sal_op_unref(op); } static void subscribe_process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_dialog_state_t dialog_state; belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); belle_sip_header_event_t *event_header; belle_sip_body_handler_t *body_handler; belle_sip_response_t* resp; const char *eventname=NULL; const char *method=belle_sip_request_get_method(req); belle_sip_dialog_t *dialog = NULL; belle_sip_object_ref(server_transaction); if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); op->pending_server_trans=server_transaction; event_header=belle_sip_message_get_header_by_type(req,belle_sip_header_event_t); body_handler = BELLE_SIP_BODY_HANDLER(sal_op_get_body_handler(op, BELLE_SIP_MESSAGE(req))); if (event_header==NULL){ ms_warning("No event header in incoming SUBSCRIBE."); resp=sal_op_create_response_from_request(op,req,400); belle_sip_server_transaction_send_response(server_transaction,resp); if (!op->dialog) sal_op_release(op); return; } if (op->event==NULL) { op->event=event_header; belle_sip_object_ref(op->event); } eventname=belle_sip_header_event_get_package_name(event_header); if (!op->dialog) { if (strcmp(method,"SUBSCRIBE")==0){ dialog = belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction)); if (!dialog){ resp=sal_op_create_response_from_request(op,req,481); belle_sip_server_transaction_send_response(server_transaction,resp); sal_op_release(op); return; } set_or_update_dialog(op, dialog); ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op)); }else{ /*this is a NOTIFY*/ handle_notify(op, req, eventname, (SalBodyHandler *)body_handler); return; } } dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { const char *type = NULL; belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_type_t); if (content_type) type = belle_sip_header_content_type_get_type(content_type); op->base.root->callbacks.subscribe_received(op, eventname, type ? (SalBodyHandler *)body_handler : NULL); break; } case BELLE_SIP_DIALOG_EARLY: ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",belle_sip_request_get_method(req),op->dialog); break; case BELLE_SIP_DIALOG_CONFIRMED: if (strcmp("NOTIFY",method)==0) { handle_notify(op, req, eventname, (SalBodyHandler *)body_handler); } else if (strcmp("SUBSCRIBE",method)==0) { /*either a refresh of an unsubscribe*/ if (expires && belle_sip_header_expires_get_expires(expires)>0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if(expires) { ms_message("Unsubscribe received from [%s]",sal_op_get_from(op)); resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); op->base.root->callbacks.incoming_subscribe_closed(op); } } break; default: { ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); } } } static belle_sip_listener_callbacks_t op_subscribe_callbacks={ 0 }; /*Invoke when sal_op_release is called by upper layer*/ static void sal_op_release_cb(struct SalOpBase* op_base) { SalOp *op =(SalOp*)op_base; if(op->refresher) { belle_sip_refresher_stop(op->refresher); belle_sip_object_unref(op->refresher); op->refresher=NULL; set_or_update_dialog(op,NULL); /*only if we have refresher. else dialog terminated event will remove association*/ } } void sal_op_subscribe_fill_cbs(SalOp*op) { if (op_subscribe_callbacks.process_io_error==NULL){ op_subscribe_callbacks.process_io_error=subscribe_process_io_error; op_subscribe_callbacks.process_response_event=subscribe_response_event; op_subscribe_callbacks.process_timeout=subscribe_process_timeout; op_subscribe_callbacks.process_transaction_terminated=subscribe_process_transaction_terminated; op_subscribe_callbacks.process_request_event=subscribe_process_request_event; op_subscribe_callbacks.process_dialog_terminated=subscribe_process_dialog_terminated; } op->callbacks=&op_subscribe_callbacks; op->type=SalOpSubscribe; op->base.release_cb=sal_op_release_cb; } int sal_subscribe(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBodyHandler *body_handler){ belle_sip_request_t *req=NULL; if (from) sal_op_set_from(op,from); if (to) sal_op_set_to(op,to); if (!op->dialog){ sal_op_subscribe_fill_cbs(op); req=sal_op_build_request(op,"SUBSCRIBE"); if( req == NULL ) { return -1; } sal_op_set_event(op, eventname); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler)); return sal_op_send_and_create_refresher(op,req,expires,subscribe_refresher_listener); }else if (op->refresher){ const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher); belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr); /* modify last request to update body*/ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(last_req), BELLE_SIP_BODY_HANDLER(body_handler)); return belle_sip_refresher_refresh(op->refresher,expires); } ms_warning("sal_subscribe(): no dialog and no refresher ?"); return -1; } int sal_unsubscribe(SalOp *op){ if (op->refresher){ const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher); belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr); belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_req), NULL, 0); belle_sip_refresher_refresh(op->refresher,0); return 0; } return -1; } int sal_subscribe_accept(SalOp *op){ belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)); belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); belle_sip_response_t* resp = sal_op_create_response_from_request(op,req,200); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(expires)); belle_sip_server_transaction_send_response(op->pending_server_trans,resp); return 0; } int sal_notify_pending_state(SalOp *op){ if (op->dialog != NULL && op->pending_server_trans) { belle_sip_request_t* notify; belle_sip_header_subscription_state_t* sub_state; ms_message("Sending NOTIFY with subscription state pending for op [%p]",op); if (!(notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"))) { ms_error("Cannot create NOTIFY on op [%p]",op); return -1; } if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event)); sub_state=belle_sip_header_subscription_state_new(); belle_sip_header_subscription_state_set_state(sub_state,BELLE_SIP_SUBSCRIPTION_STATE_PENDING); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify), BELLE_SIP_HEADER(sub_state)); return sal_op_send_request(op,notify); } else { ms_warning("NOTIFY with subscription state pending for op [%p] not implemented in this case (either dialog pending trans does not exist",op); } return 0; } int sal_subscribe_decline(SalOp *op, SalReason reason){ belle_sip_response_t* resp = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)), sal_reason_to_sip_code(reason)); belle_sip_server_transaction_send_response(op->pending_server_trans,resp); return 0; } int sal_notify(SalOp *op, const SalBodyHandler *body_handler){ belle_sip_request_t* notify; if (op->dialog){ if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; }else{ sal_op_subscribe_fill_cbs(op); notify = sal_op_build_request(op, "NOTIFY"); } if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,op->dialog ? BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600)) : BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,0)) ); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(notify), BELLE_SIP_BODY_HANDLER(body_handler)); return sal_op_send_request(op,notify); } int sal_notify_close(SalOp *op){ belle_sip_request_t* notify; if (!op->dialog) return -1; if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1))); return sal_op_send_request(op,notify); } linphone-3.12.0/coreapi/bellesip_sal/sal_op_impl.c000066400000000000000000000745231313432737600221570ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" /*create an operation */ SalOp * sal_op_new(Sal *sal){ SalOp *op=ms_new0(SalOp,1); __sal_op_init(op,sal); op->type=SalOpUnknown; op->privacy=SalPrivacyNone; op->manual_refresher=FALSE;/*tells that requests with expiry (SUBSCRIBE, PUBLISH) will be automatically refreshed*/ op->sdp_handling=sal->default_sdp_handling; sal_op_ref(op); return op; } void sal_op_kill_dialog(SalOp *op) { ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog); belle_sip_dialog_delete(op->dialog); } void sal_op_release(SalOp *op){ /*if in terminating state, keep this state because it means we are waiting for a response to be able to terminate the operation.*/ if (op->state!=SalOpStateTerminating) op->state=SalOpStateTerminated; sal_op_set_user_pointer(op,NULL);/*mandatory because releasing op doesn't not mean freeing op. Make sure back pointer will not be used later*/ if (op->base.release_cb) op->base.release_cb(&op->base); if (op->refresher) { belle_sip_refresher_stop(op->refresher); } op->op_released = TRUE; sal_op_unref(op); } void sal_op_release_impl(SalOp *op){ ms_message("Destroying op [%p] of type [%s]",op,sal_op_type_to_string(op->type)); if (op->pending_auth_transaction) belle_sip_object_unref(op->pending_auth_transaction); sal_remove_pending_auth(op->base.root,op); if (op->auth_info) { sal_auth_info_delete(op->auth_info); } if (op->sdp_answer) belle_sip_object_unref(op->sdp_answer); if (op->refresher) { belle_sip_object_unref(op->refresher); op->refresher=NULL; } if (op->result) sal_media_description_unref(op->result); if(op->replaces) belle_sip_object_unref(op->replaces); if(op->referred_by) belle_sip_object_unref(op->referred_by); if (op->pending_client_trans) belle_sip_object_unref(op->pending_client_trans); if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); if (op->pending_update_server_trans) belle_sip_object_unref(op->pending_update_server_trans); if (op->event) belle_sip_object_unref(op->event); sal_error_info_reset(&op->error_info); __sal_op_free(op); return ; } void sal_op_authenticate(SalOp *op, const SalAuthInfo *info){ if (op->type == SalOpRegister) { /*Registration authenticate is just about registering again*/ sal_register_refresh(op,-1); }else { /*for sure auth info will be accessible from the provider*/ sal_process_authentication(op); } return ; } void sal_op_cancel_authentication(SalOp *h){ ms_fatal("sal_op_cancel_authentication not implemented yet"); return ; } SalAuthInfo * sal_op_get_auth_requested(SalOp *op){ return op->auth_info; } belle_sip_header_contact_t* sal_op_create_contact(SalOp *op){ belle_sip_header_contact_t* contact_header; belle_sip_uri_t* contact_uri; if (sal_op_get_contact_address(op)) { contact_header = belle_sip_header_contact_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_contact_address(op))); } else { contact_header= belle_sip_header_contact_new(); } if (!(contact_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact_header)))) { /*no uri, just creating a new one*/ contact_uri=belle_sip_uri_new(); belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact_header),contact_uri); } belle_sip_uri_set_user_password(contact_uri,NULL); belle_sip_uri_set_secure(contact_uri,sal_op_is_secure(op)); if (op->privacy!=SalPrivacyNone){ belle_sip_uri_set_user(contact_uri,NULL); } belle_sip_header_contact_set_automatic(contact_header,op->base.root->auto_contacts); if (op->base.root->uuid){ if (belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(contact_header),"+sip.instance")==0){ char *instance_id=belle_sip_strdup_printf("\"\"",op->base.root->uuid); belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(contact_header),"+sip.instance",instance_id); belle_sip_free(instance_id); } } return contact_header; } static void add_initial_route_set(belle_sip_request_t *request, const MSList *list){ const MSList *elem; for (elem=list;elem!=NULL;elem=elem->next){ SalAddress *addr=(SalAddress*)elem->data; belle_sip_header_route_t *route; belle_sip_uri_t *uri; /*Optimization: if the initial route set only contains one URI which is the same as the request URI, ommit it*/ if (elem==list && list->next==NULL){ belle_sip_uri_t *requri=belle_sip_request_get_uri(request); /*skip the first route it is the same as the request uri*/ if (strcmp(sal_address_get_domain(addr),belle_sip_uri_get_host(requri))==0 ){ ms_message("Skipping top route of initial route-set because same as request-uri."); continue; } } route=belle_sip_header_route_create((belle_sip_header_address_t*)addr); uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)route); belle_sip_uri_set_lr_param(uri,1); belle_sip_message_add_header((belle_sip_message_t*)request,(belle_sip_header_t*)route); } } belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) { belle_sip_header_from_t* from_header; belle_sip_header_to_t* to_header; belle_sip_provider_t* prov=op->base.root->prov; belle_sip_request_t *req; belle_sip_uri_t* req_uri; belle_sip_uri_t* to_uri; belle_sip_header_call_id_t *call_id_header; const SalAddress* to_address; const MSList *elem=sal_op_get_route_addresses(op); char token[10]; /* check that the op has a correct to address */ to_address = sal_op_get_to_address(op); if( to_address == NULL ){ ms_error("No To: address, cannot build request"); return NULL; } to_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to_address)); if( to_uri == NULL ){ ms_error("To: address is invalid, cannot build request"); return NULL; } if (strcmp("REGISTER",method)==0 || op->privacy==SalPrivacyNone) { from_header = belle_sip_header_from_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op)) ,belle_sip_random_token(token,sizeof(token))); } else { from_header=belle_sip_header_from_create2("Anonymous ",belle_sip_random_token(token,sizeof(token))); } /*make sure to preserve components like headers or port*/ req_uri = (belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)to_uri); belle_sip_uri_set_secure(req_uri,sal_op_is_secure(op)); to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(to_address),NULL); call_id_header = belle_sip_provider_create_call_id(prov); if (sal_op_get_call_id(op)) { belle_sip_header_call_id_set_call_id(call_id_header, sal_op_get_call_id(op)); } req=belle_sip_request_create( req_uri, method, call_id_header, belle_sip_header_cseq_create(20,method), from_header, to_header, belle_sip_header_via_new(), 70); if (op->privacy & SalPrivacyId) { belle_sip_header_p_preferred_identity_t* p_preferred_identity=belle_sip_header_p_preferred_identity_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(p_preferred_identity)); } if (elem && strcmp(method,"REGISTER")!=0 && !op->base.root->no_initial_route){ add_initial_route_set(req,elem); } if (strcmp("REGISTER",method)!=0 && op->privacy!=SalPrivacyNone ){ belle_sip_header_privacy_t* privacy_header=belle_sip_header_privacy_new(); if (op->privacy&SalPrivacyCritical) belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyCritical)); if (op->privacy&SalPrivacyHeader) belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyHeader)); if (op->privacy&SalPrivacyId) belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyId)); if (op->privacy&SalPrivacyNone) belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyNone)); if (op->privacy&SalPrivacySession) belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacySession)); if (op->privacy&SalPrivacyUser) belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyUser)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(privacy_header)); } belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->base.root->supported); return req; } belle_sip_response_t *sal_op_create_response_from_request(SalOp *op, belle_sip_request_t *req, int code){ return sal_create_response_from_request(op->base.root,req,code); } /*ping: main purpose is to obtain its own contact address behind firewalls*/ int sal_ping(SalOp *op, const char *from, const char *to){ sal_op_set_from(op,from); sal_op_set_to(op,to); return sal_op_send_request(op,sal_op_build_request(op,"OPTIONS")); } void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces) { if (op->replaces){ belle_sip_object_unref(op->replaces); } op->replaces=replaces; belle_sip_object_ref(op->replaces); } void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message) { belle_sip_header_user_agent_t* user_agent=belle_sip_message_get_header_by_type(message,belle_sip_header_user_agent_t); char user_agent_string[256]; if (user_agent && belle_sip_header_user_agent_get_products_as_string(user_agent,user_agent_string,sizeof(user_agent_string))>0) { if (op->base.remote_ua!=NULL){ ms_free(op->base.remote_ua); } op->base.remote_ua=ms_strdup(user_agent_string); } } int sal_op_send_request_with_expires(SalOp* op, belle_sip_request_t* request,int expires) { belle_sip_header_expires_t* expires_header=(belle_sip_header_expires_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_EXPIRES); if (!expires_header && expires>=0) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header=belle_sip_header_expires_new())); } if (expires_header) belle_sip_header_expires_set_expires(expires_header,expires); return sal_op_send_request(op,request); } void sal_op_resend_request(SalOp* op, belle_sip_request_t* request) { belle_sip_header_cseq_t* cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CSEQ); belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); sal_op_send_request(op,request); } static void add_headers(SalOp *op, belle_sip_header_t *h, belle_sip_message_t *msg){ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(h,belle_sip_header_contact_t)){ belle_sip_header_contact_t* newct; /*special case for contact, we want to keep everything from the custom contact but set automatic mode and add our own parameters as well*/ sal_op_set_contact_address(op,(SalAddress*)BELLE_SIP_HEADER_ADDRESS(h)); newct = sal_op_create_contact(op); belle_sip_message_set_header(BELLE_SIP_MESSAGE(msg),BELLE_SIP_HEADER(newct)); return; } /*if a header already exists in the message, replace it*/ belle_sip_message_set_header(msg,h); } void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg){ if (op->base.sent_custom_headers){ belle_sip_message_t *ch=(belle_sip_message_t*)op->base.sent_custom_headers; belle_sip_list_t *l=belle_sip_message_get_all_headers(ch); belle_sip_list_t *elem; for(elem=l;elem!=NULL;elem=elem->next){ add_headers(op,(belle_sip_header_t*)elem->data,msg); } belle_sip_list_free(l); } } static int _sal_op_send_request_with_contact(SalOp* op, belle_sip_request_t* request, bool_t add_contact) { belle_sip_client_transaction_t* client_transaction; belle_sip_provider_t* prov=op->base.root->prov; belle_sip_uri_t* outbound_proxy=NULL; belle_sip_header_contact_t* contact; int result =-1; belle_sip_uri_t *next_hop_uri=NULL; if (add_contact && !belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_contact_t)) { contact = sal_op_create_contact(op); belle_sip_message_set_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(contact)); } /*keep existing*/ _sal_op_add_custom_headers(op, (belle_sip_message_t*)request); if (!op->dialog || belle_sip_dialog_get_state(op->dialog) == BELLE_SIP_DIALOG_NULL) { /*don't put route header if dialog is in confirmed state*/ const MSList *elem=sal_op_get_route_addresses(op); const char *transport; const char *method=belle_sip_request_get_method(request); belle_sip_listening_point_t *udplp=belle_sip_provider_get_listening_point(prov,"UDP"); if (elem) { outbound_proxy=belle_sip_header_address_get_uri((belle_sip_header_address_t*)elem->data); next_hop_uri=outbound_proxy; }else{ next_hop_uri=(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_request_get_uri(request)); } transport=belle_sip_uri_get_transport_param(next_hop_uri); if (transport==NULL){ /*compatibility mode: by default it should be udp as not explicitely set and if no udp listening point is available, then use * the first available transport*/ if (!belle_sip_uri_is_secure(next_hop_uri)){ if (udplp==NULL){ if (belle_sip_provider_get_listening_point(prov,"TCP")!=NULL){ transport="tcp"; }else if (belle_sip_provider_get_listening_point(prov,"TLS")!=NULL ){ transport="tls"; } } if (transport){ belle_sip_message("Transport is not specified, using %s because UDP is not available.",transport); belle_sip_uri_set_transport_param(next_hop_uri,transport); } } }else{ #ifdef TUNNEL_ENABLED if (udplp && BELLE_SIP_OBJECT_IS_INSTANCE_OF(udplp,belle_sip_tunnel_listening_point_t)){ /* our tunnel mode only supports UDP. Force transport to be set to UDP */ belle_sip_uri_set_transport_param(next_hop_uri,"udp"); } #endif } /*because in case of tunnel, transport can be changed*/ transport=belle_sip_uri_get_transport_param(next_hop_uri); if ((strcmp(method,"REGISTER")==0 || strcmp(method,"SUBSCRIBE")==0) && transport && (strcasecmp(transport,"TCP")==0 || strcasecmp(transport,"TLS")==0)){ /*RFC 5923: add 'alias' parameter to tell the server that we want it to keep the connection for future requests*/ belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_via_t); belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(via),"alias",NULL); } } client_transaction = belle_sip_provider_create_client_transaction(prov,request); belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),sal_op_ref(op)); if (op->pending_client_trans) belle_sip_object_unref(op->pending_client_trans); op->pending_client_trans=client_transaction; /*update pending inv for being able to cancel*/ belle_sip_object_ref(op->pending_client_trans); if (belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_user_agent_t)==NULL) belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(op->base.root->user_agent)); if (!belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION) && !belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION)) { /*hmm just in case we already have authentication param in cache*/ belle_sip_provider_add_authorization(op->base.root->prov,request,NULL,NULL,NULL,op->base.realm); } result = belle_sip_client_transaction_send_request_to(client_transaction,next_hop_uri/*might be null*/); /*update call id if not set yet for this OP*/ if (result == 0 && !op->base.call_id) { op->base.call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request), belle_sip_header_call_id_t)))); } return result; } int sal_op_send_request(SalOp* op, belle_sip_request_t* request) { bool_t need_contact=FALSE; if (request==NULL) { return -1; /*sanity check*/ } /* Header field where proxy ACK BYE CAN INV OPT REG ___________________________________________________________ Contact R o - - m o o */ if (strcmp(belle_sip_request_get_method(request),"INVITE")==0 ||strcmp(belle_sip_request_get_method(request),"REGISTER")==0 ||strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0 ||strcmp(belle_sip_request_get_method(request),"OPTIONS")==0 ||strcmp(belle_sip_request_get_method(request),"REFER")==0) /* Despite contact seems not mandatory, call flow example show a Contact in REFER requests*/ need_contact=TRUE; return _sal_op_send_request_with_contact(op, request,need_contact); } int sal_reason_to_sip_code(SalReason r){ int ret=500; switch(r){ case SalReasonNone: ret=200; break; case SalReasonIOError: ret=503; break; case SalReasonUnknown: ret=400; break; case SalReasonBusy: ret=486; break; case SalReasonDeclined: ret=603; break; case SalReasonDoNotDisturb: ret=600; break; case SalReasonForbidden: ret=403; break; case SalReasonUnsupportedContent: ret=415; break; case SalReasonNotFound: ret=404; break; case SalReasonRedirect: ret=302; break; case SalReasonTemporarilyUnavailable: ret=480; break; case SalReasonServiceUnavailable: ret=503; break; case SalReasonRequestPending: ret=491; break; case SalReasonUnauthorized: ret=401; break; case SalReasonNotAcceptable: ret=488; /*or maybe 606 Not Acceptable ?*/ break; case SalReasonNoMatch: ret=481; break; case SalReasonRequestTimeout: ret=408; break; case SalReasonMovedPermanently: ret=301; break; case SalReasonGone: ret=410; break; case SalReasonAddressIncomplete: ret=484; break; case SalReasonNotImplemented: ret=501; break; case SalReasonServerTimeout: ret=504; break; case SalReasonBadGateway: ret=502; break; case SalReasonInternalError: ret=500; break; } return ret; } SalReason _sal_reason_from_sip_code(int code) { if (code>=100 && code<300) return SalReasonNone; switch(code) { case 0: return SalReasonIOError; case 301: return SalReasonMovedPermanently; case 302: return SalReasonRedirect; case 401: case 407: return SalReasonUnauthorized; case 403: return SalReasonForbidden; case 404: return SalReasonNotFound; case 408: return SalReasonRequestTimeout; case 410: return SalReasonGone; case 415: return SalReasonUnsupportedContent; case 422: ms_error ("422 not implemented yet");; break; case 480: return SalReasonTemporarilyUnavailable; case 481: return SalReasonNoMatch; case 484: return SalReasonAddressIncomplete; case 486: return SalReasonBusy; case 487: return SalReasonNone; case 488: return SalReasonNotAcceptable; case 491: return SalReasonRequestPending; case 500: return SalReasonInternalError; case 501: return SalReasonNotImplemented; case 502: return SalReasonBadGateway; case 503: return SalReasonServiceUnavailable; case 504: return SalReasonServerTimeout; case 600: return SalReasonDoNotDisturb; case 603: return SalReasonDeclined; default: return SalReasonUnknown; } return SalReasonUnknown; } const SalErrorInfo *sal_error_info_none(void){ static SalErrorInfo none={ SalReasonNone, "Ok", 200, NULL, NULL, }; return &none; } void sal_error_info_reset(SalErrorInfo *ei){ if (ei->status_string){ ms_free(ei->status_string); ei->status_string=NULL; } if (ei->warnings){ ms_free(ei->warnings); ei->warnings=NULL; } if (ei->full_string){ ms_free(ei->full_string); ei->full_string=NULL; } if (ei->protocol){ ms_free(ei->protocol); ei->protocol = NULL; } ei->protocol_code=0; ei->reason=SalReasonNone; ei->sub_sei = NULL; } void sal_error_info_set(SalErrorInfo *ei, SalReason reason, const char *protocol, int code, const char *status_string, const char *warning){ sal_error_info_reset(ei); if (reason==SalReasonUnknown && strcmp(protocol, "SIP") == 0 && code != 0) ei->reason=_sal_reason_from_sip_code(code); else{ ei->reason=reason; if (code == 0) { code = sal_reason_to_sip_code(reason); } } ei->protocol_code=code; ei->status_string=status_string ? ms_strdup(status_string) : NULL; ei->warnings=warning ? ms_strdup(warning) : NULL; ei->protocol = protocol ? ms_strdup(protocol) : NULL; if (ei->status_string){ if (ei->warnings) ei->full_string=ms_strdup_printf("%s %s",ei->status_string,ei->warnings); else ei->full_string=ms_strdup(ei->status_string); } } void sal_op_set_reason_error_info(SalOp *op, belle_sip_message_t *msg){ belle_sip_header_reason_t* reason_header = belle_sip_message_get_header_by_type(msg,belle_sip_header_reason_t); if (reason_header){ SalErrorInfo *ei=&op->reason_error_info; // ?// const char *protocol = belle_sip_header_reason_get_protocol(reason_header); int code = belle_sip_header_reason_get_cause(reason_header); const char *text = belle_sip_header_reason_get_text(reason_header); sal_error_info_set(ei, SalReasonUnknown, protocol, code, text, NULL); } } void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *response){ int code = belle_sip_response_get_status_code(response); const char *reason_phrase=belle_sip_response_get_reason_phrase(response); belle_sip_header_t *warning=belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Warning"); SalErrorInfo *ei=&op->error_info; const char *warnings; warnings=warning ? belle_sip_header_get_unparsed_value(warning) : NULL; sal_error_info_set(ei,SalReasonUnknown,"SIP", code,reason_phrase,warnings); sal_op_set_reason_error_info(op, BELLE_SIP_MESSAGE(response)); } const SalErrorInfo *sal_op_get_error_info(const SalOp *op){ return &op->error_info; } const SalErrorInfo * sal_op_get_reason_error_info(const SalOp *op){ return &op->reason_error_info; } static void unlink_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){ belle_sip_dialog_set_application_data(dialog,NULL); sal_op_unref(op); belle_sip_object_unref(dialog); } static belle_sip_dialog_t *link_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){ belle_sip_dialog_set_application_data(dialog,sal_op_ref(op)); belle_sip_object_ref(dialog); return dialog; } void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) { ms_message("op [%p] : set_or_update_dialog() current=[%p] new=[%p]",op,op->dialog,dialog); sal_op_ref(op); if (op->dialog!=dialog){ if (op->dialog){ /*FIXME: shouldn't we delete unconfirmed dialogs ?*/ unlink_op_with_dialog(op,op->dialog); op->dialog=NULL; } if (dialog) { op->dialog=link_op_with_dialog(op,dialog); belle_sip_dialog_enable_pending_trans_checking(dialog,op->base.root->pending_trans_checking); } } sal_op_unref(op); } /*return reffed op*/ SalOp* sal_op_ref(SalOp* op) { op->ref++; return op; } /*return null, destroy op if ref count =0*/ void* sal_op_unref(SalOp* op) { op->ref--; if (op->ref==0) { sal_op_release_impl(op); }else if (op->ref<0){ ms_fatal("SalOp [%p]: too many unrefs.",op); } return NULL; } int sal_op_send_and_create_refresher(SalOp* op,belle_sip_request_t* req, int expires,belle_sip_refresher_listener_t listener ) { if (sal_op_send_request_with_expires(op,req,expires)==0) { if (op->refresher) { belle_sip_refresher_stop(op->refresher); belle_sip_object_unref(op->refresher); } if ((op->refresher = belle_sip_client_transaction_create_refresher(op->pending_client_trans))) { /*since refresher acquires the transaction, we should remove our context from the transaction, because we won't be notified * that it is terminated anymore.*/ sal_op_unref(op);/*loose the reference that was given to the transaction when creating it*/ /* Note that the refresher will replace our data with belle_sip_transaction_set_application_data(). Something in the design is not very good here, it makes things complicated to the belle-sip user. Possible ideas to improve things: refresher shall not use belle_sip_transaction_set_application_data() internally, refresher should let the first transaction notify the user as a normal transaction*/ belle_sip_refresher_set_listener(op->refresher,listener,op); belle_sip_refresher_set_retry_after(op->refresher,op->base.root->refresher_retry_after); belle_sip_refresher_set_realm(op->refresher,op->base.realm); belle_sip_refresher_enable_manual_mode(op->refresher,op->manual_refresher); return 0; } else { return -1; } } return -1; } const char* sal_op_state_to_string(const SalOpState value) { switch(value) { case SalOpStateEarly: return"SalOpStateEarly"; case SalOpStateActive: return "SalOpStateActive"; case SalOpStateTerminating: return "SalOpStateTerminating"; case SalOpStateTerminated: return "SalOpStateTerminated"; default: return "Unknown"; } } /* * Warning: this function takes owneship of the custom headers */ void sal_op_set_sent_custom_header(SalOp *op, SalCustomHeader* ch){ SalOpBase *b=(SalOpBase *)op; if (b->sent_custom_headers){ sal_custom_header_free(b->sent_custom_headers); b->sent_custom_headers=NULL; } if (ch) belle_sip_object_ref((belle_sip_message_t*)ch); b->sent_custom_headers=ch; } void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming){ if (incoming) belle_sip_object_ref(incoming); if (op->base.recv_custom_headers){ belle_sip_object_unref(op->base.recv_custom_headers); op->base.recv_custom_headers=NULL; } if (incoming){ op->base.recv_custom_headers=(SalCustomHeader*)incoming; } } const char *sal_op_get_remote_contact(const SalOp *op){ /* * remote contact is filled in process_response */ return op->base.remote_contact; } SalBodyHandler * sal_op_get_body_handler(SalOp *op, belle_sip_message_t *msg) { belle_sip_body_handler_t *body_handler = belle_sip_message_get_body_handler(msg); if (body_handler != NULL) { belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t); belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_length_t); belle_sip_header_t *content_encoding = belle_sip_message_get_header(msg, "Content-Encoding"); if (content_type != NULL) belle_sip_body_handler_add_header(body_handler, BELLE_SIP_HEADER(content_type)); if (content_length != NULL) belle_sip_body_handler_add_header(body_handler, BELLE_SIP_HEADER(content_length)); if (content_encoding != NULL) belle_sip_body_handler_add_header(body_handler, content_encoding); } return (SalBodyHandler *)body_handler; } void sal_op_set_privacy(SalOp* op,SalPrivacyMask privacy) { op->privacy=privacy; } SalPrivacyMask sal_op_get_privacy(const SalOp* op) { return op->privacy; } bool_t sal_op_is_secure(const SalOp* op) { const SalAddress* from = sal_op_get_from_address(op); const SalAddress* to = sal_op_get_to_address(op); return from && to && strcasecmp("sips",sal_address_get_scheme(from))==0 && strcasecmp("sips",sal_address_get_scheme(to))==0; } void sal_op_set_manual_refresher_mode(SalOp *op, bool_t enabled){ op->manual_refresher=enabled; } int sal_op_get_address_family(SalOp *op){ belle_sip_transaction_t *tr=NULL; belle_sip_header_address_t *contact; if (op->refresher) tr=(belle_sip_transaction_t *)belle_sip_refresher_get_transaction(op->refresher); if (tr==NULL) tr=(belle_sip_transaction_t *)op->pending_client_trans; if (tr==NULL) tr=(belle_sip_transaction_t *)op->pending_server_trans; if (tr==NULL){ ms_error("Unable to determine IP version from signaling operation."); return AF_UNSPEC; } if (op->refresher) { belle_sip_response_t *resp = belle_sip_transaction_get_response(tr); belle_sip_header_via_t *via = resp ?belle_sip_message_get_header_by_type(resp,belle_sip_header_via_t):NULL; if (!via){ ms_error("Unable to determine IP version from signaling operation, no via header found."); return AF_UNSPEC; } return (strchr(belle_sip_header_via_get_host(via),':') != NULL) ? AF_INET6 : AF_INET; } else { belle_sip_request_t *req = belle_sip_transaction_get_request(tr); contact=(belle_sip_header_address_t*)belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t); if (!contact){ ms_error("Unable to determine IP version from signaling operation, no contact header found."); } return sal_address_is_ipv6((SalAddress*)contact) ? AF_INET6 : AF_INET; } } bool_t sal_op_is_idle(SalOp *op){ if (op->dialog){ return !belle_sip_dialog_request_pending(op->dialog); } return TRUE; } void sal_op_stop_refreshing(SalOp *op){ if (op->refresher){ belle_sip_refresher_stop(op->refresher); } } void sal_call_set_sdp_handling(SalOp *h, SalOpSDPHandling handling) { if (handling != SalOpSDPNormal) ms_message("Enabling special SDP handling for SalOp[%p]!", h); h->sdp_handling = handling; } void sal_op_cnx_ip_to_0000_if_sendonly_enable(SalOp *op,bool_t yesno) { op->cnx_ip_to_0000_if_sendonly_enabled = yesno; } bool_t sal_op_cnx_ip_to_0000_if_sendonly_enabled(SalOp *op) { return op->cnx_ip_to_0000_if_sendonly_enabled; } bool_t sal_op_is_forked_of(const SalOp *op1, const SalOp *op2){ return op1->base.call_id && op2->base.call_id && strcmp(op1->base.call_id, op2->base.call_id) == 0; } int sal_op_refresh(SalOp *op) { if (op->refresher) { belle_sip_refresher_refresh(op->refresher,belle_sip_refresher_get_expires(op->refresher)); return 0; } ms_warning("sal_refresh on op [%p] of type [%s] no refresher",op,sal_op_type_to_string(op->type)); return -1; } void sal_op_set_event(SalOp *op, const char *eventname){ belle_sip_header_event_t *header = NULL; if (op->event) belle_sip_object_unref(op->event); if (eventname){ header = belle_sip_header_event_create(eventname); belle_sip_object_ref(header); } op->event = header; } const char* sal_op_get_public_address(SalOp *op, int *port) { if (op && op->refresher) { return belle_sip_refresher_get_public_address(op->refresher, port); } return NULL; } const char* sal_op_get_local_address(SalOp *op, int *port) { if (op && op->refresher) { return belle_sip_refresher_get_local_address(op->refresher, port); } return NULL; } char* sal_op_get_dialog_id(const SalOp *op) { if (op->dialog != NULL) { return ms_strdup_printf("%s;to-tag=%s;from-tag=%s", ((SalOpBase*)op)->call_id, belle_sip_dialog_get_remote_tag(op->dialog), belle_sip_dialog_get_local_tag(op->dialog)); } return NULL; } linphone-3.12.0/coreapi/bellesip_sal/sal_op_info.c000066400000000000000000000026321313432737600221410ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" int sal_send_info(SalOp *op, const char *from, const char *to, const SalBodyHandler *body_handler){ if (op->dialog && belle_sip_dialog_get_state(op->dialog) == BELLE_SIP_DIALOG_CONFIRMED){ belle_sip_request_t *req; belle_sip_dialog_enable_pending_trans_checking(op->dialog,op->base.root->pending_trans_checking); req=belle_sip_dialog_create_queued_request(op->dialog,"INFO"); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler)); return sal_op_send_request(op,req); }else{ ms_error("Cannot send INFO message on op [%p] because dialog is not in confirmed state yet.", op); } return -1; } linphone-3.12.0/coreapi/bellesip_sal/sal_op_message.c000066400000000000000000000213261313432737600226330ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" #include "linphone/core.h" #include "private.h" #include static void process_error( SalOp* op) { if (op->dir == SalOpDirOutgoing) { op->base.root->callbacks.message_delivery_update(op, SalMessageDeliveryFailed); } else { ms_warning("unexpected io error for incoming message on op [%p]",op); } op->state=SalOpStateTerminated; } static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ SalOp* op = (SalOp*)user_ctx; sal_error_info_set(&op->error_info,SalReasonIOError, "SIP", 503,"IO Error",NULL); process_error(op); } static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { SalOp* op=(SalOp*)user_ctx; sal_error_info_set(&op->error_info,SalReasonRequestTimeout, "SIP", 408,"Request timeout",NULL); process_error(op); } static void process_response_event(void *op_base, const belle_sip_response_event_t *event){ SalOp* op = (SalOp*)op_base; int code = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)); SalMessageDeliveryStatus status; sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event)); if (code>=100 && code <200) status=SalMessageDeliveryInProgress; else if (code>=200 && code <300) status=SalMessageDeliveryDone; else status=SalMessageDeliveryFailed; op->base.root->callbacks.message_delivery_update(op,status); } static bool_t is_external_body(belle_sip_header_content_type_t* content_type) { return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0 && strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0; } static void add_message_accept(SalOp *op, belle_sip_message_t *msg) { bctbx_list_t *item; const char *str; char *old; char *header = ms_strdup("xml/cipher, application/cipher.vnd.gsma.rcs-ft-http+xml"); for (item = op->base.root->supported_content_types; item != NULL; item = bctbx_list_next(item)) { str = (const char *)bctbx_list_get_data(item); old = header; header = ms_strdup_printf("%s, %s", old, str); ms_free(old); } belle_sip_message_add_header(msg, belle_sip_header_create("Accept", header)); ms_free(header); } void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){ belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,req); belle_sip_header_address_t* address; belle_sip_header_from_t* from_header; belle_sip_header_content_type_t* content_type; belle_sip_response_t* resp; int errcode = 500; belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t); belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(req,belle_sip_header_date_t); char* from; bool_t external_body=FALSE; from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t); content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t); if (content_type) { SalMessage salmsg; char message_id[256]={0}; if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); op->pending_server_trans=server_transaction; belle_sip_object_ref(op->pending_server_trans); external_body=is_external_body(content_type); address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address)); snprintf(message_id,sizeof(message_id)-1,"%s%i" ,belle_sip_header_call_id_get_call_id(call_id) ,belle_sip_header_cseq_get_seq_number(cseq)); salmsg.from=from; /* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/ salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL; salmsg.url=NULL; salmsg.content_type = ms_strdup_printf("%s/%s", belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type)); if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) { size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")); salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/ ((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/ } salmsg.message_id=message_id; salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL); op->base.root->callbacks.message_received(op,&salmsg); belle_sip_object_unref(address); belle_sip_free(from); if (salmsg.url) ms_free((char*)salmsg.url); ms_free((char *)salmsg.content_type); } else { ms_error("Unsupported MESSAGE (no Content-Type)"); resp = belle_sip_response_create_from_request(req, errcode); add_message_accept(op, (belle_sip_message_t*)resp); belle_sip_server_transaction_send_response(server_transaction,resp); sal_op_release(op); } } static void process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; sal_process_incoming_message(op,event); } int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg, const char *peer_uri){ belle_sip_request_t* req; char content_type_raw[256]; size_t content_length = msg?strlen(msg):0; time_t curtime = ms_time(NULL); const char *body; int retval; if (op->dialog){ /*for SIP MESSAGE that are sent in call's dialog*/ req=belle_sip_dialog_create_queued_request(op->dialog,"MESSAGE"); }else{ sal_op_message_fill_cbs(op); if (from) sal_op_set_from(op,from); if (to) sal_op_set_to(op,to); op->dir=SalOpDirOutgoing; req=sal_op_build_request(op,"MESSAGE"); if (req == NULL ){ return -1; } if (sal_op_get_contact_address(op)){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); } } snprintf(content_type_raw,sizeof(content_type_raw),BELLE_SIP_CONTENT_TYPE ": %s",content_type); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_parse(content_type_raw))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime))); body = msg; if (body){ /*don't call set_body() with null argument because it resets content type and content length*/ belle_sip_message_set_body(BELLE_SIP_MESSAGE(req), body, content_length); } retval = sal_op_send_request(op,req); return retval; } int sal_message_reply(SalOp *op, SalReason reason){ if (op->pending_server_trans){ int code=sal_reason_to_sip_code(reason); belle_sip_response_t *resp = belle_sip_response_create_from_request( belle_sip_transaction_get_request((belle_sip_transaction_t*)op->pending_server_trans),code); belle_sip_server_transaction_send_response(op->pending_server_trans,resp); return 0; }else ms_error("sal_message_reply(): no server transaction"); return -1; } int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) { return sal_message_send(op,from,to,"text/plain",msg, NULL); } static belle_sip_listener_callbacks_t op_message_callbacks={0}; void sal_op_message_fill_cbs(SalOp*op) { if (op_message_callbacks.process_io_error==NULL){ op_message_callbacks.process_io_error=process_io_error; op_message_callbacks.process_response_event=process_response_event; op_message_callbacks.process_timeout=process_timeout; op_message_callbacks.process_request_event=process_request_event; } op->callbacks=&op_message_callbacks; op->type=SalOpMessage; } linphone-3.12.0/coreapi/bellesip_sal/sal_op_presence.c000066400000000000000000000424261313432737600230170ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence) { char *contact_info; char *content = NULL; size_t content_length; if (presence){ belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(notify,belle_sip_header_from_t); contact_info=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from))); op->base.root->callbacks.convert_presence_to_xml_requested(op, presence, contact_info, &content); belle_sip_free(contact_info); if (content == NULL) return; } belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_TYPE); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_LENGTH); belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),NULL,0); if (content){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","pidf+xml"))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length=strlen(content)))); belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),content,content_length); ms_free(content); } } static void presence_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ SalOp* op = (SalOp*)user_ctx; belle_sip_request_t* request; belle_sip_client_transaction_t* client_transaction = NULL; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(belle_sip_io_error_event_get_source(event), belle_sip_client_transaction_t)){ client_transaction = (belle_sip_client_transaction_t*)belle_sip_io_error_event_get_source(event); } if (!client_transaction) return; request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){ if (op->refresher){ ms_warning("presence_process_io_error() refresher is present, should not happen"); return; } ms_message("subscription to [%s] io error",sal_op_get_to(op)); if (!op->op_released){ op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ } } } static void presence_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { SalOp* op= (SalOp*)ctx; if (op->dialog && belle_sip_dialog_is_server(op->dialog)) { ms_message("Incoming subscribtion from [%s] terminated",sal_op_get_from(op)); if (!op->op_released){ op->base.root->callbacks.subscribe_presence_closed(op, sal_op_get_from(op)); } set_or_update_dialog(op, NULL); }/* else client dialog is managed by refresher*/ } static void presence_refresher_listener(belle_sip_refresher_t* refresher, void* user_pointer, unsigned int status_code, const char* reason_phrase, int will_retry){ SalOp* op = (SalOp*)user_pointer; if (status_code >= 300) { ms_message("The SUBSCRIBE dialog no longer works. Let's restart a new one."); belle_sip_refresher_stop(op->refresher); if (op->dialog) { /*delete previous dialog if any*/ set_or_update_dialog(op, NULL); } if (sal_op_get_contact_address(op)) { /*contact is also probably not good*/ SalAddress* contact=sal_address_clone(sal_op_get_contact_address(op)); sal_address_set_port(contact,-1); sal_address_set_domain(contact,NULL); sal_op_set_contact_address(op,contact); sal_address_destroy(contact); } /*send a new SUBSCRIBE, that will attempt to establish a new dialog*/ sal_subscribe_presence(op,NULL,NULL,-1); } if (status_code == 0 || status_code == 503){ /*timeout or io error: the remote doesn't seem reachable.*/ if (!op->op_released){ op->base.root->callbacks.notify_presence(op,SalSubscribeActive, NULL,NULL); /*NULL = offline*/ } } } static void presence_response_event(void *op_base, const belle_sip_response_event_t *event){ SalOp* op = (SalOp*)op_base; belle_sip_dialog_state_t dialog_state; belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_response_t* response=belle_sip_response_event_get_response(event); belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); int code = belle_sip_response_get_status_code(response); belle_sip_header_expires_t* expires; sal_op_set_error_info_from_response(op,response); if (code>=300) { if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){ ms_message("subscription to [%s] rejected",sal_op_get_to(op)); if (!op->op_released){ op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ } return; } } set_or_update_dialog(op_base,belle_sip_response_event_get_dialog(event)); if (!op->dialog) { ms_message("presence op [%p] receive out of dialog answer [%i]",op,code); return; } dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: case BELLE_SIP_DIALOG_EARLY: { ms_error("presence op [%p] receive an unexpected answer [%i]",op,code); break; } case BELLE_SIP_DIALOG_CONFIRMED: { if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0) { expires=belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t); if(op->refresher) { belle_sip_refresher_stop(op->refresher); belle_sip_object_unref(op->refresher); op->refresher=NULL; } if ((expires != NULL) && (belle_sip_header_expires_get_expires(expires) > 0)) { op->refresher=belle_sip_client_transaction_create_refresher(client_transaction); belle_sip_refresher_set_listener(op->refresher,presence_refresher_listener,op); belle_sip_refresher_set_realm(op->refresher,op->base.realm); } } break; } default: { ms_error("presence op [%p] receive answer [%i] not implemented",op,code); } /* no break */ } } static void presence_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { SalOp* op = (SalOp*)user_ctx; belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event); belle_sip_request_t* request; if (!client_transaction) return; request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){ ms_message("subscription to [%s] timeout",sal_op_get_to(op)); if (!op->op_released){ op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ } } } static void presence_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { ms_message("presence_process_transaction_terminated not implemented yet"); } static SalPresenceModel * process_presence_notification(SalOp *op, belle_sip_request_t *req) { belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_type_t); belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_length_t); const char *body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); SalPresenceModel *result = NULL; if ((content_type == NULL) || (content_length == NULL)) return NULL; if (belle_sip_header_content_length_get_content_length(content_length) == 0) return NULL; if (body==NULL) return NULL; if (!op->op_released){ op->base.root->callbacks.parse_presence_requested(op, belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type), body, &result); } return result; } static void handle_notify(SalOp *op, belle_sip_request_t *req, belle_sip_dialog_t *dialog){ belle_sip_response_t* resp=NULL; belle_sip_server_transaction_t* server_transaction=op->pending_server_trans; belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t); SalSubscribeStatus sub_state; if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) { SalPresenceModel *presence_model = NULL; const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); if (op->dialog !=NULL && dialog != op->dialog){ ms_warning("Receiving a NOTIFY from a dialog we haven't stored (op->dialog=%p dialog=%p)", op->dialog, dialog); } if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) { sub_state=SalSubscribeTerminated; ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); } else { sub_state=belle_sip_message_get_subscription_state(BELLE_SIP_MESSAGE(req)); } presence_model = process_presence_notification(op, req); if (presence_model != NULL || body==NULL) { /* Presence notification body parsed successfully. */ resp = sal_op_create_response_from_request(op, req, 200); /*create first because the op may be destroyed by notify_presence */ if (!op->op_released){ op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL); } } else if (body){ /* Formatting error in presence notification body. */ ms_warning("Wrongly formatted presence document."); resp = sal_op_create_response_from_request(op, req, 488); } if (resp) belle_sip_server_transaction_send_response(server_transaction,resp); } } static void presence_process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_dialog_state_t dialog_state; belle_sip_response_t* resp; const char *method=belle_sip_request_get_method(req); belle_sip_header_event_t *event_header; belle_sip_object_ref(server_transaction); if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); op->pending_server_trans=server_transaction; event_header=belle_sip_message_get_header_by_type(req,belle_sip_header_event_t); if (event_header==NULL){ ms_warning("No event header in incoming SUBSCRIBE."); resp=sal_op_create_response_from_request(op,req,400); belle_sip_server_transaction_send_response(server_transaction,resp); if (!op->dialog) sal_op_release(op); return; } if (op->event==NULL) { op->event=event_header; belle_sip_object_ref(op->event); } if (!op->dialog) { if (strcmp(method,"SUBSCRIBE")==0){ belle_sip_dialog_t *dialog = belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction)); if (!dialog){ resp=sal_op_create_response_from_request(op,req,481); belle_sip_server_transaction_send_response(server_transaction,resp); sal_op_release(op); return; } set_or_update_dialog(op, dialog); ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op)); }else if (strcmp(method,"NOTIFY")==0 && belle_sip_request_event_get_dialog(event)) { /*special case of dialog created by notify matching subscribe*/ set_or_update_dialog(op, belle_sip_request_event_get_dialog(event)); } else {/* this is a NOTIFY */ ms_message("Receiving out of dialog notify"); handle_notify(op, req, belle_sip_request_event_get_dialog(event)); return; } } dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { if (strcmp("NOTIFY",method)==0) { handle_notify(op, req, belle_sip_request_event_get_dialog(event)); } else if (strcmp("SUBSCRIBE",method)==0) { op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op)); } break; } case BELLE_SIP_DIALOG_EARLY: ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",method,op->dialog); break; case BELLE_SIP_DIALOG_CONFIRMED: if (strcmp("NOTIFY",method)==0) { handle_notify(op, req, belle_sip_request_event_get_dialog(event)); } else if (strcmp("SUBSCRIBE",method)==0) { /*either a refresh or an unsubscribe. If it is a refresh there is nothing to notify to the app. If it is an unSUBSCRIBE, then the dialog will be terminated shortly, and this will be notified to the app through the dialog_terminated callback.*/ resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } break; default: ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); break; } } static belle_sip_listener_callbacks_t op_presence_callbacks={0}; /*Invoke when sal_op_release is called by upper layer*/ static void sal_op_release_cb(struct SalOpBase* op_base) { SalOp *op =(SalOp*)op_base; if(op->refresher) { belle_sip_refresher_stop(op->refresher); belle_sip_object_unref(op->refresher); op->refresher=NULL; set_or_update_dialog(op,NULL); /*only if we have refresher. else dialog terminated event will remove association*/ } } void sal_op_presence_fill_cbs(SalOp*op) { if (op_presence_callbacks.process_request_event==NULL){ op_presence_callbacks.process_io_error=presence_process_io_error; op_presence_callbacks.process_response_event=presence_response_event; op_presence_callbacks.process_timeout=presence_process_timeout; op_presence_callbacks.process_transaction_terminated=presence_process_transaction_terminated; op_presence_callbacks.process_request_event=presence_process_request_event; op_presence_callbacks.process_dialog_terminated=presence_process_dialog_terminated; } op->callbacks=&op_presence_callbacks; op->type=SalOpPresence; op->base.release_cb=sal_op_release_cb; } /*presence Subscribe/notify*/ int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expires){ belle_sip_request_t *req=NULL; if (from) sal_op_set_from(op,from); if (to) sal_op_set_to(op,to); sal_op_presence_fill_cbs(op); if (expires==-1){ if (op->refresher){ expires=belle_sip_refresher_get_expires(op->refresher); belle_sip_object_unref(op->refresher); op->refresher=NULL; }else{ ms_error("sal_subscribe_presence(): cannot guess expires from previous refresher."); return -1; } } if (!op->event){ op->event=belle_sip_header_event_create("presence"); belle_sip_object_ref(op->event); } belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.from_address),"tag"); belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.to_address),"tag"); req=sal_op_build_request(op,"SUBSCRIBE"); if( req ){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); } return sal_op_send_request(op,req); } static belle_sip_request_t *create_presence_notify(SalOp *op){ belle_sip_request_t* notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"); if (!notify) return NULL; belle_sip_message_add_header((belle_sip_message_t*)notify,belle_sip_header_create("Event","presence")); return notify; } static int sal_op_check_dialog_state(SalOp *op) { belle_sip_dialog_state_t state=op->dialog?belle_sip_dialog_get_state(op->dialog): BELLE_SIP_DIALOG_NULL; if (state != BELLE_SIP_DIALOG_CONFIRMED) { ms_warning("Cannot notify presence for op [%p] because dialog in state [%s]",op, belle_sip_dialog_state_to_string(state)); return -1; } else return 0; } int sal_notify_presence(SalOp *op, SalPresenceModel *presence){ belle_sip_request_t* notify=NULL; if (sal_op_check_dialog_state(op)) { return -1; } notify=create_presence_notify(op); if (!notify) return-1; sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),presence); /*FIXME, what about expires ??*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600))); return sal_op_send_request(op,notify); } int sal_notify_presence_close(SalOp *op){ belle_sip_request_t* notify=NULL; int status; if (sal_op_check_dialog_state(op)) { return -1; } notify=create_presence_notify(op); if (!notify) return-1; sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),NULL); /*FIXME, what about expires ??*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1))); status = sal_op_send_request(op,notify); set_or_update_dialog(op,NULL); /*because we may be chalanged for the notify, so we must release dialog right now*/ return status; } linphone-3.12.0/coreapi/bellesip_sal/sal_op_publish.c000066400000000000000000000110561313432737600226540ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" static void publish_refresher_listener (belle_sip_refresher_t* refresher ,void* user_pointer ,unsigned int status_code ,const char* reason_phrase, int will_retry) { SalOp* op = (SalOp*)user_pointer; const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher); belle_sip_response_t *response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(last_publish_trans)); ms_message("Publish refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase?reason_phrase:"none",sal_op_get_proxy(op)); if (status_code==0){ op->base.root->callbacks.on_expire(op); }else if (status_code>=200){ belle_sip_header_t *sip_etag; const char *sip_etag_string = NULL; if (response && (sip_etag = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response), "SIP-ETag"))) { sip_etag_string = belle_sip_header_get_unparsed_value(sip_etag); } sal_op_set_entity_tag(op, sip_etag_string); sal_error_info_set(&op->error_info,SalReasonUnknown, "SIP", status_code,reason_phrase,NULL); sal_op_assign_recv_headers(op,(belle_sip_message_t*)response); op->base.root->callbacks.on_publish_response(op); } } static void publish_response_event(void *userctx, const belle_sip_response_event_t *event){ SalOp *op=(SalOp*)userctx; sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event)); if (op->error_info.protocol_code>=200){ op->base.root->callbacks.on_publish_response(op); } } static belle_sip_listener_callbacks_t op_publish_callbacks={0}; void sal_op_publish_fill_cbs(SalOp *op) { if (op_publish_callbacks.process_response_event==NULL){ op_publish_callbacks.process_response_event=publish_response_event; } op->callbacks=&op_publish_callbacks; op->type=SalOpPublish; } int sal_publish(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBodyHandler *body_handler){ belle_sip_request_t *req=NULL; if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) { if (from) sal_op_set_from(op,from); if (to) sal_op_set_to(op,to); sal_op_publish_fill_cbs(op); req=sal_op_build_request(op,"PUBLISH"); if( req == NULL ){ return -1; } if (sal_op_get_entity_tag(op)) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("SIP-If-Match", sal_op_get_entity_tag(op))); } if (sal_op_get_contact_address(op)){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); } belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event",eventname)); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler)); if (expires!=-1) return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener); else return sal_op_send_request(op,req); } else { /*update status*/ const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher); belle_sip_request_t* last_publish=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_publish_trans)); /*update body*/ if (expires == 0) { belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_publish), NULL, 0); } else { belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(last_publish), BELLE_SIP_BODY_HANDLER(body_handler)); } return belle_sip_refresher_refresh(op->refresher,expires==-1 ? BELLE_SIP_REFRESHER_REUSE_EXPIRES : expires); } } int sal_op_unpublish(SalOp *op){ if (op->refresher){ const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher); belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr); belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_req), NULL, 0); belle_sip_refresher_refresh(op->refresher,0); return 0; } return -1; } linphone-3.12.0/coreapi/bellesip_sal/sal_op_registration.c000066400000000000000000000130311313432737600237130ustar00rootroot00000000000000/* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" static void register_refresher_listener (belle_sip_refresher_t* refresher ,void* user_pointer ,unsigned int status_code ,const char* reason_phrase, int will_retry) { SalOp* op = (SalOp*)user_pointer; belle_sip_response_t* response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher))); ms_message("Register refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase,sal_op_get_proxy(op)); if (belle_sip_refresher_get_auth_events(refresher)) { if (op->auth_info) sal_auth_info_delete(op->auth_info); /*only take first one for now*/ op->auth_info=sal_auth_info_create((belle_sip_auth_event_t*)(belle_sip_refresher_get_auth_events(refresher)->data)); } sal_error_info_set(&op->error_info,SalReasonUnknown, "SIP", status_code,reason_phrase,NULL); if (status_code>=200){ sal_op_assign_recv_headers(op,(belle_sip_message_t*)response); } if(status_code == 200) { /*check service route rfc3608*/ belle_sip_header_service_route_t* service_route; belle_sip_header_address_t* service_route_address=NULL; belle_sip_header_contact_t *contact = belle_sip_refresher_get_contact(refresher); if ((service_route=belle_sip_message_get_header_by_type(response,belle_sip_header_service_route_t))) { service_route_address=belle_sip_header_address_create(NULL,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(service_route))); } sal_op_set_service_route(op,(const SalAddress*)service_route_address); if (service_route_address) belle_sip_object_unref(service_route_address); sal_remove_pending_auth(op->base.root,op); /*just in case*/ if (contact) { sal_op_set_contact_address(op,(SalAddress*)(BELLE_SIP_HEADER_ADDRESS(contact))); /*update contact with real value*/ } op->base.root->callbacks.register_success(op,belle_sip_refresher_get_expires(op->refresher)>0); } else if (status_code>=400) { /* from rfc3608, 6.1. If the UA refreshes the registration, the stored value of the Service- Route is updated according to the Service-Route header field of the latest 200 class response. If there is no Service-Route header field in the response, the UA clears any service route for that address- of-record previously stored by the UA. If the re-registration request is refused or if an existing registration expires and the UA chooses not to re-register, the UA SHOULD discard any stored service route for that address-of-record. */ sal_op_set_service_route(op,NULL); sal_op_ref(op); /*take a ref while invoking the callback to make sure the operations done after are valid*/ op->base.root->callbacks.register_failure(op); if (op->state!=SalOpStateTerminated && op->auth_info) { /*add pending auth*/ sal_add_pending_auth(op->base.root,op); if (status_code==403 || status_code==401 || status_code==407 ) op->base.root->callbacks.auth_failure(op,op->auth_info); } sal_op_unref(op); } } int sal_register(SalOp *op, const char *proxy, const char *from, int expires,SalAddress* old_contact){ belle_sip_request_t *req; belle_sip_uri_t* req_uri; belle_sip_header_t* accept_header; if (op->refresher){ belle_sip_refresher_stop(op->refresher); belle_sip_object_unref(op->refresher); op->refresher=NULL; } op->type=SalOpRegister; sal_op_set_from(op,from); sal_op_set_to(op,from); sal_op_set_route(op,proxy); req = sal_op_build_request(op,"REGISTER"); req_uri = belle_sip_request_get_uri(req); belle_sip_uri_set_user(req_uri,NULL); /*remove userinfo if any*/ if (op->base.root->use_dates){ time_t curtime=time(NULL); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime))); } accept_header = belle_sip_header_create("Accept", "application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), accept_header); belle_sip_message_set_header(BELLE_SIP_MESSAGE(req),(belle_sip_header_t*)sal_op_create_contact(op)); if (old_contact) { belle_sip_header_contact_t *contact=belle_sip_header_contact_create((const belle_sip_header_address_t *)old_contact); if (contact) { char * tmp; belle_sip_header_contact_set_expires(contact,0); /*remove old aor*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), BELLE_SIP_HEADER(contact)); tmp = belle_sip_object_to_string(contact); ms_message("Clearing contact [%s] for op [%p]",tmp,op); ms_free(tmp); } else { ms_error("Cannot add old contact header to op [%p]",op); } } return sal_op_send_and_create_refresher(op,req,expires,register_refresher_listener); } int sal_register_refresh(SalOp *op, int expires){ if (op->refresher) return belle_sip_refresher_refresh(op->refresher,expires); else return -1; } int sal_unregister(SalOp *op){ return sal_register_refresh(op,0); } linphone-3.12.0/coreapi/bellesip_sal/sal_sdp.c000066400000000000000000001204311313432737600212740ustar00rootroot00000000000000 /* linphone Copyright (C) 2012 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal_impl.h" #define keywordcmp(key,b) strncmp(key,b,sizeof(key)) static void add_ice_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){ char buffer[1024]; const SalIceCandidate *candidate; int nb; int i; for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) { candidate = &desc->ice_candidates[i]; if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break; nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s", candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type); if (nb < 0) { ms_error("Cannot add ICE candidate attribute!"); return; } if (candidate->raddr[0] != '\0') { nb = snprintf(buffer + nb, sizeof(buffer) - nb, " raddr %s rport %d", candidate->raddr, candidate->rport); if (nb < 0) { ms_error("Cannot add ICE candidate attribute!"); return; } } belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("candidate",buffer)); } } static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){ char buffer[1024]; char *ptr = buffer; const SalIceRemoteCandidate *candidate; int offset = 0; int i; buffer[0] = '\0'; for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) { candidate = &desc->ice_remote_candidates[i]; if ((candidate->addr[0] != '\0') && (candidate->port != 0)) { offset = snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port); if (offset < 0) { ms_error("Cannot add ICE remote-candidates attribute!"); return; } ptr += offset; } } if (buffer[0] != '\0') belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("remote-candidates",buffer)); } static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint16_t *trr_int) { bctbx_list_t *pt_it; bool_t first = TRUE; for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { PayloadType *pt = (PayloadType *)pt_it->data; if (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) { if (first == TRUE) { *trr_int = payload_type_get_avpf_params(pt).trr_interval; first = FALSE; } else if (payload_type_get_avpf_params(pt).trr_interval != *trr_int) { return FALSE; } } } return TRUE; } static void add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t *media_desc, int8_t id, uint16_t trr_int) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_TRR_INT); belle_sdp_rtcp_fb_attribute_set_trr_int(attribute, trr_int); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_ack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_ACK); belle_sdp_rtcp_fb_attribute_set_param(attribute, param); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_nack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_NACK); belle_sdp_rtcp_fb_attribute_set_param(attribute, param); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_ccm_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_CCM); belle_sdp_rtcp_fb_attribute_set_param(attribute, param); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) { bctbx_list_t *pt_it; PayloadType *pt; PayloadTypeAvpfParams avpf_params; bool_t general_trr_int; uint16_t trr_int = 0; general_trr_int = is_rtcp_fb_trr_int_the_same_for_all_payloads(stream, &trr_int); if (general_trr_int == TRUE && trr_int != 0) { add_rtcp_fb_trr_int_attribute(media_desc, -1, trr_int); } if (stream->rtcp_fb.generic_nack_enabled == TRUE) { add_rtcp_fb_nack_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_NONE); } if (stream->rtcp_fb.tmmbr_enabled == TRUE) { add_rtcp_fb_ccm_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_TMMBR); } for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { pt = (PayloadType *)pt_it->data; /* AVPF/SAVPF profile is used so enable AVPF for all payload types. */ payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED); avpf_params = payload_type_get_avpf_params(pt); /* Add trr-int if not set generally. */ if (general_trr_int != TRUE && trr_int != 0) { add_rtcp_fb_trr_int_attribute(media_desc, payload_type_get_number(pt), avpf_params.trr_interval); } /* Add rtcp-fb attributes according to the AVPF features of the payload types. */ if (avpf_params.features & PAYLOAD_TYPE_AVPF_PLI) { add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_PLI); } if (avpf_params.features & PAYLOAD_TYPE_AVPF_SLI) { add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_SLI); } if (avpf_params.features & PAYLOAD_TYPE_AVPF_RPSI) { if (avpf_params.rpsi_compatibility == TRUE) { add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI); } else { add_rtcp_fb_ack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI); } } if (avpf_params.features & PAYLOAD_TYPE_AVPF_FIR) { add_rtcp_fb_ccm_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_FIR); } } } static belle_sdp_attribute_t * create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration *config) { belle_sdp_rtcp_xr_attribute_t *attribute = belle_sdp_rtcp_xr_attribute_new(); if (config->rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) { if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttAll) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "all"); else if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttSender) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "sender"); belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size(attribute, config->rcvr_rtt_max_size); } belle_sdp_rtcp_xr_attribute_set_stat_summary(attribute, (config->stat_summary_enabled == TRUE)); if (config->stat_summary_enabled == TRUE) { if (config->stat_summary_flags & OrtpRtcpXrStatSummaryLoss) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "loss"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryDup) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "dup"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryJitt) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "jitt"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryTTL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "TTL"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryHL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "HL"); } belle_sdp_rtcp_xr_attribute_set_voip_metrics(attribute, (config->voip_metrics_enabled == TRUE)); return BELLE_SDP_ATTRIBUTE(attribute); } static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) { belle_sdp_mime_parameter_t* mime_param; belle_sdp_media_description_t* media_desc; int j; bctbx_list_t* pt_it; PayloadType* pt; char buffer[1024]; char* dir=NULL; const char *rtp_addr; const char *rtcp_addr; int rtp_port; int rtcp_port; bool_t different_rtp_and_rtcp_addr; rtp_addr=stream->rtp_addr; rtcp_addr=stream->rtcp_addr; rtp_port=stream->rtp_port; rtcp_port=stream->rtcp_port; media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream) ,stream->rtp_port ,1 ,sal_media_proto_to_string ( stream->proto ) ,NULL ); if (stream->payloads) { for ( pt_it=stream->payloads; pt_it!=NULL; pt_it=pt_it->next ) { pt= ( PayloadType* ) pt_it->data; mime_param= belle_sdp_mime_parameter_create ( pt->mime_type , payload_type_get_number ( pt ) , pt->clock_rate , pt->channels>0 ? pt->channels : -1 ); belle_sdp_mime_parameter_set_parameters ( mime_param,pt->recv_fmtp ); if ( stream->ptime>0 ) { belle_sdp_mime_parameter_set_ptime ( mime_param,stream->ptime ); } belle_sdp_media_description_append_values_from_mime_parameter ( media_desc,mime_param ); belle_sip_object_unref ( mime_param ); } } else { /* to comply with SDP we cannot have an empty payload type number list */ /* as it happens only when mline is declined with a zero port, it does not matter to put whatever codec*/ belle_sip_list_t* format = belle_sip_list_append(NULL,0); belle_sdp_media_set_media_formats(belle_sdp_media_description_get_media(media_desc),format); } /*only add a c= line within the stream description if address are differents*/ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){ bool_t inet6; belle_sdp_connection_t *connection; if (strchr(rtp_addr,':')!=NULL){ inet6=TRUE; }else inet6=FALSE; connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr); if (ms_is_multicast(rtp_addr)) { /*remove session cline in case of multicast*/ belle_sdp_session_description_set_connection(session_desc,NULL); if (inet6 == FALSE) belle_sdp_connection_set_ttl(connection,stream->ttl); } belle_sdp_media_description_set_connection(media_desc,connection); } if ( stream->bandwidth>0 ) belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth ); if (sal_stream_description_has_srtp(stream)) { /* add crypto lines */ for ( j=0; jcrypto[j].algo,&desc)==0){ if (desc.params) snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s %s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key,desc.params); else snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key ); belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer)); }else break; } } /* insert DTLS session attribute if needed */ if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) { char* ssrc_attribute = ms_strdup_printf("%u cname:%s",stream->rtp_ssrc,stream->rtcp_cname); if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) { switch(stream->dtls_role) { case SalDtlsRoleIsClient: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active")); break; case SalDtlsRoleIsServer: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive")); break; case SalDtlsRoleUnset: default: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass")); break; } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); ms_free(ssrc_attribute); } /* insert zrtp-hash attribute if needed */ if (stream->haveZrtpHash == 1) { belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("zrtp-hash", (const char *)(stream->zrtphash))); } switch ( stream->dir ) { case SalStreamSendRecv: /*dir="sendrecv";*/ dir=NULL; break; case SalStreamRecvOnly: dir="recvonly"; break; case SalStreamSendOnly: dir="sendonly"; break; case SalStreamInactive: dir="inactive"; break; } if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) ); if (stream->rtcp_mux){ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create ("rtcp-mux",NULL ) ); } if (rtp_port != 0) { different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { if (different_rtp_and_rtcp_addr == TRUE) { snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr); } else { snprintf(buffer, sizeof(buffer), "%u",rtcp_port); } belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer)); } } if (stream->set_nortpproxy == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes")); } if (stream->ice_mismatch == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-mismatch",NULL)); } else { if (rtp_port != 0) { if (stream->ice_pwd[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-pwd",stream->ice_pwd)); if (stream->ice_ufrag[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-ufrag",stream->ice_ufrag)); add_ice_candidates(media_desc,stream); add_ice_remote_candidates(media_desc,stream); } } if ((rtp_port != 0) && (sal_stream_description_has_avpf(stream) || sal_stream_description_has_implicit_avpf(stream))) { add_rtcp_fb_attributes(media_desc, md, stream); } if ((rtp_port != 0) && (stream->rtcp_xr.enabled == TRUE)) { char sastr[1024] = {0}; char mastr[1024] = {0}; size_t saoff = 0; size_t maoff = 0; const belle_sdp_attribute_t *session_attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr"); belle_sdp_attribute_t *media_attribute; if (session_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)session_attribute, sastr, sizeof(sastr), &saoff); } media_attribute = create_rtcp_xr_attribute(&stream->rtcp_xr); if (media_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)media_attribute, mastr, sizeof(mastr), &maoff); } if (strcmp(sastr, mastr) != 0) { belle_sdp_media_description_add_attribute(media_desc, media_attribute); } else { belle_sip_object_unref((belle_sip_object_t*)media_attribute); } } if (stream->custom_sdp_attributes) { belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)stream->custom_sdp_attributes; belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc); belle_sip_list_t *elem; for (elem = l; elem != NULL; elem = elem->next) { belle_sdp_media_description_add_attribute(media_desc, (belle_sdp_attribute_t *)elem->data); } } /* * rfc5576 * 4.1. The "ssrc" Media Attribute * is the synchronization source (SSRC) ID of the * source being described, interpreted as a 32-bit unsigned integer in * network byte order and represented in decimal.*/ belle_sdp_session_description_add_media_description(session_desc, media_desc); } belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescription *desc ) { belle_sdp_session_description_t* session_desc=belle_sdp_session_description_new(); bool_t inet6; belle_sdp_origin_t* origin; int i; if ( strchr ( desc->addr,':' ) !=NULL ) { inet6=1; } else inet6=0; belle_sdp_session_description_set_version ( session_desc,belle_sdp_version_create ( 0 ) ); origin = belle_sdp_origin_create ( desc->username ,desc->session_id ,desc->session_ver ,"IN" , inet6 ? "IP6" :"IP4" ,desc->addr ); belle_sdp_session_description_set_origin ( session_desc,origin ); belle_sdp_session_description_set_session_name ( session_desc, belle_sdp_session_name_create ( desc->name[0]!='\0' ? desc->name : "Talk" ) ); if ( !sal_media_description_has_dir ( desc,SalStreamInactive ) || desc->ice_ufrag[0] != '\0' ) { /*in case of sendonly, setting of the IP on cnx we give a chance to receive stun packets*/ belle_sdp_session_description_set_connection ( session_desc ,belle_sdp_connection_create ( "IN",inet6 ? "IP6" :"IP4",desc->addr ) ); } else { belle_sdp_session_description_set_connection ( session_desc ,belle_sdp_connection_create ( "IN" ,inet6 ? "IP6" :"IP4" ,inet6 ? "::0" :"0.0.0.0" ) ); } belle_sdp_session_description_set_time_description ( session_desc,belle_sdp_time_description_create ( 0,0 ) ); if ( desc->bandwidth>0 ) { belle_sdp_session_description_set_bandwidth ( session_desc,"AS",desc->bandwidth ); } if (desc->set_nortpproxy == TRUE) belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("nortpproxy","yes")); if (desc->ice_pwd[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-pwd",desc->ice_pwd)); if (desc->ice_ufrag[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-ufrag",desc->ice_ufrag)); if (desc->rtcp_xr.enabled == TRUE) { belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr)); } if (desc->custom_sdp_attributes) { belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)desc->custom_sdp_attributes; belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc); belle_sip_list_t *elem; for (elem = l; elem != NULL; elem = elem->next) { belle_sdp_session_description_add_attribute(session_desc, (belle_sdp_attribute_t *)elem->data); } } for ( i=0; inb_streams; i++ ) { stream_description_to_sdp(session_desc, desc, &desc->streams[i]); } return session_desc; } static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { PayloadType *pt; PayloadTypeAvpfParams avpf_params; belle_sip_list_t* mime_param_it=NULL; belle_sdp_mime_parameter_t* mime_param; belle_sip_list_t* mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc ); memset(&avpf_params, 0, sizeof(avpf_params)); for ( mime_param_it=mime_params ; mime_param_it!=NULL ; mime_param_it=mime_param_it->next ) { mime_param=BELLE_SDP_MIME_PARAMETER ( mime_param_it->data ); pt=payload_type_new(); payload_type_set_number ( pt,belle_sdp_mime_parameter_get_media_format ( mime_param ) ); pt->clock_rate=belle_sdp_mime_parameter_get_rate ( mime_param ); pt->mime_type=ms_strdup ( belle_sdp_mime_parameter_get_type ( mime_param ) ); pt->channels=belle_sdp_mime_parameter_get_channel_count ( mime_param ); payload_type_set_send_fmtp ( pt,belle_sdp_mime_parameter_get_parameters ( mime_param ) ); payload_type_set_avpf_params(pt, avpf_params); stream->payloads=bctbx_list_append ( stream->payloads,pt ); stream->ptime=belle_sdp_mime_parameter_get_ptime ( mime_param ); ms_message ( "Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, pt->send_fmtp ? pt->send_fmtp : "" ); } if ( mime_params ) belle_sip_list_free_with_data ( mime_params,belle_sip_object_unref ); } static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { belle_sip_list_t *attribute_it; belle_sdp_attribute_t *attribute; char tmp[256], tmp2[256], parameters[256]={0}; int valid_count = 0; int nb; memset ( &stream->crypto, 0, sizeof ( stream->crypto ) ); for ( attribute_it=belle_sdp_media_description_get_attributes ( media_desc ) ; valid_count < SAL_CRYPTO_ALGO_MAX && attribute_it!=NULL; attribute_it=attribute_it->next ) { attribute=BELLE_SDP_ATTRIBUTE ( attribute_it->data ); if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) { nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s %256s", &stream->crypto[valid_count].tag, tmp, tmp2, parameters ); if ( nb >= 3 ) { MSCryptoSuite cs; MSCryptoSuiteNameParams np; np.name=tmp; np.params=parameters[0]!='\0' ? parameters : NULL; cs=ms_crypto_suite_build_from_name_params(&np); if (cs==MS_CRYPTO_SUITE_INVALID){ ms_warning ( "Failed to parse crypto-algo: '%s'", tmp ); stream->crypto[valid_count].algo = 0; }else{ char *sep; strncpy ( stream->crypto[valid_count].master_key, tmp2, sizeof(stream->crypto[valid_count].master_key)-1 ); sep=strchr(stream->crypto[valid_count].master_key,'|'); if (sep) *sep='\0'; stream->crypto[valid_count].algo = cs; ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'", stream->crypto[valid_count].tag, tmp, stream->crypto[valid_count].master_key ); valid_count++; } }else{ ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb ); } } } ms_message("Found: %d valid crypto lines", valid_count ); } static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { belle_sip_list_t *attribute_it; belle_sdp_attribute_t *attribute; const char *att_name; const char *value; int nb_ice_candidates = 0; for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) { attribute=BELLE_SDP_ATTRIBUTE(attribute_it->data); att_name = belle_sdp_attribute_get_name(attribute); value = belle_sdp_attribute_get_value(attribute); if ((nb_ice_candidates < (int)(sizeof(stream->ice_candidates)/sizeof(SalIceCandidate))) && (keywordcmp("candidate", att_name) == 0) && (value != NULL)) { SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; char proto[4]; int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d", candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port, candidate->type, candidate->raddr, &candidate->rport); if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++; else { ms_error("ice: Failed parsing a=candidate SDP attribute"); memset(candidate, 0, sizeof(*candidate)); } } else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) { SalIceRemoteCandidate candidate; unsigned int componentID; int offset; const char *ptr = value; const char *endptr = value + strlen(ptr); while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) { if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) { SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1]; strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)-1); remote_candidate->port = candidate.port; } ptr += offset; if (ptr < endptr) { if (ptr[offset] == ' ') ptr += 1; } else break; } } else if ((keywordcmp("ice-ufrag", att_name) == 0) && (value != NULL)) { strncpy(stream->ice_ufrag, value, sizeof(stream->ice_ufrag)-1); } else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) { strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd) -1); } else if (keywordcmp("ice-mismatch", att_name) == 0) { stream->ice_mismatch = TRUE; } } } static void enable_avpf_for_stream(SalStreamDescription *stream) { bctbx_list_t *pt_it; for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { PayloadType *pt = (PayloadType *)pt_it->data; payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED); } } static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb_attribute, SalStreamDescription *stream, PayloadType *pt) { PayloadTypeAvpfParams avpf_params = payload_type_get_avpf_params(pt); switch (belle_sdp_rtcp_fb_attribute_get_type(fb_attribute)) { case BELLE_SDP_RTCP_FB_ACK: if (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute) == BELLE_SDP_RTCP_FB_RPSI) { avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI; } break; case BELLE_SDP_RTCP_FB_NACK: switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) { case BELLE_SDP_RTCP_FB_PLI: avpf_params.features |= PAYLOAD_TYPE_AVPF_PLI; break; case BELLE_SDP_RTCP_FB_SLI: avpf_params.features |= PAYLOAD_TYPE_AVPF_SLI; break; case BELLE_SDP_RTCP_FB_RPSI: /* Linphone uses positive feeback for RPSI. However first versions handling * AVPF wrongly declared RPSI as negative feedback, so this is kept for compatibility * with these versions but will probably be removed at some point in time. */ avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI; avpf_params.rpsi_compatibility = TRUE; break; case BELLE_SDP_RTCP_FB_NONE: stream->rtcp_fb.generic_nack_enabled = TRUE; break; default: break; } break; case BELLE_SDP_RTCP_FB_TRR_INT: avpf_params.trr_interval = belle_sdp_rtcp_fb_attribute_get_trr_int(fb_attribute); break; case BELLE_SDP_RTCP_FB_CCM: switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) { case BELLE_SDP_RTCP_FB_FIR: avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR; break; case BELLE_SDP_RTCP_FB_TMMBR: stream->rtcp_fb.tmmbr_enabled = TRUE; break; default: break; } break; default: break; } payload_type_set_avpf_params(pt, avpf_params); } static bool_t sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { belle_sip_list_t *it; belle_sdp_attribute_t *attribute; belle_sdp_rtcp_fb_attribute_t *fb_attribute; bctbx_list_t *pt_it; PayloadType *pt; int8_t pt_num; bool_t retval = FALSE; /* Handle rtcp-fb attributes that concern all payload types. */ for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) { attribute = BELLE_SDP_ATTRIBUTE(it->data); if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) { fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute); if (belle_sdp_rtcp_fb_attribute_get_id(fb_attribute) == -1) { for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { pt = (PayloadType *)pt_it->data; apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt); retval = TRUE; } } } } /* Handle rtcp-fb attributes that are specefic to a payload type. */ for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) { attribute = BELLE_SDP_ATTRIBUTE(it->data); if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) { fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute); pt_num = belle_sdp_rtcp_fb_attribute_get_id(fb_attribute); for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { pt = (PayloadType *)pt_it->data; retval = TRUE; if (payload_type_get_number(pt) == (int)pt_num) { apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt); } } } } return retval; } static void sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration *config) { config->enabled = FALSE; config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone; config->rcvr_rtt_max_size = -1; config->stat_summary_flags = 0; config->voip_metrics_enabled = FALSE; } static void sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t *attribute, OrtpRtcpXrConfiguration *config) { if (attribute != NULL) { const belle_sdp_rtcp_xr_attribute_t *xr_attr; const char *rcvr_rtt_mode; sal_init_rtcp_xr_description(config); xr_attr = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute); rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(xr_attr); if (rcvr_rtt_mode != NULL) { if (strcasecmp(rcvr_rtt_mode, "all") == 0) { config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttAll; } else if (strcasecmp(rcvr_rtt_mode, "sender") == 0) { config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttSender; } config->rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(xr_attr); } config->stat_summary_enabled = (belle_sdp_rtcp_xr_attribute_has_stat_summary(xr_attr) != 0); if (config->stat_summary_enabled) { const belle_sip_list_t *stat_summary_flag_it; for (stat_summary_flag_it = belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(xr_attr); stat_summary_flag_it != NULL; stat_summary_flag_it = stat_summary_flag_it->next ) { const char *flag = (const char *)stat_summary_flag_it->data; if (flag != NULL) { if (strcasecmp(flag, "loss") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryLoss; else if (strcasecmp(flag, "dup") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryDup; else if (strcasecmp(flag, "jitt") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryJitt; else if (strcasecmp(flag, "TTL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryTTL; else if (strcasecmp(flag, "HL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryHL; } } } config->voip_metrics_enabled = (belle_sdp_rtcp_xr_attribute_has_voip_metrics(xr_attr) != 0); config->enabled = TRUE; } } static void sdp_parse_session_rtcp_xr_parameters(belle_sdp_session_description_t *session_desc, OrtpRtcpXrConfiguration *config) { const belle_sdp_attribute_t *attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr"); sdp_parse_rtcp_xr_parameters(attribute, config); } static void sdp_parse_media_rtcp_xr_parameters(belle_sdp_media_description_t *media_desc, OrtpRtcpXrConfiguration *config) { const belle_sdp_attribute_t *attribute = belle_sdp_media_description_get_attribute(media_desc, "rtcp-xr"); sdp_parse_rtcp_xr_parameters(attribute, config); } static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, belle_sdp_media_description_t *media_desc) { SalStreamDescription *stream; belle_sdp_connection_t* cnx; belle_sdp_media_t* media; belle_sdp_attribute_t* attribute; belle_sip_list_t *custom_attribute_it; const char* value; const char *mtype,*proto; bool_t has_avpf_attributes; stream=&md->streams[md->nb_streams]; media=belle_sdp_media_description_get_media ( media_desc ); proto = belle_sdp_media_get_protocol ( media ); stream->proto=SalProtoOther; if ( proto ) { if (strcasecmp(proto, "RTP/AVP") == 0) { stream->proto = SalProtoRtpAvp; } else if (strcasecmp(proto, "RTP/SAVP") == 0) { stream->proto = SalProtoRtpSavp; } else if (strcasecmp(proto, "RTP/AVPF") == 0) { stream->proto = SalProtoRtpAvpf; } else if (strcasecmp(proto, "RTP/SAVPF") == 0) { stream->proto = SalProtoRtpSavpf; } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) { stream->proto = SalProtoUdpTlsRtpSavp; } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) { stream->proto = SalProtoUdpTlsRtpSavpf; } else { strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1); } } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 ); stream->ttl=belle_sdp_connection_get_ttl(cnx); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); mtype = belle_sdp_media_get_media_type ( media ); if ( strcasecmp ( "audio", mtype ) == 0 ) { stream->type=SalAudio; } else if ( strcasecmp ( "video", mtype ) == 0 ) { stream->type=SalVideo; } else if ( strcasecmp ( "text", mtype ) == 0 ) { stream->type=SalText; } else { stream->type=SalOther; strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 ); } if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) { stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ); } if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) { stream->dir=SalStreamSendRecv; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) { stream->dir=SalStreamSendOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) { stream->dir=SalStreamRecvOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) { stream->dir=SalStreamInactive; } else { stream->dir=md->dir; /*takes default value if not present*/ } stream->rtcp_mux = belle_sdp_media_description_get_attribute(media_desc, "rtcp-mux") != NULL; /* Get media payload types */ sdp_parse_payload_types(media_desc, stream); /* Get media specific RTCP attribute */ stream->rtcp_port = stream->rtp_port + 1; snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr); attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ char tmp[256]; int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp); if (nb == 1) { /* SDP rtcp attribute only contains the port */ } else if (nb == 2) { strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)-1); } else { ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb); } } /* Read DTLS specific attributes : check is some are found in the stream description otherwise copy the session description one(which are at least set to Invalid) */ if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) { attribute=belle_sdp_media_description_get_attribute(media_desc,"setup"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ if (strncmp(value, "actpass", 7) == 0) { stream->dtls_role = SalDtlsRoleUnset; } else if (strncmp(value, "active", 6) == 0) { stream->dtls_role = SalDtlsRoleIsClient; } else if (strncmp(value, "passive", 7) == 0) { stream->dtls_role = SalDtlsRoleIsServer; } } if (stream->dtls_role != SalDtlsRoleInvalid && (attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"))) { strncpy(stream->dtls_fingerprint, belle_sdp_attribute_get_value(attribute),sizeof(stream->dtls_fingerprint)); } } /* Read crypto lines if any */ if (sal_stream_description_has_srtp(stream)) { sdp_parse_media_crypto_parameters(media_desc, stream); } /* Read zrtp-hash attribute */ if ((attribute=belle_sdp_media_description_get_attribute(media_desc,"zrtp-hash"))!=NULL) { if ((value=belle_sdp_attribute_get_value(attribute))!=NULL) { strncpy((char *)(stream->zrtphash), belle_sdp_attribute_get_value(attribute),sizeof(stream->zrtphash)); stream->haveZrtpHash = 1; } } /* Get ICE candidate attributes if any */ sdp_parse_media_ice_parameters(media_desc, stream); has_avpf_attributes = sdp_parse_rtcp_fb_parameters(media_desc, stream); /* Get RTCP-FB attributes if any */ if (sal_stream_description_has_avpf(stream)) { enable_avpf_for_stream(stream); }else if (has_avpf_attributes ){ enable_avpf_for_stream(stream); stream->implicit_rtcp_fb = TRUE; } /* Get RTCP-XR attributes if any */ stream->rtcp_xr = md->rtcp_xr; // Use session parameters if no stream parameters are defined sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr); /* Get the custom attributes */ for (custom_attribute_it = belle_sdp_media_description_get_attributes(media_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) { belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data; stream->custom_sdp_attributes = sal_custom_sdp_attribute_append(stream->custom_sdp_attributes, belle_sdp_attribute_get_name(attr), belle_sdp_attribute_get_value(attr)); } md->nb_streams++; return stream; } int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, SalMediaDescription *desc ) { belle_sdp_connection_t* cnx; belle_sip_list_t* media_desc_it; belle_sdp_media_description_t* media_desc; belle_sdp_session_name_t *sname; belle_sip_list_t *custom_attribute_it; const char* value; SalDtlsRole session_role=SalDtlsRoleInvalid; int i; desc->nb_streams = 0; desc->dir = SalStreamSendRecv; if ( ( cnx=belle_sdp_session_description_get_connection ( session_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( desc->addr,belle_sdp_connection_get_address ( cnx ),sizeof ( desc->addr ) -1 ); } if ( (sname=belle_sdp_session_description_get_session_name(session_desc)) && belle_sdp_session_name_get_value(sname) ){ strncpy(desc->name,belle_sdp_session_name_get_value(sname),sizeof(desc->name) - 1); } if ( belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ) >0 ) { desc->bandwidth=belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ); } /*in some very rare case, session attribute may set stream dir*/ if ( belle_sdp_session_description_get_attribute ( session_desc,"sendrecv" ) ) { desc->dir=SalStreamSendRecv; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) { desc->dir=SalStreamSendOnly; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) { desc->dir=SalStreamRecvOnly; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) { desc->dir=SalStreamInactive; } /*DTLS attributes can be defined at session level.*/ value=belle_sdp_session_description_get_attribute_value(session_desc,"setup"); if (value){ if (strncmp(value, "actpass", 7) == 0) { session_role = SalDtlsRoleUnset; } else if (strncmp(value, "active", 6) == 0) { session_role = SalDtlsRoleIsClient; } else if (strncmp(value, "passive", 7) == 0) { session_role = SalDtlsRoleIsServer; } } value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint"); /*copy dtls attributes to every streams, might be overwritten stream by stream*/ for (i=0;istreams[i].dtls_fingerprint, value, sizeof(desc->streams[i].dtls_fingerprint)); desc->streams[i].dtls_role=session_role; /*set or reset value*/ } /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag"); if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1); value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd"); if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd)-1); value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite"); if (value) desc->ice_lite = TRUE; /* Get session RTCP-XR attributes if any */ sdp_parse_session_rtcp_xr_parameters(session_desc, &desc->rtcp_xr); /* Get the custom attributes */ for (custom_attribute_it = belle_sdp_session_description_get_attributes(session_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) { belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data; desc->custom_sdp_attributes = sal_custom_sdp_attribute_append(desc->custom_sdp_attributes, belle_sdp_attribute_get_name(attr), belle_sdp_attribute_get_value(attr)); } for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc ) ; media_desc_it!=NULL ; media_desc_it=media_desc_it->next ) { if (desc->nb_streams==SAL_MEDIA_DESCRIPTION_MAX_STREAMS){ ms_warning("Cannot convert mline at position [%i] from SDP to SalMediaDescription",desc->nb_streams); break; } media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data ); sdp_to_stream_description(desc, media_desc); } return 0; } linphone-3.12.0/coreapi/buffer.c000066400000000000000000000061261313432737600164660ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2014 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "private.h" static void linphone_buffer_destroy(LinphoneBuffer *buffer) { if (buffer->content) belle_sip_free(buffer->content); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneBuffer); BELLE_SIP_INSTANCIATE_VPTR(LinphoneBuffer, belle_sip_object_t, (belle_sip_object_destroy_t)linphone_buffer_destroy, NULL, // clone NULL, // marshal TRUE ); LinphoneBuffer * linphone_buffer_new(void) { LinphoneBuffer *buffer = belle_sip_object_new(LinphoneBuffer); belle_sip_object_ref(buffer); return buffer; } LinphoneBuffer * linphone_buffer_new_from_data(const uint8_t *data, size_t size) { LinphoneBuffer *buffer = linphone_buffer_new(); linphone_buffer_set_content(buffer, data, size); return buffer; } LinphoneBuffer * linphone_buffer_new_from_string(const char *data) { LinphoneBuffer *buffer = linphone_buffer_new(); linphone_buffer_set_string_content(buffer, data); return buffer; } LinphoneBuffer * linphone_buffer_ref(LinphoneBuffer *buffer) { belle_sip_object_ref(buffer); return buffer; } void linphone_buffer_unref(LinphoneBuffer *buffer) { belle_sip_object_unref(buffer); } void *linphone_buffer_get_user_data(const LinphoneBuffer *buffer) { return buffer->user_data; } void linphone_buffer_set_user_data(LinphoneBuffer *buffer, void *ud) { buffer->user_data = ud; } const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer) { return buffer->content; } void linphone_buffer_set_content(LinphoneBuffer *buffer, const uint8_t *content, size_t size) { buffer->size = size; if (buffer->content) belle_sip_free(buffer->content); buffer->content = belle_sip_malloc(size + 1); memcpy(buffer->content, content, size); ((char *)buffer->content)[size] = '\0'; } const char * linphone_buffer_get_string_content(const LinphoneBuffer *buffer) { return (const char *)buffer->content; } void linphone_buffer_set_string_content(LinphoneBuffer *buffer, const char *content) { buffer->size = strlen(content); if (buffer->content) belle_sip_free(buffer->content); buffer->content = (uint8_t *)belle_sip_strdup(content); } size_t linphone_buffer_get_size(const LinphoneBuffer *buffer) { return buffer->size; } void linphone_buffer_set_size(LinphoneBuffer *buffer, size_t size) { buffer->size = size; } bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer) { return (buffer->size == 0) ? TRUE : FALSE; } linphone-3.12.0/coreapi/call_log.c000066400000000000000000000526241313432737600167750ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2014 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define _XOPEN_SOURCE 700 /*required for strptime of GNU libc*/ #include #include "private.h" #ifdef SQLITE_STORAGE_ENABLED #ifndef _WIN32 #if !defined(__ANDROID__) && !defined(__QNXNTO__) # include # include # include #endif #else #include #endif #define MAX_PATH_SIZE 1024 #include "sqlite3.h" #endif typedef struct _CallLogStorageResult { LinphoneCore *core; bctbx_list_t *result; } CallLogStorageResult; /******************************************************************************* * Internal functions * ******************************************************************************/ /*prevent a gcc bug with %c*/ static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){ return strftime(s, max, fmt, tm); } static time_t string_to_time(const char *date){ #ifndef _WIN32 struct tm tmtime={0}; strptime(date,"%c",&tmtime); return mktime(&tmtime); #else return 0; #endif } static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){ struct tm loctime; #ifdef _WIN32 #if !defined(_WIN32_WCE) loctime=*localtime(&start_time); /*FIXME*/ #endif /*_WIN32_WCE*/ #else localtime_r(&start_time,&loctime); #endif my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime); } /******************************************************************************* * Private functions * ******************************************************************************/ void call_logs_write_to_config_file(LinphoneCore *lc){ bctbx_list_t *elem; char logsection[32]; int i; char *tmp; LpConfig *cfg=lc->config; if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return; if (lc->max_call_logs == LINPHONE_MAX_CALL_HISTORY_UNLIMITED) return; for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){ LinphoneCallLog *cl=(LinphoneCallLog*)elem->data; snprintf(logsection,sizeof(logsection),"call_log_%i",i); lp_config_clean_section(cfg,logsection); lp_config_set_int(cfg,logsection,"dir",cl->dir); lp_config_set_int(cfg,logsection,"status",cl->status); tmp=linphone_address_as_string(cl->from); lp_config_set_string(cfg,logsection,"from",tmp); ms_free(tmp); tmp=linphone_address_as_string(cl->to); lp_config_set_string(cfg,logsection,"to",tmp); ms_free(tmp); if (cl->start_date_time) lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time); else lp_config_set_string(cfg,logsection,"start_date",cl->start_date); lp_config_set_int(cfg,logsection,"duration",cl->duration); if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey); lp_config_set_float(cfg,logsection,"quality",cl->quality); lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled); lp_config_set_string(cfg,logsection,"call_id",cl->call_id); } for(;imax_call_logs;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); lp_config_clean_section(cfg,logsection); } } bctbx_list_t * call_logs_read_from_config_file(LinphoneCore *lc){ char logsection[32]; int i; const char *tmp; uint64_t sec; LpConfig *cfg=lc->config; bctbx_list_t *call_logs = NULL; for(i=0;;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); if (lp_config_has_section(cfg,logsection)){ LinphoneCallLog *cl; LinphoneAddress *from=NULL,*to=NULL; tmp=lp_config_get_string(cfg,logsection,"from",NULL); if (tmp) from=linphone_address_new(tmp); tmp=lp_config_get_string(cfg,logsection,"to",NULL); if (tmp) to=linphone_address_new(tmp); if (!from || !to) continue; cl=linphone_call_log_new(lp_config_get_int(cfg,logsection,"dir",0),from,to); cl->status=lp_config_get_int(cfg,logsection,"status",0); sec=lp_config_get_int64(cfg,logsection,"start_date_time",0); if (sec) { /*new call log format with date expressed in seconds */ cl->start_date_time=(time_t)sec; set_call_log_date(cl,cl->start_date_time); }else{ tmp=lp_config_get_string(cfg,logsection,"start_date",NULL); if (tmp) { strncpy(cl->start_date,tmp,sizeof(cl->start_date)); cl->start_date_time=string_to_time(cl->start_date); } } cl->duration=lp_config_get_int(cfg,logsection,"duration",0); tmp=lp_config_get_string(cfg,logsection,"refkey",NULL); if (tmp) cl->refkey=ms_strdup(tmp); cl->quality=lp_config_get_float(cfg,logsection,"quality",-1); cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0); tmp=lp_config_get_string(cfg,logsection,"call_id",NULL); if (tmp) cl->call_id=ms_strdup(tmp); call_logs=bctbx_list_append(call_logs,cl); }else break; } return call_logs; } /******************************************************************************* * Public functions * ******************************************************************************/ const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){ return cl->call_id; } LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl){ return cl->dir; } int linphone_call_log_get_duration(LinphoneCallLog *cl){ return cl->duration; } LinphoneAddress *linphone_call_log_get_from_address(LinphoneCallLog *cl){ return cl->from; } const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){ return &cl->local_stats; } float linphone_call_log_get_quality(LinphoneCallLog *cl){ return cl->quality; } const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){ return cl->refkey; } LinphoneAddress *linphone_call_log_get_remote_address(LinphoneCallLog *cl){ return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to; } const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){ return &cl->remote_stats; } time_t linphone_call_log_get_start_date(LinphoneCallLog *cl){ return cl->start_date_time; } LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl){ return cl->status; } LinphoneAddress *linphone_call_log_get_to_address(LinphoneCallLog *cl){ return cl->to; } void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey){ if (cl->refkey!=NULL){ ms_free(cl->refkey); cl->refkey=NULL; } if (refkey) cl->refkey=ms_strdup(refkey); } char * linphone_call_log_to_str(LinphoneCallLog *cl){ char *status; char *tmp; char *from=linphone_address_as_string (cl->from); char *to=linphone_address_as_string (cl->to); switch(cl->status){ case LinphoneCallAborted: status=_("aborted"); break; case LinphoneCallSuccess: status=_("completed"); break; case LinphoneCallMissed: status=_("missed"); break; default: status=_("unknown"); } tmp=ms_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"), (cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"), cl->start_date, from, to, status, cl->duration/60, cl->duration%60); ms_free(from); ms_free(to); return tmp; } bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl) { return cl->video_enabled; } bool_t linphone_call_log_was_conference(LinphoneCallLog *cl) { return cl->was_conference; } const LinphoneErrorInfo *linphone_call_log_get_error_info(LinphoneCallLog *cl){ return cl->error_info; } /******************************************************************************* * Reference and user data handling functions * ******************************************************************************/ void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) { return cl->user_data; } void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) { cl->user_data = ud; } LinphoneCallLog * linphone_call_log_ref(LinphoneCallLog *cl) { belle_sip_object_ref(cl); return cl; } void linphone_call_log_unref(LinphoneCallLog *cl) { belle_sip_object_unref(cl); } /******************************************************************************* * Constructor and destructor functions * ******************************************************************************/ static void _linphone_call_log_destroy(LinphoneCallLog *cl) { if (cl->from!=NULL) linphone_address_unref(cl->from); if (cl->to!=NULL) linphone_address_unref(cl->to); if (cl->refkey!=NULL) ms_free(cl->refkey); if (cl->call_id) ms_free(cl->call_id); if (cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]); if (cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]); if (cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]); if (cl->error_info) linphone_error_info_unref(cl->error_info); } LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to) { LinphoneCallLog *cl=belle_sip_object_new(LinphoneCallLog); cl->dir=dir; cl->start_date_time=time(NULL); set_call_log_date(cl,cl->start_date_time); cl->from=from; cl->to=to; cl->status=LinphoneCallAborted; /*default status*/ cl->quality=-1; cl->storage_id=0; cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new(); cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new(); cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]=linphone_reporting_new(); cl->connected_date_time=0; return cl; } /* DEPRECATED */ void linphone_call_log_destroy(LinphoneCallLog *cl) { belle_sip_object_unref(cl); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallLog); BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallLog, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_call_log_destroy, NULL, // clone NULL, // marshal FALSE ); /******************************************************************************* * SQL storage related functions * ******************************************************************************/ #ifdef SQLITE_STORAGE_ENABLED static void linphone_create_call_log_table(sqlite3* db) { char* errmsg=NULL; int ret; ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS call_history (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "caller TEXT NOT NULL," // Can't name a field "from"... "callee TEXT NOT NULL," "direction INTEGER," "duration INTEGER," "start_time TEXT NOT NULL," "connected_time TEXT NOT NULL," "status INTEGER," "videoEnabled INTEGER," "quality REAL" ");", 0,0,&errmsg); if(ret != SQLITE_OK) { ms_error("Error in creation: %s.\n", errmsg); sqlite3_free(errmsg); } } static void linphone_update_call_log_table(sqlite3* db) { char* errmsg=NULL; int ret; // for image url storage ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN call_id TEXT;",NULL,NULL,&errmsg); if(ret != SQLITE_OK) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN refkey TEXT;",NULL,NULL,&errmsg); if(ret != SQLITE_OK) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { ms_debug("Table call_history updated successfully for call_id and refkey."); } } } void linphone_core_call_log_storage_init(LinphoneCore *lc) { int ret; const char *errmsg; sqlite3 *db; linphone_core_call_log_storage_close(lc); ret=_linphone_sqlite3_open(lc->logs_db_file, &db); if(ret != SQLITE_OK) { errmsg = sqlite3_errmsg(db); ms_error("Error in the opening: %s.\n", errmsg); sqlite3_close(db); return; } linphone_create_call_log_table(db); linphone_update_call_log_table(db); lc->logs_db = db; // Load the existing call logs linphone_core_get_call_history(lc); } void linphone_core_call_log_storage_close(LinphoneCore *lc) { if (lc->logs_db){ sqlite3_close(lc->logs_db); lc->logs_db = NULL; } } static LinphoneCallLog * find_call_log_by_storage_id(bctbx_list_t *call_logs, unsigned int storage_id) { bctbx_list_t *item; for (item = call_logs; item != NULL; item = bctbx_list_next(item)) { LinphoneCallLog *call_log = bctbx_list_get_data(item); if (call_log->storage_id == storage_id) return call_log; } return NULL; } /* DB layout: * | 0 | storage_id * | 1 | from * | 2 | to * | 3 | direction flag * | 4 | duration * | 5 | start date time (time_t) * | 6 | connected date time (time_t) * | 7 | status * | 8 | video enabled (1 or 0) * | 9 | quality * | 10 | call_id * | 11 | refkey */ static int create_call_log(void *data, int argc, char **argv, char **colName) { CallLogStorageResult *clsres = (CallLogStorageResult *)data; LinphoneAddress *from; LinphoneAddress *to; LinphoneCallDir dir; LinphoneCallLog *log; unsigned int storage_id = (unsigned int)atoi(argv[0]); log = find_call_log_by_storage_id(clsres->core->call_logs, storage_id); if (log != NULL) { clsres->result = bctbx_list_append(clsres->result, linphone_call_log_ref(log)); return 0; } from = linphone_address_new(argv[1]); to = linphone_address_new(argv[2]); if (from == NULL || to == NULL) goto error; dir = (LinphoneCallDir) atoi(argv[3]); log = linphone_call_log_new(dir, from, to); log->storage_id = storage_id; log->duration = atoi(argv[4]); log->start_date_time = (time_t)atol(argv[5]); set_call_log_date(log,log->start_date_time); log->connected_date_time = (time_t)atol(argv[6]); log->status = (LinphoneCallStatus) atoi(argv[7]); log->video_enabled = atoi(argv[8]) == 1; log->quality = (float)atof(argv[9]); if (argc > 10) { if (argv[10] != NULL) { log->call_id = ms_strdup(argv[10]); } if (argv[10] != NULL) { log->refkey = ms_strdup(argv[11]); } } clsres->result = bctbx_list_append(clsres->result, log); return 0; error: if (from){ linphone_address_unref(from); } if (to){ linphone_address_unref(to); } ms_error("Bad call log at storage_id %u", storage_id); return 0; } static void linphone_sql_request_call_log(sqlite3 *db, const char *stmt, CallLogStorageResult *clsres) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, create_call_log, clsres, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } } static int linphone_sql_request_generic(sqlite3* db, const char *stmt) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) { if (lc && lc->logs_db){ char *from, *to; char *buf; from = linphone_address_as_string(log->from); to = linphone_address_as_string(log->to); buf = sqlite3_mprintf("INSERT INTO call_history VALUES(NULL,%Q,%Q,%i,%i,%lld,%lld,%i,%i,%f,%Q,%Q);", from, to, log->dir, log->duration, (int64_t)log->start_date_time, (int64_t)log->connected_date_time, log->status, log->video_enabled ? 1 : 0, log->quality, log->call_id, log->refkey ); linphone_sql_request_generic(lc->logs_db, buf); sqlite3_free(buf); ms_free(from); ms_free(to); log->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->logs_db); } if (lc) { lc->call_logs = bctbx_list_prepend(lc->call_logs, linphone_call_log_ref(log)); } } const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) { char *buf; uint64_t begin,end; CallLogStorageResult clsres; if (!lc || lc->logs_db == NULL) return NULL; if (lc->call_logs != NULL) return lc->call_logs; if (lc->max_call_logs != LINPHONE_MAX_CALL_HISTORY_UNLIMITED){ buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC LIMIT %i", lc->max_call_logs); }else{ buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC"); } clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); lc->call_logs = clsres.result; return lc->call_logs; } void linphone_core_delete_call_history(LinphoneCore *lc) { char *buf; if (!lc || lc->logs_db == NULL) return ; buf = sqlite3_mprintf("DELETE FROM call_history"); linphone_sql_request_generic(lc->logs_db, buf); sqlite3_free(buf); } void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) { char *buf; if (!lc || lc->logs_db == NULL) return ; buf = sqlite3_mprintf("DELETE FROM call_history WHERE id = %u", log->storage_id); linphone_sql_request_generic(lc->logs_db, buf); sqlite3_free(buf); } int linphone_core_get_call_history_size(LinphoneCore *lc) { int numrows = 0; char *buf; sqlite3_stmt *selectStatement; int returnValue; if (!lc || lc->logs_db == NULL) return 0; buf = sqlite3_mprintf("SELECT count(*) FROM call_history"); returnValue = sqlite3_prepare_v2(lc->logs_db, buf, -1, &selectStatement, NULL); if (returnValue == SQLITE_OK){ if(sqlite3_step(selectStatement) == SQLITE_ROW){ numrows = sqlite3_column_int(selectStatement, 0); } } sqlite3_finalize(selectStatement); sqlite3_free(buf); return numrows; } bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) { char *buf; char *sipAddress; uint64_t begin,end; CallLogStorageResult clsres; if (!lc || lc->logs_db == NULL || addr == NULL) return NULL; /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/ sipAddress = linphone_address_as_string_uri_only(addr); buf = sqlite3_mprintf("SELECT * FROM call_history WHERE caller LIKE '%%%q%%' OR callee LIKE '%%%q%%' ORDER BY id DESC", sipAddress, sipAddress); // The '%%%q%%' takes care of the eventual presence of a display name clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); ms_free(sipAddress); return clsres.result; } LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) { char *buf; uint64_t begin,end; CallLogStorageResult clsres; LinphoneCallLog *result = NULL; if (!lc || lc->logs_db == NULL) return NULL; /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/ buf = sqlite3_mprintf("SELECT * FROM call_history WHERE direction = 0 ORDER BY id DESC LIMIT 1"); clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); if (clsres.result != NULL) { result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result); } return result; } LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) { char *buf; uint64_t begin,end; CallLogStorageResult clsres; LinphoneCallLog* result = NULL; if (!lc || lc->logs_db == NULL) return NULL; /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/ buf = sqlite3_mprintf("SELECT * FROM call_history WHERE call_id = '%q' ORDER BY id DESC LIMIT 1", call_id); clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); if (clsres.result != NULL) { result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result); } return result; } #else void linphone_core_call_log_storage_init(LinphoneCore *lc) { } void linphone_core_call_log_storage_close(LinphoneCore *lc) { } void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) { } const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) { return NULL; } void linphone_core_delete_call_history(LinphoneCore *lc) { } void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) { } int linphone_core_get_call_history_size(LinphoneCore *lc) { return 0; } bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) { return NULL; } LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) { return NULL; } LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) { return NULL; } #endif linphone-3.12.0/coreapi/call_params.c000066400000000000000000000356301313432737600174750ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2014 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/call_params.h" #include "private.h" /******************************************************************************* * Internal functions * ******************************************************************************/ SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params) { if ((params->media_encryption == LinphoneMediaEncryptionSRTP) && params->avpf_enabled) return SalProtoRtpSavpf; if (params->media_encryption == LinphoneMediaEncryptionSRTP) return SalProtoRtpSavp; if ((params->media_encryption == LinphoneMediaEncryptionDTLS) && params->avpf_enabled) return SalProtoUdpTlsRtpSavpf; if (params->media_encryption == LinphoneMediaEncryptionDTLS) return SalProtoUdpTlsRtpSavp; if (params->avpf_enabled) return SalProtoRtpAvpf; return SalProtoRtpAvp; } SalStreamDir sal_dir_from_call_params_dir(LinphoneMediaDirection cpdir) { switch (cpdir) { case LinphoneMediaDirectionInactive: return SalStreamInactive; case LinphoneMediaDirectionSendOnly: return SalStreamSendOnly; case LinphoneMediaDirectionRecvOnly: return SalStreamRecvOnly; case LinphoneMediaDirectionSendRecv: return SalStreamSendRecv; case LinphoneMediaDirectionInvalid: ms_error("LinphoneMediaDirectionInvalid shall not be used."); return SalStreamInactive; } return SalStreamSendRecv; } LinphoneMediaDirection media_direction_from_sal_stream_dir(SalStreamDir dir){ switch (dir) { case SalStreamInactive: return LinphoneMediaDirectionInactive; case SalStreamSendOnly: return LinphoneMediaDirectionSendOnly; case SalStreamRecvOnly: return LinphoneMediaDirectionRecvOnly; case SalStreamSendRecv: return LinphoneMediaDirectionSendRecv; } return LinphoneMediaDirectionSendRecv; } SalStreamDir get_audio_dir_from_call_params(const LinphoneCallParams *params) { return sal_dir_from_call_params_dir(linphone_call_params_get_audio_direction(params)); } SalStreamDir get_video_dir_from_call_params(const LinphoneCallParams *params) { return sal_dir_from_call_params_dir(linphone_call_params_get_video_direction(params)); } void linphone_call_params_set_custom_headers(LinphoneCallParams *params, const SalCustomHeader *ch){ if (params->custom_headers){ sal_custom_header_free(params->custom_headers); params->custom_headers = NULL; } if (ch){ params->custom_headers = sal_custom_header_clone(ch); } } void linphone_call_params_set_custom_sdp_attributes(LinphoneCallParams *params, const SalCustomSdpAttribute *csa) { if (params->custom_sdp_attributes) { sal_custom_sdp_attribute_free(params->custom_sdp_attributes); params->custom_sdp_attributes = NULL; } if (csa) { params->custom_sdp_attributes = sal_custom_sdp_attribute_clone(csa); } } void linphone_call_params_set_custom_sdp_media_attributes(LinphoneCallParams *params, LinphoneStreamType type, const SalCustomSdpAttribute *csa) { if (params->custom_sdp_media_attributes[type]) { sal_custom_sdp_attribute_free(params->custom_sdp_media_attributes[type]); params->custom_sdp_media_attributes[type] = NULL; } if (csa) { params->custom_sdp_media_attributes[type] = sal_custom_sdp_attribute_clone(csa); } } /******************************************************************************* * Public functions * ******************************************************************************/ void linphone_call_params_add_custom_header(LinphoneCallParams *params, const char *header_name, const char *header_value){ params->custom_headers=sal_custom_header_append(params->custom_headers,header_name,header_value); } void linphone_call_params_add_custom_sdp_attribute(LinphoneCallParams *params, const char *attribute_name, const char *attribute_value) { params->custom_sdp_attributes = sal_custom_sdp_attribute_append(params->custom_sdp_attributes, attribute_name, attribute_value); } void linphone_call_params_add_custom_sdp_media_attribute(LinphoneCallParams *params, LinphoneStreamType type, const char *attribute_name, const char *attribute_value) { params->custom_sdp_media_attributes[type] = sal_custom_sdp_attribute_append(params->custom_sdp_media_attributes[type], attribute_name, attribute_value); } void linphone_call_params_clear_custom_sdp_attributes(LinphoneCallParams *params) { linphone_call_params_set_custom_sdp_attributes(params, NULL); } void linphone_call_params_clear_custom_sdp_media_attributes(LinphoneCallParams *params, LinphoneStreamType type) { linphone_call_params_set_custom_sdp_media_attributes(params, type, NULL); } LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){ return (LinphoneCallParams *)belle_sip_object_clone((const belle_sip_object_t *)cp); } bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){ return cp->real_early_media; } void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){ cp->real_early_media=enabled; } void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled){ cp->low_bandwidth=enabled; } void linphone_call_params_enable_audio(LinphoneCallParams *cp, bool_t enabled){ cp->has_audio=enabled; if (enabled && cp->audio_dir==LinphoneMediaDirectionInactive) cp->audio_dir=LinphoneMediaDirectionSendRecv; } LinphoneStatus linphone_call_params_enable_realtime_text(LinphoneCallParams *params, bool_t yesno) { params->realtimetext_enabled=yesno; return 0; } void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){ cp->has_video=enabled; if (enabled && cp->video_dir==LinphoneMediaDirectionInactive) cp->video_dir=LinphoneMediaDirectionSendRecv; } const char *linphone_call_params_get_custom_header(const LinphoneCallParams *params, const char *header_name){ return sal_custom_header_find(params->custom_headers,header_name); } const char * linphone_call_params_get_custom_sdp_attribute(const LinphoneCallParams *params, const char *attribute_name) { return sal_custom_sdp_attribute_find(params->custom_sdp_attributes, attribute_name); } const char * linphone_call_params_get_custom_sdp_media_attribute(const LinphoneCallParams *params, LinphoneStreamType type, const char *attribute_name) { return sal_custom_sdp_attribute_find(params->custom_sdp_media_attributes[type], attribute_name); } bool_t linphone_call_params_get_local_conference_mode(const LinphoneCallParams *cp){ return cp->in_conference; } LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) { return cp->media_encryption; } LinphonePrivacyMask linphone_call_params_get_privacy(const LinphoneCallParams *params) { return params->privacy; } float linphone_call_params_get_received_framerate(const LinphoneCallParams *cp){ return cp->received_fps; } MSVideoSize linphone_call_params_get_received_video_size(const LinphoneCallParams *cp) { return cp->recv_vsize; } const LinphoneVideoDefinition * linphone_call_params_get_received_video_definition(const LinphoneCallParams *cp) { return cp->recv_vdef; } const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp){ return cp->record_file; } const char * linphone_call_params_get_rtp_profile(const LinphoneCallParams *cp) { return sal_media_proto_to_string(get_proto_from_call_params(cp)); } float linphone_call_params_get_sent_framerate(const LinphoneCallParams *cp){ return cp->sent_fps; } MSVideoSize linphone_call_params_get_sent_video_size(const LinphoneCallParams *cp) { return cp->sent_vsize; } const LinphoneVideoDefinition * linphone_call_params_get_sent_video_definition(const LinphoneCallParams *cp) { return cp->sent_vdef; } const char *linphone_call_params_get_session_name(const LinphoneCallParams *cp){ return cp->session_name; } LinphonePayloadType *linphone_call_params_get_used_audio_payload_type(const LinphoneCallParams *cp) { return cp->audio_codec ? linphone_payload_type_new(NULL, cp->audio_codec) : NULL; } LinphonePayloadType *linphone_call_params_get_used_video_payload_type(const LinphoneCallParams *cp) { return cp->video_codec ? linphone_payload_type_new(NULL, cp->video_codec) : NULL; } LinphonePayloadType *linphone_call_params_get_used_text_payload_type(const LinphoneCallParams *cp) { return cp->text_codec ? linphone_payload_type_new(NULL, cp->text_codec) : NULL; } const OrtpPayloadType *linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) { return cp->audio_codec; } const OrtpPayloadType *linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) { return cp->video_codec; } const OrtpPayloadType *linphone_call_params_get_used_text_codec(const LinphoneCallParams *cp) { return cp->text_codec; } bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) { return cp->low_bandwidth; } void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){ cp->audio_bw=bandwidth; } void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, LinphoneMediaEncryption e) { cp->media_encryption = e; } void linphone_call_params_set_privacy(LinphoneCallParams *params, LinphonePrivacyMask privacy) { params->privacy=privacy; } void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){ if (cp->record_file){ ms_free(cp->record_file); cp->record_file=NULL; } if (path) cp->record_file=ms_strdup(path); } void linphone_call_params_set_session_name(LinphoneCallParams *cp, const char *name){ if (cp->session_name){ ms_free(cp->session_name); cp->session_name=NULL; } if (name) cp->session_name=ms_strdup(name); } bool_t linphone_call_params_audio_enabled(const LinphoneCallParams *cp){ return cp->has_audio; } bool_t linphone_call_params_realtime_text_enabled(const LinphoneCallParams *params) { return params->realtimetext_enabled; } bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){ return cp->has_video; } LinphoneMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp) { return cp->audio_dir; } LinphoneMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp) { return cp->video_dir; } void linphone_call_params_set_audio_direction(LinphoneCallParams *cp,LinphoneMediaDirection dir) { cp->audio_dir=dir; } void linphone_call_params_set_video_direction(LinphoneCallParams *cp,LinphoneMediaDirection dir) { cp->video_dir=dir; } /******************************************************************************* * Reference and user data handling functions * ******************************************************************************/ void *linphone_call_params_get_user_data(const LinphoneCallParams *cp) { return cp->user_data; } void linphone_call_params_set_user_data(LinphoneCallParams *cp, void *ud) { cp->user_data = ud; } LinphoneCallParams * linphone_call_params_ref(LinphoneCallParams *cp) { belle_sip_object_ref(cp); return cp; } void linphone_call_params_unref(LinphoneCallParams *cp) { belle_sip_object_unref(cp); } void linphone_call_params_enable_audio_multicast(LinphoneCallParams *params, bool_t yesno) { params->audio_multicast_enabled=yesno; } bool_t linphone_call_params_audio_multicast_enabled(const LinphoneCallParams *params) { return params->audio_multicast_enabled; } void linphone_call_params_enable_video_multicast(LinphoneCallParams *params, bool_t yesno) { params->video_multicast_enabled=yesno; } bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *params) { return params->video_multicast_enabled; } /******************************************************************************* * Constructor and destructor functions * ******************************************************************************/ static void _linphone_call_params_uninit(LinphoneCallParams *cp){ unsigned int i; if (cp->record_file) ms_free(cp->record_file); if (cp->custom_headers) sal_custom_header_free(cp->custom_headers); if (cp->custom_sdp_attributes) sal_custom_sdp_attribute_free(cp->custom_sdp_attributes); for (i = 0; i < (unsigned int)LinphoneStreamTypeUnknown; i++) { if (cp->custom_sdp_media_attributes[i]) sal_custom_sdp_attribute_free(cp->custom_sdp_media_attributes[i]); } if (cp->session_name) ms_free(cp->session_name); if (cp->sent_vdef != NULL) linphone_video_definition_unref(cp->sent_vdef); if (cp->recv_vdef != NULL) linphone_video_definition_unref(cp->recv_vdef); } static void _linphone_call_params_clone(LinphoneCallParams *dst, const LinphoneCallParams *src) { unsigned int i; /* * Save the belle_sip_object_t part, copy the entire structure and restore the belle_sip_object_t part */ belle_sip_object_t tmp = dst->base; memcpy(dst, src, sizeof(LinphoneCallParams)); dst->base = tmp; if (src->sent_vdef) dst->sent_vdef = linphone_video_definition_ref(src->sent_vdef); if (src->recv_vdef) dst->recv_vdef = linphone_video_definition_ref(src->recv_vdef); if (src->record_file) dst->record_file=ms_strdup(src->record_file); if (src->session_name) dst->session_name=ms_strdup(src->session_name); /* * The management of the custom headers is not optimal. We copy everything while ref counting would be more efficient. */ if (src->custom_headers) dst->custom_headers=sal_custom_header_clone(src->custom_headers); if (src->custom_sdp_attributes) dst->custom_sdp_attributes = sal_custom_sdp_attribute_clone(src->custom_sdp_attributes); for (i = 0; i < (unsigned int)LinphoneStreamTypeUnknown; i++) { if (src->custom_sdp_media_attributes[i]) dst->custom_sdp_media_attributes[i] = sal_custom_sdp_attribute_clone(src->custom_sdp_media_attributes[i]); } } LinphoneCallParams * linphone_call_params_new(void) { LinphoneCallParams *cp=belle_sip_object_new(LinphoneCallParams); cp->audio_dir=LinphoneMediaDirectionSendRecv; cp->video_dir=LinphoneMediaDirectionSendRecv; cp->has_audio=TRUE; cp->realtimetext_enabled = FALSE; return cp; } /* DEPRECATED */ void linphone_call_params_destroy(LinphoneCallParams *cp) { linphone_call_params_unref(cp); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallParams); BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallParams, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_call_params_uninit, (belle_sip_object_clone_t)_linphone_call_params_clone, // clone NULL, // marshal FALSE ); linphone-3.12.0/coreapi/callbacks.c000066400000000000000000001626421313432737600171420ustar00rootroot00000000000000/* linphone Copyright (C) 2010 Simon MORLAT (simon.morlat@free.fr) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sal/sal.h" #include "linphone/core.h" #include "private.h" #include "mediastreamer2/mediastream.h" #include "linphone/lpconfig.h" #include // stat #ifndef _WIN32 #include #include #include #endif static void register_failure(SalOp *op); static int media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd) { int result=0; int otherdesc_changed; char *tmp1=NULL; char *tmp2=NULL; if (call->params->in_conference != call->current_params->in_conference) return SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION; if (call->up_bw != linphone_core_get_upload_bandwidth(call->core)) return SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION; if (call->localdesc_changed) ms_message("Local description has changed: %s", tmp1 = sal_media_description_print_differences(call->localdesc_changed)); otherdesc_changed = sal_media_description_equals(oldmd, newmd); if (otherdesc_changed) ms_message("Other description has changed: %s", tmp2 = sal_media_description_print_differences(otherdesc_changed)); result = call->localdesc_changed | otherdesc_changed; if (tmp1) ms_free(tmp1); if (tmp2) ms_free(tmp2); return result; } void linphone_core_update_streams_destinations(LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) { SalStreamDescription *new_audiodesc = NULL; SalStreamDescription *new_videodesc = NULL; char *rtp_addr, *rtcp_addr; int i; for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&new_md->streams[i])) continue; if (new_md->streams[i].type == SalAudio) { new_audiodesc = &new_md->streams[i]; } else if (new_md->streams[i].type == SalVideo) { new_videodesc = &new_md->streams[i]; } } if (call->audiostream && new_audiodesc) { rtp_addr = (new_audiodesc->rtp_addr[0] != '\0') ? new_audiodesc->rtp_addr : new_md->addr; rtcp_addr = (new_audiodesc->rtcp_addr[0] != '\0') ? new_audiodesc->rtcp_addr : new_md->addr; ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port); rtp_session_set_remote_addr_full(call->audiostream->ms.sessions.rtp_session, rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port); } #ifdef VIDEO_ENABLED if (call->videostream && new_videodesc) { rtp_addr = (new_videodesc->rtp_addr[0] != '\0') ? new_videodesc->rtp_addr : new_md->addr; rtcp_addr = (new_videodesc->rtcp_addr[0] != '\0') ? new_videodesc->rtcp_addr : new_md->addr; ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port); rtp_session_set_remote_addr_full(call->videostream->ms.sessions.rtp_session, rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port); } #else (void)new_videodesc; #endif } static void _clear_early_media_destinations(LinphoneCall *call, MediaStream *ms){ RtpSession *session=ms->sessions.rtp_session; rtp_session_clear_aux_remote_addr(session); if (!call->ice_session) rtp_session_set_symmetric_rtp(session,linphone_core_symmetric_rtp_enabled(call->core));/*restore symmetric rtp if ICE is not used*/ } static void clear_early_media_destinations(LinphoneCall *call){ if (call->audiostream){ _clear_early_media_destinations(call,(MediaStream*)call->audiostream); } if (call->videostream){ _clear_early_media_destinations(call,(MediaStream*)call->videostream); } } static void prepare_early_media_forking(LinphoneCall *call){ /*we need to disable symmetric rtp otherwise our outgoing streams will be switching permanently between the multiple destinations*/ if (call->audiostream){ rtp_session_set_symmetric_rtp(call->audiostream->ms.sessions.rtp_session,FALSE); } if (call->videostream){ rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,FALSE); } } void linphone_call_update_frozen_payloads(LinphoneCall *call, SalMediaDescription *result_desc){ SalMediaDescription *local=call->localdesc; int i; for(i=0;inb_streams;++i){ bctbx_list_t *elem; for (elem=result_desc->streams[i].payloads;elem!=NULL;elem=elem->next){ PayloadType *pt=(PayloadType*)elem->data; if (is_payload_type_number_available(local->streams[i].already_assigned_payloads, payload_type_get_number(pt), NULL)){ /*new codec, needs to be added to the list*/ local->streams[i].already_assigned_payloads=bctbx_list_append(local->streams[i].already_assigned_payloads, payload_type_clone(pt)); ms_message("LinphoneCall[%p] : payload type %i %s/%i fmtp=%s added to frozen list.", call, payload_type_get_number(pt), pt->mime_type, pt->clock_rate, pt->recv_fmtp ? pt->recv_fmtp : ""); } } } } void linphone_call_update_streams(LinphoneCall *call, SalMediaDescription *new_md, LinphoneCallState target_state) { LinphoneCore *lc = linphone_call_get_core(call); SalMediaDescription *oldmd = call->resultdesc; int md_changed = 0; if (!((call->state == LinphoneCallIncomingEarlyMedia) && (linphone_core_get_ring_during_incoming_early_media(lc)))) { linphone_core_stop_ringing(lc); } if (!new_md) { ms_error("linphone_call_update_streams() called with null media description"); return; } linphone_call_update_biggest_desc(call, call->localdesc); sal_media_description_ref(new_md); call->resultdesc = new_md; if ((call->audiostream && (call->audiostream->ms.state == MSStreamStarted)) || (call->videostream && (call->videostream->ms.state == MSStreamStarted))) { clear_early_media_destinations(call); /* We already started media: check if we really need to restart it */ if (oldmd) { md_changed = media_parameters_changed(call, oldmd, new_md); /*might not be mandatory to restart stream for each ice restart as it leads bad user experience, specially in video. See 0002495 for better background on this*/ if ((md_changed & ( SAL_MEDIA_DESCRIPTION_CODEC_CHANGED |SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED |SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED |SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED |SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION ))) { ms_message("Media descriptions are different, need to restart the streams."); } else if (call->playing_ringbacktone) { ms_message("Playing ringback tone, will restart the streams."); } else { if (call->all_muted && target_state == LinphoneCallStreamsRunning) { ms_message("Early media finished, unmuting inputs..."); /* We were in early media, now we want to enable real media */ call->all_muted = FALSE; if (call->audiostream) linphone_core_enable_mic(lc, linphone_core_mic_enabled(lc)); #ifdef VIDEO_ENABLED if (call->videostream && call->camera_enabled) { linphone_call_enable_camera(call, linphone_call_camera_enabled(call)); } #endif } if (md_changed == SAL_MEDIA_DESCRIPTION_UNCHANGED) { /*FIXME ZRTP, might be restarted in any cases ? */ ms_message("No need to restart streams, SDP is unchanged."); goto end; } else { if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED) { ms_message("Network parameters have changed, update them."); linphone_core_update_streams_destinations(call, oldmd, new_md); } if (md_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED) { ms_message("Crypto parameters have changed, update them."); linphone_call_update_crypto_parameters(call, oldmd, new_md); } goto end; } } } linphone_call_stop_media_streams(call); if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED) { ms_message("Media ip type has changed, destroying sessions context on call [%p]", call); ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]); ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]); ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); } linphone_call_init_media_streams(call); } if (call->audiostream == NULL) { /* This happens after pausing the call locally. The streams are destroyed and then we wait the 200Ok to recreate them */ linphone_call_init_media_streams(call); } if (call->params->real_early_media && (call->state == LinphoneCallOutgoingEarlyMedia)) { prepare_early_media_forking(call); } linphone_call_start_media_streams(call, target_state); if ((call->state == LinphoneCallPausing) && call->paused_by_app && (bctbx_list_size(lc->calls) == 1)) { linphone_core_play_named_tone(lc, LinphoneToneCallOnHold); } linphone_call_update_frozen_payloads(call, new_md); end: if (oldmd) sal_media_description_unref(oldmd); } #if 0 static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){ bctbx_list_t *elem; for(elem=lc->calls;elem!=NULL;elem=elem->next){ LinphoneCall *call=(LinphoneCall*)elem->data; if (linphone_address_weak_equal(call->log->from,from) && linphone_address_weak_equal(call->log->to, to)){ return TRUE; } } return FALSE; } #endif static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const LinphoneAddress *remote) { bctbx_list_t *elem; ms_message("Searching for already_a_call_with_remote_address."); for(elem=lc->calls;elem!=NULL;elem=elem->next){ const LinphoneCall *call=(LinphoneCall*)elem->data; const LinphoneAddress *cRemote=linphone_call_get_remote_address(call); if (linphone_address_weak_equal(cRemote,remote)) { ms_warning("already_a_call_with_remote_address found."); return TRUE; } } return FALSE; } static LinphoneCall * look_for_broken_call_to_replace(SalOp *h, LinphoneCore *lc) { const bctbx_list_t *calls = linphone_core_get_calls(lc); const bctbx_list_t *it = calls; while (it != NULL) { LinphoneCall *replaced_call = NULL; LinphoneCall *call = (LinphoneCall *)bctbx_list_get_data(it); SalOp *replaced_op = sal_call_get_replaces(h); if (replaced_op) replaced_call = (LinphoneCall*)sal_op_get_user_pointer(replaced_op); if ((call->broken && sal_call_compare_op(h, call->op)) || ((replaced_call == call) && (strcmp(sal_op_get_from(h), sal_op_get_from(replaced_op)) == 0) && (strcmp(sal_op_get_to(h), sal_op_get_to(replaced_op)) == 0))) { return call; } it = bctbx_list_next(it); } return NULL; } static void call_received(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); LinphoneCall *call; LinphoneCall *replaced_call; char *alt_contact; LinphoneAddress *from_addr=NULL; LinphoneAddress *to_addr=NULL; LinphoneAddress *from_address_to_search_if_me=NULL; /*address used to know if I'm the caller*/ SalMediaDescription *md; const char * p_asserted_id; LinphoneErrorInfo *ei = NULL; LinphonePresenceActivity *activity = NULL; /* Look if this INVITE is for a call that has already been notified but broken because of network failure */ replaced_call = look_for_broken_call_to_replace(h, lc); if (replaced_call != NULL) { linphone_call_replace_op(replaced_call, h); return; } p_asserted_id = sal_custom_header_find(sal_op_get_recv_custom_header(h),"P-Asserted-Identity"); /*in some situation, better to trust the network rather than the UAC*/ if (lp_config_get_int(lc->config,"sip","call_logs_use_asserted_id_instead_of_from",0)) { LinphoneAddress *p_asserted_id_addr; if (!p_asserted_id) { ms_warning("No P-Asserted-Identity header found so cannot use it for op [%p] instead of from",h); } else { p_asserted_id_addr = linphone_address_new(p_asserted_id); if (!p_asserted_id_addr) { ms_warning("Unsupported P-Asserted-Identity header for op [%p] ",h); } else { ms_message("Using P-Asserted-Identity [%s] instead of from [%s] for op [%p]",p_asserted_id,sal_op_get_from(h),h); from_addr=p_asserted_id_addr; } } } if (!from_addr) from_addr=linphone_address_new(sal_op_get_from(h)); to_addr=linphone_address_new(sal_op_get_to(h)); /* first check if we can answer successfully to this invite */ if (linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed && (activity = linphone_presence_model_get_activity(lc->presence_model))) { switch (linphone_presence_activity_get_type(activity)) { case LinphonePresenceActivityPermanentAbsence: alt_contact = linphone_presence_model_get_contact(lc->presence_model); if (alt_contact != NULL) { SalErrorInfo sei = { 0 }; sal_error_info_set(&sei,SalReasonRedirect, "SIP", 0, NULL, NULL); sal_call_decline_with_error_info(h, &sei,alt_contact); ms_free(alt_contact); ei = linphone_error_info_new(); linphone_error_info_set(ei, NULL, LinphoneReasonMovedPermanently, 302, "Moved permanently", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, from_addr, to_addr, ei); sal_op_release(h); sal_error_info_reset(&sei); return; } break; default: /*nothing special to be done*/ break; } } if (!linphone_core_can_we_add_call(lc)){/*busy*/ sal_call_decline(h,SalReasonBusy,NULL); ei = linphone_error_info_new(); linphone_error_info_set(ei, NULL, LinphoneReasonBusy, 486, "Busy - too many calls", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, from_addr, to_addr, ei); sal_op_release(h); return; } if (sal_op_get_privacy(h) == SalPrivacyNone) { from_address_to_search_if_me=linphone_address_clone(from_addr); } else if (p_asserted_id) { from_address_to_search_if_me = linphone_address_new(p_asserted_id); } else { ms_warning ("Hidden from identity, don't know if it's me"); } if (from_address_to_search_if_me && already_a_call_with_remote_address(lc,from_address_to_search_if_me)){ char *addr = linphone_address_as_string(from_addr); ms_warning("Receiving a call while one with same address [%s] is initiated, refusing this one with busy message.",addr); sal_call_decline(h,SalReasonBusy,NULL); ei = linphone_error_info_new(); linphone_error_info_set(ei, NULL, LinphoneReasonBusy, 486, "Busy - duplicated call", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, from_addr, to_addr, ei); sal_op_release(h); linphone_address_unref(from_address_to_search_if_me); ms_free(addr); return; } else if (from_address_to_search_if_me) { linphone_address_unref(from_address_to_search_if_me); } call=linphone_call_new_incoming(lc,from_addr,to_addr,h); linphone_call_make_local_media_description(call); sal_call_set_local_media_description(call->op,call->localdesc); md=sal_call_get_final_media_description(call->op); if (md){ if (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md)){ ei = linphone_error_info_new(); linphone_error_info_set(ei, NULL, LinphoneReasonNotAcceptable, 488, "Not acceptable here", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, linphone_address_ref(from_addr), linphone_address_ref(to_addr), ei); sal_call_decline(call->op,SalReasonNotAcceptable,NULL); linphone_call_unref(call); return; } } /* the call is acceptable so we can now add it to our list */ linphone_core_add_call(lc,call); linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */ call->bg_task_id=sal_begin_background_task("liblinphone call notification", NULL, NULL); if (call->defer_notify_incoming) { /* Defer ringing until the end of the ICE candidates gathering process. */ ms_message("Defer ringing to gather ICE candidates"); return; } #ifdef BUILD_UPNP if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) && (call->upnp_session != NULL)) { /* Defer ringing until the end of the ICE candidates gathering process. */ ms_message("Defer ringing to gather uPnP candidates"); return; } #endif //BUILD_UPNP linphone_core_notify_incoming_call(lc,call); } static void call_rejected(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); LinphoneErrorInfo *ei = linphone_error_info_new(); linphone_error_info_from_sal_op(ei, h); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, linphone_address_new(sal_op_get_from(h)), linphone_address_new(sal_op_get_to(h)), ei); } static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md){ SalMediaDescription *cur_md=call->resultdesc; int i; SalStreamDescription *ref_stream,*new_stream; ms_message("Early media response received from another branch, checking if media can be forked to this new destination."); for (i=0;istreams[i])) continue; ref_stream=&cur_md->streams[i]; new_stream=&md->streams[i]; if (ref_stream->type==new_stream->type && ref_stream->payloads && new_stream->payloads){ PayloadType *refpt, *newpt; refpt=(PayloadType*)ref_stream->payloads->data; newpt=(PayloadType*)new_stream->payloads->data; if (strcmp(refpt->mime_type,newpt->mime_type)==0 && refpt->clock_rate==newpt->clock_rate && payload_type_get_number(refpt)==payload_type_get_number(newpt)){ MediaStream *ms=NULL; if (ref_stream->type==SalAudio){ ms=(MediaStream*)call->audiostream; }else if (ref_stream->type==SalVideo){ ms=(MediaStream*)call->videostream; } if (ms){ RtpSession *session=ms->sessions.rtp_session; const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr; const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr; if (ms_is_multicast(rtp_addr)) ms_message("Multicast addr [%s/%i] does not need auxiliary rtp's destination for call [%p]", rtp_addr,new_stream->rtp_port,call); else rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); } } } } } static void start_remote_ring(LinphoneCore *lc, LinphoneCall *call) { if (lc->sound_conf.play_sndcard!=NULL){ MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate); /*we release sound before playing ringback tone*/ if (call->audiostream) audio_stream_unprepare_sound(call->audiostream); if( lc->sound_conf.remote_ring ){ lc->ringstream=ring_start(lc->factory, lc->sound_conf.remote_ring,2000,ringcard); } } } static void call_ringing(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h); SalMediaDescription *md; if (call==NULL) return; /*set privacy*/ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op); linphone_core_notify_display_status(lc,_("Remote ringing.")); md=sal_call_get_final_media_description(h); if (md==NULL){ linphone_core_stop_dtmf_stream(lc); if (call->state==LinphoneCallOutgoingEarlyMedia){ /*already doing early media */ return; } if (lc->ringstream == NULL) start_remote_ring(lc, call); ms_message("Remote ringing..."); linphone_core_notify_display_status(lc,_("Remote ringing...")); linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing"); }else{ /*initialize the remote call params by invoking linphone_call_get_remote_params(). This is useful as the SDP may not be present in the 200Ok*/ linphone_call_get_remote_params(call); /*accept early media */ if ((call->audiostream && audio_stream_started(call->audiostream)) #ifdef VIDEO_ENABLED || (call->videostream && video_stream_started(call->videostream)) #endif ) { /*streams already started */ try_early_media_forking(call,md); #ifdef VIDEO_ENABLED if (call->videostream){ /*just request for iframe*/ video_stream_send_vfu(call->videostream); } #endif return; } linphone_core_notify_show_interface(lc); linphone_core_notify_display_status(lc,_("Early media.")); linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media"); linphone_core_stop_ringing(lc); ms_message("Doing early media..."); linphone_call_update_streams(call, md, call->state); if ((linphone_call_params_get_audio_direction(linphone_call_get_current_params(call)) == LinphoneMediaDirectionInactive) && call->audiostream) { if (lc->ringstream != NULL) return; /* Already ringing! */ start_remote_ring(lc, call); } } } static void start_pending_refer(LinphoneCall *call){ linphone_core_start_refered_call(call->core, call,NULL); } static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *op){ SalMediaDescription *md, *rmd; LinphoneCallState next_state = LinphoneCallIdle; const char *next_state_str = NULL; LinphoneTaskList tl; switch (call->state){/*immediately notify the connected state, even if errors occur after*/ case LinphoneCallOutgoingProgress: case LinphoneCallOutgoingRinging: case LinphoneCallOutgoingEarlyMedia: /*immediately notify the connected state*/ linphone_call_set_state(call,LinphoneCallConnected,"Connected"); { char *tmp=linphone_call_get_remote_address_as_string (call); char *msg=ms_strdup_printf(_("Call answered by %s"),tmp); linphone_core_notify_display_status(lc,msg); ms_free(tmp); ms_free(msg); } break; default: break; } linphone_task_list_init(&tl); rmd=sal_call_get_remote_media_description(op); /*set privacy*/ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op); /*reset the internal call update flag, so it doesn't risk to be copied and used in further re-INVITEs*/ if (call->params->internal_call_update) call->params->internal_call_update = FALSE; #ifdef BUILD_UPNP if (call->upnp_session != NULL && rmd) { linphone_call_update_upnp_from_remote_media_description(call, rmd); } #endif //BUILD_UPNP md=sal_call_get_final_media_description(op); if (md == NULL && call->prevstate == LinphoneCallOutgoingEarlyMedia && call->resultdesc != NULL){ ms_message("Using early media SDP since none was received with the 200 OK"); md = call->resultdesc; } if (md && (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md))){ md = NULL; } if (md){ /*there is a valid SDP in the response, either offer or answer, and we're able to start/update the streams*/ /* Handle remote ICE attributes if any. */ if (call->ice_session != NULL && rmd) { linphone_call_update_ice_from_remote_media_description(call, rmd, !sal_call_is_offerer(op)); } switch (call->state){ case LinphoneCallResuming: linphone_core_notify_display_status(lc,_("Call resumed.")); BCTBX_NO_BREAK; /*intentionally no break*/ case LinphoneCallConnected: if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call); BCTBX_NO_BREAK; /*intentionally no break*/ case LinphoneCallUpdating: case LinphoneCallUpdatedByRemote: if (!sal_media_description_has_dir(call->localdesc, SalStreamInactive) && (sal_media_description_has_dir(md,SalStreamRecvOnly) || sal_media_description_has_dir(md,SalStreamInactive))){ next_state = LinphoneCallPausedByRemote; next_state_str = "Call paused by remote"; }else{ if (!call->params->in_conference) lc->current_call=call; next_state = LinphoneCallStreamsRunning; next_state_str = "Streams running"; } break; case LinphoneCallEarlyUpdating: next_state_str = "Early update accepted"; next_state = call->prevstate; break; case LinphoneCallPausing: /*when we entered the pausing state, we always reach the paused state whatever the content of the remote SDP is. Our streams are all send-only (with music), soundcard and camera are never used*/ next_state = LinphoneCallPaused; next_state_str = "Call paused"; if (call->refer_pending) linphone_task_list_add(&tl, (LinphoneCoreIterateHook)start_pending_refer, call); break; default: ms_error("call_accepted(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state)); break; } if (next_state != LinphoneCallIdle){ linphone_call_update_remote_session_id_and_ver(call); linphone_call_update_ice_state_in_call_stats(call); linphone_call_update_streams(call, md, next_state); linphone_call_fix_call_parameters(call, rmd); linphone_call_set_state(call, next_state, next_state_str); }else{ ms_error("BUG: next_state is not set in call_accepted(), current state is %s", linphone_call_state_to_string(call->state)); } }else{ /*invalid or no SDP*/ switch (call->prevstate){ /*send a bye only in case of early states*/ case LinphoneCallOutgoingInit: case LinphoneCallOutgoingProgress: case LinphoneCallOutgoingRinging: case LinphoneCallOutgoingEarlyMedia: case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: ms_error("Incompatible SDP answer received, need to abort the call"); linphone_call_abort(call, _("Incompatible, check codecs or security settings...")); break; /*otherwise we are able to resume previous state*/ default: ms_error("Incompatible SDP answer received"); switch(call->state) { case LinphoneCallPausedByRemote: break; case LinphoneCallPaused: break; case LinphoneCallStreamsRunning: break; default: ms_message("Incompatible SDP answer received, restoring previous state [%s]",linphone_call_state_to_string(call->prevstate)); linphone_call_set_state(call,call->prevstate,_("Incompatible media parameters.")); break; } break; } } linphone_task_list_run(&tl); linphone_task_list_free(&tl); } /* * could be reach : * - when the call is accepted * - when a request is accepted (pause, resume) */ static void call_accepted(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call == NULL){ ms_warning("call_accepted: call does no longer exist."); return ; } process_call_accepted(lc, call, op); } static void call_resumed(LinphoneCore *lc, LinphoneCall *call){ linphone_core_notify_display_status(lc,_("We have been resumed.")); _linphone_call_accept_update(call,NULL,LinphoneCallStreamsRunning,"Connected (streams running)"); } static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ LinphoneCallParams *params; /* we are being paused */ linphone_core_notify_display_status(lc,_("We are paused by other party.")); params = linphone_call_params_copy(call->params); if (lp_config_get_int(lc->config, "sip", "inactive_video_on_pause", 0)) { linphone_call_params_set_video_direction(params, LinphoneMediaDirectionInactive); } _linphone_call_accept_update(call,params,LinphoneCallPausedByRemote,"Call paused by remote"); linphone_call_params_unref(params); } static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){ linphone_core_notify_display_status(lc,_("Call is updated by remote.")); linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote"); if (call->defer_update == FALSE){ if (call->state == LinphoneCallUpdatedByRemote){ linphone_call_accept_update(call, NULL); }else{ /*otherwise it means that the app responded by linphone_core_accept_call_update * within the callback, so job is already done.*/ } }else{ if (call->state == LinphoneCallUpdatedByRemote){ ms_message("LinphoneCall [%p]: UpdatedByRemoted was signaled but defered. LinphoneCore expects the application to call " "linphone_core_accept_call_update() later.", call); } } } /* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t is_update){ SalErrorInfo sei = { 0 }; SalMediaDescription *rmd=sal_call_get_remote_media_description(op); call->defer_update = lp_config_get_int(lc->config, "sip", "defer_update_default", FALSE); switch(call->state){ case LinphoneCallPausedByRemote: if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){ call_resumed(lc,call); }else{ call_updated_by_remote(lc, call); } break; /*SIP UPDATE CASE*/ case LinphoneCallOutgoingRinging: case LinphoneCallOutgoingEarlyMedia: case LinphoneCallIncomingEarlyMedia: if (is_update) { linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote"); _linphone_call_accept_update(call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); } break; case LinphoneCallStreamsRunning: case LinphoneCallConnected: case LinphoneCallUpdatedByRemote: // Can happen on UAC connectivity loss if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){ call_paused_by_remote(lc,call); }else{ call_updated_by_remote(lc, call); } break; case LinphoneCallPaused: /*we'll remain in pause state but accept the offer anyway according to default parameters*/ _linphone_call_accept_update(call,NULL,call->state,linphone_call_state_to_string(call->state)); break; case LinphoneCallUpdating: case LinphoneCallPausing: case LinphoneCallResuming: sal_error_info_set(&sei,SalReasonInternalError, "SIP", 0, NULL, NULL); sal_call_decline_with_error_info(call->op, &sei,NULL); BCTBX_NO_BREAK; /*no break*/ case LinphoneCallIdle: case LinphoneCallOutgoingInit: case LinphoneCallEnd: case LinphoneCallIncomingReceived: case LinphoneCallOutgoingProgress: case LinphoneCallRefered: case LinphoneCallError: case LinphoneCallReleased: case LinphoneCallEarlyUpdatedByRemote: case LinphoneCallEarlyUpdating: ms_warning("Receiving reINVITE or UPDATE while in state [%s], should not happen.",linphone_call_state_to_string(call->state)); break; } sal_error_info_reset(&sei); } /* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/ static void call_updating(SalOp *op, bool_t is_update){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); SalMediaDescription *rmd=sal_call_get_remote_media_description(op); SalErrorInfo sei = {0}; if (!call) { ms_error("call_updating(): call doesn't exist anymore"); return ; } linphone_call_fix_call_parameters(call, rmd); if (call->state!=LinphoneCallPaused){ /*Refresh the local description, but in paused state, we don't change anything.*/ if (rmd == NULL && lp_config_get_int(call->core->config,"sip","sdp_200_ack_follow_video_policy",0)) { LinphoneCallParams *p=linphone_core_create_call_params(lc, NULL); ms_message("Applying default policy for offering SDP on call [%p]",call); _linphone_call_set_new_params(call, p); linphone_call_params_unref(p); } linphone_call_make_local_media_description(call); sal_call_set_local_media_description(call->op,call->localdesc); } if (rmd == NULL){ /* case of a reINVITE or UPDATE without SDP */ call->expect_media_in_ack = TRUE; sal_call_accept(op); /*respond with an offer*/ /*don't do anything else in this case, wait for the ACK to receive to notify the app*/ }else { SalMediaDescription *md; SalMediaDescription *prev_result_desc=call->resultdesc; call->expect_media_in_ack = FALSE; md=sal_call_get_final_media_description(call->op); if (md && (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md))){ sal_error_info_set(&sei,SalReasonNotAcceptable, "SIP", 0, NULL, NULL); sal_call_decline_with_error_info(call->op, &sei,NULL); sal_error_info_reset(&sei); return; } if (is_update && prev_result_desc && md){ int diff=sal_media_description_equals(prev_result_desc,md); if (diff & (SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED|SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED)){ ms_warning("Cannot accept this update, it is changing parameters that require user approval"); sal_error_info_set(&sei,SalReasonUnknown, "SIP", 504, "Cannot change the session parameters without prompting the user", NULL); sal_call_decline_with_error_info(call->op, &sei,NULL); sal_error_info_reset(&sei); return; } } call_updated(lc, call, op, is_update); } } static void call_ack_received(SalOp *op, SalCustomHeader *ack){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call == NULL){ ms_warning("call_ack(): no call for which an ack is expected"); return; } linphone_call_notify_ack_processing(call, ack, TRUE); if (call->expect_media_in_ack){ switch(call->state){ case LinphoneCallStreamsRunning: case LinphoneCallPausedByRemote: linphone_call_set_state(call, LinphoneCallUpdatedByRemote, "UpdatedByRemote"); break; default: break; } process_call_accepted(lc, call, op); } } static void call_ack_being_sent(SalOp *op, SalCustomHeader *ack){ LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call == NULL){ ms_warning("call_ack(): no call for which an ack is supposed to be sent"); return; } linphone_call_notify_ack_processing(call, ack, FALSE); } static void call_terminated(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call==NULL) return; switch(linphone_call_get_state(call)){ case LinphoneCallEnd: case LinphoneCallError: ms_warning("call_terminated: already terminated, ignoring."); return; break; case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: if(!sal_op_get_reason_error_info(op)->protocol || strcmp(sal_op_get_reason_error_info(op)->protocol, "") == 0) { linphone_error_info_set(call->ei,NULL, LinphoneReasonNotAnswered, 0, "Incoming call cancelled", NULL); call->non_op_error = TRUE; } break; default: break; } ms_message("Current call terminated..."); if (call->refer_pending){ linphone_core_start_refered_call(lc,call,NULL); } //we stop the call only if we have this current call or if we are in call if ((bctbx_list_size(lc->calls) == 1) || linphone_core_in_call(lc)) { linphone_core_stop_ringing(lc); } linphone_call_stop_media_streams(call); linphone_core_notify_show_interface(lc); linphone_core_notify_display_status(lc,_("Call terminated.")); #ifdef BUILD_UPNP linphone_call_delete_upnp_session(call); #endif //BUILD_UPNP linphone_call_set_state(call, LinphoneCallEnd,"Call ended"); } static int resume_call_after_failed_transfer(LinphoneCall *call){ if (call->was_automatically_paused && call->state==LinphoneCallPausing) return BELLE_SIP_CONTINUE; /*was still in pausing state*/ if (call->was_automatically_paused && call->state==LinphoneCallPaused){ if (sal_op_is_idle(call->op)){ linphone_call_resume(call); }else { ms_message("resume_call_after_failed_transfer(), salop was busy"); return BELLE_SIP_CONTINUE; } } linphone_call_unref(call); return BELLE_SIP_STOP; } static void call_failure(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); const SalErrorInfo *ei=sal_op_get_error_info(op); char *msg486=_("User is busy."); char *msg480=_("User is temporarily unavailable."); /*char *retrymsg=_("%s. Retry after %i minute(s).");*/ char *msg600=_("User does not want to be disturbed."); char *msg603=_("Call declined."); const char *msg=ei->full_string; LinphoneCall *referer; LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); bool_t stop_ringing = TRUE; bctbx_list_t *calls = lc->calls; if (call==NULL){ ms_warning("Call faillure reported on already terminated call."); return ; } referer=call->referer; linphone_core_notify_show_interface(lc); switch(ei->reason){ case SalReasonNone: break; case SalReasonRequestTimeout: msg=_("Request timeout."); linphone_core_notify_display_status(lc,msg); break; case SalReasonDeclined: msg=msg603; linphone_core_notify_display_status(lc,msg603); break; case SalReasonBusy: msg=msg486; linphone_core_notify_display_status(lc,msg486); break; case SalReasonRedirect: { linphone_call_stop_media_streams(call); if ( call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress || call->state==LinphoneCallOutgoingRinging /*push case*/ || call->state==LinphoneCallOutgoingEarlyMedia){ LinphoneAddress* redirection_to = (LinphoneAddress*)sal_op_get_remote_contact_address(call->op); if( redirection_to ){ char* url = linphone_address_as_string(redirection_to); ms_warning("Redirecting call [%p] to %s",call, url); ms_free(url); if( call->log->to != NULL ) { linphone_address_unref(call->log->to); } call->log->to = linphone_address_ref(redirection_to); linphone_call_restart_invite(call); return; } } msg=_("Redirected"); linphone_core_notify_display_status(lc,msg); } break; case SalReasonTemporarilyUnavailable: msg=msg480; linphone_core_notify_display_status(lc,msg480); break; case SalReasonNotFound: linphone_core_notify_display_status(lc,msg); break; case SalReasonDoNotDisturb: msg=msg600; linphone_core_notify_display_status(lc,msg600); break; case SalReasonUnsupportedContent: /*state == LinphoneCallOutgoingInit) || (call->state == LinphoneCallOutgoingProgress) || (call->state == LinphoneCallOutgoingRinging) /* Push notification case */ || (call->state == LinphoneCallOutgoingEarlyMedia)) { int i; for (i = 0; i < call->localdesc->nb_streams; i++) { if (!sal_stream_description_active(&call->localdesc->streams[i])) continue; if (call->params->media_encryption == LinphoneMediaEncryptionSRTP) { if (call->params->avpf_enabled == TRUE) { if (i == 0) ms_message("Retrying call [%p] with SAVP", call); call->params->avpf_enabled = FALSE; linphone_call_restart_invite(call); return; } else if (!linphone_core_is_media_encryption_mandatory(lc)) { if (i == 0) ms_message("Retrying call [%p] with AVP", call); call->params->media_encryption = LinphoneMediaEncryptionNone; memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); linphone_call_restart_invite(call); return; } } else if (call->params->avpf_enabled == TRUE) { if (i == 0) ms_message("Retrying call [%p] with AVP", call); call->params->avpf_enabled = FALSE; linphone_call_restart_invite(call); return; } } } msg=_("Incompatible media parameters."); linphone_core_notify_display_status(lc,msg); break; default: linphone_core_notify_display_status(lc,_("Call failed.")); } /*some call errors are not fatal*/ switch (call->state) { case LinphoneCallUpdating: case LinphoneCallPausing: case LinphoneCallResuming: if (ei->reason != SalReasonNoMatch){ ms_message("Call error on state [%s], restoring previous state",linphone_call_state_to_string(call->prevstate)); linphone_call_set_state(call, call->prevstate,ei->full_string); return; } default: break; /*nothing to do*/ } /* Stop ringing */ bool_t ring_during_early_media = linphone_core_get_ring_during_incoming_early_media(lc); while(calls) { if (((LinphoneCall *)calls->data)->state == LinphoneCallIncomingReceived || (ring_during_early_media && ((LinphoneCall *)calls->data)->state == LinphoneCallIncomingEarlyMedia)) { stop_ringing = FALSE; break; } calls = calls->next; } if(stop_ringing) { linphone_core_stop_ringing(lc); } linphone_call_stop_media_streams(call); #ifdef BUILD_UPNP linphone_call_delete_upnp_session(call); #endif //BUILD_UPNP if (call->state!=LinphoneCallEnd && call->state!=LinphoneCallError){ if (ei->reason==SalReasonDeclined){ linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); }else{ if (linphone_call_state_is_early(call->state)){ linphone_call_set_state(call,LinphoneCallError,ei->full_string); }else{ linphone_call_set_state(call, LinphoneCallEnd, ei->full_string); } } if (ei->reason!=SalReasonNone) linphone_core_play_call_error_tone(lc,linphone_reason_from_sal(ei->reason)); } if (referer){ /*notify referer of the failure*/ linphone_core_notify_refer_state(lc,referer,call); /*schedule automatic resume of the call. This must be done only after the notifications are completed due to dialog serialization of requests.*/ linphone_core_queue_task(lc,(belle_sip_source_func_t)resume_call_after_failed_transfer,linphone_call_ref(referer),"Automatic call resuming after failed transfer"); } } static void call_released(SalOp *op){ LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call!=NULL){ linphone_call_set_state(call,LinphoneCallReleased,"Call released"); }else{ /*we can arrive here when the core manages call at Sal level without creating a LinphoneCall object. Typicially: * - when declining an incoming call with busy because maximum number of calls is reached. */ } } static void call_cancel_done(SalOp *op) { LinphoneCall *call = (LinphoneCall *)sal_op_get_user_pointer(op); if (call->reinvite_on_cancel_response_requested == TRUE) { call->reinvite_on_cancel_response_requested = FALSE; linphone_call_reinvite_to_recover_from_connection_loss(call); } } static void auth_failure(SalOp *op, SalAuthInfo* info) { LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneAuthInfo *ai = NULL; if (info != NULL) { ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, TRUE); if (ai){ LinphoneAuthMethod method = info->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls; LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, info->username, NULL, NULL, NULL, info->realm, info->domain); ms_message("%s/%s/%s/%s authentication fails.", info->realm, info->username, info->domain, info->mode == SalAuthModeHttpDigest ? "HttpDigest" : "Tls"); /*ask again for password if auth info was already supplied but apparently not working*/ linphone_core_notify_authentication_requested(lc, auth_info, method); linphone_auth_info_unref(auth_info); // Deprecated linphone_core_notify_auth_info_requested(lc, info->realm, info->username, info->domain); } } } static void register_success(SalOp *op, bool_t registered){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); char *msg; if (!cfg){ ms_message("Registration success for deleted proxy config, ignored"); return; } linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared , registered ? "Registration successful" : "Unregistration done"); { if (registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op)); else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op)); linphone_core_notify_display_status(lc,msg); ms_free(msg); } } static void register_failure(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); const SalErrorInfo *ei=sal_op_get_error_info(op); const char *details=ei->full_string; if (cfg==NULL){ ms_warning("Registration failed for unknown proxy config."); return ; } if (details==NULL) details=_("no response timeout"); { char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op), details); linphone_core_notify_display_status(lc,msg); ms_free(msg); } if ((ei->reason == SalReasonServiceUnavailable || ei->reason == SalReasonIOError) && linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk) { linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,_("Service unavailable, retrying")); } else { linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details); } if (cfg->presence_publish_event){ /*prevent publish to be sent now until registration gets successful*/ linphone_event_terminate(cfg->presence_publish_event); cfg->presence_publish_event=NULL; cfg->send_publish=cfg->publish; } } static void vfu_request(SalOp *op){ #ifdef VIDEO_ENABLED LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op); if (call==NULL){ ms_warning("VFU request but no call !"); return ; } if (call->videostream) video_stream_send_vfu(call->videostream); #endif } static void dtmf_received(SalOp *op, char dtmf){ LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (!call) return; linphone_call_notify_dtmf_received(call, dtmf); } static void refer_received(Sal *sal, SalOp *op, const char *referto){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); LinphoneAddress *refer_to_addr = linphone_address_new(referto); char method[20] = ""; if(refer_to_addr) { const char *tmp = linphone_address_get_method_param(refer_to_addr); if(tmp) strncpy(method, tmp, sizeof(method)); linphone_address_unref(refer_to_addr); } if (call && (strlen(method) == 0 || strcmp(method, "INVITE") == 0)) { if (call->refer_to!=NULL){ ms_free(call->refer_to); } call->refer_to=ms_strdup(referto); call->refer_pending=TRUE; linphone_call_set_state(call,LinphoneCallRefered,"Refered"); { char *msg=ms_strdup_printf(_("We are transferred to %s"),referto); linphone_core_notify_display_status(lc,msg); ms_free(msg); } if (call->refer_pending) linphone_core_start_refered_call(lc,call,NULL); }else { linphone_core_notify_refer_received(lc,referto); } } static void message_received(SalOp *op, const SalMessage *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); LinphoneReason reason = lc->chat_deny_code; if (reason == LinphoneReasonNone) { linphone_core_message_received(lc, op, msg); } sal_message_reply(op, linphone_reason_to_sal(reason)); if (!call) sal_op_release(op); } static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { linphone_notify_parse_presence(content_type, content_subtype, body, result); } static void convert_presence_to_xml_requested(SalOp *op, SalPresenceModel *presence, const char *contact, char **content) { /*for backward compatibility because still used by notify. No loguer used for publish*/ if(linphone_presence_model_get_presentity((LinphonePresenceModel*)presence) == NULL) { LinphoneAddress * presentity = linphone_address_new(contact); linphone_presence_model_set_presentity((LinphonePresenceModel*)presence, presentity); linphone_address_unref(presentity); } *content = linphone_presence_model_to_xml((LinphonePresenceModel*)presence); } static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_notify_recv(lc,op,ss,model); } static void subscribe_presence_received(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_subscription_new(lc,op,from); } static void subscribe_presence_closed(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_subscription_closed(lc,op); } static void ping_reply(SalOp *op){ LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); ms_message("ping reply !"); if (call){ if (call->state==LinphoneCallOutgoingInit){ call->ping_replied=TRUE; linphone_call_proceed_with_invite_if_ready(call, NULL); } } else { ms_warning("ping reply without call attached..."); } } static bool_t fill_auth_info_with_client_certificate(LinphoneCore *lc, SalAuthInfo* sai) { const char *chain_file = linphone_core_get_tls_cert_path(lc); const char *key_file = linphone_core_get_tls_key_path(lc); if (key_file && chain_file) { #ifndef _WIN32 // optinal check for files struct stat st; if (stat(key_file, &st)) { ms_warning("No client certificate key found in %s", key_file); return FALSE; } if (stat(chain_file, &st)) { ms_warning("No client certificate chain found in %s", chain_file); return FALSE; } #endif sal_certificates_chain_parse_file(sai, chain_file, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse_file(sai, key_file, ""); } else if (lc->tls_cert && lc->tls_key) { sal_certificates_chain_parse(sai, lc->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse(sai, lc->tls_key, ""); } return sai->certificates && sai->key; } static bool_t fill_auth_info(LinphoneCore *lc, SalAuthInfo* sai) { LinphoneAuthInfo *ai = NULL; if (sai->mode == SalAuthModeTls) { ai = (LinphoneAuthInfo*)_linphone_core_find_tls_auth_info(lc); } else { ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,sai->realm,sai->username,sai->domain, FALSE); } if (ai) { if (sai->mode == SalAuthModeHttpDigest) { sai->userid = ms_strdup(ai->userid ? ai->userid : ai->username); sai->password = ai->passwd?ms_strdup(ai->passwd) : NULL; sai->ha1 = ai->ha1 ? ms_strdup(ai->ha1) : NULL; } else if (sai->mode == SalAuthModeTls) { if (ai->tls_cert && ai->tls_key) { sal_certificates_chain_parse(sai, ai->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse(sai, ai->tls_key, ""); } else if (ai->tls_cert_path && ai->tls_key_path) { sal_certificates_chain_parse_file(sai, ai->tls_cert_path, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse_file(sai, ai->tls_key_path, ""); } else { fill_auth_info_with_client_certificate(lc, sai); } } if (sai->realm && !ai->realm){ /*if realm was not known, then set it so that ha1 may eventually be calculated and clear text password dropped*/ linphone_auth_info_set_realm(ai, sai->realm); linphone_core_write_auth_info(lc, ai); } return TRUE; } else { if (sai->mode == SalAuthModeTls) { return fill_auth_info_with_client_certificate(lc, sai); } return FALSE; } } static bool_t auth_requested(Sal* sal, SalAuthInfo* sai) { LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal); if (fill_auth_info(lc,sai)) { return TRUE; } else { LinphoneAuthMethod method = sai->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls; LinphoneAuthInfo *ai = linphone_core_create_auth_info(lc, sai->username, NULL, NULL, NULL, sai->realm, sai->domain); linphone_core_notify_authentication_requested(lc, ai, method); linphone_auth_info_unref(ai); // Deprecated linphone_core_notify_auth_info_requested(lc, sai->realm, sai->username, sai->domain); if (fill_auth_info(lc, sai)) { return TRUE; } return FALSE; } } static void notify_refer(SalOp *op, SalReferStatus status){ LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); LinphoneCallState cstate; if (call==NULL) { ms_warning("Receiving notify_refer for unknown call."); return ; } switch(status){ case SalReferTrying: cstate=LinphoneCallOutgoingProgress; break; case SalReferSuccess: cstate=LinphoneCallConnected; break; case SalReferFailed: cstate=LinphoneCallError; break; default: cstate=LinphoneCallError; } linphone_call_set_transfer_state(call, cstate); if (cstate==LinphoneCallConnected){ /*automatically terminate the call as the transfer is complete.*/ linphone_call_terminate(call); } } static LinphoneChatMessageState chatStatusSal2Linphone(SalMessageDeliveryStatus status){ switch(status){ case SalMessageDeliveryInProgress: return LinphoneChatMessageStateInProgress; case SalMessageDeliveryDone: return LinphoneChatMessageStateDelivered; case SalMessageDeliveryFailed: return LinphoneChatMessageStateNotDelivered; } return LinphoneChatMessageStateIdle; } static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status){ LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op); if (chat_msg == NULL) { // Do not handle delivery status for isComposing messages. return; } // check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks if (chat_msg->chat_room != NULL) { linphone_chat_message_update_state(chat_msg, chatStatusSal2Linphone(status)); } if (status != SalMessageDeliveryInProgress) { /*only release op if not in progress*/ linphone_chat_message_destroy(chat_msg); } } static void info_received(SalOp *op, SalBodyHandler *body_handler){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_core_notify_info_message(lc,op,body_handler); } static void subscribe_response(SalOp *op, SalSubscribeStatus status, int will_retry){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); if (lev==NULL) return; if (status==SalSubscribeActive){ linphone_event_set_state(lev,LinphoneSubscriptionActive); }else if (status==SalSubscribePending){ linphone_event_set_state(lev,LinphoneSubscriptionPending); }else{ if (will_retry){ linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress); } else linphone_event_set_state(lev,LinphoneSubscriptionError); } } static void notify(SalOp *op, SalSubscribeStatus st, const char *eventname, SalBodyHandler *body_handler){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); bool_t out_of_dialog = (lev==NULL); if (out_of_dialog) { /*out of dialog notify */ lev = linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname); } { LinphoneContent *ct=linphone_content_from_sal_body_handler(body_handler); if (ct) { linphone_core_notify_notify_received(lc,lev,eventname,ct); linphone_content_unref(ct); } } if (out_of_dialog){ /*out of dialog NOTIFY do not create an implicit subscription*/ linphone_event_set_state(lev, LinphoneSubscriptionTerminated); }else if (st!=SalSubscribeNone){ linphone_event_set_state(lev,linphone_subscription_state_from_sal(st)); } } static void subscribe_received(SalOp *op, const char *eventname, const SalBodyHandler *body_handler){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); if (lev==NULL) { lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionIncoming,eventname); linphone_event_set_state(lev,LinphoneSubscriptionIncomingReceived); }else{ /*subscribe refresh, unhandled*/ } } static void incoming_subscribe_closed(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); linphone_event_set_state(lev,LinphoneSubscriptionTerminated); } static void on_publish_response(SalOp* op){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); const SalErrorInfo *ei=sal_op_get_error_info(op); if (lev==NULL) return; if (ei->reason==SalReasonNone){ if (!lev->terminating) linphone_event_set_publish_state(lev,LinphonePublishOk); else linphone_event_set_publish_state(lev,LinphonePublishCleared); }else{ if (lev->publish_state==LinphonePublishOk){ linphone_event_set_publish_state(lev,LinphonePublishProgress); }else{ linphone_event_set_publish_state(lev,LinphonePublishError); } } } static void on_expire(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); if (lev==NULL) return; if (linphone_event_get_publish_state(lev)==LinphonePublishOk){ linphone_event_set_publish_state(lev,LinphonePublishExpiring); }else if (linphone_event_get_subscription_state(lev)==LinphoneSubscriptionActive){ linphone_event_set_state(lev,LinphoneSubscriptionExpiring); } } static void on_notify_response(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); if (lev==NULL) return; /*this is actually handling out of dialogs notify - for the moment*/ if (!lev->is_out_of_dialog_op) return; switch (linphone_event_get_subscription_state(lev)){ case LinphoneSubscriptionIncomingReceived: if (sal_op_get_error_info(op)->reason == SalReasonNone){ linphone_event_set_state(lev, LinphoneSubscriptionTerminated); }else{ linphone_event_set_state(lev, LinphoneSubscriptionError); } break; default: ms_warning("Unhandled on_notify_response() case %s", linphone_subscription_state_to_string(linphone_event_get_subscription_state(lev))); } } SalCallbacks linphone_sal_callbacks={ call_received, call_rejected, call_ringing, call_accepted, call_ack_received, call_ack_being_sent, call_updating, call_terminated, call_failure, call_released, call_cancel_done, auth_failure, register_success, register_failure, vfu_request, dtmf_received, refer_received, message_received, message_delivery_update, notify_refer, subscribe_received, incoming_subscribe_closed, subscribe_response, notify, subscribe_presence_received, subscribe_presence_closed, parse_presence_requested, convert_presence_to_xml_requested, notify_presence, ping_reply, auth_requested, info_received, on_publish_response, on_expire, on_notify_response }; linphone-3.12.0/coreapi/carddav.c000066400000000000000000000715331313432737600166250ustar00rootroot00000000000000/* carddav.c Copyright (C) 2015 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "private.h" LinphoneCardDavContext* linphone_carddav_context_new(LinphoneFriendList *lfl) { LinphoneCardDavContext *carddav_context = NULL; if (!linphone_core_vcard_supported()) { ms_error("[carddav] vCard isn't available (maybe it wasn't compiled), can't do CardDAV sync"); return NULL; } if (!lfl || !lfl->uri) { return NULL; } carddav_context = (LinphoneCardDavContext *)ms_new0(LinphoneCardDavContext, 1); carddav_context->friend_list = linphone_friend_list_ref(lfl); return carddav_context; } void linphone_carddav_context_destroy(LinphoneCardDavContext *cdc) { if (cdc) { if (cdc->friend_list) { linphone_friend_list_unref(cdc->friend_list); cdc->friend_list = NULL; } if (cdc->auth_info) { linphone_auth_info_unref(cdc->auth_info); cdc->auth_info = NULL; } ms_free(cdc); } } void linphone_carddav_set_user_data(LinphoneCardDavContext *cdc, void *ud) { cdc->user_data = ud; } void* linphone_carddav_get_user_data(LinphoneCardDavContext *cdc) { return cdc->user_data; } void linphone_carddav_synchronize(LinphoneCardDavContext *cdc) { cdc->ctag = cdc->friend_list->revision; linphone_carddav_get_current_ctag(cdc); } static void linphone_carddav_client_to_server_sync_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { if (!success) { ms_error("[carddav] CardDAV client to server sync failure: %s", msg); } if (cdc->sync_done_cb) { cdc->sync_done_cb(cdc, success, msg); } } static void linphone_carddav_server_to_client_sync_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { if (success) { ms_debug("CardDAV sync successful, saving new cTag: %i", cdc->ctag); linphone_friend_list_update_revision(cdc->friend_list, cdc->ctag); } else { ms_error("[carddav] CardDAV server to client sync failure: %s", msg); } if (cdc->sync_done_cb) { cdc->sync_done_cb(cdc, success, msg); } } static int find_matching_friend(LinphoneFriend *lf1, LinphoneFriend *lf2) { LinphoneVcard *lvc1 = linphone_friend_get_vcard(lf1); LinphoneVcard *lvc2 = linphone_friend_get_vcard(lf2); const char *uid1 = NULL, *uid2 = NULL; if (!lvc1 || !lvc2) { return 1; } uid1 = linphone_vcard_get_uid(lvc1); uid2 = linphone_vcard_get_uid(lvc2); if (!uid1 || !uid2) { return 1; } return strcmp(uid1, uid2); } static void linphone_carddav_response_free(LinphoneCardDavResponse *response) { if (response->etag) ms_free(response->etag); if (response->url) ms_free(response->url); if (response->vcard) ms_free(response->vcard); ms_free(response); } static void linphone_carddav_vcards_pulled(LinphoneCardDavContext *cdc, bctbx_list_t *vCards) { bctbx_list_t *vCards_remember = vCards; if (vCards != NULL && bctbx_list_size(vCards) > 0) { bctbx_list_t *friends = cdc->friend_list->friends; while (vCards) { LinphoneCardDavResponse *vCard = (LinphoneCardDavResponse *)vCards->data; if (vCard) { LinphoneVcard *lvc = linphone_vcard_context_get_vcard_from_buffer(cdc->friend_list->lc->vcard_context, vCard->vcard); LinphoneFriend *lf = NULL; bctbx_list_t *local_friend = NULL; if (lvc) { // Compute downloaded vCards' URL and save it (+ eTag) char *vCard_name = strrchr(vCard->url, '/'); char full_url[300]; snprintf(full_url, sizeof(full_url), "%s%s", cdc->friend_list->uri, vCard_name); linphone_vcard_set_url(lvc, full_url); linphone_vcard_set_etag(lvc, vCard->etag); ms_debug("Downloaded vCard etag/url are %s and %s", vCard->etag, full_url); lf = linphone_friend_new_from_vcard(lvc); linphone_vcard_unref(lvc); /*ref is now owned by friend*/ if (lf) { local_friend = bctbx_list_find_custom(friends, (int (*)(const void*, const void*))find_matching_friend, lf); if (local_friend) { LinphoneFriend *lf2 = (LinphoneFriend *)local_friend->data; lf->storage_id = lf2->storage_id; lf->pol = lf2->pol; lf->subscribe = lf2->subscribe; lf->refkey = ms_strdup(lf2->refkey); lf->presence_received = lf2->presence_received; lf->lc = lf2->lc; lf->friend_list = lf2->friend_list; if (cdc->contact_updated_cb) { ms_debug("Contact updated: %s", linphone_friend_get_name(lf)); cdc->contact_updated_cb(cdc, lf, lf2); } } else { if (cdc->contact_created_cb) { ms_debug("Contact created: %s", linphone_friend_get_name(lf)); cdc->contact_created_cb(cdc, lf); } } linphone_friend_unref(lf); } else { ms_error("[carddav] Couldn't create a friend from vCard"); } } else { ms_error("[carddav] Couldn't parse vCard %s", vCard->vcard); } } vCards = bctbx_list_next(vCards); } bctbx_list_free_with_data(vCards_remember, (void (*)(void *))linphone_carddav_response_free); } linphone_carddav_server_to_client_sync_done(cdc, TRUE, NULL); } static bctbx_list_t* parse_vcards_from_xml_response(const char *body) { bctbx_list_t *result = NULL; xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; linphone_xml_xpath_context_init_carddav_ns(xml_ctx); { xmlXPathObjectPtr responses = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/d:multistatus/d:response"); if (responses != NULL && responses->nodesetval != NULL) { xmlNodeSetPtr responses_nodes = responses->nodesetval; if (responses_nodes->nodeNr >= 1) { int i; for (i = 0; i < responses_nodes->nodeNr; i++) { xmlNodePtr response_node = responses_nodes->nodeTab[i]; xml_ctx->xpath_ctx->node = response_node; { char *etag = linphone_get_xml_text_content(xml_ctx, "d:propstat/d:prop/d:getetag"); char *url = linphone_get_xml_text_content(xml_ctx, "d:href"); char *vcard = linphone_get_xml_text_content(xml_ctx, "d:propstat/d:prop/card:address-data"); LinphoneCardDavResponse *response = ms_new0(LinphoneCardDavResponse, 1); response->etag = ms_strdup(etag); response->url = ms_strdup(url); response->vcard = ms_strdup(vcard); result = bctbx_list_append(result, response); ms_debug("Added vCard object with eTag %s, URL %s and vCard %s", etag, url, vcard); linphone_free_xml_text_content(etag); linphone_free_xml_text_content(url); linphone_free_xml_text_content(vcard); } } } xmlXPathFreeObject(responses); } } } end: linphone_xmlparsing_context_destroy(xml_ctx); return result; } static int find_matching_vcard(LinphoneCardDavResponse *response, LinphoneFriend *lf) { if (!response->url || !lf || !lf->vcard || !linphone_vcard_get_url(lf->vcard)) { return 1; } return strcmp(response->url, linphone_vcard_get_url(lf->vcard)); } static void linphone_carddav_vcards_fetched(LinphoneCardDavContext *cdc, bctbx_list_t *vCards) { if (vCards != NULL && bctbx_list_size(vCards) > 0) { bctbx_list_t *friends = cdc->friend_list->friends; bctbx_list_t *friends_to_remove = NULL; bctbx_list_t *temp_list = NULL; while (friends) { LinphoneFriend *lf = (LinphoneFriend *)friends->data; if (lf) { bctbx_list_t *vCard = bctbx_list_find_custom(vCards, (int (*)(const void*, const void*))find_matching_vcard, lf); if (!vCard) { ms_debug("Local friend %s isn't in the remote vCard list, delete it", linphone_friend_get_name(lf)); temp_list = bctbx_list_append(temp_list, linphone_friend_ref(lf)); } else { LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)vCard->data; ms_debug("Local friend %s is in the remote vCard list, check eTag", linphone_friend_get_name(lf)); if (response) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); const char *etag = linphone_vcard_get_etag(lvc); ms_debug("Local friend eTag is %s, remote vCard eTag is %s", etag, response->etag); if (lvc && etag && strcmp(etag, response->etag) == 0) { bctbx_list_remove(vCards, vCard); linphone_carddav_response_free(response); } } } } friends = bctbx_list_next(friends); } friends_to_remove = temp_list; while(friends_to_remove) { LinphoneFriend *lf = (LinphoneFriend *)friends_to_remove->data; if (lf) { if (cdc->contact_removed_cb) { ms_debug("Contact removed: %s", linphone_friend_get_name(lf)); cdc->contact_removed_cb(cdc, lf); } } friends_to_remove = bctbx_list_next(friends_to_remove); } temp_list = bctbx_list_free_with_data(temp_list, (void (*)(void *))linphone_friend_unref); linphone_carddav_pull_vcards(cdc, vCards); bctbx_list_free_with_data(vCards, (void (*)(void *))linphone_carddav_response_free); } } static bctbx_list_t* parse_vcards_etags_from_xml_response(const char *body) { bctbx_list_t *result = NULL; xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; linphone_xml_xpath_context_init_carddav_ns(xml_ctx); { xmlXPathObjectPtr responses = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/d:multistatus/d:response"); if (responses != NULL && responses->nodesetval != NULL) { xmlNodeSetPtr responses_nodes = responses->nodesetval; if (responses_nodes->nodeNr >= 1) { int i; for (i = 0; i < responses_nodes->nodeNr; i++) { xmlNodePtr response_node = responses_nodes->nodeTab[i]; xml_ctx->xpath_ctx->node = response_node; { char *etag = linphone_get_xml_text_content(xml_ctx, "d:propstat/d:prop/d:getetag"); char *url = linphone_get_xml_text_content(xml_ctx, "d:href"); LinphoneCardDavResponse *response = ms_new0(LinphoneCardDavResponse, 1); response->etag = ms_strdup(etag); response->url = ms_strdup(url); result = bctbx_list_append(result, response); ms_debug("Added vCard object with eTag %s and URL %s", etag, url); linphone_free_xml_text_content(etag); linphone_free_xml_text_content(url); } } } xmlXPathFreeObject(responses); } } } end: linphone_xmlparsing_context_destroy(xml_ctx); return result; } static void linphone_carddav_ctag_fetched(LinphoneCardDavContext *cdc, int ctag) { ms_debug("Remote cTag for CardDAV addressbook is %i, local one is %i", ctag, cdc->ctag); if (ctag == -1 || ctag > cdc->ctag) { cdc->ctag = ctag; linphone_carddav_fetch_vcards(cdc); } else { ms_message("No changes found on server, skipping sync"); linphone_carddav_server_to_client_sync_done(cdc, TRUE, "Synchronization skipped because cTag already up to date"); } } static int parse_ctag_value_from_xml_response(const char *body) { int result = -1; xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { char *response = NULL; if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; linphone_xml_xpath_context_init_carddav_ns(xml_ctx); response = linphone_get_xml_text_content(xml_ctx, "/d:multistatus/d:response/d:propstat/d:prop/x1:getctag"); if (response) { result = atoi(response); linphone_free_xml_text_content(response); } } end: linphone_xmlparsing_context_destroy(xml_ctx); return result; } static void linphone_carddav_query_free(LinphoneCardDavQuery *query) { if (!query) { return; } if (query->http_request_listener) { belle_sip_object_unref(query->http_request_listener); query->http_request_listener = NULL; } // Context will be freed later (in sync_done) query->context = NULL; if (query->url) { ms_free(query->url); } if (query->body) { ms_free(query->body); } ms_free(query); } static bool_t is_query_client_to_server_sync(LinphoneCardDavQuery *query) { if (!query) { ms_error("[carddav] query is NULL..."); return FALSE; } switch(query->type) { case LinphoneCardDavQueryTypePropfind: case LinphoneCardDavQueryTypeAddressbookQuery: case LinphoneCardDavQueryTypeAddressbookMultiget: return FALSE; case LinphoneCardDavQueryTypePut: case LinphoneCardDavQueryTypeDelete: return TRUE; default: ms_error("[carddav] Unknown request: %i", query->type); } return FALSE; } static void process_response_from_carddav_request(void *data, const belle_http_response_event_t *event) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)data; if (event->response) { int code = belle_http_response_get_status_code(event->response); if (code == 207 || code == 200 || code == 201 || code == 204) { const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response); switch(query->type) { case LinphoneCardDavQueryTypePropfind: linphone_carddav_ctag_fetched(query->context, parse_ctag_value_from_xml_response(body)); break; case LinphoneCardDavQueryTypeAddressbookQuery: linphone_carddav_vcards_fetched(query->context, parse_vcards_etags_from_xml_response(body)); break; case LinphoneCardDavQueryTypeAddressbookMultiget: linphone_carddav_vcards_pulled(query->context, parse_vcards_from_xml_response(body)); break; case LinphoneCardDavQueryTypePut: { belle_sip_header_t *header = belle_sip_message_get_header((belle_sip_message_t *)event->response, "ETag"); LinphoneFriend *lf = (LinphoneFriend *)query->user_data; LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lf && lvc) { if (header) { const char *etag = belle_sip_header_get_unparsed_value(header); if (!linphone_vcard_get_etag(lvc)) { ms_debug("eTag for newly created vCard is: %s", etag); } else { ms_debug("eTag for updated vCard is: %s", etag); } linphone_vcard_set_etag(lvc, etag); linphone_carddav_client_to_server_sync_done(query->context, TRUE, NULL); linphone_friend_unref(lf); } else { // For some reason, server didn't return the eTag of the updated/created vCard // We need to do a GET on the vCard to get the correct one bctbx_list_t *vcard = NULL; LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)ms_new0(LinphoneCardDavResponse, 1); response->url = ms_strdup(linphone_vcard_get_url(lvc)); vcard = bctbx_list_append(vcard, response); linphone_carddav_pull_vcards(query->context, vcard); bctbx_list_free_with_data(vcard, (void (*)(void *))linphone_carddav_response_free); } } else { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "No LinphoneFriend found in user_data field of query"); } } break; case LinphoneCardDavQueryTypeDelete: linphone_carddav_client_to_server_sync_done(query->context, TRUE, NULL); break; default: ms_error("[carddav] Unknown request: %i", query->type); break; } } else { char msg[100]; snprintf(msg, sizeof(msg), "Unexpected HTTP response code: %i", code); if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, msg); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, msg); } } } else { if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "No response found"); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, "No response found"); } } linphone_carddav_query_free(query); } static void process_io_error_from_carddav_request(void *data, const belle_sip_io_error_event_t *event) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)data; ms_error("[carddav] I/O error during CardDAV request sending"); if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "I/O error during CardDAV request sending"); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, "I/O error during CardDAV request sending"); } linphone_carddav_query_free(query); } static void process_auth_requested_from_carddav_request(void *data, belle_sip_auth_event_t *event) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)data; LinphoneCardDavContext *cdc = query->context; const char *realm = belle_sip_auth_event_get_realm(event); belle_generic_uri_t *uri = belle_generic_uri_parse(query->url); const char *domain = belle_generic_uri_get_host(uri); if (cdc->auth_info) { belle_sip_auth_event_set_username(event, cdc->auth_info->username); belle_sip_auth_event_set_passwd(event, cdc->auth_info->passwd); belle_sip_auth_event_set_ha1(event, cdc->auth_info->ha1); } else { LinphoneCore *lc = cdc->friend_list->lc; const bctbx_list_t *auth_infos = linphone_core_get_auth_info_list(lc); ms_debug("Looking for auth info for domain %s and realm %s", domain, realm); while (auth_infos) { LinphoneAuthInfo *auth_info = (LinphoneAuthInfo *)auth_infos->data; if (auth_info->domain && strcmp(domain, auth_info->domain) == 0) { if (!auth_info->realm || strcmp(realm, auth_info->realm) == 0) { belle_sip_auth_event_set_username(event, auth_info->username); belle_sip_auth_event_set_passwd(event, auth_info->passwd); belle_sip_auth_event_set_ha1(event, auth_info->ha1); cdc->auth_info = linphone_auth_info_clone(auth_info); break; } } auth_infos = bctbx_list_next(auth_infos); } if (!auth_infos) { ms_error("[carddav] Authentication requested during CardDAV request sending, and username/password weren't provided"); if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "Authentication requested during CardDAV request sending, and username/password weren't provided"); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, "Authentication requested during CardDAV request sending, and username/password weren't provided"); } linphone_carddav_query_free(query); } } } static void linphone_carddav_send_query(LinphoneCardDavQuery *query) { belle_http_request_listener_callbacks_t cbs = { 0 }; belle_generic_uri_t *uri = NULL; belle_http_request_t *req = NULL; belle_sip_memory_body_handler_t *bh = NULL; LinphoneCardDavContext *cdc = query->context; char* ua = NULL; uri = belle_generic_uri_parse(query->url); if (!uri) { if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, "Could not send request, URL is invalid"); } belle_sip_error("Could not send request, URL %s is invalid", query->url); linphone_carddav_query_free(query); return; } req = belle_http_request_create(query->method, uri, belle_sip_header_content_type_create("application", "xml; charset=utf-8"), NULL); if (!req) { if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, "Could not create belle_http_request_t"); } belle_sip_object_unref(uri); belle_sip_error("Could not create belle_http_request_t"); linphone_carddav_query_free(query); return; } ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent(cdc->friend_list->lc), linphone_core_get_version()); belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("User-Agent", ua)); ms_free(ua); if (query->depth) { belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("Depth", query->depth)); } else if (query->ifmatch) { belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("If-Match", query->ifmatch)); } else if (strcmp(query->method, "PUT")) { belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("If-None-Match", "*")); } if (query->body) { bh = belle_sip_memory_body_handler_new_copy_from_buffer(query->body, strlen(query->body), NULL, NULL); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), bh ? BELLE_SIP_BODY_HANDLER(bh) : NULL); } cbs.process_response = process_response_from_carddav_request; cbs.process_io_error = process_io_error_from_carddav_request; cbs.process_auth_requested = process_auth_requested_from_carddav_request; query->http_request_listener = belle_http_request_listener_create_from_callbacks(&cbs, query); belle_http_provider_send_request(query->context->friend_list->lc->http_provider, req, query->http_request_listener); } static LinphoneCardDavQuery* linphone_carddav_create_put_query(LinphoneCardDavContext *cdc, LinphoneVcard *lvc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = NULL; query->ifmatch = linphone_vcard_get_etag(lvc); query->body = ms_strdup(linphone_vcard_as_vcard4_string(lvc)); query->method = "PUT"; query->url = ms_strdup(linphone_vcard_get_url(lvc)); query->type = LinphoneCardDavQueryTypePut; return query; } static char* generate_url_from_server_address_and_uid(const char *server_url) { char *result = NULL; if (server_url) { char *uuid = ms_malloc(64); if (sal_generate_uuid(uuid, 64) == 0) { char *url = ms_malloc(300); snprintf(url, 300, "%s/linphone-%s.vcf", server_url, uuid); ms_debug("Generated url is %s", url); result = ms_strdup(url); ms_free(url); } ms_free(uuid); } return result; } void linphone_carddav_put_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lvc) { LinphoneCardDavQuery *query = NULL; if (!linphone_vcard_get_uid(lvc)) { linphone_vcard_generate_unique_id(lvc); } if (!linphone_vcard_get_url(lvc)) { char *url = generate_url_from_server_address_and_uid(cdc->friend_list->uri); if (url) { linphone_vcard_set_url(lvc, url); ms_free(url); } else { const char *msg = "vCard doesn't have an URL, and friendlist doesn't have a CardDAV server set either, can't push it"; ms_warning("%s", msg); if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } return; } } query = linphone_carddav_create_put_query(cdc, lvc); query->user_data = linphone_friend_ref(lf); linphone_carddav_send_query(query); } else { const char *msg = NULL; if (!lvc) { msg = "LinphoneVcard is NULL"; } else { msg = "Unknown error"; } if (msg) { ms_error("[carddav] %s", msg); } if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } } } static LinphoneCardDavQuery* linphone_carddav_create_delete_query(LinphoneCardDavContext *cdc, LinphoneVcard *lvc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = NULL; query->ifmatch = linphone_vcard_get_etag(lvc); query->body = NULL; query->method = "DELETE"; query->url = ms_strdup(linphone_vcard_get_url(lvc)); query->type = LinphoneCardDavQueryTypeDelete; return query; } void linphone_carddav_delete_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lvc && linphone_vcard_get_uid(lvc) && linphone_vcard_get_etag(lvc)) { LinphoneCardDavQuery *query = NULL; if (!linphone_vcard_get_url(lvc)) { char *url = generate_url_from_server_address_and_uid(cdc->friend_list->uri); if (url) { linphone_vcard_set_url(lvc, url); ms_free(url); } else { const char *msg = "vCard doesn't have an URL, and friendlist doesn't have a CardDAV server set either, can't delete it"; ms_warning("%s", msg); if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } return; } } query = linphone_carddav_create_delete_query(cdc, lvc); linphone_carddav_send_query(query); } else { const char *msg = NULL; if (!lvc) { msg = "LinphoneVcard is NULL"; } else if (!linphone_vcard_get_uid(lvc)) { msg = "LinphoneVcard doesn't have an UID"; } else if (!linphone_vcard_get_etag(lvc)) { msg = "LinphoneVcard doesn't have an eTag"; } if (msg) { ms_error("[carddav] %s", msg); } if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } } } void linphone_carddav_set_synchronization_done_callback(LinphoneCardDavContext *cdc, LinphoneCardDavSynchronizationDoneCb cb) { cdc->sync_done_cb = cb; } void linphone_carddav_set_new_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactCreatedCb cb) { cdc->contact_created_cb = cb; } void linphone_carddav_set_updated_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactUpdatedCb cb) { cdc->contact_updated_cb = cb; } void linphone_carddav_set_removed_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactRemovedCb cb) { cdc->contact_removed_cb = cb; } static LinphoneCardDavQuery* linphone_carddav_create_propfind_query(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = "0"; query->ifmatch = NULL; query->body = ms_strdup(""); query->method = "PROPFIND"; query->url = ms_strdup(cdc->friend_list->uri); query->type = LinphoneCardDavQueryTypePropfind; return query; } void linphone_carddav_get_current_ctag(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = linphone_carddav_create_propfind_query(cdc); linphone_carddav_send_query(query); } static LinphoneCardDavQuery* linphone_carddav_create_addressbook_query(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = "1"; query->ifmatch = NULL; query->body = ms_strdup(""); query->method = "REPORT"; query->url = ms_strdup(cdc->friend_list->uri); query->type = LinphoneCardDavQueryTypeAddressbookQuery; return query; } void linphone_carddav_fetch_vcards(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = linphone_carddav_create_addressbook_query(cdc); linphone_carddav_send_query(query); } static LinphoneCardDavQuery* linphone_carddav_create_addressbook_multiget_query(LinphoneCardDavContext *cdc, bctbx_list_t *vcards) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); char *body = (char *)ms_malloc((bctbx_list_size(vcards) + 1) * 300 * sizeof(char)); bctbx_list_t *iterator = vcards; query->context = cdc; query->depth = "1"; query->ifmatch = NULL; query->method = "REPORT"; query->url = ms_strdup(cdc->friend_list->uri); query->type = LinphoneCardDavQueryTypeAddressbookMultiget; sprintf(body, "%s", ""); while (iterator) { LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)iterator->data; if (response) { char temp_body[300]; snprintf(temp_body, sizeof(temp_body), "%s", response->url); strcat(body, temp_body); iterator = bctbx_list_next(iterator); } } strcat(body, ""); query->body = ms_strdup(body); ms_free(body); return query; } void linphone_carddav_pull_vcards(LinphoneCardDavContext *cdc, bctbx_list_t *vcards_to_pull) { LinphoneCardDavQuery *query = linphone_carddav_create_addressbook_multiget_query(cdc, vcards_to_pull); linphone_carddav_send_query(query); } linphone-3.12.0/coreapi/carddav.h000066400000000000000000000134611313432737600166260ustar00rootroot00000000000000/* carddav.h Copyright (C) 2015 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LINPHONE_CARDDAV_H #define LINPHONE_CARDDAV_H #include "linphone/core.h" #ifdef __cplusplus extern "C" { #endif /** * @addtogroup carddav_vcard * @{ */ typedef struct _LinphoneCardDavContext LinphoneCardDavContext; typedef enum _LinphoneCardDavQueryType { LinphoneCardDavQueryTypePropfind, LinphoneCardDavQueryTypeAddressbookQuery, LinphoneCardDavQueryTypeAddressbookMultiget, LinphoneCardDavQueryTypePut, LinphoneCardDavQueryTypeDelete } LinphoneCardDavQueryType; typedef struct _LinphoneCardDavQuery LinphoneCardDavQuery; typedef struct _LinphoneCardDavResponse LinphoneCardDavResponse; /** * Callback used to notify a new contact has been created on the CardDAV server **/ typedef void (*LinphoneCardDavContactCreatedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Callback used to notify a contact has been updated on the CardDAV server **/ typedef void (*LinphoneCardDavContactUpdatedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *new_friend, LinphoneFriend *old_friend); /** * Callback used to notify a contact has been removed on the CardDAV server **/ typedef void (*LinphoneCardDavContactRemovedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Callback used to notify a contact has been removed on the CardDAV server **/ typedef void (*LinphoneCardDavSynchronizationDoneCb)(LinphoneCardDavContext *cdc, bool_t success, const char *message); /** * Creates a CardDAV context for all related operations * @param lfl LinphoneFriendList object * @return LinphoneCardDavContext object if vCard support is enabled and server URL is available, NULL otherwise */ LINPHONE_PUBLIC LinphoneCardDavContext* linphone_carddav_context_new(LinphoneFriendList *lfl); /** * Deletes a LinphoneCardDavContext object * @param cdc LinphoneCardDavContext object */ LINPHONE_PUBLIC void linphone_carddav_context_destroy(LinphoneCardDavContext *cdc); /** * Sets a user pointer to the LinphoneCardDAVContext object * @param cdc LinphoneCardDavContext object * @param ud The user data pointer */ LINPHONE_PUBLIC void linphone_carddav_set_user_data(LinphoneCardDavContext *cdc, void *ud); /** * Gets the user pointer set in the LinphoneCardDAVContext object * @param cdc LinphoneCardDavContext object * @return The user data pointer if set, NULL otherwise */ LINPHONE_PUBLIC void* linphone_carddav_get_user_data(LinphoneCardDavContext *cdc); /** * Starts a synchronization with the remote server to update local friends with server changes * @param cdc LinphoneCardDavContext object */ LINPHONE_PUBLIC void linphone_carddav_synchronize(LinphoneCardDavContext *cdc); /** * Sends a LinphoneFriend to the CardDAV server for update or creation * @param cdc LinphoneCardDavContext object * @param lf a LinphoneFriend object to update/create on the server */ LINPHONE_PUBLIC void linphone_carddav_put_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Deletes a LinphoneFriend on the CardDAV server * @param cdc LinphoneCardDavContext object * @param lf a LinphoneFriend object to delete on the server */ LINPHONE_PUBLIC void linphone_carddav_delete_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Set the synchronization done callback. * @param cdc LinphoneCardDavContext object * @param cb The synchronization done callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_synchronization_done_callback(LinphoneCardDavContext *cdc, LinphoneCardDavSynchronizationDoneCb cb); /** * Set the new contact callback. * @param cdc LinphoneCardDavContext object * @param cb The new contact callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_new_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactCreatedCb cb); /** * Set the updated contact callback. * @param cdc LinphoneCardDavContext object * @param cb The updated contact callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_updated_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactUpdatedCb cb); /** * Set the removed contact callback. * @param cdc LinphoneCardDavContext object * @param cb The removed contact callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_removed_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactRemovedCb cb); /** * Retrieves the current cTag value for the remote server * @param cdc LinphoneCardDavContext object */ void linphone_carddav_get_current_ctag(LinphoneCardDavContext *cdc); /** * Retrieves a list of all the vCards on server side to be able to detect changes * @param cdc LinphoneCardDavContext object */ void linphone_carddav_fetch_vcards(LinphoneCardDavContext *cdc); /** * Download asked vCards from the server * @param cdc LinphoneCardDavContext object * @param vcards_to_pull a MSList of LinphoneCardDavResponse objects with at least the url field filled */ void linphone_carddav_pull_vcards(LinphoneCardDavContext *cdc, MSList *vcards_to_pull); /** * @} */ #ifdef __cplusplus } #endif #endif linphone-3.12.0/coreapi/chat.c000066400000000000000000001775721313432737600161520ustar00rootroot00000000000000/*************************************************************************** * chat.c * * Sun Jun 5 19:34:18 2005 * Copyright 2005 Simon Morlat * Email simon dot morlat at linphone dot org ****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "private.h" #include "linphone/lpconfig.h" #include "belle-sip/belle-sip.h" #include "ortp/b64.h" #include "linphone/wrapper_utils.h" #include #include #include #define COMPOSING_DEFAULT_IDLE_TIMEOUT 15 #define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60 #define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120 static void linphone_chat_message_release(LinphoneChatMessage *msg); static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr); static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr); static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr); static void _linphone_chat_message_destroy(LinphoneChatMessage *msg); static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text); static void linphone_chat_room_notify_imdn(LinphoneChatRoom *cr, const char *text); static void linphone_chat_message_deactivate(LinphoneChatMessage *msg); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessageCbs); BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatMessageCbs, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // marshal FALSE); LinphoneChatMessageCbs *linphone_chat_message_cbs_new(void) { return belle_sip_object_new(LinphoneChatMessageCbs); } LinphoneChatMessageCbs *linphone_chat_message_cbs_ref(LinphoneChatMessageCbs *cbs) { belle_sip_object_ref(cbs); return cbs; } void linphone_chat_message_cbs_unref(LinphoneChatMessageCbs *cbs) { belle_sip_object_unref(cbs); } void *linphone_chat_message_cbs_get_user_data(const LinphoneChatMessageCbs *cbs) { return cbs->user_data; } void linphone_chat_message_cbs_set_user_data(LinphoneChatMessageCbs *cbs, void *ud) { cbs->user_data = ud; } LinphoneChatMessageCbsMsgStateChangedCb linphone_chat_message_cbs_get_msg_state_changed(const LinphoneChatMessageCbs *cbs) { return cbs->msg_state_changed; } void linphone_chat_message_cbs_set_msg_state_changed(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsMsgStateChangedCb cb) { cbs->msg_state_changed = cb; } LinphoneChatMessageCbsFileTransferRecvCb linphone_chat_message_cbs_get_file_transfer_recv(const LinphoneChatMessageCbs *cbs) { return cbs->file_transfer_recv; } void linphone_chat_message_cbs_set_file_transfer_recv(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferRecvCb cb) { cbs->file_transfer_recv = cb; } LinphoneChatMessageCbsFileTransferSendCb linphone_chat_message_cbs_get_file_transfer_send(const LinphoneChatMessageCbs *cbs) { return cbs->file_transfer_send; } void linphone_chat_message_cbs_set_file_transfer_send(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferSendCb cb) { cbs->file_transfer_send = cb; } LinphoneChatMessageCbsFileTransferProgressIndicationCb linphone_chat_message_cbs_get_file_transfer_progress_indication(const LinphoneChatMessageCbs *cbs) { return cbs->file_transfer_progress_indication; } void linphone_chat_message_cbs_set_file_transfer_progress_indication( LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferProgressIndicationCb cb) { cbs->file_transfer_progress_indication = cb; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessage); static void _linphone_chat_room_destroy(LinphoneChatRoom *cr) { bctbx_list_free_with_data(cr->transient_messages, (void (*)(void *))linphone_chat_message_release); if (cr->received_rtt_characters) { cr->received_rtt_characters = bctbx_list_free_with_data(cr->received_rtt_characters, (void (*)(void *))ms_free); } linphone_chat_room_delete_composing_idle_timer(cr); linphone_chat_room_delete_composing_refresh_timer(cr); linphone_chat_room_delete_remote_composing_refresh_timer(cr); if (cr->lc != NULL) { if (bctbx_list_find(cr->lc->chatrooms, cr)) { ms_error("LinphoneChatRoom[%p] is destroyed while still being used by the LinphoneCore. This is abnormal." " linphone_core_get_chat_room() doesn't give a reference, there is no need to call " "linphone_chat_room_unref(). " "In order to remove a chat room from the core, use linphone_core_delete_chat_room().", cr); cr->lc->chatrooms = bctbx_list_remove(cr->lc->chatrooms, cr); } } linphone_address_unref(cr->peer_url); if (cr->pending_message) linphone_chat_message_destroy(cr->pending_message); ms_free(cr->peer); if (cr->weak_messages != NULL) bctbx_list_free(cr->weak_messages); } void linphone_chat_message_set_state(LinphoneChatMessage *msg, LinphoneChatMessageState state) { /* do not invoke callbacks on orphan messages */ if (state != msg->state && msg->chat_room != NULL) { if (((msg->state == LinphoneChatMessageStateDisplayed) || (msg->state == LinphoneChatMessageStateDeliveredToUser)) && ((state == LinphoneChatMessageStateDeliveredToUser) || (state == LinphoneChatMessageStateDelivered) || (state == LinphoneChatMessageStateNotDelivered))) { /* If the message has been displayed or delivered to user we must not go back to the delivered or not delivered state. */ return; } ms_message("Chat message %p: moving from state %s to %s", msg, linphone_chat_message_state_to_string(msg->state), linphone_chat_message_state_to_string(state)); msg->state = state; if (msg->message_state_changed_cb) { msg->message_state_changed_cb(msg, msg->state, msg->message_state_changed_user_data); } if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) { linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, msg->state); } } } BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatMessage, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_chat_message_destroy, NULL, // clone NULL, // marshal FALSE); void linphone_core_disable_chat(LinphoneCore *lc, LinphoneReason deny_reason) { lc->chat_deny_code = deny_reason; } void linphone_core_enable_chat(LinphoneCore *lc) { lc->chat_deny_code = LinphoneReasonNone; } bool_t linphone_core_chat_enabled(const LinphoneCore *lc) { return lc->chat_deny_code != LinphoneReasonNone; } const bctbx_list_t *linphone_core_get_chat_rooms(LinphoneCore *lc) { return lc->chatrooms; } static bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from) { return linphone_address_weak_equal(cr->peer_url, from); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatRoom); BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatRoom, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_chat_room_destroy, NULL, // clone NULL, // marshal FALSE); static LinphoneChatRoom *_linphone_core_create_chat_room_base(LinphoneCore *lc, LinphoneAddress *addr){ LinphoneChatRoom *cr = belle_sip_object_new(LinphoneChatRoom); cr->lc = lc; cr->peer = linphone_address_as_string(addr); cr->peer_url = addr; cr->unread_count = -1; cr->received_rtt_characters = NULL; return cr; } static LinphoneChatRoom *_linphone_core_create_chat_room(LinphoneCore *lc, LinphoneAddress *addr) { LinphoneChatRoom *cr = _linphone_core_create_chat_room_base(lc, addr); lc->chatrooms = bctbx_list_append(lc->chatrooms, (void *)cr); return cr; } LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call){ LinphoneChatRoom *cr = _linphone_core_create_chat_room_base(call->core, linphone_address_clone(linphone_call_get_remote_address(call))); cr->call = call; return cr; } static LinphoneChatRoom *_linphone_core_create_chat_room_from_url(LinphoneCore *lc, const char *to) { LinphoneAddress *parsed_url = NULL; if ((parsed_url = linphone_core_interpret_url(lc, to)) != NULL) { return _linphone_core_create_chat_room(lc, parsed_url); } return NULL; } LinphoneChatRoom *_linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr) { LinphoneChatRoom *cr = NULL; bctbx_list_t *elem; for (elem = lc->chatrooms; elem != NULL; elem = bctbx_list_next(elem)) { cr = (LinphoneChatRoom *)elem->data; if (linphone_chat_room_matches(cr, addr)) { break; } cr = NULL; } return cr; } static LinphoneChatRoom *_linphone_core_get_or_create_chat_room(LinphoneCore *lc, const char *to) { LinphoneAddress *to_addr = linphone_core_interpret_url(lc, to); LinphoneChatRoom *ret; if (to_addr == NULL) { ms_error("linphone_core_get_or_create_chat_room(): Cannot make a valid address with %s", to); return NULL; } ret = _linphone_core_get_chat_room(lc, to_addr); linphone_address_unref(to_addr); if (!ret) { ret = _linphone_core_create_chat_room_from_url(lc, to); } return ret; } LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr) { LinphoneChatRoom *ret = _linphone_core_get_chat_room(lc, addr); if (!ret) { ret = _linphone_core_create_chat_room(lc, linphone_address_clone(addr)); } return ret; } void linphone_core_delete_chat_room(LinphoneCore *lc, LinphoneChatRoom *cr) { if (bctbx_list_find(lc->chatrooms, cr)) { lc->chatrooms = bctbx_list_remove(cr->lc->chatrooms, cr); linphone_chat_room_delete_history(cr); linphone_chat_room_unref(cr); } else { ms_error("linphone_core_delete_chat_room(): chatroom [%p] isn't part of LinphoneCore.", cr); } } LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) { return _linphone_core_get_or_create_chat_room(lc, to); } static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) { if (cr->composing_idle_timer) { if (cr->lc && cr->lc->sal) sal_cancel_timer(cr->lc->sal, cr->composing_idle_timer); belle_sip_object_unref(cr->composing_idle_timer); cr->composing_idle_timer = NULL; } } static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr) { if (cr->composing_refresh_timer) { if (cr->lc && cr->lc->sal) sal_cancel_timer(cr->lc->sal, cr->composing_refresh_timer); belle_sip_object_unref(cr->composing_refresh_timer); cr->composing_refresh_timer = NULL; } } static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr) { if (cr->remote_composing_refresh_timer) { if (cr->lc && cr->lc->sal) sal_cancel_timer(cr->lc->sal, cr->remote_composing_refresh_timer); belle_sip_object_unref(cr->remote_composing_refresh_timer); cr->remote_composing_refresh_timer = NULL; } } void linphone_chat_room_destroy(LinphoneChatRoom *cr) { linphone_chat_room_unref(cr); } void linphone_chat_room_release(LinphoneChatRoom *cr) { linphone_chat_room_delete_composing_idle_timer(cr); linphone_chat_room_delete_composing_refresh_timer(cr); linphone_chat_room_delete_remote_composing_refresh_timer(cr); bctbx_list_for_each(cr->weak_messages, (bctbx_list_iterate_func)linphone_chat_message_deactivate); cr->lc = NULL; linphone_chat_room_unref(cr); } static void on_weak_message_destroy(void *obj, belle_sip_object_t *message_being_destroyed) { LinphoneChatRoom *cr = (LinphoneChatRoom *)obj; cr->weak_messages = bctbx_list_remove(cr->weak_messages, message_being_destroyed); } void linphone_chat_room_add_weak_message(LinphoneChatRoom *cr, LinphoneChatMessage *cm) { bctbx_list_t *item = bctbx_list_find(cr->weak_messages, cm); if (item == NULL) { cr->weak_messages = bctbx_list_append(cr->weak_messages, belle_sip_object_weak_ref(cm, on_weak_message_destroy, cr)); } } LinphoneChatRoom *linphone_chat_room_ref(LinphoneChatRoom *cr) { belle_sip_object_ref(cr); return cr; } void linphone_chat_room_unref(LinphoneChatRoom *cr) { belle_sip_object_unref(cr); } void *linphone_chat_room_get_user_data(const LinphoneChatRoom *cr) { return cr->user_data; } void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void *ud) { cr->user_data = ud; } void linphone_chat_room_add_transient_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { if (bctbx_list_find(msg->chat_room->transient_messages, msg) == NULL) { cr->transient_messages = bctbx_list_append(cr->transient_messages, linphone_chat_message_ref(msg)); } } void linphone_chat_room_remove_transient_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { if (bctbx_list_find(msg->chat_room->transient_messages, msg) != NULL) { cr->transient_messages = bctbx_list_remove(cr->transient_messages, msg); linphone_chat_message_unref(msg); } } static void store_or_update_chat_message(LinphoneChatMessage *msg) { if (msg->storage_id != 0) { /* The message has already been stored (probably because of file transfer), update it */ linphone_chat_message_store_update(msg); } else { /* Store the new message */ msg->storage_id = linphone_chat_message_store(msg); } } void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { int retval = -1; LinphoneCore *lc = cr->lc; LinphoneImEncryptionEngine *imee = lc->im_encryption_engine; /*stubed rtt text*/ if (cr->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(cr->call))) { uint32_t new_line = 0x2028; linphone_chat_message_put_char(msg, new_line); // New Line linphone_chat_message_unref(msg); return; } msg->dir = LinphoneChatMessageOutgoing; /* Check if we shall upload a file to a server */ if (msg->file_transfer_information != NULL && msg->content_type == NULL) { /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */ if (linphone_chat_room_upload_file(msg) == 0) { /* Add to transient list only if message is going out */ linphone_chat_room_add_transient_message(cr, msg); /* Store the message so that even if the upload is stopped, it can be done again */ msg->storage_id = linphone_chat_message_store(msg); } else { linphone_chat_message_unref(msg); return; } } else { SalOp *op = msg->op; LinphoneCall *call=NULL; char *content_type; const char *identity = NULL; char *clear_text_message = NULL; char *clear_text_content_type = NULL; if (msg->message) { clear_text_message = ms_strdup(msg->message); } if (msg->content_type) { clear_text_content_type = ms_strdup(msg->content_type); } /* Add to transient list */ linphone_chat_room_add_transient_message(cr, msg); msg->time = ms_time(0); if (lp_config_get_int(cr->lc->config, "sip", "chat_use_call_dialogs", 0) != 0) { if ((call = linphone_core_get_call_by_remote_address(cr->lc, cr->peer)) != NULL) { if (call->state == LinphoneCallConnected || call->state == LinphoneCallStreamsRunning || call->state == LinphoneCallPaused || call->state == LinphoneCallPausing || call->state == LinphoneCallPausedByRemote) { ms_message("send SIP msg through the existing call."); op = call->op; identity = linphone_core_find_best_identity(cr->lc, linphone_call_get_remote_address(call)); } } } if (!identity) { LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(cr->lc, cr->peer_url); if (proxy) { identity = linphone_proxy_config_get_identity(proxy); } else { identity = linphone_core_get_primary_contact(cr->lc); } } if (msg->from){ /* * BUG * the file transfer message constructor sets the from, but doesn't do it as well as here. */ linphone_address_unref(msg->from); } msg->from = linphone_address_new(identity); if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsOutgoingMessageCb cb_process_outgoing_message = linphone_im_encryption_engine_cbs_get_process_outgoing_message(imee_cbs); if (cb_process_outgoing_message) { retval = cb_process_outgoing_message(imee, cr, msg); if(retval == 0) { msg->is_secured = TRUE; } } } if (op == NULL) { /*sending out of calls*/ msg->op = op = sal_op_new(cr->lc->sal); linphone_configure_op(cr->lc, op, cr->peer_url, msg->custom_headers, lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0)); sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/ } if (retval > 0) { sal_error_info_set((SalErrorInfo *)sal_op_get_error_info(op), SalReasonNotAcceptable, "SIP", retval, "Unable to encrypt IM", NULL); store_or_update_chat_message(msg); linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered); linphone_chat_message_unref(msg); return; } if (msg->external_body_url) { content_type = ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"", msg->external_body_url); sal_message_send(op, identity, cr->peer, content_type, NULL, NULL); ms_free(content_type); } else { char *peer_uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr)); const char *content_type = msg->content_type; if (content_type == NULL) { sal_text_send(op, identity, cr->peer, msg->message); } else { sal_message_send(op, identity, cr->peer, content_type, msg->message, peer_uri); } ms_free(peer_uri); } if (msg->message && clear_text_message && strcmp(msg->message, clear_text_message) != 0) { // We replace the encrypted message by the original one so it can be correctly stored and displayed by the application ms_free(msg->message); msg->message = ms_strdup(clear_text_message); } if (msg->content_type && clear_text_content_type && (strcmp(msg->content_type, clear_text_content_type) != 0)) { /* We replace the encrypted content type by the original one */ ms_free(msg->content_type); msg->content_type = ms_strdup(clear_text_content_type); } msg->message_id = ms_strdup(sal_op_get_call_id(op)); /* must be known at that time */ store_or_update_chat_message(msg); if (cr->is_composing == LinphoneIsComposingActive) { cr->is_composing = LinphoneIsComposingIdle; } linphone_chat_room_delete_composing_idle_timer(cr); linphone_chat_room_delete_composing_refresh_timer(cr); if (clear_text_message) { ms_free(clear_text_message); } if (clear_text_content_type) { ms_free(clear_text_content_type); } if (call && call->op == op) { /*In this case, chat delivery status is not notified, so unrefing chat message right now*/ /*Might be better fixed by delivering status, but too costly for now*/ linphone_chat_room_remove_transient_message(msg->chat_room, msg); linphone_chat_message_unref(msg); return; } } // if operation failed, we should not change message state if (msg->dir == LinphoneChatMessageOutgoing) { linphone_chat_message_set_state(msg, LinphoneChatMessageStateInProgress); } } void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state) { linphone_chat_message_set_state(msg, new_state); linphone_chat_message_store_state(msg); if (msg->state == LinphoneChatMessageStateDelivered || msg->state == LinphoneChatMessageStateNotDelivered) { if (bctbx_list_find(msg->chat_room->transient_messages, msg) != NULL) { // msg is not transient anymore, we can remove it from our transient list and unref it linphone_chat_room_add_weak_message(msg->chat_room, msg); linphone_chat_room_remove_transient_message(msg->chat_room, msg); } else { // msg has already been removed from the transient messages, do nothing. */ } } } void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg) { _linphone_chat_room_send_message(cr, linphone_chat_room_create_message(cr, msg)); } static bool_t is_file_transfer(const char *content_type) { return (strcmp("application/vnd.gsma.rcs-ft-http+xml", content_type) == 0); } static bool_t is_im_iscomposing(const char* content_type) { return (strcmp("application/im-iscomposing+xml", content_type) == 0); } static bool_t is_imdn(const char *content_type) { return (strcmp("message/imdn+xml", content_type) == 0); } static bool_t is_text(const char *content_type) { return (strcmp("text/plain", content_type) == 0); } void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, LinphoneChatMessage *msg) { if (msg->message) { /*legacy API*/ linphone_core_notify_text_message_received(lc, cr, msg->from, msg->message); } linphone_core_notify_message_received(lc, cr, msg); if(!is_imdn(msg->content_type) && !is_im_iscomposing(msg->content_type)) { cr->remote_is_composing = LinphoneIsComposingIdle; linphone_core_notify_is_composing_received(cr->lc, cr); linphone_chat_message_send_delivery_notification(msg, LinphoneReasonNone); } } static void create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(LinphoneChatMessage *msg) { xmlChar *file_url = NULL; xmlDocPtr xmlMessageBody; xmlNodePtr cur; /* parse the msg body to get all informations from it */ xmlMessageBody = xmlParseDoc((const xmlChar *)msg->message); msg->file_transfer_information = linphone_content_new(); cur = xmlDocGetRootElement(xmlMessageBody); if (cur != NULL) { cur = cur->xmlChildrenNode; while (cur != NULL) { if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check if it has a type="file" attribute */ xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type"); if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */ cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */ while (cur != NULL) { if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) { xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1); linphone_content_set_size(msg->file_transfer_information, strtol((const char *)fileSizeString, NULL, 10)); xmlFree(fileSizeString); } if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) { xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1); linphone_content_set_name(msg->file_transfer_information, (char *)filename); xmlFree(filename); } if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) { xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1); int contentTypeIndex = 0; char *type; char *subtype; while (contentType[contentTypeIndex] != '/' && contentType[contentTypeIndex] != '\0') { contentTypeIndex++; } type = ms_strndup((char *)contentType, contentTypeIndex); subtype = ms_strdup(((char *)contentType + contentTypeIndex + 1)); linphone_content_set_type(msg->file_transfer_information, type); linphone_content_set_subtype(msg->file_transfer_information, subtype); ms_free(subtype); ms_free(type); xmlFree(contentType); } if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) { file_url = xmlGetProp(cur, (const xmlChar *)"url"); } if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) { /* there is a key in the msg: file has been encrypted */ /* convert the key from base 64 */ xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1); size_t keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0); uint8_t *keyBuffer = (uint8_t *)malloc(keyLength); /* decode the key into local key buffer */ b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength); linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength); /* duplicate key value into the linphone content private structure */ xmlFree(keyb64); free(keyBuffer); } cur = cur->next; } xmlFree(typeAttribute); break; } xmlFree(typeAttribute); } cur = cur->next; } } xmlFreeDoc(xmlMessageBody); linphone_chat_message_set_external_body_url(msg, (const char *)file_url); xmlFree(file_url); } LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) { LinphoneChatRoom *cr = NULL; LinphoneAddress *addr; LinphoneAddress *to; LinphoneChatMessage *msg = NULL; LinphoneImEncryptionEngine *imee = lc->im_encryption_engine; const SalCustomHeader *ch; LinphoneReason reason = LinphoneReasonNone; int retval = -1; bool_t increase_msg_count = TRUE; addr = linphone_address_new(sal_msg->from); linphone_address_clean(addr); cr = linphone_core_get_chat_room(lc, addr); /* Check if this is a duplicate message */ if ((msg = linphone_chat_room_find_message_with_dir(cr, sal_op_get_call_id(op), LinphoneChatMessageIncoming))) { reason = lc->chat_deny_code; goto end; } msg = linphone_chat_room_create_message(cr, sal_msg->text); linphone_chat_message_set_content_type(msg, sal_msg->content_type); linphone_chat_message_set_from(msg, cr->peer_url); to = sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op)) : linphone_address_new(linphone_core_get_identity(lc)); msg->to = to; msg->time = sal_msg->time; msg->state = LinphoneChatMessageStateDelivered; msg->dir = LinphoneChatMessageIncoming; msg->message_id = ms_strdup(sal_op_get_call_id(op)); ch = sal_op_get_recv_custom_header(op); if (ch) { msg->custom_headers = sal_custom_header_clone(ch); } if (sal_msg->url) { linphone_chat_message_set_external_body_url(msg, sal_msg->url); } if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsIncomingMessageCb cb_process_incoming_message = linphone_im_encryption_engine_cbs_get_process_incoming_message(imee_cbs); if (cb_process_incoming_message) { retval = cb_process_incoming_message(imee, cr, msg); if(retval == 0) { msg->is_secured = TRUE; } else if(retval > 0) { // Unable to decrypt message linphone_core_notify_message_received_unable_decrypt(cr->lc, cr, msg); reason = linphone_error_code_to_reason(retval); linphone_chat_message_send_delivery_notification(msg, reason); // return LinphoneReasonNone to avoid flexisip resending us a message we can't decrypt reason = LinphoneReasonNone; goto end; } } } if ((retval <= 0) && (linphone_core_is_content_type_supported(lc, msg->content_type) == FALSE)) { retval = 415; ms_error("Unsupported MESSAGE (content-type %s not recognized)", msg->content_type); } if (retval > 0) { reason = linphone_error_code_to_reason(retval); linphone_chat_message_send_delivery_notification(msg, reason); goto end; } if (is_file_transfer(msg->content_type)) { create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(msg); linphone_chat_message_set_to_be_stored(msg, TRUE); } else if (is_im_iscomposing(msg->content_type)) { linphone_chat_room_notify_is_composing(cr, msg->message); linphone_chat_message_set_to_be_stored(msg, FALSE); increase_msg_count = FALSE; if(lp_config_get_int(cr->lc->config, "sip", "deliver_imdn", 0) != 1) { goto end; } } else if (is_imdn(msg->content_type)) { linphone_chat_room_notify_imdn(cr, msg->message); linphone_chat_message_set_to_be_stored(msg, FALSE); increase_msg_count = FALSE; if(lp_config_get_int(cr->lc->config, "sip", "deliver_imdn", 0) != 1) { goto end; } } else if (is_text(msg->content_type)) { linphone_chat_message_set_to_be_stored(msg, TRUE); } if (increase_msg_count == TRUE) { if (cr->unread_count < 0) cr->unread_count = 1; else cr->unread_count++; /* Mark the message as pending so that if linphone_core_chat_room_mark_as_read() is called in the linphone_chat_room_message_received() callback, it will effectively be marked as being read before being stored. */ cr->pending_message = msg; } linphone_chat_room_message_received(cr, lc, msg); if(linphone_chat_message_get_to_be_stored(msg)) { msg->storage_id = linphone_chat_message_store(msg); } cr->pending_message = NULL; end: linphone_address_unref(addr); if (msg != NULL) linphone_chat_message_unref(msg); return reason; } static int linphone_chat_room_remote_refresh_composing_expired(void *data, unsigned int revents) { LinphoneChatRoom *cr = (LinphoneChatRoom *)data; belle_sip_object_unref(cr->remote_composing_refresh_timer); cr->remote_composing_refresh_timer = NULL; cr->remote_is_composing = LinphoneIsComposingIdle; linphone_core_notify_is_composing_received(cr->lc, cr); return BELLE_SIP_STOP; } static const char *iscomposing_prefix = "/xsi:isComposing"; static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsing_context_t *xml_ctx) { char xpath_str[MAX_XPATH_LENGTH]; xmlXPathObjectPtr iscomposing_object; const char *state_str = NULL; const char *refresh_str = NULL; int refresh_duration = lp_config_get_int(cr->lc->config, "sip", "composing_remote_refresh_timeout", COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT); int i; LinphoneIsComposingState state = LinphoneIsComposingIdle; if (linphone_create_xml_xpath_context(xml_ctx) < 0) return; xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"xsi", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing"); iscomposing_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, iscomposing_prefix); if (iscomposing_object != NULL) { if (iscomposing_object->nodesetval != NULL) { for (i = 1; i <= iscomposing_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:state", iscomposing_prefix, i); state_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (state_str == NULL) continue; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:refresh", iscomposing_prefix, i); refresh_str = linphone_get_xml_text_content(xml_ctx, xpath_str); } } xmlXPathFreeObject(iscomposing_object); } if (state_str != NULL) { if (strcmp(state_str, "active") == 0) { state = LinphoneIsComposingActive; if (refresh_str != NULL) { refresh_duration = atoi(refresh_str); } if (!cr->remote_composing_refresh_timer) { cr->remote_composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_remote_refresh_composing_expired, cr, refresh_duration * 1000, "composing remote refresh timeout"); } else { belle_sip_source_set_timeout(cr->remote_composing_refresh_timer, refresh_duration * 1000); } } else { linphone_chat_room_delete_remote_composing_refresh_timer(cr); } cr->remote_is_composing = state; linphone_core_notify_is_composing_received(cr->lc, cr); linphone_free_xml_text_content(state_str); } if (refresh_str != NULL) { linphone_free_xml_text_content(refresh_str); } } static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text) { xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char *)text, 0, NULL, 0); if (xml_ctx->doc != NULL) { process_im_is_composing_notification(cr, xml_ctx); } else { ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer); } linphone_xmlparsing_context_destroy(xml_ctx); } bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) { return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE; } static const char *imdn_prefix = "/imdn:imdn"; static void process_imdn(LinphoneChatRoom *cr, xmlparsing_context_t *xml_ctx) { char xpath_str[MAX_XPATH_LENGTH]; xmlXPathObjectPtr imdn_object; xmlXPathObjectPtr delivery_status_object; xmlXPathObjectPtr display_status_object; const char *message_id_str = NULL; const char *datetime_str = NULL; LinphoneCore *lc = linphone_chat_room_get_core(cr); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(lc); if (linphone_create_xml_xpath_context(xml_ctx) < 0) return; xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"imdn", (const xmlChar *)"urn:ietf:params:xml:ns:imdn"); imdn_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, imdn_prefix); if (imdn_object != NULL) { if ((imdn_object->nodesetval != NULL) && (imdn_object->nodesetval->nodeNr >= 1)) { snprintf(xpath_str, sizeof(xpath_str), "%s[1]/imdn:message-id", imdn_prefix); message_id_str = linphone_get_xml_text_content(xml_ctx, xpath_str); snprintf(xpath_str, sizeof(xpath_str), "%s[1]/imdn:datetime", imdn_prefix); datetime_str = linphone_get_xml_text_content(xml_ctx, xpath_str); } xmlXPathFreeObject(imdn_object); } if ((message_id_str != NULL) && (datetime_str != NULL)) { LinphoneChatMessage *cm = linphone_chat_room_find_message_with_dir(cr, message_id_str, LinphoneChatMessageOutgoing); if (cm == NULL) { ms_warning("Received IMDN for unknown message %s", message_id_str); } else { snprintf(xpath_str, sizeof(xpath_str), "%s[1]/imdn:delivery-notification/imdn:status", imdn_prefix); delivery_status_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); snprintf(xpath_str, sizeof(xpath_str), "%s[1]/imdn:display-notification/imdn:status", imdn_prefix); display_status_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); if ((delivery_status_object != NULL) && (linphone_im_notif_policy_get_recv_imdn_delivered(policy) == TRUE)) { if ((delivery_status_object->nodesetval != NULL) && (delivery_status_object->nodesetval->nodeNr >= 1)) { xmlNodePtr node = delivery_status_object->nodesetval->nodeTab[0]; if ((node->children != NULL) && (node->children->name != NULL)) { if (strcmp((const char *)node->children->name, "delivered") == 0) { linphone_chat_message_update_state(cm, LinphoneChatMessageStateDeliveredToUser); } else if (strcmp((const char *)node->children->name, "error") == 0) { linphone_chat_message_update_state(cm, LinphoneChatMessageStateNotDelivered); } } } xmlXPathFreeObject(delivery_status_object); } if ((display_status_object != NULL) && (linphone_im_notif_policy_get_recv_imdn_displayed(policy) == TRUE)) { if ((display_status_object->nodesetval != NULL) && (display_status_object->nodesetval->nodeNr >= 1)) { xmlNodePtr node = display_status_object->nodesetval->nodeTab[0]; if ((node->children != NULL) && (node->children->name != NULL)) { if (strcmp((const char *)node->children->name, "displayed") == 0) { linphone_chat_message_update_state(cm, LinphoneChatMessageStateDisplayed); } } } xmlXPathFreeObject(display_status_object); } linphone_chat_message_unref(cm); } } if (message_id_str != NULL) linphone_free_xml_text_content(message_id_str); if (datetime_str != NULL) linphone_free_xml_text_content(datetime_str); } static void linphone_chat_room_notify_imdn(LinphoneChatRoom *cr, const char *text) { xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char *)text, 0, NULL, 0); if (xml_ctx->doc != NULL) { process_imdn(cr, xml_ctx); } else { ms_warning("Wrongly formatted IMDN XML: %s", xml_ctx->errorBuffer); } linphone_xmlparsing_context_destroy(xml_ctx); } LinphoneCore *linphone_chat_room_get_lc(LinphoneChatRoom *cr) { return linphone_chat_room_get_core(cr); } LinphoneCore *linphone_chat_room_get_core(LinphoneChatRoom *cr) { return cr->lc; } const LinphoneAddress *linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) { return cr->peer_url; } LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *cr, const char *message) { LinphoneChatMessage *msg = belle_sip_object_new(LinphoneChatMessage); msg->state = LinphoneChatMessageStateIdle; msg->callbacks = linphone_chat_message_cbs_new(); msg->chat_room = (LinphoneChatRoom *)cr; msg->message = message ? ms_strdup(message) : NULL; msg->content_type = ms_strdup("text/plain"); msg->file_transfer_information = NULL; /* this property is used only when transfering file */ msg->http_request = NULL; msg->time = ms_time(0); msg->is_secured = FALSE; return msg; } LinphoneChatMessage *linphone_chat_room_create_message_2(LinphoneChatRoom *cr, const char *message, const char *external_body_url, LinphoneChatMessageState state, time_t time, bool_t is_read, bool_t is_incoming) { LinphoneChatMessage *msg = linphone_chat_room_create_message(cr, message); LinphoneCore *lc = linphone_chat_room_get_core(cr); msg->external_body_url = external_body_url ? ms_strdup(external_body_url) : NULL; msg->time = time; msg->is_secured = FALSE; linphone_chat_message_set_state(msg, state); if (is_incoming) { msg->dir = LinphoneChatMessageIncoming; linphone_chat_message_set_from(msg, linphone_chat_room_get_peer_address(cr)); msg->to = linphone_address_new(linphone_core_get_identity(lc)); /*direct assignment*/ } else { msg->dir = LinphoneChatMessageOutgoing; linphone_chat_message_set_to(msg, linphone_chat_room_get_peer_address(cr)); msg->from = linphone_address_new(linphone_core_get_identity(lc));/*direct assignment*/ } return msg; } void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage *msg, LinphoneChatMessageStateChangedCb status_cb, void *ud) { msg->message_state_changed_cb = status_cb; msg->message_state_changed_user_data = ud; _linphone_chat_room_send_message(cr, msg); } void linphone_chat_room_send_chat_message_2(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { linphone_chat_message_ref(msg); _linphone_chat_room_send_message(cr, msg); } void linphone_chat_room_send_chat_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { _linphone_chat_room_send_message(cr, msg); } void _linphone_chat_message_resend(LinphoneChatMessage *msg, bool_t ref_msg) { LinphoneChatMessageState state = linphone_chat_message_get_state(msg); LinphoneChatRoom *cr; if (state != LinphoneChatMessageStateNotDelivered) { ms_warning("Cannot resend chat message in state %s", linphone_chat_message_state_to_string(state)); return; } cr = linphone_chat_message_get_chat_room(msg); if (ref_msg) linphone_chat_message_ref(msg); _linphone_chat_room_send_message(cr, msg); } void linphone_chat_message_resend(LinphoneChatMessage *msg) { _linphone_chat_message_resend(msg, FALSE); } void linphone_chat_message_resend_2(LinphoneChatMessage *msg) { _linphone_chat_message_resend(msg, TRUE); } static char *linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) { xmlBufferPtr buf; xmlTextWriterPtr writer; int err; char *content = NULL; buf = xmlBufferCreate(); if (buf == NULL) { ms_error("Error creating the XML buffer"); return content; } writer = xmlNewTextWriterMemory(buf, 0); if (writer == NULL) { ms_error("Error creating the XML writer"); return content; } err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (err >= 0) { err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"isComposing", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing"); } if (err >= 0) { err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi", NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance"); } if (err >= 0) { err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xsi", (const xmlChar *)"schemaLocation", NULL, (const xmlChar *)"urn:ietf:params:xml:ns:im-composing iscomposing.xsd"); } if (err >= 0) { err = xmlTextWriterWriteElement(writer, (const xmlChar *)"state", (cr->is_composing == LinphoneIsComposingActive) ? (const xmlChar *)"active" : (const xmlChar *)"idle"); } if ((err >= 0) && (cr->is_composing == LinphoneIsComposingActive)) { char refresh_str[4] = {0}; int refresh_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT); snprintf(refresh_str, sizeof(refresh_str), "%u", refresh_timeout); err = xmlTextWriterWriteElement(writer, (const xmlChar *)"refresh", (const xmlChar *)refresh_str); } if (err >= 0) { /* Close the "isComposing" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { err = xmlTextWriterEndDocument(writer); } if (err > 0) { /* xmlTextWriterEndDocument returns the size of the content. */ content = ms_strdup((char *)buf->content); } xmlFreeTextWriter(writer); xmlBufferFree(buf); return content; } static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *cr) { SalOp *op = NULL; const char *identity = NULL; char *content = NULL; LinphoneCore *lc = linphone_chat_room_get_core(cr); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(lc); if (linphone_im_notif_policy_get_send_is_composing(policy) == TRUE) { LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(lc, cr->peer_url); LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc); LinphoneChatMessage *msg = NULL; if (proxy) identity = linphone_proxy_config_get_identity(proxy); else identity = linphone_core_get_primary_contact(lc); /*sending out of calls*/ op = sal_op_new(lc->sal); linphone_configure_op(lc, op, cr->peer_url, NULL, lp_config_get_int(lc->config, "sip", "chat_msg_with_contact", 0)); content = linphone_chat_room_create_is_composing_xml(cr); if (content != NULL) { int retval = -1; LinphoneAddress *from_addr = linphone_address_new(identity); LinphoneAddress *to_addr = linphone_address_new(cr->peer); msg = linphone_chat_room_create_message(cr, content); linphone_chat_message_set_from_address(msg, from_addr); linphone_chat_message_set_to_address(msg, to_addr); linphone_chat_message_set_content_type(msg, "application/im-iscomposing+xml"); if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsOutgoingMessageCb cb_process_outgoing_message = linphone_im_encryption_engine_cbs_get_process_outgoing_message(imee_cbs); if (cb_process_outgoing_message) { retval = cb_process_outgoing_message(imee, cr, msg); } } if (retval <= 0) { sal_message_send(op, identity, cr->peer, msg->content_type, msg->message, NULL); } linphone_chat_message_unref(msg); linphone_address_unref(from_addr); linphone_address_unref(to_addr); ms_free(content); sal_op_unref(op); } } } enum ImdnType { ImdnTypeDelivery, ImdnTypeDisplay }; static char *linphone_chat_message_create_imdn_xml(LinphoneChatMessage *cm, enum ImdnType imdn_type, LinphoneReason reason) { xmlBufferPtr buf; xmlTextWriterPtr writer; int err; char *content = NULL; char *datetime = NULL; const char *message_id; /* Check that the chat message has a message id */ message_id = linphone_chat_message_get_message_id(cm); if (message_id == NULL) return NULL; buf = xmlBufferCreate(); if (buf == NULL) { ms_error("Error creating the XML buffer"); return content; } writer = xmlNewTextWriterMemory(buf, 0); if (writer == NULL) { ms_error("Error creating the XML writer"); return content; } datetime = linphone_timestamp_to_rfc3339_string(linphone_chat_message_get_time(cm)); err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (err >= 0) { err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"imdn", (const xmlChar *)"urn:ietf:params:xml:ns:imdn"); } if ((err >= 0) && (reason != LinphoneReasonNone)) { err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"linphoneimdn", NULL, (const xmlChar *)"http://www.linphone.org/xsds/imdn.xsd"); } if (err >= 0) { err = xmlTextWriterWriteElement(writer, (const xmlChar *)"message-id", (const xmlChar *)message_id); } if (err >= 0) { err = xmlTextWriterWriteElement(writer, (const xmlChar *)"datetime", (const xmlChar *)datetime); } if (err >= 0) { if (imdn_type == ImdnTypeDelivery) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"delivery-notification"); } else { err = xmlTextWriterStartElement(writer, (const xmlChar *)"display-notification"); } } if (err >= 0) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"status"); } if (err >= 0) { if (reason == LinphoneReasonNone) { if (imdn_type == ImdnTypeDelivery) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"delivered"); } else { err = xmlTextWriterStartElement(writer, (const xmlChar *)"displayed"); } } else { err = xmlTextWriterStartElement(writer, (const xmlChar *)"error"); } } if (err >= 0) { /* Close the "delivered", "displayed" or "error" element. */ err = xmlTextWriterEndElement(writer); } if ((err >= 0) && (reason != LinphoneReasonNone)) { err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"linphoneimdn", (const xmlChar *)"reason", NULL); if (err >= 0) { char codestr[16]; snprintf(codestr, 16, "%d", linphone_reason_to_error_code(reason)); err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"code", (const xmlChar *)codestr); } if (err >= 0) { err = xmlTextWriterWriteString(writer, (const xmlChar *)linphone_reason_to_string(reason)); } if (err >= 0) { err = xmlTextWriterEndElement(writer); } } if (err >= 0) { /* Close the "status" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { /* Close the "delivery-notification" or "display-notification" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { /* Close the "imdn" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { err = xmlTextWriterEndDocument(writer); } if (err > 0) { /* xmlTextWriterEndDocument returns the size of the content. */ content = ms_strdup((char *)buf->content); } xmlFreeTextWriter(writer); xmlBufferFree(buf); ms_free(datetime); return content; } static void linphone_chat_message_send_imdn(LinphoneChatMessage *cm, enum ImdnType imdn_type, LinphoneReason reason) { SalOp *op = NULL; const char *identity = NULL; char *content = NULL; LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(cm); LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(cr->lc, cr->peer_url); LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(cr->lc); LinphoneChatMessage *msg; if (proxy) identity = linphone_proxy_config_get_identity(proxy); else identity = linphone_core_get_primary_contact(cr->lc); /* Sending out of calls */ op = sal_op_new(cr->lc->sal); linphone_configure_op(cr->lc, op, cr->peer_url, NULL, lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0)); content = linphone_chat_message_create_imdn_xml(cm, imdn_type, reason); if (content != NULL) { int retval = -1; LinphoneAddress *from_addr = linphone_address_new(identity); LinphoneAddress *to_addr = linphone_address_new(cr->peer); msg = linphone_chat_room_create_message(cr, content); linphone_chat_message_set_from_address(msg, from_addr); linphone_chat_message_set_to_address(msg, to_addr); linphone_chat_message_set_content_type(msg, "message/imdn+xml"); /* Do not try to encrypt the notification when it is reporting an error (maybe it should be bypassed only for some reasons). */ if (imee && (reason == LinphoneReasonNone)) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsOutgoingMessageCb cb_process_outgoing_message = linphone_im_encryption_engine_cbs_get_process_outgoing_message(imee_cbs); if (cb_process_outgoing_message) { retval = cb_process_outgoing_message(imee, cr, msg); } } if (retval <= 0) { sal_message_send(op, identity, cr->peer, msg->content_type, msg->message, NULL); } linphone_chat_message_unref(msg); linphone_address_unref(from_addr); linphone_address_unref(to_addr); ms_free(content); } sal_op_unref(op); } void linphone_chat_message_send_delivery_notification(LinphoneChatMessage *cm, LinphoneReason reason) { LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(cm); LinphoneCore *lc = linphone_chat_room_get_core(cr); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(lc); if (linphone_im_notif_policy_get_send_imdn_delivered(policy) == TRUE) { linphone_chat_message_send_imdn(cm, ImdnTypeDelivery, reason); } } void linphone_chat_message_send_display_notification(LinphoneChatMessage *cm) { LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(cm); LinphoneCore *lc = linphone_chat_room_get_core(cr); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(lc); if (linphone_im_notif_policy_get_send_imdn_displayed(policy) == TRUE) { linphone_chat_message_send_imdn(cm, ImdnTypeDisplay, LinphoneReasonNone); } } static char* utf8_to_char(uint32_t ic) { char *result = ms_malloc(sizeof(char) * 5); int size = 0; if (ic < 0x80) { result[0] = ic; size = 1; } else if (ic < 0x800) { result[1] = 0x80 + ((ic & 0x3F)); result[0] = 0xC0 + ((ic >> 6) & 0x1F); size = 2; } else if (ic < 0x100000) { result[2] = 0x80 + (ic & 0x3F); result[1] = 0x80 + ((ic >> 6) & 0x3F); result[0] = 0xE0 + ((ic >> 12) & 0xF); size = 3; } else if (ic < 0x110000) { result[3] = 0x80 + (ic & 0x3F); result[2] = 0x80 + ((ic >> 6) & 0x3F); result[1] = 0x80 + ((ic >> 12) & 0x3F); result[0] = 0xF0 + ((ic >> 18) & 0x7); size = 4; } result[size] = '\0'; return result; } void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call) { uint32_t new_line = 0x2028; uint32_t crlf = 0x0D0A; uint32_t lf = 0x0A; if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { LinphoneChatMessageCharacter *cmc = ms_new0(LinphoneChatMessageCharacter, 1); if (cr->pending_message == NULL) { cr->pending_message = linphone_chat_room_create_message(cr, ""); } cmc->value = character; cmc->has_been_read = FALSE; cr->received_rtt_characters = bctbx_list_append(cr->received_rtt_characters, (void *)cmc); cr->remote_is_composing = LinphoneIsComposingActive; linphone_core_notify_is_composing_received(cr->lc, cr); if (character == new_line || character == crlf || character == lf) { // End of message LinphoneChatMessage *msg = cr->pending_message; ms_debug("New line received, forge a message with content %s", cr->pending_message->message); linphone_chat_message_set_from(msg, cr->peer_url); if (msg->to) linphone_address_unref(msg->to); msg->to = call->dest_proxy ? linphone_address_clone(call->dest_proxy->identity_address) : linphone_address_new(linphone_core_get_identity(lc)); msg->time = ms_time(0); msg->state = LinphoneChatMessageStateDelivered; msg->dir = LinphoneChatMessageIncoming; if (lp_config_get_int(lc->config, "misc", "store_rtt_messages", 1) == 1) { msg->storage_id = linphone_chat_message_store(msg); } if (cr->unread_count < 0) cr->unread_count = 1; else cr->unread_count++; linphone_chat_room_message_received(cr, lc, msg); linphone_chat_message_unref(msg); cr->pending_message = NULL; cr->received_rtt_characters = bctbx_list_free_with_data(cr->received_rtt_characters, (void (*)(void *))ms_free); } else { char *value = utf8_to_char(character); cr->pending_message->message = ms_strcat_printf(cr->pending_message->message, value); ms_debug("Received RTT character: %s (%lu), pending text is %s", value, (unsigned long)character, cr->pending_message->message); ms_free(value); } } } uint32_t linphone_chat_room_get_char(const LinphoneChatRoom *cr) { if (cr && cr->received_rtt_characters) { bctbx_list_t *characters = cr->received_rtt_characters; while (characters != NULL) { LinphoneChatMessageCharacter *cmc = (LinphoneChatMessageCharacter *)characters->data; if (!cmc->has_been_read) { cmc->has_been_read = TRUE; return cmc->value; } characters = bctbx_list_next(characters); } } return 0; } LinphoneStatus linphone_chat_message_put_char(LinphoneChatMessage *msg, uint32_t character) { LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(msg); LinphoneCall *call = cr->call; LinphoneCore *lc = cr->lc; uint32_t new_line = 0x2028; uint32_t crlf = 0x0D0A; uint32_t lf = 0x0A; if (!call || !call->textstream) { return -1; } if (character == new_line || character == crlf || character == lf) { if (lc && lp_config_get_int(lc->config, "misc", "store_rtt_messages", 1) == 1) { ms_debug("New line sent, forge a message with content %s", msg->message); msg->time = ms_time(0); msg->state = LinphoneChatMessageStateDisplayed; msg->dir = LinphoneChatMessageOutgoing; if (msg->from) linphone_address_unref(msg->from); msg->from = linphone_address_new(linphone_core_get_identity(lc)); msg->storage_id = linphone_chat_message_store(msg); ms_free(msg->message); msg->message = NULL; } } else { char *value = utf8_to_char(character); msg->message = ms_strcat_printf(msg->message, value); ms_debug("Sent RTT character: %s (%lu), pending text is %s", value, (unsigned long)character, msg->message); ms_free(value); } text_stream_putchar32(call->textstream, character); return 0; } const char* linphone_chat_message_get_message_id(const LinphoneChatMessage *cm) { return cm->message_id; } static int linphone_chat_room_stop_composing(void *data, unsigned int revents) { LinphoneChatRoom *cr = (LinphoneChatRoom *)data; cr->is_composing = LinphoneIsComposingIdle; linphone_chat_room_send_is_composing_notification(cr); linphone_chat_room_delete_composing_refresh_timer(cr); belle_sip_object_unref(cr->composing_idle_timer); cr->composing_idle_timer = NULL; return BELLE_SIP_STOP; } static int linphone_chat_room_refresh_composing(void *data, unsigned int revents) { LinphoneChatRoom *cr = (LinphoneChatRoom *)data; linphone_chat_room_send_is_composing_notification(cr); return BELLE_SIP_CONTINUE; } void linphone_chat_room_compose(LinphoneChatRoom *cr) { int idle_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_idle_timeout", COMPOSING_DEFAULT_IDLE_TIMEOUT); int refresh_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT); if (cr->is_composing == LinphoneIsComposingIdle) { cr->is_composing = LinphoneIsComposingActive; linphone_chat_room_send_is_composing_notification(cr); if (!cr->composing_refresh_timer) { cr->composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_refresh_composing, cr, refresh_timeout * 1000, "composing refresh timeout"); } else { belle_sip_source_set_timeout(cr->composing_refresh_timer, refresh_timeout * 1000); } if (!cr->composing_idle_timer) { cr->composing_idle_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_stop_composing, cr, idle_timeout * 1000, "composing idle timeout"); } } belle_sip_source_set_timeout(cr->composing_idle_timer, idle_timeout * 1000); } const char *linphone_chat_message_state_to_string(const LinphoneChatMessageState state) { switch (state) { case LinphoneChatMessageStateIdle: return "LinphoneChatMessageStateIdle"; case LinphoneChatMessageStateInProgress: return "LinphoneChatMessageStateInProgress"; case LinphoneChatMessageStateDelivered: return "LinphoneChatMessageStateDelivered"; case LinphoneChatMessageStateNotDelivered: return "LinphoneChatMessageStateNotDelivered"; case LinphoneChatMessageStateFileTransferError: return "LinphoneChatMessageStateFileTransferError"; case LinphoneChatMessageStateFileTransferDone: return "LinphoneChatMessageStateFileTransferDone "; case LinphoneChatMessageStateDeliveredToUser: return "LinphoneChatMessageStateDeliveredToUser"; case LinphoneChatMessageStateDisplayed: return "LinphoneChatMessageStateDisplayed"; } return NULL; } LinphoneChatRoom *linphone_chat_message_get_chat_room(LinphoneChatMessage *msg) { return msg->chat_room; } const LinphoneAddress *linphone_chat_message_get_peer_address(LinphoneChatMessage *msg) { return linphone_chat_room_get_peer_address(msg->chat_room); } void linphone_chat_message_set_user_data(LinphoneChatMessage *msg, void *ud) { msg->message_userdata = ud; } void *linphone_chat_message_get_user_data(const LinphoneChatMessage *msg) { return msg->message_userdata; } const char *linphone_chat_message_get_external_body_url(const LinphoneChatMessage *msg) { return msg->external_body_url; } void linphone_chat_message_set_external_body_url(LinphoneChatMessage *msg, const char *url) { if (msg->external_body_url) { ms_free(msg->external_body_url); } msg->external_body_url = url ? ms_strdup(url) : NULL; } const char * linphone_chat_message_get_content_type(const LinphoneChatMessage *msg) { return msg->content_type; } void linphone_chat_message_set_content_type(LinphoneChatMessage *msg, const char *content_type) { if (msg->content_type) { ms_free(msg->content_type); } msg->content_type = content_type ? ms_strdup(content_type) : NULL; } bool_t linphone_chat_message_is_file_transfer(const LinphoneChatMessage *msg) { return is_file_transfer(msg->content_type); } bool_t linphone_chat_message_is_text(const LinphoneChatMessage *msg) { return is_text(msg->content_type); } bool_t linphone_chat_message_get_to_be_stored(const LinphoneChatMessage *msg) { return msg->to_be_stored; } void linphone_chat_message_set_to_be_stored(LinphoneChatMessage *msg, bool_t to_be_stored) { msg->to_be_stored = to_be_stored; } const char *linphone_chat_message_get_appdata(const LinphoneChatMessage *msg) { return msg->appdata; } void linphone_chat_message_set_appdata(LinphoneChatMessage *msg, const char *data) { if (msg->appdata) { ms_free(msg->appdata); } msg->appdata = data ? ms_strdup(data) : NULL; linphone_chat_message_store_appdata(msg); } void linphone_chat_message_set_from_address(LinphoneChatMessage *msg, const LinphoneAddress *from) { if (msg->from) linphone_address_unref(msg->from); msg->from = linphone_address_clone(from); } const LinphoneAddress *linphone_chat_message_get_from_address(const LinphoneChatMessage *msg) { return msg->from; } void linphone_chat_message_set_to_address(LinphoneChatMessage *msg, const LinphoneAddress *to) { if (msg->to) linphone_address_unref(msg->to); msg->to = linphone_address_clone(to); } const LinphoneAddress *linphone_chat_message_get_to_address(const LinphoneChatMessage *msg) { if (msg->to) return msg->to; if (msg->dir == LinphoneChatMessageOutgoing) { return msg->chat_room->peer_url; } return NULL; } void linphone_chat_message_set_is_secured(LinphoneChatMessage *msg, bool_t secured) { msg->is_secured = secured; } bool_t linphone_chat_message_is_secured(LinphoneChatMessage *msg) { return msg->is_secured; } LinphoneAddress *linphone_chat_message_get_local_address(const LinphoneChatMessage *msg) { return msg->dir == LinphoneChatMessageOutgoing ? msg->from : msg->to; } time_t linphone_chat_message_get_time(const LinphoneChatMessage *msg) { return msg->time; } LinphoneChatMessageState linphone_chat_message_get_state(const LinphoneChatMessage *msg) { return msg->state; } const char *linphone_chat_message_get_text(const LinphoneChatMessage *msg) { return msg->message; } int linphone_chat_message_set_text(LinphoneChatMessage *msg, const char* text) { if (msg->message) ms_free(msg->message); if (text) msg->message = ms_strdup(text); else msg->message = NULL; return 0; } void linphone_chat_message_add_custom_header(LinphoneChatMessage *msg, const char *header_name, const char *header_value) { msg->custom_headers = sal_custom_header_append(msg->custom_headers, header_name, header_value); } const char *linphone_chat_message_get_custom_header(LinphoneChatMessage *msg, const char *header_name) { return sal_custom_header_find(msg->custom_headers, header_name); } void linphone_chat_message_remove_custom_header(LinphoneChatMessage *msg, const char *header_name) { msg->custom_headers = sal_custom_header_remove(msg->custom_headers, header_name); } bool_t linphone_chat_message_is_read(LinphoneChatMessage *msg) { LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(msg); LinphoneCore *lc = linphone_chat_room_get_core(cr); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(lc); if ((linphone_im_notif_policy_get_recv_imdn_displayed(policy) == TRUE) && (msg->state == LinphoneChatMessageStateDisplayed)) return TRUE; if ((linphone_im_notif_policy_get_recv_imdn_delivered(policy) == TRUE) && (msg->state == LinphoneChatMessageStateDeliveredToUser || msg->state == LinphoneChatMessageStateDisplayed)) return TRUE; return (msg->state == LinphoneChatMessageStateDelivered || msg->state == LinphoneChatMessageStateDisplayed || msg->state == LinphoneChatMessageStateDeliveredToUser); } bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage *msg) { return msg->dir == LinphoneChatMessageOutgoing; } unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage *msg) { return msg->storage_id; } LinphoneChatMessage *linphone_chat_message_clone(const LinphoneChatMessage *msg) { LinphoneChatMessage *new_message = linphone_chat_room_create_message(msg->chat_room, msg->message); if (msg->external_body_url) new_message->external_body_url = ms_strdup(msg->external_body_url); if (msg->appdata) new_message->appdata = ms_strdup(msg->appdata); new_message->message_state_changed_cb = msg->message_state_changed_cb; new_message->message_state_changed_user_data = msg->message_state_changed_user_data; new_message->message_userdata = msg->message_userdata; new_message->time = msg->time; new_message->state = msg->state; new_message->storage_id = msg->storage_id; if (msg->from) new_message->from = linphone_address_clone(msg->from); if (msg->file_transfer_filepath) new_message->file_transfer_filepath = ms_strdup(msg->file_transfer_filepath); if (msg->file_transfer_information) new_message->file_transfer_information = linphone_content_copy(msg->file_transfer_information); return new_message; } void linphone_chat_message_destroy(LinphoneChatMessage *msg) { belle_sip_object_unref(msg); } static void _linphone_chat_message_destroy(LinphoneChatMessage *msg) { if (msg->op) sal_op_release(msg->op); if (msg->ei) linphone_error_info_unref(msg->ei); if (msg->message) ms_free(msg->message); if (msg->external_body_url) ms_free(msg->external_body_url); if (msg->appdata) ms_free(msg->appdata); if (msg->from) linphone_address_unref(msg->from); if (msg->to) linphone_address_unref(msg->to); if (msg->message_id) ms_free(msg->message_id); if (msg->custom_headers) sal_custom_header_free(msg->custom_headers); if (msg->content_type) ms_free(msg->content_type); if (msg->file_transfer_information) { linphone_content_unref(msg->file_transfer_information); } if (msg->file_transfer_filepath != NULL) { ms_free(msg->file_transfer_filepath); } if (msg->callbacks) { linphone_chat_message_cbs_unref(msg->callbacks); } } LinphoneChatMessage *linphone_chat_message_ref(LinphoneChatMessage *msg) { belle_sip_object_ref(msg); return msg; } void linphone_chat_message_unref(LinphoneChatMessage *msg) { belle_sip_object_unref(msg); } static void linphone_chat_message_deactivate(LinphoneChatMessage *msg){ /*mark the chat msg as orphan (it has no chat room anymore)*/ msg->chat_room = NULL; if (msg->file_transfer_information != NULL) { linphone_chat_message_cancel_file_transfer(msg); } } static void linphone_chat_message_release(LinphoneChatMessage *msg) { linphone_chat_message_deactivate(msg); linphone_chat_message_unref(msg); } const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg) { if (!msg->ei) ((LinphoneChatMessage*)msg)->ei = linphone_error_info_new(); /*let's do it mutable*/ linphone_error_info_from_sal_op(msg->ei, msg->op); return msg->ei; } LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage *msg) { return linphone_error_info_get_reason(linphone_chat_message_get_error_info(msg)); } LinphoneChatMessageCbs *linphone_chat_message_get_callbacks(const LinphoneChatMessage *msg) { return msg->callbacks; } LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *room) { return room->call; } linphone-3.12.0/coreapi/chat_file_transfer.c000066400000000000000000000715511313432737600210430ustar00rootroot00000000000000/*************************************************************************** * chat_file_transfer.c * * Sun Jun 5 19:34:18 2005 * Copyright 2005 Simon Morlat * Email simon dot morlat at linphone dot org ****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "private.h" #include "ortp/b64.h" static bool_t file_transfer_in_progress_and_valid(LinphoneChatMessage* msg) { return (msg->chat_room && msg->chat_room->lc && msg->http_request && !belle_http_request_is_cancelled(msg->http_request)); } static void _release_http_request(LinphoneChatMessage* msg) { if (msg->http_request) { belle_sip_object_unref(msg->http_request); msg->http_request = NULL; if (msg->http_listener){ belle_sip_object_unref(msg->http_listener); msg->http_listener = NULL; // unhold the reference that the listener was holding on the message linphone_chat_message_unref(msg); } } } static void linphone_chat_message_process_io_error_upload(void *data, const belle_sip_io_error_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; ms_error("I/O Error during file upload of msg [%p]", msg); linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered); _release_http_request(msg); linphone_chat_room_remove_transient_message(msg->chat_room, msg); linphone_chat_message_unref(msg); } static void linphone_chat_message_process_auth_requested_upload(void *data, belle_sip_auth_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; ms_error("Error during file upload: auth requested for msg [%p]", msg); linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered); _release_http_request(msg); linphone_chat_room_remove_transient_message(msg->chat_room, msg); linphone_chat_message_unref(msg); } static void linphone_chat_message_process_io_error_download(void *data, const belle_sip_io_error_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; ms_error("I/O Error during file download msg [%p]", msg); linphone_chat_message_update_state(msg, LinphoneChatMessageStateFileTransferError); _release_http_request(msg); } static void linphone_chat_message_process_auth_requested_download(void *data, belle_sip_auth_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; ms_error("Error during file download : auth requested for msg [%p]", msg); linphone_chat_message_update_state(msg, LinphoneChatMessageStateFileTransferError); _release_http_request(msg); } static void linphone_chat_message_file_transfer_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *m, void *data, size_t offset, size_t total) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; if (!file_transfer_in_progress_and_valid(msg)) { ms_warning("Cancelled request for %s msg [%p], ignoring %s", msg->chat_room?"":"ORPHAN", msg, __FUNCTION__); _release_http_request(msg); return; } if (linphone_chat_message_cbs_get_file_transfer_progress_indication(msg->callbacks)) { linphone_chat_message_cbs_get_file_transfer_progress_indication(msg->callbacks)( msg, msg->file_transfer_information, offset, total); } else { /* Legacy: call back given by application level */ linphone_core_notify_file_transfer_progress_indication(msg->chat_room->lc, msg, msg->file_transfer_information, offset, total); } } static int on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m, void *data, size_t offset, uint8_t *buffer, size_t *size) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; LinphoneCore *lc = NULL; LinphoneImEncryptionEngine *imee = NULL; int retval = -1; if (!file_transfer_in_progress_and_valid(msg)) { if (msg->http_request) { ms_warning("Cancelled request for %s msg [%p], ignoring %s", msg->chat_room?"":"ORPHAN", msg, __FUNCTION__); _release_http_request(msg); } return BELLE_SIP_STOP; } lc = msg->chat_room->lc; /* if we've not reach the end of file yet, ask for more data */ /* in case of file body handler, won't be called */ if (msg->file_transfer_filepath == NULL && offset < linphone_content_get_size(msg->file_transfer_information)) { /* get data from call back */ LinphoneChatMessageCbsFileTransferSendCb file_transfer_send_cb = linphone_chat_message_cbs_get_file_transfer_send(msg->callbacks); if (file_transfer_send_cb) { LinphoneBuffer *lb = file_transfer_send_cb(msg, msg->file_transfer_information, offset, *size); if (lb == NULL) { *size = 0; } else { *size = linphone_buffer_get_size(lb); memcpy(buffer, linphone_buffer_get_content(lb), *size); linphone_buffer_unref(lb); } } else { /* Legacy */ linphone_core_notify_file_transfer_send(lc, msg, msg->file_transfer_information, (char *)buffer, size); } } imee = linphone_core_get_im_encryption_engine(lc); if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsUploadingFileCb cb_process_uploading_file = linphone_im_encryption_engine_cbs_get_process_uploading_file(imee_cbs); if (cb_process_uploading_file) { size_t max_size = *size; uint8_t *encrypted_buffer = (uint8_t *)ms_malloc0(max_size); retval = cb_process_uploading_file(imee, msg, offset, (const uint8_t *)buffer, size, encrypted_buffer); if (retval == 0) { if (*size > max_size) { ms_error("IM encryption engine process upload file callback returned a size bigger than the size of the buffer, so it will be truncated !"); *size = max_size; } memcpy(buffer, encrypted_buffer, *size); } ms_free(encrypted_buffer); } } return retval <= 0 ? BELLE_SIP_CONTINUE : BELLE_SIP_STOP; } static void on_send_end(belle_sip_user_body_handler_t *bh, void *data) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; LinphoneCore *lc = msg->chat_room->lc; LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc); if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsUploadingFileCb cb_process_uploading_file = linphone_im_encryption_engine_cbs_get_process_uploading_file(imee_cbs); if (cb_process_uploading_file) { cb_process_uploading_file(imee, msg, 0, NULL, NULL, NULL); } } } static void file_upload_end_background_task(LinphoneChatMessage *obj){ if (obj->bg_task_id){ ms_message("channel [%p]: ending file upload background task with id=[%lx].",obj,obj->bg_task_id); sal_end_background_task(obj->bg_task_id); obj->bg_task_id=0; } } static void file_upload_background_task_ended(LinphoneChatMessage *obj){ ms_warning("channel [%p]: file upload background task has to be ended now, but work isn't finished.",obj); file_upload_end_background_task(obj); } static void file_upload_begin_background_task(LinphoneChatMessage *obj){ if (obj->bg_task_id==0){ obj->bg_task_id=sal_begin_background_task("file transfer upload",(void (*)(void*))file_upload_background_task_ended, obj); if (obj->bg_task_id) ms_message("channel [%p]: starting file upload background task with id=[%lx].",obj,obj->bg_task_id); } } static void linphone_chat_message_process_response_from_post_file(void *data, const belle_http_response_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; if (msg->http_request && !file_transfer_in_progress_and_valid(msg)) { ms_warning("Cancelled request for %s msg [%p], ignoring %s", msg->chat_room?"":"ORPHAN", msg, __FUNCTION__); _release_http_request(msg); return; } /* check the answer code */ if (event->response) { int code = belle_http_response_get_status_code(event->response); if (code == 204) { /* this is the reply to the first post to the server - an empty msg */ /* start uploading the file */ belle_sip_multipart_body_handler_t *bh; char *first_part_header; belle_sip_body_handler_t *first_part_bh; bool_t is_file_encryption_enabled = FALSE; LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(msg->chat_room->lc); if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsIsEncryptionEnabledForFileTransferCb is_encryption_enabled_for_file_transfer_cb = linphone_im_encryption_engine_cbs_get_is_encryption_enabled_for_file_transfer(imee_cbs); if (is_encryption_enabled_for_file_transfer_cb) { is_file_encryption_enabled = is_encryption_enabled_for_file_transfer_cb(imee, msg->chat_room); } } /* shall we encrypt the file */ if (is_file_encryption_enabled) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsGenerateFileTransferKeyCb generate_file_transfer_key_cb = linphone_im_encryption_engine_cbs_get_generate_file_transfer_key(imee_cbs); if (generate_file_transfer_key_cb) { generate_file_transfer_key_cb(imee, msg->chat_room, msg); } /* temporary storage for the Content-disposition header value : use a generic filename to not leak it * Actual filename stored in msg->file_transfer_information->name will be set in encrypted msg * sended to the */ first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"filename.txt\""); } else { /* temporary storage for the Content-disposition header value */ first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", linphone_content_get_name(msg->file_transfer_information)); } /* create a user body handler to take care of the file and add the content disposition and content-type * headers */ first_part_bh = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new( linphone_content_get_size(msg->file_transfer_information), linphone_chat_message_file_transfer_on_progress, NULL, NULL, on_send_body, on_send_end, msg); if (msg->file_transfer_filepath != NULL) { belle_sip_user_body_handler_t *body_handler = (belle_sip_user_body_handler_t *)first_part_bh; first_part_bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath, NULL, msg); // No need to add again the callback for progression, otherwise it will be called twice linphone_content_set_size(msg->file_transfer_information, belle_sip_file_body_handler_get_file_size((belle_sip_file_body_handler_t *)first_part_bh)); belle_sip_file_body_handler_set_user_body_handler((belle_sip_file_body_handler_t *)first_part_bh, body_handler); } else if (linphone_content_get_buffer(msg->file_transfer_information) != NULL) { first_part_bh = (belle_sip_body_handler_t *)belle_sip_memory_body_handler_new_from_buffer( linphone_content_get_buffer(msg->file_transfer_information), linphone_content_get_size(msg->file_transfer_information), linphone_chat_message_file_transfer_on_progress, msg); } belle_sip_body_handler_add_header(first_part_bh, belle_sip_header_create("Content-disposition", first_part_header)); belle_sip_free(first_part_header); belle_sip_body_handler_add_header(first_part_bh, (belle_sip_header_t *)belle_sip_header_content_type_create( linphone_content_get_type(msg->file_transfer_information), linphone_content_get_subtype(msg->file_transfer_information))); /* insert it in a multipart body handler which will manage the boundaries of multipart msg */ bh = belle_sip_multipart_body_handler_new(linphone_chat_message_file_transfer_on_progress, msg, first_part_bh, NULL); linphone_chat_message_ref(msg); _release_http_request(msg); file_upload_begin_background_task(msg); linphone_chat_room_upload_file(msg); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(msg->http_request), BELLE_SIP_BODY_HANDLER(bh)); linphone_chat_message_unref(msg); } else if (code == 200) { /* file has been uploaded correctly, get server reply and send it */ const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response); if (body && strlen(body) > 0) { /* if we have an encryption key for the file, we must insert it into the msg and restore the correct * filename */ const char *content_key = linphone_content_get_key(msg->file_transfer_information); size_t content_key_size = linphone_content_get_key_size(msg->file_transfer_information); if (content_key != NULL) { /* parse the msg body */ xmlDocPtr xmlMessageBody = xmlParseDoc((const xmlChar *)body); xmlNodePtr cur = xmlDocGetRootElement(xmlMessageBody); if (cur != NULL) { cur = cur->xmlChildrenNode; while (cur != NULL) { if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a type="file" attribute */ xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type"); if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for : add a file-key children node */ xmlNodePtr fileInfoNodeChildren = cur ->xmlChildrenNode; /* need to parse the children node to update the file-name one */ /* convert key to base64 */ size_t b64Size = b64_encode(NULL, content_key_size, NULL, 0); char *keyb64 = (char *)ms_malloc0(b64Size + 1); int xmlStringLength; b64Size = b64_encode(content_key, content_key_size, keyb64, b64Size); keyb64[b64Size] = '\0'; /* libxml need a null terminated string */ /* add the node containing the key to the file-info node */ xmlNewTextChild(cur, NULL, (const xmlChar *)"file-key", (const xmlChar *)keyb64); xmlFree(typeAttribute); ms_free(keyb64); /* look for the file-name node and update its content */ while (fileInfoNodeChildren != NULL) { if (!xmlStrcmp( fileInfoNodeChildren->name, (const xmlChar *)"file-name")) { /* we found a the file-name node, update its content with the real filename */ /* update node content */ xmlNodeSetContent(fileInfoNodeChildren, (const xmlChar *)(linphone_content_get_name( msg->file_transfer_information))); break; } fileInfoNodeChildren = fileInfoNodeChildren->next; } /* dump the xml into msg->message */ xmlDocDumpFormatMemoryEnc(xmlMessageBody, (xmlChar **)&msg->message, &xmlStringLength, "UTF-8", 0); break; } xmlFree(typeAttribute); } cur = cur->next; } } xmlFreeDoc(xmlMessageBody); } else { /* no encryption key, transfer in plain, just copy the msg sent by server */ msg->message = ms_strdup(body); } msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml"); linphone_chat_message_ref(msg); linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferDone); _release_http_request(msg); _linphone_chat_room_send_message(msg->chat_room, msg); file_upload_end_background_task(msg); linphone_chat_message_unref(msg); } else { ms_warning("Received empty response from server, file transfer failed"); linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered); _release_http_request(msg); file_upload_end_background_task(msg); linphone_chat_message_unref(msg); } } else { ms_warning("Unhandled HTTP code response %d for file transfer", code); linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered); _release_http_request(msg); file_upload_end_background_task(msg); linphone_chat_message_unref(msg); } } } const LinphoneContent *linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage *msg) { return msg->file_transfer_information; } static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m, void *data, size_t offset, uint8_t *buffer, size_t size) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; LinphoneCore *lc = NULL; LinphoneImEncryptionEngine *imee = NULL; int retval = -1; uint8_t *decrypted_buffer = NULL; if (!msg->chat_room) { linphone_chat_message_cancel_file_transfer(msg); return; } lc = msg->chat_room->lc; if (lc == NULL){ return; /*might happen during linphone_core_destroy()*/ } if (!msg->http_request || belle_http_request_is_cancelled(msg->http_request)) { ms_warning("Cancelled request for msg [%p], ignoring %s", msg, __FUNCTION__); return; } /* first call may be with a zero size, ignore it */ if (size == 0) { return; } decrypted_buffer = (uint8_t *)ms_malloc0(size); imee = linphone_core_get_im_encryption_engine(lc); if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsDownloadingFileCb cb_process_downloading_file = linphone_im_encryption_engine_cbs_get_process_downloading_file(imee_cbs); if (cb_process_downloading_file) { retval = cb_process_downloading_file(imee, msg, offset, (const uint8_t *)buffer, size, decrypted_buffer); if (retval == 0) { memcpy(buffer, decrypted_buffer, size); } } } ms_free(decrypted_buffer); if (retval <= 0) { if (msg->file_transfer_filepath == NULL) { if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) { LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size); linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb); linphone_buffer_unref(lb); } else { /* Legacy: call back given by application level */ linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, (const char *)buffer, size); } } } else { ms_warning("File transfer decrypt failed with code %d", (int)retval); linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferError); } return; } static void on_recv_end(belle_sip_user_body_handler_t *bh, void *data) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; LinphoneCore *lc = msg->chat_room->lc; LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc); int retval = -1; if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsDownloadingFileCb cb_process_downloading_file = linphone_im_encryption_engine_cbs_get_process_downloading_file(imee_cbs); if (cb_process_downloading_file) { retval = cb_process_downloading_file(imee, msg, 0, NULL, 0, NULL); } } if (retval <= 0) { if (msg->file_transfer_filepath == NULL) { if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) { LinphoneBuffer *lb = linphone_buffer_new(); linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb); linphone_buffer_unref(lb); } else { /* Legacy: call back given by application level */ linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, NULL, 0); } } } if (retval <= 0 && linphone_chat_message_get_state(msg) != LinphoneChatMessageStateFileTransferError) { linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferDone); } } static LinphoneContent *linphone_chat_create_file_transfer_information_from_headers(const belle_sip_message_t *m) { LinphoneContent *content = linphone_content_new(); belle_sip_header_content_length_t *content_length_hdr = BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_message_get_header(m, "Content-Length")); belle_sip_header_content_type_t *content_type_hdr = BELLE_SIP_HEADER_CONTENT_TYPE(belle_sip_message_get_header(m, "Content-Type")); const char *type = NULL, *subtype = NULL; linphone_content_set_name(content, ""); if (content_type_hdr) { type = belle_sip_header_content_type_get_type(content_type_hdr); subtype = belle_sip_header_content_type_get_subtype(content_type_hdr); ms_message("Extracted content type %s / %s from header", type ? type : "", subtype ? subtype : ""); if (type) linphone_content_set_type(content, type); if (subtype) linphone_content_set_subtype(content, subtype); } if (content_length_hdr) { linphone_content_set_size(content, belle_sip_header_content_length_get_content_length(content_length_hdr)); ms_message("Extracted content length %i from header", (int)linphone_content_get_size(content)); } return content; } static void linphone_chat_process_response_headers_from_get_file(void *data, const belle_http_response_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; if (event->response) { /*we are receiving a response, set a specific body handler to acquire the response. * if not done, belle-sip will create a memory body handler, the default*/ belle_sip_message_t *response = BELLE_SIP_MESSAGE(event->response); belle_sip_body_handler_t *body_handler = NULL; size_t body_size = 0; if (msg->file_transfer_information == NULL) { ms_warning("No file transfer information for msg %p: creating...", msg); msg->file_transfer_information = linphone_chat_create_file_transfer_information_from_headers(response); } if (msg->file_transfer_information) { body_size = linphone_content_get_size(msg->file_transfer_information); } body_handler = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(body_size, linphone_chat_message_file_transfer_on_progress, NULL, on_recv_body, NULL, on_recv_end, msg); if (msg->file_transfer_filepath != NULL) { belle_sip_user_body_handler_t *bh = (belle_sip_user_body_handler_t *)body_handler; body_handler = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new( msg->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, msg); if (belle_sip_body_handler_get_size((belle_sip_body_handler_t *)body_handler) == 0) { /* If the size of the body has not been initialized from the file stat, use the one from the * file_transfer_information. */ belle_sip_body_handler_set_size((belle_sip_body_handler_t *)body_handler, body_size); } belle_sip_file_body_handler_set_user_body_handler((belle_sip_file_body_handler_t *)body_handler, bh); } belle_sip_message_set_body_handler((belle_sip_message_t *)event->response, body_handler); } } static void linphone_chat_process_response_from_get_file(void *data, const belle_http_response_event_t *event) { LinphoneChatMessage *msg = (LinphoneChatMessage *)data; /* check the answer code */ if (event->response) { int code = belle_http_response_get_status_code(event->response); if (code >= 400 && code < 500) { ms_warning("File transfer failed with code %d", code); linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferError); } else if (code != 200) { ms_warning("Unhandled HTTP code response %d for file transfer", code); } _release_http_request(msg); } } int _linphone_chat_room_start_http_transfer(LinphoneChatMessage *msg, const char* url, const char* action, const belle_http_request_listener_callbacks_t *cbs) { belle_generic_uri_t *uri = NULL; const char* ua = linphone_core_get_user_agent(msg->chat_room->lc); if (url == NULL) { ms_warning("Cannot process file transfer msg: no file remote URI configured."); goto error; } uri = belle_generic_uri_parse(url); if (uri == NULL || belle_generic_uri_get_host(uri)==NULL) { ms_warning("Cannot process file transfer msg: incorrect file remote URI configured '%s'.", url); goto error; } msg->http_request = belle_http_request_create(action, uri, belle_sip_header_create("User-Agent", ua), NULL); if (msg->http_request == NULL) { ms_warning("Could not create http request for uri %s", url); goto error; } /* keep a reference to the http request to be able to cancel it during upload */ belle_sip_object_ref(msg->http_request); /* give msg to listener to be able to start the actual file upload when server answer a 204 No content */ msg->http_listener = belle_http_request_listener_create_from_callbacks(cbs, linphone_chat_message_ref(msg)); belle_http_provider_send_request(msg->chat_room->lc->http_provider, msg->http_request, msg->http_listener); return 0; error: if (uri) { belle_sip_object_unref(uri); } return -1; } int linphone_chat_room_upload_file(LinphoneChatMessage *msg) { belle_http_request_listener_callbacks_t cbs = {0}; int err; if (msg->http_request){ ms_error("linphone_chat_room_upload_file(): there is already an upload in progress."); return -1; } cbs.process_response = linphone_chat_message_process_response_from_post_file; cbs.process_io_error = linphone_chat_message_process_io_error_upload; cbs.process_auth_requested = linphone_chat_message_process_auth_requested_upload; err = _linphone_chat_room_start_http_transfer(msg, linphone_core_get_file_transfer_server(msg->chat_room->lc), "POST", &cbs); if (err == -1){ linphone_chat_message_set_state(msg, LinphoneChatMessageStateNotDelivered); } return err; } LinphoneStatus linphone_chat_message_download_file(LinphoneChatMessage *msg) { belle_http_request_listener_callbacks_t cbs = {0}; int err; if (msg->http_request){ ms_error("linphone_chat_message_download_file(): there is already a download in progress"); return -1; } cbs.process_response_headers = linphone_chat_process_response_headers_from_get_file; cbs.process_response = linphone_chat_process_response_from_get_file; cbs.process_io_error = linphone_chat_message_process_io_error_download; cbs.process_auth_requested = linphone_chat_message_process_auth_requested_download; err = _linphone_chat_room_start_http_transfer(msg, msg->external_body_url, "GET", &cbs); if (err == -1) return -1; /* start the download, status is In Progress */ linphone_chat_message_set_state(msg, LinphoneChatMessageStateInProgress); return 0; } void linphone_chat_message_start_file_download(LinphoneChatMessage *msg, LinphoneChatMessageStateChangedCb status_cb, void *ud) { msg->message_state_changed_cb = status_cb; msg->message_state_changed_user_data = ud; linphone_chat_message_download_file(msg); } void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage *msg) { if (msg->http_request) { if (msg->state == LinphoneChatMessageStateInProgress) { linphone_chat_message_set_state(msg, LinphoneChatMessageStateNotDelivered); } if (!belle_http_request_is_cancelled(msg->http_request)) { if (msg->chat_room) { ms_message("Canceling file transfer %s - msg [%p] chat room[%p]" , (msg->external_body_url == NULL) ? linphone_core_get_file_transfer_server(msg->chat_room->lc) : msg->external_body_url , msg , msg->chat_room); belle_http_provider_cancel_request(msg->chat_room->lc->http_provider, msg->http_request); if (msg->dir == LinphoneChatMessageOutgoing) { // must release it linphone_chat_message_unref(msg); } } else { ms_message("Warning: http request still running for ORPHAN msg [%p]: this is a memory leak", msg); } } _release_http_request(msg); } else { ms_message("No existing file transfer - nothing to cancel"); } } void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath) { if (msg->file_transfer_filepath != NULL) { ms_free(msg->file_transfer_filepath); } msg->file_transfer_filepath = ms_strdup(filepath); } const char *linphone_chat_message_get_file_transfer_filepath(LinphoneChatMessage *msg) { return msg->file_transfer_filepath; } LinphoneChatMessage *linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr, const LinphoneContent *initial_content) { LinphoneChatMessage *msg = belle_sip_object_new(LinphoneChatMessage); msg->callbacks = linphone_chat_message_cbs_new(); msg->chat_room = (LinphoneChatRoom *)cr; msg->message = NULL; msg->file_transfer_information = linphone_content_copy(initial_content); msg->dir = LinphoneChatMessageOutgoing; linphone_chat_message_set_to(msg, linphone_chat_room_get_peer_address(cr)); msg->from = linphone_address_new(linphone_core_get_identity(cr->lc)); /*direct assignment*/ /* this will be set to application/vnd.gsma.rcs-ft-http+xml when we will transfer the xml reply from server to the peers */ msg->content_type = NULL; /* this will store the http request during file upload to the server */ msg->http_request = NULL; msg->time = ms_time(0); return msg; } linphone-3.12.0/coreapi/conference.cc000066400000000000000000001101551313432737600174650ustar00rootroot00000000000000/******************************************************************************* * conference.cc * * Thu Nov 26, 2015 * Copyright 2015 Belledonne Communications * Author: Linphone's team * Email info@belledonne-communications.com ******************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "private.h" #include "conference_private.h" #include #include #include #include namespace Linphone { template inline std::list<_type> toStd(const bctbx_list_t *l){ std::list<_type> ret; for(; l != NULL; l = l->next){ ret.push_back(static_cast<_type>(l->data)); } return ret; } class Conference { public: class Participant { public: Participant(LinphoneCall *call) { m_uri = linphone_address_clone(linphone_call_get_remote_address(call)); m_call = call; } ~Participant() { linphone_address_unref(m_uri); if(m_call) m_call->conf_ref = NULL; } const LinphoneAddress *getUri() const { return m_uri; } LinphoneCall *getCall() const { return m_call; } private: Participant(const Participant &src); Participant &operator=(const Participant &src); private: LinphoneAddress *m_uri; LinphoneCall *m_call; friend class RemoteConference; }; class Params { public: Params(const LinphoneCore *core = NULL) { m_enableVideo = false; if(core) { const LinphoneVideoPolicy *policy = linphone_core_get_video_policy(core); if(policy->automatically_initiate) m_enableVideo = true; } m_stateChangedCb = NULL; m_userData = NULL; } void enableVideo(bool enable) {m_enableVideo = enable;} bool videoRequested() const {return m_enableVideo;} void setStateChangedCallback(LinphoneConferenceStateChangedCb cb, void *userData) { m_stateChangedCb = cb; m_userData = userData; } private: bool m_enableVideo; LinphoneConferenceStateChangedCb m_stateChangedCb; void *m_userData; friend class Conference; }; Conference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL); virtual ~Conference() {}; const Params &getCurrentParams() const {return m_currentParams;} virtual int inviteAddresses(const std::list &addresses, const LinphoneCallParams *params) = 0; virtual int addParticipant(LinphoneCall *call) = 0; virtual int removeParticipant(LinphoneCall *call) = 0; virtual int removeParticipant(const LinphoneAddress *uri) = 0; virtual int terminate() = 0; virtual int enter() = 0; virtual int leave() = 0; virtual bool isIn() const = 0; AudioStream *getAudioStream() const {return m_localParticipantStream;} int muteMicrophone(bool val); bool microphoneIsMuted() const {return m_isMuted;} float getInputVolume() const; virtual int getSize() const {return (int)m_participants.size() + (isIn()?1:0);} const std::list &getParticipants() const {return m_participants;} virtual int startRecording(const char *path) = 0; virtual int stopRecording() = 0; virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) {}; virtual void onCallStreamStopping(LinphoneCall *call) {}; virtual void onCallTerminating(LinphoneCall *call) {}; LinphoneConferenceState getState() const {return m_state;} LinphoneCore *getCore()const{ return m_core; } static const char *stateToString(LinphoneConferenceState state); protected: void setState(LinphoneConferenceState state); Participant *findParticipant(const LinphoneCall *call) const; Participant *findParticipant(const LinphoneAddress *uri) const; protected: LinphoneCore *m_core; AudioStream *m_localParticipantStream; bool m_isMuted; std::list m_participants; Params m_currentParams; LinphoneConferenceState m_state; LinphoneConference *m_conference; }; class LocalConference: public Conference { public: LocalConference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL); virtual ~LocalConference(); virtual int inviteAddresses(const std::list &addresses, const LinphoneCallParams *params); virtual int addParticipant(LinphoneCall *call); virtual int removeParticipant(LinphoneCall *call); virtual int removeParticipant(const LinphoneAddress *uri); virtual int terminate(); virtual int enter(); virtual int leave(); virtual bool isIn() const {return m_localParticipantStream!=NULL;} virtual int getSize() const; virtual int startRecording(const char *path); virtual int stopRecording(); virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote); virtual void onCallStreamStopping(LinphoneCall *call); virtual void onCallTerminating(LinphoneCall *call); private: void addLocalEndpoint(); int remoteParticipantsCount(); void removeLocalEndpoint(); int removeFromConference(LinphoneCall *call, bool_t active); int convertConferenceToCall(); static RtpProfile *sMakeDummyProfile(int samplerate); MSAudioConference *m_conf; MSAudioEndpoint *m_localEndpoint; MSAudioEndpoint *m_recordEndpoint; RtpProfile *m_localDummyProfile; bool_t m_terminating; }; class RemoteConference: public Conference { public: RemoteConference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL); virtual ~RemoteConference(); virtual int inviteAddresses(const std::list &addresses, const LinphoneCallParams *params); virtual int addParticipant(LinphoneCall *call); virtual int removeParticipant(LinphoneCall *call) {return -1;} virtual int removeParticipant(const LinphoneAddress *uri); virtual int terminate(); virtual int enter(); virtual int leave(); virtual bool isIn() const; virtual int startRecording(const char *path) {return 0;} virtual int stopRecording() {return 0;} private: bool focusIsReady() const; bool transferToFocus(LinphoneCall *call); void reset(); void onFocusCallSateChanged(LinphoneCallState state); void onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state); void onTransferingCallStateChanged(LinphoneCall *transfered, LinphoneCallState newCallState); static void callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message); static void transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state); const char *m_focusAddr; char *m_focusContact; LinphoneCall *m_focusCall; LinphoneCoreCbs *m_coreCbs; std::list m_pendingCalls; std::list m_transferingCalls; }; }; using namespace Linphone; using namespace std; Conference::Conference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params): m_core(core), m_localParticipantStream(NULL), m_isMuted(false), m_currentParams(), m_state(LinphoneConferenceStopped), m_conference(conf) { if(params) m_currentParams = *params; } int Conference::addParticipant(LinphoneCall *call) { Participant *p =new Participant(call); m_participants.push_back(p); call->conf_ref = m_conference; return 0; } int Conference::removeParticipant(LinphoneCall *call) { Participant *p = findParticipant(call); if(p == NULL) return -1; delete p; m_participants.remove(p); return 0; } int Conference::removeParticipant(const LinphoneAddress *uri) { Participant *p = findParticipant(uri); if(p == NULL) return -1; delete p; m_participants.remove(p); return 0; } int Conference::terminate() { for(list::iterator it = m_participants.begin(); it!=m_participants.end(); it++) { delete *it; } m_participants.clear(); return 0; } int Conference::muteMicrophone(bool val) { if (val) { audio_stream_set_mic_gain(m_localParticipantStream, 0); } else { audio_stream_set_mic_gain_db(m_localParticipantStream, m_core->sound_conf.soft_mic_lev); } if ( linphone_core_get_rtp_no_xmit_on_audio_mute(m_core) ){ audio_stream_mute_rtp(m_localParticipantStream, val); } m_isMuted=val; return 0; } float Conference::getInputVolume() const { AudioStream *st=m_localParticipantStream; if (st && st->volsend && !m_isMuted){ float vol=0; ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol); return vol; } return LINPHONE_VOLUME_DB_LOWEST; } const char *Conference::stateToString(LinphoneConferenceState state) { switch(state) { case LinphoneConferenceStopped: return "Stopped"; case LinphoneConferenceStarting: return "Starting"; case LinphoneConferenceRunning: return "Ready"; case LinphoneConferenceStartingFailed: return "Startig failed"; default: return "Invalid state"; } } void Conference::setState(LinphoneConferenceState state) { if(m_state != state) { ms_message("Switching conference [%p] into state '%s'", this, stateToString(state)); m_state = state; if(m_currentParams.m_stateChangedCb) { m_currentParams.m_stateChangedCb(m_conference, state, m_currentParams.m_userData); } } } Conference::Participant *Conference::findParticipant(const LinphoneCall *call) const { for(list::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) { if((*it)->getCall() == call) return *it; } return NULL; } Conference::Participant *Conference::findParticipant(const LinphoneAddress *uri) const { for(list::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) { if(linphone_address_equal((*it)->getUri(), uri)) return *it; } return NULL; } LocalConference::LocalConference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params): Conference(core, conf, params), m_conf(NULL), m_localEndpoint(NULL), m_recordEndpoint(NULL), m_localDummyProfile(NULL), m_terminating(FALSE) { MSAudioConferenceParams ms_conf_params; ms_conf_params.samplerate = lp_config_get_int(m_core->config, "sound","conference_rate",16000); m_conf=ms_audio_conference_new(&ms_conf_params, core->factory); m_state= LinphoneConferenceRunning; } LocalConference::~LocalConference() { terminate(); ms_audio_conference_destroy(m_conf); } RtpProfile *LocalConference::sMakeDummyProfile(int samplerate){ RtpProfile *prof=rtp_profile_new("dummy"); PayloadType *pt=payload_type_clone(&payload_type_l16_mono); pt->clock_rate=samplerate; rtp_profile_set_payload(prof,0,pt); return prof; } void LocalConference::addLocalEndpoint() { /*create a dummy audiostream in order to extract the local part of it */ /* network address and ports have no meaning and are not used here. */ AudioStream *st=audio_stream_new(m_core->factory, 65000,65001,FALSE); MSSndCard *playcard=m_core->sound_conf.lsd_card ? m_core->sound_conf.lsd_card : m_core->sound_conf.play_sndcard; MSSndCard *captcard=m_core->sound_conf.capt_sndcard; const MSAudioConferenceParams *params=ms_audio_conference_get_params(m_conf); m_localDummyProfile=sMakeDummyProfile(params->samplerate); audio_stream_start_full(st, m_localDummyProfile, "127.0.0.1", 65000, "127.0.0.1", 65001, 0, 40, NULL, NULL, playcard, captcard, linphone_core_echo_cancellation_enabled(m_core) ); _post_configure_audio_stream(st,m_core,FALSE); m_localParticipantStream=st; m_localEndpoint=ms_audio_endpoint_get_from_stream(st,FALSE); ms_message("conference: adding local endpoint"); ms_audio_conference_add_member(m_conf,m_localEndpoint); } int LocalConference::inviteAddresses(const std::list &addresses, const LinphoneCallParams *params){ for (std::list::const_iterator it = addresses.begin(); it != addresses.end(); ++it){ const LinphoneAddress *addr = *it; LinphoneCall * call = linphone_core_get_call_by_remote_address2(m_core, addr); if (!call){ /*start a new call by indicating that it has to be put into the conference directlly*/ LinphoneCallParams * new_params = params ? linphone_call_params_copy(params) : linphone_core_create_call_params(m_core, NULL); LinphoneCall *call; /*toggle this flag so the call is immediately added to the conference upon acceptance*/ new_params->in_conference = TRUE; linphone_call_params_enable_video(new_params, FALSE); /*turn off video as it is not supported for conferencing at this time*/ call = linphone_core_invite_address_with_params(m_core, addr, new_params); if (!call){ ms_error("LocalConference::inviteAddresses(): could not invite participant"); } linphone_call_params_unref(new_params); }else{ /*there is already a call to this address, so simply join it to the local conference if not already done*/ if (!call->current_params->in_conference) addParticipant(call); } /*if the local participant is not yet created, created it and it to the conference */ if (!m_localEndpoint) addLocalEndpoint(); } return 0; } int LocalConference::addParticipant(LinphoneCall *call) { if (call->current_params->in_conference){ ms_error("Already in conference"); return -1; } if (call->state==LinphoneCallPaused){ call->params->in_conference=TRUE; call->params->has_video=FALSE; linphone_call_resume(call); }else if (call->state==LinphoneCallStreamsRunning){ LinphoneCallParams *params = linphone_core_create_call_params(m_core, call); params->in_conference=TRUE; params->has_video=FALSE; if (call->audiostream || call->videostream){ linphone_call_stop_media_streams(call); /*free the audio & video local resources*/ linphone_call_init_media_streams(call); } if (call==m_core->current_call){ m_core->current_call=NULL; } /*this will trigger a reINVITE that will later redraw the streams */ /*FIXME probably a bit too much to just redraw streams !*/ linphone_call_update(call,params); linphone_call_params_unref(params); addLocalEndpoint(); }else{ ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state)); return -1; } return 0; } int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){ int err=0; char *str; if (!call->current_params->in_conference){ if (call->params->in_conference){ ms_warning("Not (yet) in conference, be patient"); return -1; }else{ ms_error("Not in a conference."); return -1; } } call->params->in_conference=FALSE; str=linphone_call_get_remote_address_as_string(call); ms_message("%s will be removed from conference", str); ms_free(str); if (active){ LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call)); params->in_conference=FALSE; // reconnect local audio with this call if (isIn()){ ms_message("Leaving conference for reconnecting with unique call."); leave(); } ms_message("Updating call to actually remove from conference"); err=linphone_call_update(call,params); linphone_call_params_unref(params); } else{ ms_message("Pausing call to actually remove from conference"); err=_linphone_call_pause(call); } return err; } int LocalConference::remoteParticipantsCount() { int count=getSize(); if (count==0) return 0; if (!m_localParticipantStream) return count; return count -1; } int LocalConference::convertConferenceToCall(){ int err=0; bctbx_list_t *calls=m_core->calls; if (remoteParticipantsCount()!=1){ ms_error("No unique call remaining in conference."); return -1; } while (calls) { LinphoneCall *rc=(LinphoneCall*)calls->data; calls=calls->next; if (rc->params->in_conference) { // not using current_param bool_t active_after_removed=isIn(); err=removeFromConference(rc, active_after_removed); break; } } return err; } int LocalConference::removeParticipant(LinphoneCall *call) { int err; char * str=linphone_call_get_remote_address_as_string(call); ms_message("Removing call %s from the conference", str); ms_free(str); err=removeFromConference(call, FALSE); if (err){ ms_error("Error removing participant from conference."); return err; } if (remoteParticipantsCount()==1){ ms_message("conference size is 1: need to be converted to plain call"); err=convertConferenceToCall(); } else { ms_message("the conference need not to be converted as size is %i", remoteParticipantsCount()); } return err; } int LocalConference::removeParticipant(const LinphoneAddress *uri) { const Participant *participant = findParticipant(uri); if(participant == NULL) return -1; LinphoneCall *call = participant->getCall(); if(call == NULL) return -1; return removeParticipant(call); } int LocalConference::terminate() { bctbx_list_t *calls=m_core->calls; m_terminating =TRUE; while (calls) { LinphoneCall *call=(LinphoneCall*)calls->data; calls=calls->next; if (call->current_params->in_conference) { linphone_call_terminate(call); } } Conference::terminate(); m_terminating = FALSE; return 0; } int LocalConference::enter() { if (linphone_core_sound_resources_locked(m_core)) { return -1; } if (m_core->current_call != NULL) { _linphone_call_pause(m_core->current_call); } if (m_localParticipantStream==NULL) addLocalEndpoint(); return 0; } void LocalConference::removeLocalEndpoint(){ if (m_localEndpoint){ ms_audio_conference_remove_member(m_conf,m_localEndpoint); ms_audio_endpoint_release_from_stream(m_localEndpoint); m_localEndpoint=NULL; audio_stream_stop(m_localParticipantStream); m_localParticipantStream=NULL; rtp_profile_destroy(m_localDummyProfile); } } int LocalConference::leave() { if (isIn()) removeLocalEndpoint(); return 0; } int LocalConference::getSize() const { if (m_conf == NULL) { return 0; } return ms_audio_conference_get_size(m_conf) - (m_recordEndpoint ? 1 : 0); } int LocalConference::startRecording(const char *path) { if (m_conf == NULL) { ms_warning("linphone_core_start_conference_recording(): no conference now."); return -1; } if (m_recordEndpoint==NULL){ m_recordEndpoint=ms_audio_endpoint_new_recorder(m_core->factory); ms_audio_conference_add_member(m_conf,m_recordEndpoint); } ms_audio_recorder_endpoint_start(m_recordEndpoint,path); return 0; } int LocalConference::stopRecording() { if (m_conf == NULL) { ms_warning("linphone_core_stop_conference_recording(): no conference now."); return -1; } if (m_recordEndpoint==NULL){ ms_warning("linphone_core_stop_conference_recording(): no record active."); return -1; } ms_audio_recorder_endpoint_stop(m_recordEndpoint); return 0; } void LocalConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) { call->params->has_video = FALSE; call->camera_enabled = FALSE; ms_message("LocalConference::onCallStreamStarting(): joining AudioStream [%p] of call [%p] into conference.", call->audiostream, call); MSAudioEndpoint *ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE); ms_audio_conference_add_member(m_conf,ep); ms_audio_conference_mute_member(m_conf,ep,isPausedByRemote); call->endpoint=ep; setState(LinphoneConferenceRunning); Conference::addParticipant(call); } void LocalConference::onCallStreamStopping(LinphoneCall *call) { ms_audio_conference_remove_member(m_conf,call->endpoint); ms_audio_endpoint_release_from_stream(call->endpoint); call->endpoint=NULL; Conference::removeParticipant(call); } void LocalConference::onCallTerminating(LinphoneCall *call) { int remote_count=remoteParticipantsCount(); ms_message("conference_check_uninit(): size=%i", getSize()); if (remote_count==1 && !m_terminating){ convertConferenceToCall(); } if (remote_count==0){ if (m_localParticipantStream) removeLocalEndpoint(); if (m_recordEndpoint){ ms_audio_conference_remove_member(m_conf, m_recordEndpoint); ms_audio_endpoint_destroy(m_recordEndpoint); } setState(LinphoneConferenceStopped); } } RemoteConference::RemoteConference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params): Conference(core, conf, params) { m_focusAddr = NULL; m_focusContact = NULL; m_focusCall = NULL; m_coreCbs = NULL; m_focusAddr = lp_config_get_string(m_core->config, "misc", "conference_focus_addr", ""); m_coreCbs = linphone_factory_create_core_cbs(linphone_factory_get()); linphone_core_cbs_set_call_state_changed(m_coreCbs, callStateChangedCb); linphone_core_cbs_set_transfer_state_changed(m_coreCbs, transferStateChanged); linphone_core_cbs_set_user_data(m_coreCbs, this); _linphone_core_add_callbacks(m_core, m_coreCbs, TRUE); } RemoteConference::~RemoteConference() { terminate(); linphone_core_remove_callbacks(m_core, m_coreCbs); linphone_core_cbs_unref(m_coreCbs); } int RemoteConference::inviteAddresses(const std::list &addresses, const LinphoneCallParams *params){ ms_error("RemoteConference::inviteAddresses() not implemented"); return -1; } int RemoteConference::addParticipant(LinphoneCall *call) { LinphoneAddress *addr; LinphoneCallParams *params; switch(m_state) { case LinphoneConferenceStopped: case LinphoneConferenceStartingFailed: Conference::addParticipant(call); ms_message("Calling the conference focus (%s)", m_focusAddr); addr = linphone_address_new(m_focusAddr); if(addr) { params = linphone_core_create_call_params(m_core, NULL); linphone_call_params_enable_video(params, m_currentParams.videoRequested()); m_focusCall = linphone_core_invite_address_with_params(m_core, addr, params); m_localParticipantStream = m_focusCall->audiostream; m_pendingCalls.push_back(call); LinphoneCallLog *callLog = linphone_call_get_call_log(m_focusCall); callLog->was_conference = TRUE; linphone_address_unref(addr); linphone_call_params_unref(params); setState(LinphoneConferenceStarting); return 0; } else return -1; case LinphoneConferenceStarting: Conference::addParticipant(call); if(focusIsReady()) { transferToFocus(call); } else { m_pendingCalls.push_back(call); } return 0; case LinphoneConferenceRunning: Conference::addParticipant(call); transferToFocus(call); return 0; default: ms_error("Could not add call %p to the conference. Bad conference state (%s)", call, stateToString(m_state)); return -1; } } int RemoteConference::removeParticipant(const LinphoneAddress *uri) { char *refer_to; LinphoneAddress *refer_to_addr; int res; switch(m_state) { case LinphoneConferenceRunning: if(findParticipant(uri) == NULL) { char *tmp = linphone_address_as_string(uri); ms_error("Conference: could not remove participant '%s': not in the participants list", tmp); ms_free(tmp); return -1; } refer_to_addr = linphone_address_clone(uri); linphone_address_set_method_param(refer_to_addr, "BYE"); refer_to = linphone_address_as_string(refer_to_addr); linphone_address_unref(refer_to_addr); res = sal_call_refer(m_focusCall->op, refer_to); ms_free(refer_to); if(res == 0) { return Conference::removeParticipant(uri); } else { char *tmp = linphone_address_as_string(uri); ms_error("Conference: could not remove participant '%s': REFER with BYE has failed", tmp); ms_free(tmp); return -1; } default: ms_error("Cannot remove %s from conference: Bad conference state (%s)", linphone_address_as_string(uri), stateToString(m_state)); return -1; } } int RemoteConference::terminate() { switch(m_state) { case LinphoneConferenceRunning: case LinphoneConferenceStarting: linphone_call_terminate(m_focusCall); break; default: break; } return 0; } int RemoteConference::enter() { if(m_state != LinphoneConferenceRunning) { ms_error("Could not enter in the conference: bad conference state (%s)", stateToString(m_state)); return -1; } LinphoneCallState callState = linphone_call_get_state(m_focusCall); switch(callState) { case LinphoneCallStreamsRunning: break; case LinphoneCallPaused: linphone_call_resume(m_focusCall); break; default: ms_error("Could not join the conference: bad focus call state (%s)", linphone_call_state_to_string(callState)); return -1; } return 0; } int RemoteConference::leave() { if(m_state != LinphoneConferenceRunning) { ms_error("Could not leave the conference: bad conference state (%s)", stateToString(m_state)); return -1; } LinphoneCallState callState = linphone_call_get_state(m_focusCall); switch(callState) { case LinphoneCallPaused: break; case LinphoneCallStreamsRunning: linphone_call_pause(m_focusCall); break; default: ms_error("Could not leave the conference: bad focus call state (%s)", linphone_call_state_to_string(callState)); return -1; } return 0; } bool RemoteConference::isIn() const { if(m_state != LinphoneConferenceRunning) return false; LinphoneCallState callState = linphone_call_get_state(m_focusCall); return callState == LinphoneCallStreamsRunning; } bool RemoteConference::focusIsReady() const { LinphoneCallState focusState; if(m_focusCall == NULL) return false; focusState = linphone_call_get_state(m_focusCall); return focusState == LinphoneCallStreamsRunning || focusState == LinphoneCallPaused; } bool RemoteConference::transferToFocus(LinphoneCall *call) { if(linphone_call_transfer(call, m_focusContact) == 0) { m_transferingCalls.push_back(call); return true; } else { ms_error("Conference: could not transfer call [%p] to %s", call, m_focusContact); return false; } } void RemoteConference::reset() { m_localParticipantStream = NULL; m_focusAddr = NULL; if(m_focusContact) { ms_free(m_focusContact); m_focusContact = NULL; } m_focusCall = NULL; m_pendingCalls.clear(); m_transferingCalls.clear(); } void RemoteConference::onFocusCallSateChanged(LinphoneCallState state) { list::iterator it; switch (state) { case LinphoneCallConnected: m_focusContact = ms_strdup(linphone_call_get_remote_contact(m_focusCall)); it = m_pendingCalls.begin(); while (it != m_pendingCalls.end()) { LinphoneCall *pendingCall = *it; LinphoneCallState pendingCallState = linphone_call_get_state(pendingCall); if(pendingCallState == LinphoneCallStreamsRunning || pendingCallState == LinphoneCallPaused) { it = m_pendingCalls.erase(it); transferToFocus(pendingCall); } else { it++; } } setState(LinphoneConferenceRunning); break; case LinphoneCallError: reset(); Conference::terminate(); setState(LinphoneConferenceStartingFailed); break; case LinphoneCallEnd: reset(); Conference::terminate(); setState(LinphoneConferenceStopped); break; default: break; } } void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) { switch(state) { case LinphoneCallStreamsRunning: case LinphoneCallPaused: if(m_state == LinphoneConferenceRunning) { m_pendingCalls.remove(call); m_transferingCalls.push_back(call); linphone_call_transfer(call, m_focusContact); } break; case LinphoneCallError: case LinphoneCallEnd: m_pendingCalls.remove(call); Conference::removeParticipant(call); if(m_participants.size() + m_pendingCalls.size() + m_transferingCalls.size() == 0) { terminate(); } break; default: break; } } void RemoteConference::onTransferingCallStateChanged(LinphoneCall* transfered, LinphoneCallState newCallState) { switch (newCallState) { case LinphoneCallConnected: m_transferingCalls.push_back(transfered); findParticipant(transfered)->m_call = NULL; break; case LinphoneCallError: m_transferingCalls.remove(transfered); Conference::removeParticipant(transfered); if(m_participants.size() + m_pendingCalls.size() + m_transferingCalls.size() == 0) { terminate(); } break; default: break; } } void RemoteConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable); if (call == conf->m_focusCall) { conf->onFocusCallSateChanged(cstate); } else { list::iterator it = find(conf->m_pendingCalls.begin(), conf->m_pendingCalls.end(), call); if(it != conf->m_pendingCalls.end()) { conf->onPendingCallStateChanged(call, cstate); } } } void RemoteConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable); list::iterator it = find(conf->m_transferingCalls.begin(), conf->m_transferingCalls.end(), transfered); if (it != conf->m_transferingCalls.end()) { conf->onTransferingCallStateChanged(transfered, new_call_state); } } const char *linphone_conference_state_to_string(LinphoneConferenceState state) { return Conference::stateToString(state); } struct _LinphoneConferenceParams { ::belle_sip_object_t base; Conference::Params *params; }; static void _linphone_conference_params_uninit(LinphoneConferenceParams *params); static void _linphone_conference_params_clone(LinphoneConferenceParams *params, const LinphoneConferenceParams *orig); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneConferenceParams); BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneConferenceParams); BELLE_SIP_INSTANCIATE_VPTR(LinphoneConferenceParams, belle_sip_object_t, _linphone_conference_params_uninit, // uninit _linphone_conference_params_clone, // clone NULL, // marshal FALSE // unown ); LinphoneConferenceParams *linphone_conference_params_new(const LinphoneCore *core) { LinphoneConferenceParams *obj = belle_sip_object_new(LinphoneConferenceParams); obj->params = new Conference::Params(core); return obj; } static void _linphone_conference_params_uninit(LinphoneConferenceParams *params) { delete params->params; } LinphoneConferenceParams *linphone_conference_params_ref(LinphoneConferenceParams *params) { return (LinphoneConferenceParams *)belle_sip_object_ref(params); } void linphone_conference_params_unref(LinphoneConferenceParams *params) { belle_sip_object_unref(params); } void linphone_conference_params_free(LinphoneConferenceParams *params) { linphone_conference_params_unref(params); } static void _linphone_conference_params_clone(LinphoneConferenceParams *params, const LinphoneConferenceParams *orig) { params->params = new Conference::Params(*orig->params); } LinphoneConferenceParams *linphone_conference_params_clone(const LinphoneConferenceParams *params) { return (LinphoneConferenceParams *)belle_sip_object_clone((const belle_sip_object_t *)params); } void linphone_conference_params_enable_video(LinphoneConferenceParams *params, bool_t enable) { params->params->enableVideo(enable ? true : false); } bool_t linphone_conference_params_video_requested(const LinphoneConferenceParams *params) { return params->params->videoRequested() ? TRUE : FALSE; } void linphone_conference_params_set_state_changed_callback(LinphoneConferenceParams *params, LinphoneConferenceStateChangedCb cb, void *user_data) { params->params->setStateChangedCallback(cb, user_data); } struct _LinphoneConference { belle_sip_object_t base; Conference *conf; }; static void _linphone_conference_uninit(LinphoneConference *conf); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneConference); BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneConference); BELLE_SIP_INSTANCIATE_VPTR(LinphoneConference, belle_sip_object_t, _linphone_conference_uninit, // uninit NULL, // clone NULL, // marshal FALSE // unown ); LinphoneConference *linphone_local_conference_new(LinphoneCore *core) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new LocalConference(core, conf); return conf; } LinphoneConference *linphone_local_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new LocalConference(core, conf, params->params); return conf; } LinphoneConference *linphone_remote_conference_new(LinphoneCore *core) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new RemoteConference(core, conf); return conf; } LinphoneConference *linphone_remote_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new RemoteConference(core, conf, params->params); return conf; } static void _linphone_conference_uninit(LinphoneConference *conf) { delete conf->conf; } LinphoneConference *linphone_conference_ref(LinphoneConference *conf) { return (LinphoneConference *)belle_sip_object_ref(conf); } void linphone_conference_unref(LinphoneConference *conf) { belle_sip_object_unref(conf); } LinphoneConferenceState linphone_conference_get_state(const LinphoneConference *obj) { return obj->conf->getState(); } int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call) { return obj->conf->addParticipant(call); } LinphoneStatus linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri) { return obj->conf->removeParticipant(uri); } int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call) { return obj->conf->removeParticipant(call); } int linphone_conference_terminate(LinphoneConference *obj) { return obj->conf->terminate(); } int linphone_conference_enter(LinphoneConference *obj) { return obj->conf->enter(); } int linphone_conference_leave(LinphoneConference *obj) { return obj->conf->leave(); } bool_t linphone_conference_is_in(const LinphoneConference *obj) { return obj->conf->isIn(); } AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj) { return obj->conf->getAudioStream(); } int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val) { return obj->conf->muteMicrophone(val ? true : false); } bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj) { return obj->conf->microphoneIsMuted() ? TRUE : FALSE; } float linphone_conference_get_input_volume(const LinphoneConference *obj) { return obj->conf->getInputVolume(); } int linphone_conference_get_size(const LinphoneConference *obj) { return obj->conf->getSize(); } bctbx_list_t *linphone_conference_get_participants(const LinphoneConference *obj) { const list &participants = obj->conf->getParticipants(); bctbx_list_t *participants_list = NULL; for(list::const_iterator it=participants.begin();it!=participants.end();it++) { LinphoneAddress *uri = linphone_address_clone((*it)->getUri()); participants_list = bctbx_list_append(participants_list, uri); } return participants_list; } int linphone_conference_start_recording(LinphoneConference *obj, const char *path) { return obj->conf->startRecording(path); } int linphone_conference_stop_recording(LinphoneConference *obj) { return obj->conf->stopRecording(); } void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote) { obj->conf->onCallStreamStarting(call, is_paused_by_remote ? true : false); } void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call) { obj->conf->onCallStreamStopping(call); } void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call) { obj->conf->onCallTerminating(call); } bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class) { switch(_class) { case LinphoneConferenceClassLocal: return typeid(obj->conf) == typeid(LocalConference); case LinphoneConferenceClassRemote: return typeid(obj->conf) == typeid(RemoteConference); default: return FALSE; } } LinphoneStatus linphone_conference_invite_participants(LinphoneConference *obj, const bctbx_list_t *addresses, const LinphoneCallParams *params){ return obj->conf->inviteAddresses(toStd(addresses), params); } linphone-3.12.0/coreapi/conference_private.h000066400000000000000000000113121313432737600210540ustar00rootroot00000000000000/******************************************************************************* * conference_private.h * * Tue Jan 12, 2015 * Copyright 2015 Belledonne Communications * Author: Linphone's team * Email info@belledonne-communications.com ******************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef CONFERENCE_PRIVATE_H #define CONFERENCE_PRIVATE_H #include "linphone/core.h" #include "linphone/conference.h" #ifdef __cplusplus extern "C" { #endif typedef enum { LinphoneConferenceClassLocal, LinphoneConferenceClassRemote } LinphoneConferenceClass; /** * List of states used by #LinphoneConference */ typedef enum { LinphoneConferenceStopped, /*< Initial state */ LinphoneConferenceStarting, /*< A participant has been added but the conference is not running yet */ LinphoneConferenceRunning, /*< The conference is running */ LinphoneConferenceStartingFailed /*< A participant has been added but the initialization of the conference has failed */ } LinphoneConferenceState; /** * Type of the funtion to pass as callback to linphone_conference_params_set_state_changed_callback() * @param conference The conference instance which the state has changed * @param new_state The new state of the conferenece * @param user_data Pointer pass to user_data while linphone_conference_params_set_state_changed_callback() was being called */ typedef void (*LinphoneConferenceStateChangedCb)(LinphoneConference *conference, LinphoneConferenceState new_state, void *user_data); /** * A function to converte a #LinphoneConferenceState into a string */ const char *linphone_conference_state_to_string(LinphoneConferenceState state); /** * Set a callback which will be called when the state of the conferenec is switching * @param params A #LinphoneConferenceParams object * @param cb The callback to call * @param user_data Pointer to pass to the user_data parameter of #LinphoneConferenceStateChangedCb */ void linphone_conference_params_set_state_changed_callback(LinphoneConferenceParams *params, LinphoneConferenceStateChangedCb cb, void *user_data); LinphoneConference *linphone_local_conference_new(LinphoneCore *core); LinphoneConference *linphone_local_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params); LinphoneConference *linphone_remote_conference_new(LinphoneCore *core); LinphoneConference *linphone_remote_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params); /** * Get the state of a conference */ LinphoneConferenceState linphone_conference_get_state(const LinphoneConference *obj); int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call); int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call); int linphone_conference_terminate(LinphoneConference *obj); int linphone_conference_get_size(const LinphoneConference *obj); int linphone_conference_enter(LinphoneConference *obj); int linphone_conference_leave(LinphoneConference *obj); bool_t linphone_conference_is_in(const LinphoneConference *obj); AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj); int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val); bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj); float linphone_conference_get_input_volume(const LinphoneConference *obj); int linphone_conference_start_recording(LinphoneConference *obj, const char *path); int linphone_conference_stop_recording(LinphoneConference *obj); void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote); void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call); void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call); LINPHONE_PUBLIC bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class); #ifdef __cplusplus } #endif #endif //CONFERENCE_PRIVATE_H linphone-3.12.0/coreapi/contact_providers_priv.h000066400000000000000000000047221313432737600220120ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef CONTACT_PROVIDERS_PRIV_H #define CONTACT_PROVIDERS_PRIV_H #include "private.h" #include "linphone/core.h" /* Base for contact search and contact provider */ struct _LinphoneContactSearch{ belle_sip_object_t base; LinphoneContactSearchID id; char* predicate; ContactSearchCallback cb; void* data; }; #define LINPHONE_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneContactSearch) BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneContactSearch) struct _LinphoneContactProvider { belle_sip_object_t base; LinphoneCore* lc; }; #define LINPHONE_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneContactProvider) typedef LinphoneContactSearch* (*LinphoneContactProviderStartSearchMethod)( LinphoneContactProvider* thiz, const char* predicate, ContactSearchCallback cb, void* data ); typedef unsigned int (*LinphoneContactProviderCancelSearchMethod)( LinphoneContactProvider* thiz, LinphoneContactSearch *request ); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN_NO_EXPORT(LinphoneContactProvider,belle_sip_object_t) const char* name; /*!< Name of the contact provider (LDAP, Google, ...) */ /* pure virtual methods: inheriting objects must implement these */ LinphoneContactProviderStartSearchMethod begin_search; LinphoneContactProviderCancelSearchMethod cancel_search; BELLE_SIP_DECLARE_CUSTOM_VPTR_END /* LDAP search and contact providers */ #define LINPHONE_LDAP_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactSearch) BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneLDAPContactSearch) #define LINPHONE_LDAP_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactProvider) BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN_NO_EXPORT(LinphoneLDAPContactProvider,LinphoneContactProvider) BELLE_SIP_DECLARE_CUSTOM_VPTR_END #endif // CONTACT_PROVIDERS_PRIV_H linphone-3.12.0/coreapi/contactprovider.c000066400000000000000000000101411313432737600204130ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "contact_providers_priv.h" #include "linphone/contactprovider.h" #include /* ############################ * * LinphoneContactSearchRequest * * ############################ */ void linphone_contact_search_init(LinphoneContactSearch* obj, const char* predicate, ContactSearchCallback cb, void* cb_data) { static unsigned int request_id_counter = 1; obj->id = request_id_counter++; // unique id obj->predicate = ms_strdup(predicate?predicate:""); obj->cb = cb; obj->data = cb_data; } static void linphone_contact_search_destroy( LinphoneContactSearch* req) { if( req->predicate ) ms_free(req->predicate); } LinphoneContactSearchID linphone_contact_search_get_id(LinphoneContactSearch* obj) { return obj->id; } const char*linphone_contact_search_get_predicate(LinphoneContactSearch* obj) { return obj->predicate; } void linphone_contact_search_invoke_cb(LinphoneContactSearch* req, MSList* friends) { if( req->cb ) req->cb(req, friends, req->data); } int linphone_contact_search_compare(const void* a, const void* b) { LinphoneContactSearch *ra=((LinphoneContactSearch*)a); LinphoneContactSearch *rb=((LinphoneContactSearch*)b); return !(ra->id == rb->id); // return 0 if id is equal, 1 otherwise } LinphoneContactSearch*linphone_contact_search_ref(void* obj) { return LINPHONE_CONTACT_SEARCH(belle_sip_object_ref(obj)); } void linphone_ldap_contact_search_unref(void* obj) { belle_sip_object_unref(obj); } LinphoneContactSearch* linphone_contact_search_cast(void* obj) { return LINPHONE_CONTACT_SEARCH(obj); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactSearch); BELLE_SIP_INSTANCIATE_VPTR(LinphoneContactSearch,belle_sip_object_t, (belle_sip_object_destroy_t)linphone_contact_search_destroy, NULL, // clone NULL, // marshal FALSE ); /* ####################### * * LinphoneContactProvider * * ####################### */ void linphone_contact_provider_init(LinphoneContactProvider* obj, LinphoneCore* lc){ obj->lc = lc; } static void contact_provider_destroy(LinphoneContactProvider* obj){ (void)obj; } LinphoneContactSearch* linphone_contact_provider_begin_search(LinphoneContactProvider* obj, const char* predicate, ContactSearchCallback cb, void* data) { return BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->begin_search( LINPHONE_CONTACT_PROVIDER(obj), predicate, cb, data); } unsigned int linphone_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch* request) { return BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->cancel_search( LINPHONE_CONTACT_PROVIDER(obj), request); } LinphoneContactProvider* linphone_contact_provider_ref(void* obj) { return LINPHONE_CONTACT_PROVIDER(belle_sip_object_ref(obj)); } void linphone_contact_provider_unref(void* obj) { belle_sip_object_unref(obj); } LinphoneContactProvider*linphone_contact_provider_cast(void* obj) { return LINPHONE_CONTACT_PROVIDER(obj); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactProvider); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider) { BELLE_SIP_VPTR_INIT(LinphoneContactProvider,belle_sip_object_t,TRUE), (belle_sip_object_destroy_t) contact_provider_destroy, NULL,/*no clone*/ NULL,/*no marshal*/ BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "", // Pure virtual NULL, /* begin_search -> pure virtual */ NULL /* cancel_search -> pure virtual */ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END linphone-3.12.0/coreapi/content.c000066400000000000000000000205341313432737600166660ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2014 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "private.h" static void linphone_content_set_sal_body_handler(LinphoneContent *content, SalBodyHandler *body_handler) { if (content->body_handler != NULL) { sal_body_handler_unref(content->body_handler); content->body_handler = NULL; } content->body_handler = sal_body_handler_ref(body_handler); } static LinphoneContent * linphone_content_new_with_body_handler(SalBodyHandler *body_handler) { LinphoneContent *content = belle_sip_object_new(LinphoneContent); belle_sip_object_ref(content); content->owned_fields = TRUE; content->cryptoContext = NULL; /* this field is managed externally by encryption/decryption functions so be careful to initialise it to NULL */ if (body_handler == NULL) { linphone_content_set_sal_body_handler(content, sal_body_handler_new()); } else { linphone_content_set_sal_body_handler(content, body_handler); } return content; } static void linphone_content_destroy(LinphoneContent *content) { if (content->owned_fields == TRUE) { if (content->body_handler) sal_body_handler_unref(content->body_handler); if (content->name) belle_sip_free(content->name); if (content->key) belle_sip_free(content->key); /* note : crypto context is allocated/destroyed by the encryption function */ } } static void linphone_content_clone(LinphoneContent *obj, const LinphoneContent *ref) { obj->owned_fields = TRUE; linphone_content_set_sal_body_handler(obj, sal_body_handler_new()); if ((linphone_content_get_type(ref) != NULL) || (linphone_content_get_subtype(ref) != NULL)) { linphone_content_set_type(obj, linphone_content_get_type(ref)); linphone_content_set_subtype(obj, linphone_content_get_subtype(ref)); } if (linphone_content_get_encoding(ref) != NULL) { linphone_content_set_encoding(obj, linphone_content_get_encoding(ref)); } linphone_content_set_name(obj, linphone_content_get_name(ref)); linphone_content_set_key(obj, linphone_content_get_key(ref), linphone_content_get_key_size(ref)); if (linphone_content_get_buffer(ref) != NULL) { linphone_content_set_buffer(obj, linphone_content_get_buffer(ref), linphone_content_get_size(ref)); } else { linphone_content_set_size(obj, linphone_content_get_size(ref)); } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContent); BELLE_SIP_INSTANCIATE_VPTR(LinphoneContent, belle_sip_object_t, (belle_sip_object_destroy_t)linphone_content_destroy, (belle_sip_object_clone_t)linphone_content_clone, NULL, // marshal TRUE ); LinphoneContent * linphone_core_create_content(LinphoneCore *lc) { return linphone_content_new(); } LinphoneContent * linphone_content_ref(LinphoneContent *content) { belle_sip_object_ref(content); return content; } void linphone_content_unref(LinphoneContent *content) { belle_sip_object_unref(content); } void *linphone_content_get_user_data(const LinphoneContent *content) { return content->user_data; } void linphone_content_set_user_data(LinphoneContent *content, void *ud) { content->user_data = ud; } const char * linphone_content_get_type(const LinphoneContent *content) { return sal_body_handler_get_type(content->body_handler); } void linphone_content_set_type(LinphoneContent *content, const char *type) { sal_body_handler_set_type(content->body_handler, type); } const char * linphone_content_get_subtype(const LinphoneContent *content) { return sal_body_handler_get_subtype(content->body_handler); } void linphone_content_set_subtype(LinphoneContent *content, const char *subtype) { sal_body_handler_set_subtype(content->body_handler, subtype); } void * linphone_content_get_buffer(const LinphoneContent *content) { return sal_body_handler_get_data(content->body_handler); } void linphone_content_set_buffer(LinphoneContent *content, const void *buffer, size_t size) { void *data; sal_body_handler_set_size(content->body_handler, size); data = belle_sip_malloc(size + 1); memcpy(data, buffer, size); ((char *)data)[size] = '\0'; sal_body_handler_set_data(content->body_handler, data); } const char * linphone_content_get_string_buffer(const LinphoneContent *content) { return (const char *)linphone_content_get_buffer(content); } void linphone_content_set_string_buffer(LinphoneContent *content, const char *buffer) { sal_body_handler_set_size(content->body_handler, strlen(buffer)); sal_body_handler_set_data(content->body_handler, belle_sip_strdup(buffer)); } size_t linphone_content_get_size(const LinphoneContent *content) { return sal_body_handler_get_size(content->body_handler); } void linphone_content_set_size(LinphoneContent *content, size_t size) { sal_body_handler_set_size(content->body_handler, size); } const char * linphone_content_get_encoding(const LinphoneContent *content) { return sal_body_handler_get_encoding(content->body_handler); } void linphone_content_set_encoding(LinphoneContent *content, const char *encoding) { sal_body_handler_set_encoding(content->body_handler, encoding); } const char * linphone_content_get_name(const LinphoneContent *content) { return content->name; } void linphone_content_set_name(LinphoneContent *content, const char *name) { if (content->name != NULL) { belle_sip_free(content->name); content->name = NULL; } if (name != NULL) { content->name = belle_sip_strdup(name); } } size_t linphone_content_get_key_size(const LinphoneContent *content) { return content->keyLength; } const char * linphone_content_get_key(const LinphoneContent *content) { return content->key; } void linphone_content_set_key(LinphoneContent *content, const char *key, const size_t keyLength) { if (content->key != NULL) { belle_sip_free(content->key); content->key = NULL; } if (key != NULL) { content->key = belle_sip_malloc(keyLength + 1); memcpy(content->key, key, keyLength); content->key[keyLength] = '\0'; content->keyLength = keyLength; } } /* crypto context is managed(allocated/freed) by the encryption function, so provide the address of field in the private structure */ void ** linphone_content_get_cryptoContext_address(LinphoneContent *content) { return &(content->cryptoContext); } bool_t linphone_content_is_multipart(const LinphoneContent *content) { return sal_body_handler_is_multipart(content->body_handler); } LinphoneContent * linphone_content_get_part(const LinphoneContent *content, int idx) { SalBodyHandler *part_body_handler; if (!linphone_content_is_multipart(content)) return NULL; part_body_handler = sal_body_handler_get_part(content->body_handler, idx); return linphone_content_from_sal_body_handler(part_body_handler); } LinphoneContent * linphone_content_find_part_by_header(const LinphoneContent *content, const char *header_name, const char *header_value) { SalBodyHandler *part_body_handler; if (!linphone_content_is_multipart(content)) return NULL; part_body_handler = sal_body_handler_find_part_by_header(content->body_handler, header_name, header_value); return linphone_content_from_sal_body_handler(part_body_handler); } const char * linphone_content_get_custom_header(const LinphoneContent *content, const char *header_name) { return sal_body_handler_get_header(content->body_handler, header_name); } LinphoneContent * linphone_content_new(void) { return linphone_content_new_with_body_handler(NULL); } LinphoneContent * linphone_content_copy(const LinphoneContent *ref) { return (LinphoneContent *)belle_sip_object_ref(belle_sip_object_clone(BELLE_SIP_OBJECT(ref))); } LinphoneContent * linphone_content_from_sal_body_handler(SalBodyHandler *body_handler) { if (body_handler) { return linphone_content_new_with_body_handler(body_handler); } return NULL; } SalBodyHandler * sal_body_handler_from_content(const LinphoneContent *content) { if (content == NULL) return NULL; return content->body_handler; } linphone-3.12.0/coreapi/dial_plan.c000066400000000000000000000446401313432737600171430ustar00rootroot00000000000000/* linphone Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org) */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core_utils.h" /* * http://en.wikipedia.org/wiki/Telephone_numbering_plan * http://en.wikipedia.org/wiki/Telephone_numbers_in_Europe * imported from https://en.wikipedia.org/wiki/List_of_mobile_phone_number_series_by_country */ static LinphoneDialPlan const dial_plans[]={ //Country , iso country code, e164 country calling code, number length, international usual prefix {"Afghanistan" ,"AF" , "93" , 9 , "00" }, {"Albania" ,"AL" , "355" , 9 , "00" }, {"Algeria" ,"DZ" , "213" , 9 , "00" }, {"American Samoa" ,"AS" , "1" , 10 , "011" }, {"Andorra" ,"AD" , "376" , 6 , "00" }, {"Angola" ,"AO" , "244" , 9 , "00" }, {"Anguilla" ,"AI" , "1" , 10 , "011" }, {"Antigua and Barbuda" ,"AG" , "1" , 10 , "011" }, {"Argentina" ,"AR" , "54" , 10 , "00" }, {"Armenia" ,"AM" , "374" , 8 , "00" }, {"Aruba" ,"AW" , "297" , 7 , "011" }, {"Australia" ,"AU" , "61" , 9 , "0011"}, {"Austria" ,"AT" , "43" , 10 , "00" }, {"Azerbaijan" ,"AZ" , "994" , 9 , "00" }, {"Bahamas" ,"BS" , "1" , 10 , "011" }, {"Bahrain" ,"BH" , "973" , 8 , "00" }, {"Bangladesh" ,"BD" , "880" , 10 , "00" }, {"Barbados" ,"BB" , "1" , 10 , "011" }, {"Belarus" ,"BY" , "375" , 9 , "00" }, {"Belgium" ,"BE" , "32" , 9 , "00" }, {"Belize" ,"BZ" , "501" , 7 , "00" }, {"Benin" ,"BJ" , "229" , 8 , "00" }, {"Bermuda" ,"BM" , "1" , 10 , "011" }, {"Bhutan" ,"BT" , "975" , 8 , "00" }, {"Bolivia" ,"BO" , "591" , 8 , "00" }, {"Bosnia and Herzegovina" ,"BA" , "387" , 8 , "00" }, {"Botswana" ,"BW" , "267" , 8 , "00" }, {"Brazil" ,"BR" , "55" , 10 , "00" }, {"Brunei Darussalam" ,"BN" , "673" , 7 , "00" }, {"Bulgaria" ,"BG" , "359" , 9 , "00" }, {"Burkina Faso" ,"BF" , "226" , 8 , "00" }, {"Burundi" ,"BI" , "257" , 8 , "011" }, {"Cambodia" ,"KH" , "855" , 9 , "00" }, {"Cameroon" ,"CM" , "237" , 8 , "00" }, {"Canada" ,"CA" , "1" , 10 , "011" }, {"Cape Verde" ,"CV" , "238" , 7 , "00" }, {"Cayman Islands" ,"KY" , "1" , 10 , "011" }, {"Central African Republic" ,"CF" , "236" , 8 , "00" }, {"Chad" ,"TD" , "235" , 8 , "00" }, {"Chile" ,"CL" , "56" , 9 , "00" }, {"China" ,"CN" , "86" , 11 , "00" }, {"Colombia" ,"CO" , "57" , 10 , "00" }, {"Comoros" ,"KM" , "269" , 7 , "00" }, {"Congo" ,"CG" , "242" , 9 , "00" }, {"Congo Democratic Republic" ,"CD" , "243" , 9 , "00" }, {"Cook Islands" ,"CK" , "682" , 5 , "00" }, {"Costa Rica" ,"CR" , "506" , 8 , "00" }, {"Cote d'Ivoire" ,"AD" , "225" , 8 , "00" }, {"Croatia" ,"HR" , "385" , 9 , "00" }, {"Cuba" ,"CU" , "53" , 8 , "119" }, {"Cyprus" ,"CY" , "357" , 8 , "00" }, {"Czech Republic" ,"CZ" , "420" , 9 , "00" }, {"Denmark" ,"DK" , "45" , 8 , "00" }, {"Djibouti" ,"DJ" , "253" , 8 , "00" }, {"Dominica" ,"DM" , "1" , 10 , "011" }, {"Dominican Republic" ,"DO" , "1" , 10 , "011" }, {"Ecuador" ,"EC" , "593" , 9 , "00" }, {"Egypt" ,"EG" , "20" , 10 , "00" }, {"El Salvador" ,"SV" , "503" , 8 , "00" }, {"Equatorial Guinea" ,"GQ" , "240" , 9 , "00" }, {"Eritrea" ,"ER" , "291" , 7 , "00" }, {"Estonia" ,"EE" , "372" , 8 , "00" }, {"Ethiopia" ,"ET" , "251" , 9 , "00" }, {"Falkland Islands" ,"FK" , "500" , 5 , "00" }, {"Faroe Islands" ,"FO" , "298" , 6 , "00" }, {"Fiji" ,"FJ" , "679" , 7 , "00" }, {"Finland" ,"FI" , "358" , 9 , "00" }, {"France" ,"FR" , "33" , 9 , "00" }, {"French Guiana" ,"GF" , "594" , 9 , "00" }, {"French Polynesia" ,"PF" , "689" , 6 , "00" }, {"Gabon" ,"GA" , "241" , 8 , "00" }, {"Gambia" ,"GM" , "220" , 7 , "00" }, {"Georgia" ,"GE" , "995" , 9 , "00" }, {"Germany" ,"DE" , "49" , 11 , "00" }, {"Ghana" ,"GH" , "233" , 9 , "00" }, {"Gibraltar" ,"GI" , "350" , 8 , "00" }, {"Greece" ,"GR" , "30" ,10 , "00" }, {"Greenland" ,"GL" , "299" , 6 , "00" }, {"Grenada" ,"GD" , "1" , 10 , "011" }, {"Guadeloupe" ,"GP" , "590" , 9 , "00" }, {"Guam" ,"GU" , "1" , 10 , "011" }, {"Guatemala" ,"GT" , "502" , 8 , "00" }, {"Guinea" ,"GN" , "224" , 8 , "00" }, {"Guinea-Bissau" ,"GW" , "245" , 7 , "00" }, {"Guyana" ,"GY" , "592" , 7 , "001" }, {"Haiti" ,"HT" , "509" , 8 , "00" }, {"Honduras" ,"HN" , "504" , 8 , "00" }, {"Hong Kong" ,"HK" , "852" , 8 , "001" }, {"Hungary" ,"HU" , "36" , 9 , "00" }, {"Iceland" ,"IS" , "354" , 9 , "00" }, {"India" ,"IN" , "91" , 10 , "00" }, {"Indonesia" ,"ID" , "62" , 10 , "001" }, {"Iran" ,"IR" , "98" , 10 , "00" }, {"Iraq" ,"IQ" , "964" , 10 , "00" }, {"Ireland" ,"IE" , "353" , 9 , "00" }, {"Israel" ,"IL" , "972" , 9 , "00" }, {"Italy" ,"IT" , "39" , 10 , "00" }, /* {"Jersey" ,"JE" , "44" , 10 , "00" },*/ {"Jamaica" ,"JM" , "1" , 10 , "011" }, {"Japan" ,"JP" , "81" , 10 , "010" }, {"Jordan" ,"JO" , "962" , 9 , "00" }, {"Kazakhstan" ,"KZ" , "7" , 10 , "00" }, {"Kenya" ,"KE" , "254" , 9 , "000" }, {"Kiribati" ,"KI" , "686" , 5 , "00" }, {"Korea, North" ,"KP" , "850" , 12 , "99" }, {"Korea, South" ,"KR" , "82" , 12 , "001" }, {"Kuwait" ,"KW" , "965" , 8 , "00" }, {"Kyrgyzstan" ,"KG" , "996" , 9 , "00" }, {"Laos" ,"LA" , "856" , 10 , "00" }, {"Latvia" ,"LV" , "371" , 8 , "00" }, {"Lebanon" ,"LB" , "961" , 7 , "00" }, {"Lesotho" ,"LS" , "266" , 8 , "00" }, {"Liberia" ,"LR" , "231" , 8 , "00" }, {"Libya" ,"LY" , "218" , 8 , "00" }, {"Liechtenstein" ,"LI" , "423" , 7 , "00" }, {"Lithuania" ,"LT" , "370" , 8 , "00" }, {"Luxembourg" ,"LU" , "352" , 9 , "00" }, {"Macau" ,"MO" , "853" , 8 , "00" }, {"Macedonia" ,"MK" , "389" , 8 , "00" }, {"Madagascar" ,"MG" , "261" , 9 , "00" }, {"Malawi" ,"MW" , "265" , 9 , "00" }, {"Malaysia" ,"MY" , "60" , 9 , "00" }, {"Maldives" ,"MV" , "960" , 7 , "00" }, {"Mali" ,"ML" , "223" , 8 , "00" }, {"Malta" ,"MT" , "356" , 8 , "00" }, {"Marshall Islands" ,"MH" , "692" , 7 , "011" }, {"Martinique" ,"MQ" , "596" , 9 , "00" }, {"Mauritania" ,"MR" , "222" , 8 , "00" }, {"Mauritius" ,"MU" , "230" , 7 , "00" }, {"Mayotte Island" ,"YT" , "262" , 9 , "00" }, {"Mexico" ,"MX" , "52" , 10 , "00" }, /*The following is a pseudo dial plan for Mexican mobile phones. See https://en.wikipedia.org/wiki/Telephone_numbers_in_Mexico*/ {"Mexico" ,"MX" , "521" , 10 , "00" }, {"Micronesia" ,"FM" , "691" , 7 , "011" }, {"Moldova" ,"MD" , "373" , 8 , "00" }, {"Monaco" ,"MC" , "377" , 8 , "00" }, {"Mongolia" ,"MN" , "976" , 8 , "001" }, {"Montenegro" ,"ME" , "382" , 8 , "00" }, {"Montserrat" ,"MS" , "664" , 10 , "011" }, {"Morocco" ,"MA" , "212" , 9 , "00" }, {"Mozambique" ,"MZ" , "258" , 9 , "00" }, {"Myanmar" ,"MM" , "95" , 8 , "00" }, {"Namibia" ,"NA" , "264" , 9 , "00" }, {"Nauru" ,"NR" , "674" , 7 , "00" }, {"Nepal" ,"NP" , "43" , 10 , "00" }, {"Netherlands" ,"NL" , "31" , 9 , "00" }, {"New Caledonia" ,"NC" , "687" , 6 , "00" }, {"New Zealand" ,"NZ" , "64" , 8 , "00" }, {"Nicaragua" ,"NI" , "505" , 8 , "00" }, {"Niger" ,"NE" , "227" , 8 , "00" }, {"Nigeria" ,"NG" , "234" , 10 , "009" }, {"Niue" ,"NU" , "683" , 4 , "00" }, {"Norfolk Island" ,"NF" , "672" , 5 , "00" }, {"Northern Mariana Islands" ,"MP" , "1" , 10 , "011" }, {"Norway" ,"NO" , "47" , 8 , "00" }, {"Oman" ,"OM" , "968" , 8 , "00" }, {"Pakistan" ,"PK" , "92" , 10 , "00" }, {"Palau" ,"PW" , "680" , 7 , "011" }, {"Palestine" ,"PS" , "970" , 9 , "00" }, {"Panama" ,"PA" , "507" , 8 , "00" }, {"Papua New Guinea" ,"PG" , "675" , 8 , "00" }, {"Paraguay" ,"PY" , "595" , 9 , "00" }, {"Peru" ,"PE" , "51" , 9 , "00" }, {"Philippines" ,"PH" , "63" , 10 , "00" }, {"Poland" ,"PL" , "48" , 9 , "00" }, {"Portugal" ,"PT" , "351" , 9 , "00" }, {"Puerto Rico" ,"PR" , "1" , 10 , "011" }, {"Qatar" ,"QA" , "974" , 8 , "00" }, {"R�union Island" ,"RE" , "262" , 9 , "011" }, {"Romania" ,"RO" , "40" , 9 , "00" }, {"Russian Federation" ,"RU" , "7" , 10 , "8" }, {"Rwanda" ,"RW" , "250" , 9 , "00" }, {"Saint Helena" ,"SH" , "290" , 4 , "00" }, {"Saint Kitts and Nevis" ,"KN" , "1" , 10 , "011" }, {"Saint Lucia" ,"LC" , "1" , 10 , "011" }, {"Saint Pierre and Miquelon" ,"PM" , "508" , 6 , "00" }, {"Saint Vincent and the Grenadines","VC" , "1" , 10 , "011" }, {"Samoa" ,"WS" , "685" , 7 , "0" }, {"San Marino" ,"SM" , "378" , 10 , "00" }, {"Sao Tome and Principe" ,"ST" , "239" , 7 , "00" }, {"Saudi Arabia" ,"SA" , "966" , 9 , "00" }, {"Senegal" ,"SN" , "221" , 9 , "00" }, {"Serbia" ,"RS" , "381" , 9 , "00" }, {"Seychelles" ,"SC" , "248" , 7 , "00" }, {"Sierra Leone" ,"SL" , "232" , 8 , "00" }, {"Singapore" ,"SG" , "65" , 8 , "001" }, {"Slovakia" ,"SK" , "421" , 9 , "00" }, {"Slovenia" ,"SI" , "386" , 8 , "00" }, {"Solomon Islands" ,"SB" , "677" , 7 , "00" }, {"Somalia" ,"SO" , "252" , 8 , "00" }, {"South Africa" ,"ZA" , "27" , 9 , "00" }, {"Spain" ,"ES" , "34" , 9 , "00" }, {"Sri Lanka" ,"LK" , "94" , 9 , "00" }, {"Sudan" ,"SD" , "249" , 9 , "00" }, {"Suriname" ,"SR" , "597" , 7 , "00" }, {"Swaziland" ,"SZ" , "268" , 8 , "00" }, {"Sweden" ,"SE" , "46" , 9 , "00" }, {"Switzerland" ,"XK" , "41" , 9 , "00" }, {"Syria" ,"SY" , "963" , 9 , "00" }, {"Taiwan" ,"TW" , "886" , 9 , "810" }, {"Tajikistan" ,"TJ" , "992" , 9 , "002" }, {"Tanzania" ,"TZ" , "255" , 9 , "000" }, {"Thailand" ,"TH" , "66" , 9 , "001" }, {"Togo" ,"TG" , "228" , 8 , "00" }, {"Tokelau" ,"TK" , "690" , 4 , "00" }, {"Tonga" ,"TO" , "676" , 5 , "00" }, {"Trinidad and Tobago" ,"TT" , "1" , 10 , "011" }, {"Tunisia" ,"TN" , "216" , 8 , "00" }, {"Turkey" ,"TR" , "90" , 10 , "00" }, {"Turkmenistan" ,"TM" , "993" , 8 , "00" }, {"Turks and Caicos Islands" ,"TC" , "1" , 7 , "0" }, {"Tuvalu" ,"TV" , "688" , 5 , "00" }, {"Uganda" ,"UG" , "256" , 9 , "000" }, {"Ukraine" ,"UA" , "380" , 9 , "00" }, {"United Arab Emirates" ,"AE" , "971" , 9 , "00" }, {"United Kingdom" ,"GB" , "44" , 10 , "00" }, /* {"United Kingdom" ,"UK" , "44" , 10 , "00" },*/ {"United States" ,"US" , "1" , 10 , "011" }, {"Uruguay" ,"UY" , "598" , 8 , "00" }, {"Uzbekistan" ,"UZ" , "998" , 9 , "8" }, {"Vanuatu" ,"VU" , "678" , 7 , "00" }, {"Venezuela" ,"VE" , "58" , 10 , "00" }, {"Vietnam" ,"VN" , "84" , 9 , "00" }, {"Wallis and Futuna" ,"WF" , "681" , 5 , "00" }, {"Yemen" ,"YE" , "967" , 9 , "00" }, {"Zambia" ,"ZM" , "260" , 9 , "00" }, {"Zimbabwe" ,"ZW" , "263" , 9 , "00" }, {NULL ,NULL , "" , 0 , NULL } }; static LinphoneDialPlan most_common_dialplan={ "generic" ,"", "", 10, "00"}; int linphone_dial_plan_lookup_ccc_from_e164(const char* e164) { LinphoneDialPlan* dial_plan; LinphoneDialPlan* elected_dial_plan=NULL; unsigned int found; unsigned int i=0; if (e164[0]!='+') { return -1;/*not an e164 number*/ } if (e164[1]=='1') { /*USA case*/ return 1; } do { found=0; i++; for (dial_plan=(LinphoneDialPlan*)dial_plans; dial_plan->country!=NULL; dial_plan++) { if (strncmp(dial_plan->ccc,&e164[1],i) == 0) { elected_dial_plan=dial_plan; found++; } } } while ((found>1 || found==0) && i < sizeof(dial_plan->ccc)); if (found==1) { return atoi(elected_dial_plan->ccc); } else { return -1; /*not found */ } } int linphone_dial_plan_lookup_ccc_from_iso(const char* iso) { LinphoneDialPlan* dial_plan; for (dial_plan=(LinphoneDialPlan*)dial_plans; dial_plan->country!=NULL; dial_plan++) { if (strcmp(iso, dial_plan->iso_country_code)==0) { return atoi(dial_plan->ccc); } } return -1; } const LinphoneDialPlan* linphone_dial_plan_by_ccc_as_int(int ccc) { int i; char ccc_as_char[16] = {0}; snprintf(ccc_as_char,sizeof(ccc_as_char)-1,"%i",ccc); for(i=0;dial_plans[i].country!=NULL;++i){ if (strcmp(ccc_as_char,dial_plans[i].ccc)==0){ return &dial_plans[i]; } } /*else return a generic "most common" dial plan*/ return &most_common_dialplan; } const LinphoneDialPlan* linphone_dial_plan_by_ccc(const char *ccc) { if (!ccc) { return &most_common_dialplan; } return linphone_dial_plan_by_ccc_as_int((int)strtol(ccc,NULL,10)); } const LinphoneDialPlan* linphone_dial_plan_get_all() { return dial_plans; } bool_t linphone_dial_plan_is_generic(const LinphoneDialPlan *ccc) { if (strcmp(ccc->country, most_common_dialplan.country) == 0) return TRUE; return FALSE; } linphone-3.12.0/coreapi/dict.c000066400000000000000000000102601313432737600161320ustar00rootroot00000000000000/* linphone Copyright (C) 2009 Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "linphone/lpconfig.h" #include "private.h" #include #include #include /** * @addtogroup misc * @{ **/ LinphoneDictionary* linphone_dictionary_new() { return belle_sip_dict_create(); } LinphoneDictionary* linphone_dictionary_clone(const LinphoneDictionary* src) { LinphoneDictionary* cloned = linphone_dictionary_new(); if( cloned ){ belle_sip_dict_clone(src, cloned); } return cloned; } LinphoneDictionary* linphone_dictionary_ref(LinphoneDictionary* obj) { return BELLE_SIP_DICT(belle_sip_object_ref(obj)); } void linphone_dictionary_unref(LinphoneDictionary *obj) { belle_sip_object_unref(obj); } void linphone_dictionary_set_int(LinphoneDictionary* obj, const char* key, int value) { belle_sip_dict_set_int(obj, key, value); } int linphone_dictionary_get_int(LinphoneDictionary* obj, const char* key, int default_value) { return belle_sip_dict_get_int(obj, key, default_value); } void linphone_dictionary_set_string(LinphoneDictionary* obj, const char* key, const char*value) { belle_sip_dict_set_string(obj, key, value); } const char* linphone_dictionary_get_string(LinphoneDictionary* obj, const char* key, const char* default_value) { return belle_sip_dict_get_string(obj, key, default_value); } void linphone_dictionary_set_int64(LinphoneDictionary* obj, const char* key, int64_t value) { belle_sip_dict_set_int64(obj, key, value); } int64_t linphone_dictionary_get_int64(LinphoneDictionary* obj, const char* key, int64_t default_value) { return belle_sip_dict_get_int64(obj, key, default_value); } LinphoneStatus linphone_dictionary_remove(LinphoneDictionary* obj, const char* key) { return belle_sip_dict_remove(obj, key); } void linphone_dictionary_clear(LinphoneDictionary* obj) { belle_sip_dict_clear(obj); } LinphoneStatus linphone_dictionary_haskey(const LinphoneDictionary* obj, const char* key) { return belle_sip_dict_haskey(obj, key); } void linphone_dictionary_foreach(const LinphoneDictionary* obj, void (*apply_func)(const char*, void*, void*), void* userdata) { belle_sip_dict_foreach(obj, apply_func, userdata); } struct lp_config_to_dict { const char* section; const LpConfig* config; LinphoneDictionary* dict; }; static void lp_config_section_to_dict_cb(const char*key, struct lp_config_to_dict* userdata) { const char* value = lp_config_get_string(userdata->config, userdata->section, key, ""); linphone_dictionary_set_string(userdata->dict, key, value); } LinphoneDictionary* lp_config_section_to_dict(const LpConfig* lpconfig, const char* section) { LinphoneDictionary* dict = NULL; struct lp_config_to_dict fd; fd.config = lpconfig; fd.section = section; dict = linphone_dictionary_new(); fd.dict = dict; lp_config_for_each_entry(lpconfig, section, (void (*)(const char*, void*))lp_config_section_to_dict_cb, &fd); return dict; } struct lp_config_from_dict { const char* section; LpConfig* config; }; static void lp_config_dict_dump_cb( const char* key, void* value, void* userdata) { struct lp_config_from_dict* fd= (struct lp_config_from_dict*)userdata; lp_config_set_string(fd->config, fd->section, key, (const char*)value); } void lp_config_load_dict_to_section(LpConfig* lpconfig, const char* section, const LinphoneDictionary* dict) { struct lp_config_from_dict pvdata = { section, lpconfig }; linphone_dictionary_foreach(dict,lp_config_dict_dump_cb, &pvdata); } /** * @} **/ linphone-3.12.0/coreapi/ec-calibrator.c000066400000000000000000000254011313432737600177210ustar00rootroot00000000000000/* linphone Copyright (C) 2011 Belledonne Communications SARL Author: Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "private.h" #include "mediastreamer2/mstonedetector.h" #include "mediastreamer2/dtmfgen.h" #include "linphone/lpconfig.h" static void ecc_init_filters(EcCalibrator *ecc){ unsigned int rate; int channels = 1; int ecc_channels = 1; MSTickerParams params={0}; params.name="Echo calibrator"; params.prio=MS_TICKER_PRIO_HIGH; ecc->ticker=ms_ticker_new_with_params(¶ms); ecc->sndread=ms_snd_card_create_reader(ecc->capt_card); ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->sndread,MS_FILTER_GET_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->sndread,MS_FILTER_SET_NCHANNELS,&ecc_channels); ms_filter_call_method(ecc->sndread,MS_FILTER_GET_NCHANNELS,&channels); ecc->read_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_NCHANNELS,&channels); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&ecc_channels); ecc->det=ms_factory_create_filter(ecc->factory, MS_TONE_DETECTOR_ID); ms_filter_call_method(ecc->det,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ecc->rec=ms_factory_create_filter(ecc->factory, MS_VOID_SINK_ID); ms_filter_link(ecc->sndread,0,ecc->read_resampler,0); ms_filter_link(ecc->read_resampler,0,ecc->det,0); ms_filter_link(ecc->det,0,ecc->rec,0); ecc->play=ms_factory_create_filter(ecc->factory, MS_VOID_SOURCE_ID); ecc->gen=ms_factory_create_filter(ecc->factory, MS_DTMF_GEN_ID); ms_filter_call_method(ecc->gen,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ecc->write_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID); ecc->sndwrite=ms_snd_card_create_writer(ecc->play_card); ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_NCHANNELS,&ecc_channels); ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_NCHANNELS,&channels); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_NCHANNELS,&ecc_channels); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels); ms_filter_link(ecc->play,0,ecc->gen,0); ms_filter_link(ecc->gen,0,ecc->write_resampler,0); ms_filter_link(ecc->write_resampler,0,ecc->sndwrite,0); ms_ticker_attach(ecc->ticker,ecc->sndread); ms_ticker_attach(ecc->ticker,ecc->play); if (ecc->audio_init_cb != NULL) { (*ecc->audio_init_cb)(ecc->cb_data); } } static void ecc_deinit_filters(EcCalibrator *ecc){ if (ecc->audio_uninit_cb != NULL) { (*ecc->audio_uninit_cb)(ecc->cb_data); } ms_ticker_detach(ecc->ticker,ecc->sndread); ms_ticker_detach(ecc->ticker,ecc->play); ms_filter_unlink(ecc->play,0,ecc->gen,0); ms_filter_unlink(ecc->gen,0,ecc->write_resampler,0); ms_filter_unlink(ecc->write_resampler,0,ecc->sndwrite,0); ms_filter_unlink(ecc->sndread,0,ecc->read_resampler,0); ms_filter_unlink(ecc->read_resampler,0,ecc->det,0); ms_filter_unlink(ecc->det,0,ecc->rec,0); ms_filter_destroy(ecc->sndread); ms_filter_destroy(ecc->det); ms_filter_destroy(ecc->rec); ms_filter_destroy(ecc->play); ms_filter_destroy(ecc->gen); ms_filter_destroy(ecc->read_resampler); ms_filter_destroy(ecc->write_resampler); ms_filter_destroy(ecc->sndwrite); ms_ticker_destroy(ecc->ticker); } static void on_tone_sent(void *data, MSFilter *f, unsigned int event_id, void *arg){ MSDtmfGenEvent *ev=(MSDtmfGenEvent*)arg; EcCalibrator *ecc=(EcCalibrator*)data; if (ev->tone_name[0] != '\0'){ ecc->acc-=ev->tone_start_time; ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time); } } static bool_t is_valid_tone(EcCalibrator *ecc, MSToneDetectorEvent *ev){ bool_t *toneflag=NULL; if (strcmp(ev->tone_name,"freq1")==0){ toneflag=&ecc->freq1; }else if (strcmp(ev->tone_name,"freq2")==0){ toneflag=&ecc->freq2; }else if (strcmp(ev->tone_name,"freq3")==0){ toneflag=&ecc->freq3; }else{ ms_error("Calibrator bug."); return FALSE; } if (*toneflag){ ms_message("Duplicated tone event, ignored."); return FALSE; } *toneflag=TRUE; return TRUE; } static void on_tone_received(void *data, MSFilter *f, unsigned int event_id, void *arg){ MSToneDetectorEvent *ev=(MSToneDetectorEvent*)arg; EcCalibrator *ecc=(EcCalibrator*)data; if (is_valid_tone(ecc,ev)){ ecc->acc+=ev->tone_start_time; ms_message("Received tone at %u",(unsigned int)ev->tone_start_time); } } static void ecc_play_tones(EcCalibrator *ecc){ MSDtmfGenCustomTone tone; MSToneDetectorDef expected_tone; memset(&tone,0,sizeof(tone)); memset(&expected_tone,0,sizeof(expected_tone)); ms_filter_add_notify_callback(ecc->det,on_tone_received,ecc,TRUE); /* configure the tones to be scanned */ strncpy(expected_tone.tone_name,"freq1",sizeof(expected_tone.tone_name)); expected_tone.frequency=(int)2349.32; expected_tone.min_duration=40; expected_tone.min_amplitude=0.1f; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); strncpy(expected_tone.tone_name,"freq2",sizeof(expected_tone.tone_name)); expected_tone.frequency=(int)2637.02; expected_tone.min_duration=40; expected_tone.min_amplitude=0.1f; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); strncpy(expected_tone.tone_name,"freq3",sizeof(expected_tone.tone_name)); expected_tone.frequency=(int)2093; expected_tone.min_duration=40; expected_tone.min_amplitude=0.1f; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); /*play an initial tone to startup the audio playback/capture*/ tone.frequencies[0]=140; tone.duration=1000; tone.amplitude=0.5; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_sleep(2); ms_filter_add_notify_callback(ecc->gen,on_tone_sent,ecc,TRUE); /* play the three tones*/ if (ecc->play_cool_tones){ strncpy(tone.tone_name, "D", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2349.32; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "E", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2637.02; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "C", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2093; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); }else{ strncpy(tone.tone_name, "C", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2093; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "D", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2349.32; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "E", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2637.02; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); } /*these two next ones are for lyrism*/ if (ecc->play_cool_tones){ tone.tone_name[0]='\0'; tone.frequencies[0]=(int)1046.5; tone.duration=400; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); tone.tone_name[0]='\0'; tone.frequencies[0]=(int)1567.98; tone.duration=400; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); } ms_sleep(1); if (ecc->freq1 && ecc->freq2 && ecc->freq3) { int delay=(int)(ecc->acc/3); if (delay<0){ ms_error("Quite surprising calibration result, delay=%i",delay); ecc->status=LinphoneEcCalibratorFailed; }else{ ms_message("Echo calibration estimated delay to be %i ms",delay); ecc->delay=delay; ecc->status=LinphoneEcCalibratorDone; } } else if ((ecc->freq1 || ecc->freq2 || ecc->freq3)==FALSE) { ms_message("Echo calibration succeeded, no echo has been detected"); ecc->status = LinphoneEcCalibratorDoneNoEcho; } else { ecc->status = LinphoneEcCalibratorFailed; } if (ecc->status == LinphoneEcCalibratorFailed) { ms_error("Echo calibration failed."); } } static void * ecc_thread(void *p){ EcCalibrator *ecc=(EcCalibrator*)p; ecc_init_filters(ecc); ecc_play_tones(ecc); ecc_deinit_filters(ecc); ms_thread_exit(NULL); return NULL; } EcCalibrator * ec_calibrator_new(MSFactory *factory, MSSndCard *play_card, MSSndCard *capt_card, unsigned int rate, LinphoneEcCalibrationCallback cb, LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){ EcCalibrator *ecc=ms_new0(EcCalibrator,1); ecc->rate=rate; ecc->cb=cb; ecc->cb_data=cb_data; ecc->audio_init_cb=audio_init_cb; ecc->audio_uninit_cb=audio_uninit_cb; ecc->capt_card=capt_card; ecc->play_card=play_card; ecc->factory=factory; return ecc; } void ec_calibrator_start(EcCalibrator *ecc){ ms_thread_create(&ecc->thread,NULL,ecc_thread,ecc); } LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc){ return ecc->status; } void ec_calibrator_destroy(EcCalibrator *ecc){ if (ecc->thread != 0) ms_thread_join(ecc->thread,NULL); ms_free(ecc); } int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb, LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){ unsigned int rate; if (lc->ecc!=NULL){ ms_error("Echo calibration is still on going !"); return -1; } rate = lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000); lc->ecc=ec_calibrator_new(lc->factory, lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard,rate,cb,audio_init_cb,audio_uninit_cb,cb_data); lc->ecc->play_cool_tones = lp_config_get_int(lc->config, "sound", "ec_calibrator_cool_tones", 0); ec_calibrator_start(lc->ecc); return 0; } linphone-3.12.0/coreapi/echo-tester.c000066400000000000000000000064451313432737600174430ustar00rootroot00000000000000/* linphone Copyright (C) 2016 Belledonne Communications SARL Author: Erwan CROZE (erwan.croze@belledonne-communications.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "private.h" #include "mediastreamer2/msfilter.h" #include "mediastreamer2/mssndcard.h" #include "mediastreamer2/msticker.h" #include EchoTester* ec_tester_new(MSFactory *factory, MSSndCard *capture_card, MSSndCard *playback_card, unsigned int rate) { EchoTester *ect = ms_new0(EchoTester,1); ect->factory = factory; ect->capture_card = capture_card; ect->playback_card = playback_card; ect->rate = rate; return ect; } static void ect_init_filters(EchoTester *ect) { unsigned int rate; int channels = 1; int ect_channels = 1; MSTickerParams params={0}; params.name="Echo tester"; params.prio=MS_TICKER_PRIO_HIGH; ect->ticker=ms_ticker_new_with_params(¶ms); ect->in = ms_snd_card_create_reader(ect->capture_card); ect->out = ms_snd_card_create_writer(ect->playback_card); ms_filter_call_method(ect->in,MS_FILTER_SET_SAMPLE_RATE,&ect->rate); ms_filter_call_method(ect->in,MS_FILTER_GET_SAMPLE_RATE,&rate); ms_filter_call_method(ect->in,MS_FILTER_SET_NCHANNELS,&ect_channels); ms_filter_call_method(ect->in,MS_FILTER_GET_NCHANNELS,&channels); ms_filter_call_method(ect->out,MS_FILTER_SET_SAMPLE_RATE,&ect->rate); ms_filter_call_method(ect->out,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate); ms_filter_call_method(ect->out,MS_FILTER_SET_NCHANNELS,&ect_channels); ms_filter_call_method(ect->out,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels); ms_filter_link(ect->in,0,ect->out,0); ms_ticker_attach(ect->ticker,ect->in); ms_ticker_attach(ect->ticker,ect->out); } static void ect_uninit_filters(EchoTester *ect) { ms_ticker_detach(ect->ticker,ect->in); ms_ticker_detach(ect->ticker,ect->out); ms_filter_unlink(ect->in,0,ect->out,0); ms_filter_destroy(ect->in); ms_filter_destroy(ect->out); ms_ticker_destroy(ect->ticker); } void ec_tester_destroy(EchoTester *ect) { ms_free(ect); } LinphoneStatus linphone_core_start_echo_tester(LinphoneCore *lc, unsigned int rate) { if (lc->ect != NULL) { ms_error("Echo tester is still on going !"); return -1; } lc->ect = ec_tester_new(lc->factory, lc->sound_conf.capt_sndcard ,lc->sound_conf.play_sndcard, rate); ect_init_filters(lc->ect); return 1; } LinphoneStatus linphone_core_stop_echo_tester(LinphoneCore *lc) { if (lc->ect == NULL) { ms_error("Echo tester is not running !"); return -1; } ect_uninit_filters(lc->ect); ec_tester_destroy(lc->ect); lc->ect = NULL; return 1; } linphone-3.12.0/coreapi/enum.c000066400000000000000000000075431313432737600161650ustar00rootroot00000000000000/* linphone Copyright (C) 2000-2009 Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* enum lookup code */ #ifndef _WIN32_WCE #include #endif #include #include "enum.h" #define DNS_ANSWER_MAX_SIZE 2048 static char *create_enum_domain(const char *number){ long len=(long)strlen(number); char *domain=ms_malloc((len*2)+10); long i,j; for (i=0,j=len-1;j>=0;j--){ domain[i]=number[j]; i++; domain[i]='.'; i++; } strcpy(&domain[i],"e164.arpa"); ms_message("enum domain for %s is %s",number,domain); return domain; } static bool_t is_a_number(const char *str){ char *p=(char *)str; bool_t res=FALSE; bool_t space_found=FALSE; for(;;p++){ switch(p[0]){ case '9': case '8': case '7': case '6': case '5': case '4': case '3': case '2': case '1': case '0': res=TRUE; if (space_found) return FALSE; /* avoid splited numbers */ break; case '\0': return res; break; case ' ': space_found=TRUE; break; default: return FALSE; } } return FALSE; } //4970072278724 bool_t is_enum(const char *sipaddress, char **enum_domain){ char *p; p=strstr(sipaddress,"sip:"); if (p==NULL) return FALSE; /* enum should look like sip:4369959250*/ else p+=4; if (is_a_number(p)){ if (enum_domain!=NULL){ *enum_domain=create_enum_domain(p); } return TRUE; } return FALSE; } int enum_lookup(const char *enum_domain, enum_lookup_res_t **res){ int err; //char dns_answer[DNS_ANSWER_MAX_SIZE]; char *begin,*end; char *host_result, *command; int i; bool_t forkok; /* ns_msg handle; int count; memset(&handle,0,sizeof(handle)); *res=NULL; ms_message("Resolving %s...",enum_domain); err=res_search(enum_domain,ns_c_in,ns_t_naptr,dns_answer,DNS_ANSWER_MAX_SIZE); if (err<0){ ms_warning("Error resolving enum:",herror(h_errno)); return -1; } ns_initparse(dns_answer,DNS_ANSWER_MAX_SIZE,&handle); count=ns_msg_count(handle,ns_s_an); for(i=0;isip_address[i]=ms_strdup(begin); err++; begin=strstr(end+1,"sip:"); if (begin==NULL) break; } ms_free(host_result); return err; parse_error: ms_free(*res); ms_free(host_result); *res=NULL; ms_warning("Parse error in enum_lookup()."); return -1; } void enum_lookup_res_free(enum_lookup_res_t *res){ int i; for (i=0;isip_address[i]!=NULL) ms_free(res->sip_address[i]); } ms_free(res); } linphone-3.12.0/coreapi/enum.h000066400000000000000000000021661313432737600161660ustar00rootroot00000000000000/* linphone Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ENUM_LOOKUP_H #define ENUM_LOOKUP_H #include "private.h" #define MAX_ENUM_LOOKUP_RESULTS 10 typedef struct enum_lookup_res{ char *sip_address[MAX_ENUM_LOOKUP_RESULTS]; }enum_lookup_res_t; bool_t is_enum(const char *sipaddress, char **enum_domain); int enum_lookup(const char *enum_domain, enum_lookup_res_t **res); void enum_lookup_res_free(enum_lookup_res_t *res); #endif linphone-3.12.0/coreapi/error_info.c000066400000000000000000000221061313432737600173550ustar00rootroot00000000000000/* error_info.c Copyright (C) 2016 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "private.h" BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneErrorInfo); static void linphone_error_info_reset(LinphoneErrorInfo *ei); static void error_info_destroy(LinphoneErrorInfo *ei){ linphone_error_info_reset(ei); } static void error_info_clone(LinphoneErrorInfo *ei, const LinphoneErrorInfo *other){ linphone_error_info_set_reason(ei, linphone_error_info_get_reason(other)); ei->protocol = bctbx_strdup(other->protocol); ei->phrase = bctbx_strdup(other->phrase); ei->warnings = bctbx_strdup(other->warnings); ei->full_string = bctbx_strdup(other->full_string); ei->protocol_code = other->protocol_code; } BELLE_SIP_INSTANCIATE_VPTR(LinphoneErrorInfo, belle_sip_object_t, error_info_destroy, // destroy error_info_clone, // clone NULL, // Marshall FALSE ); LinphoneErrorInfo *linphone_error_info_new(void){ LinphoneErrorInfo *ei = belle_sip_object_new(LinphoneErrorInfo); return ei; } LinphoneErrorInfo* linphone_error_info_ref ( LinphoneErrorInfo* ei ) { return (LinphoneErrorInfo*) belle_sip_object_ref(ei); } void linphone_error_info_unref ( LinphoneErrorInfo* ei ) { belle_sip_object_unref(ei); } const char *linphone_reason_to_string(LinphoneReason err){ switch(err) { case LinphoneReasonNone: return "No error"; case LinphoneReasonNoResponse: return "No response"; case LinphoneReasonForbidden: return "Bad credentials"; case LinphoneReasonDeclined: return "Call declined"; case LinphoneReasonNotFound: return "User not found"; case LinphoneReasonNotAnswered: return "Not answered"; case LinphoneReasonBusy: return "Busy"; case LinphoneReasonMedia: return "Incompatible media capabilities"; case LinphoneReasonIOError: return "IO error"; case LinphoneReasonDoNotDisturb: return "Do not disturb"; case LinphoneReasonUnauthorized: return "Unauthorized"; case LinphoneReasonNotAcceptable: return "Not acceptable here"; case LinphoneReasonNoMatch: return "No match"; case LinphoneReasonMovedPermanently: return "Moved permanently"; case LinphoneReasonGone: return "Gone"; case LinphoneReasonTemporarilyUnavailable: return "Temporarily unavailable"; case LinphoneReasonAddressIncomplete: return "Address incomplete"; case LinphoneReasonNotImplemented: return "Not implemented"; case LinphoneReasonBadGateway: return "Bad gateway"; case LinphoneReasonServerTimeout: return "Server timeout"; case LinphoneReasonUnknown: return "Unknown error"; } return "unknown error"; } typedef struct _error_code_reason_map { int error_code; LinphoneReason reason; } error_code_reason_map_t; static const error_code_reason_map_t error_code_reason_map[] = { { 200, LinphoneReasonNone }, { 301, LinphoneReasonMovedPermanently }, { 400, LinphoneReasonUnknown }, { 401, LinphoneReasonUnauthorized }, { 403, LinphoneReasonForbidden }, { 404, LinphoneReasonNotFound }, { 410, LinphoneReasonGone }, { 415, LinphoneReasonUnsupportedContent }, { 480, LinphoneReasonTemporarilyUnavailable }, { 481, LinphoneReasonNoMatch }, { 484, LinphoneReasonAddressIncomplete }, { 486, LinphoneReasonBusy }, { 488, LinphoneReasonNotAcceptable }, { 501, LinphoneReasonNotImplemented }, { 502, LinphoneReasonBadGateway }, { 503, LinphoneReasonIOError }, { 504, LinphoneReasonServerTimeout }, { 600, LinphoneReasonDoNotDisturb }, { 603, LinphoneReasonDeclined } }; LinphoneReason linphone_error_code_to_reason(int err) { size_t i; for (i = 0; i < (sizeof(error_code_reason_map) / sizeof(error_code_reason_map[0])); i++) { if (error_code_reason_map[i].error_code == err) return error_code_reason_map[i].reason; } return LinphoneReasonUnknown; } int linphone_reason_to_error_code(LinphoneReason reason) { size_t i; for (i = 0; i < (sizeof(error_code_reason_map) / sizeof(error_code_reason_map[0])); i++) { if (error_code_reason_map[i].reason == reason) return error_code_reason_map[i].error_code; } return 400; } static void linphone_error_info_reset(LinphoneErrorInfo *ei){ ei->reason = LinphoneReasonNone; STRING_RESET(ei->protocol); STRING_RESET(ei->phrase); STRING_RESET(ei->full_string); STRING_RESET(ei->warnings); ei->protocol_code = 0; if (ei->sub_ei) { linphone_error_info_unref(ei->sub_ei); ei->sub_ei = NULL; } } void linphone_error_info_from_sal(LinphoneErrorInfo *ei, const SalErrorInfo *sei){ ei->reason = linphone_reason_from_sal(sei->reason); ei->phrase = bctbx_strdup(sei->status_string); ei->full_string = bctbx_strdup(sei->full_string); ei->warnings = bctbx_strdup(sei->warnings); ei->protocol_code = sei->protocol_code; ei->protocol = bctbx_strdup(sei->protocol); } /* If a reason header is provided (in reason_ei), then create a sub LinphoneErrorInfo attached to the first one, unless the reason header is in the request, in which case no primary error is given.*/ void linphone_error_info_from_sal_reason_ei(LinphoneErrorInfo *ei, const SalErrorInfo *reason_ei){ if (ei->reason == LinphoneReasonNone){ /*no primary error given*/ linphone_error_info_reset(ei); linphone_error_info_from_sal(ei, reason_ei); return; } if (ei->sub_ei){ if (reason_ei->reason == SalReasonNone){ linphone_error_info_unref(ei->sub_ei); ei->sub_ei = NULL; } }else{ if (reason_ei->reason != SalReasonNone){ ei->sub_ei = linphone_error_info_new(); } } if (reason_ei->reason != SalReasonNone){ linphone_error_info_from_sal(ei->sub_ei, reason_ei); } } void linphone_error_info_from_sal_op(LinphoneErrorInfo *ei, const SalOp *op){ if (op==NULL) { /*leave previous values in LinphoneErrorInfo, the op may have been released already.*/ return; }else{ const SalErrorInfo *sei; linphone_error_info_reset(ei); sei = sal_op_get_error_info(op); linphone_error_info_from_sal(ei, sei); sei = sal_op_get_reason_error_info(op); linphone_error_info_from_sal_reason_ei(ei, sei); } } void linphone_error_info_fields_to_sal(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ sei->reason = linphone_reason_to_sal(linphone_error_info_get_reason(ei)); sei->status_string = bctbx_strdup(ei->phrase); sei->full_string = bctbx_strdup(ei->full_string); sei->warnings = bctbx_strdup(ei->warnings); sei->protocol_code = ei->protocol_code; sei->protocol = bctbx_strdup(ei->protocol); } void linphone_error_info_to_sal(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ linphone_error_info_fields_to_sal(ei, sei); if (ei->sub_ei !=NULL) { linphone_error_info_to_sal(ei->sub_ei, sei->sub_sei); } } void linphone_error_info_set(LinphoneErrorInfo *ei, const char *protocol, LinphoneReason reason, int code, const char *status_string, const char *warning){ linphone_error_info_reset(ei); ei->reason = reason; ei->protocol_code = code; ei->protocol = bctbx_strdup(protocol ? protocol : "SIP"); ei->phrase = bctbx_strdup(status_string); ei->warnings = bctbx_strdup(warning); } LinphoneReason linphone_error_info_get_reason(const LinphoneErrorInfo *ei) { return ei->reason; } const char *linphone_error_info_get_protocol(const LinphoneErrorInfo *ei){ return ei->protocol; } const char *linphone_error_info_get_phrase(const LinphoneErrorInfo *ei) { return ei->phrase; } /*deprecated, kept for binary compatibility*/ const char *linphone_error_info_get_details(const LinphoneErrorInfo *ei){ return linphone_error_info_get_warnings(ei); } const char *linphone_error_info_get_warnings(const LinphoneErrorInfo *ei) { return ei->warnings; } int linphone_error_info_get_protocol_code(const LinphoneErrorInfo *ei) { return ei->protocol_code; } LinphoneErrorInfo * linphone_error_info_get_sub_error_info(const LinphoneErrorInfo *ei){ return ei->sub_ei; } void linphone_error_info_set_reason(LinphoneErrorInfo *ei, LinphoneReason reason){ ei->reason = reason; } void linphone_error_info_set_protocol(LinphoneErrorInfo *ei, const char *proto){ STRING_SET(ei->protocol, proto); } void linphone_error_info_set_protocol_code(LinphoneErrorInfo *ei, int code){ ei->protocol_code = code; } void linphone_error_info_set_phrase(LinphoneErrorInfo *ei, const char *phrase){ STRING_SET(ei->phrase, phrase); } void linphone_error_info_set_warnings(LinphoneErrorInfo *ei, const char *warnings){ STRING_SET(ei->warnings, warnings); } void linphone_error_info_set_sub_error_info(LinphoneErrorInfo *ei, LinphoneErrorInfo *appended_ei){ if (appended_ei != NULL){ linphone_error_info_ref(appended_ei); } if (ei->sub_ei){ linphone_error_info_unref(ei->sub_ei); } ei->sub_ei = appended_ei; } linphone-3.12.0/coreapi/event.c000066400000000000000000000414451313432737600163410ustar00rootroot00000000000000/* linphone Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/event.h" #include "private.h" #include "linphone/lpconfig.h" const char * linphone_subscription_dir_to_string(LinphoneSubscriptionDir dir){ switch(dir){ case LinphoneSubscriptionIncoming: return "LinphoneSubscriptionIncoming"; case LinphoneSubscriptionOutgoing: return "LinphoneSubscriptionOutgoing"; case LinphoneSubscriptionInvalidDir: return "LinphoneSubscriptionInvalidDir"; } return "INVALID"; } LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss){ switch(ss){ case SalSubscribeNone: return LinphoneSubscriptionNone; case SalSubscribePending: return LinphoneSubscriptionPending; case SalSubscribeTerminated: return LinphoneSubscriptionTerminated; case SalSubscribeActive: return LinphoneSubscriptionActive; } return LinphoneSubscriptionNone; } const char *linphone_subscription_state_to_string(LinphoneSubscriptionState state){ switch(state){ case LinphoneSubscriptionNone: return "LinphoneSubscriptionNone"; case LinphoneSubscriptionIncomingReceived: return "LinphoneSubscriptionIncomingReceived"; case LinphoneSubscriptionOutgoingProgress: return "LinphoneSubscriptionOutgoingProgress"; case LinphoneSubscriptionPending: return "LinphoneSubscriptionPending"; case LinphoneSubscriptionActive: return "LinphoneSubscriptionActive"; case LinphoneSubscriptionTerminated: return "LinphoneSubscriptionTerminated"; case LinphoneSubscriptionError: return "LinphoneSubscriptionError"; case LinphoneSubscriptionExpiring: return "LinphoneSubscriptionExpiring"; } return NULL; } LINPHONE_PUBLIC const char *linphone_publish_state_to_string(LinphonePublishState state){ switch(state){ case LinphonePublishNone: return "LinphonePublishNone"; case LinphonePublishProgress: return "LinphonePublishProgress"; case LinphonePublishOk: return "LinphonePublishOk"; case LinphonePublishError: return "LinphonePublishError"; case LinphonePublishCleared: return "LinphonePublishCleared"; case LinphonePublishExpiring: return "LinphonePublishExpiring"; } return NULL; } static void linphone_event_release(LinphoneEvent *lev){ if (lev->op) { /*this will stop the refresher*/ sal_op_stop_refreshing(lev->op); } linphone_event_unref(lev); } static LinphoneEvent * linphone_event_new_base(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, SalOp *op){ LinphoneEvent *lev=belle_sip_object_new(LinphoneEvent); lev->lc=lc; lev->dir=dir; lev->op=op; lev->name=ms_strdup(name); sal_op_set_user_pointer(lev->op,lev); return lev; } LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, int expires){ LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, sal_op_new(lc->sal)); lev->expires=expires; return lev; } static LinphoneEvent *linphone_event_new_with_op_base(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name, bool_t is_out_of_dialog){ LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, op); lev->is_out_of_dialog_op=is_out_of_dialog; return lev; } LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name) { return linphone_event_new_with_op_base(lc,op,dir,name,FALSE); } LinphoneEvent *linphone_event_new_with_out_of_dialog_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name) { return linphone_event_new_with_op_base(lc,op,dir,name,TRUE); } void linphone_event_set_internal(LinphoneEvent *lev, bool_t internal) { lev->internal = internal; } bool_t linphone_event_is_internal(LinphoneEvent *lev) { return lev->internal; } void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state){ if (lev->subscription_state!=state){ ms_message("LinphoneEvent [%p] moving to subscription state %s",lev,linphone_subscription_state_to_string(state)); lev->subscription_state=state; linphone_core_notify_subscription_state_changed(lev->lc,lev,state); if (state==LinphoneSubscriptionTerminated || state == LinphoneSubscriptionError){ linphone_event_release(lev); } } } void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState state){ if (lev->publish_state!=state){ ms_message("LinphoneEvent [%p] moving to publish state %s",lev,linphone_publish_state_to_string(state)); lev->publish_state=state; linphone_core_notify_publish_state_changed(lev->lc,lev,state); switch(state){ case LinphonePublishCleared: linphone_event_release(lev); break; case LinphonePublishOk: if (lev->oneshot) linphone_event_release(lev); break; case LinphonePublishError: linphone_event_release(lev); break; case LinphonePublishNone: case LinphonePublishProgress: case LinphonePublishExpiring: /*nothing special to do*/ break; } } } LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev){ return lev->publish_state; } const LinphoneErrorInfo *linphone_event_get_error_info(const LinphoneEvent *lev){ if (!lev->ei) ((LinphoneEvent*)lev)->ei = linphone_error_info_new(); linphone_error_info_from_sal_op(lev->ei, lev->op); return lev->ei; } LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){ return linphone_error_info_get_reason(linphone_event_get_error_info(lev)); } LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){ LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing, event, expires); linphone_configure_op(lc,lev->op,resource,NULL,TRUE); sal_op_set_manual_refresher_mode(lev->op,!lp_config_get_int(lc->config,"sip","refresh_generic_subscribe",1)); return lev; } LinphoneEvent *linphone_core_create_notify(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){ LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionIncoming, event, -1); linphone_configure_op(lc,lev->op,resource,NULL,TRUE); lev->subscription_state = LinphoneSubscriptionIncomingReceived; sal_op_set_event(lev->op, event); lev->is_out_of_dialog_op = TRUE; return lev; } LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ LinphoneEvent *lev=linphone_core_create_subscribe(lc,resource,event,expires); linphone_event_send_subscribe(lev,body); return lev; } LinphoneStatus linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *body){ SalBodyHandler *body_handler; int err; if (lev->dir!=LinphoneSubscriptionOutgoing){ ms_error("linphone_event_send_subscribe(): cannot send or update something that is not an outgoing subscription."); return -1; } switch (lev->subscription_state){ case LinphoneSubscriptionIncomingReceived: case LinphoneSubscriptionTerminated: case LinphoneSubscriptionOutgoingProgress: ms_error("linphone_event_send_subscribe(): cannot update subscription while in state [%s]", linphone_subscription_state_to_string(lev->subscription_state)); return -1; break; case LinphoneSubscriptionNone: case LinphoneSubscriptionActive: case LinphoneSubscriptionExpiring: case LinphoneSubscriptionError: case LinphoneSubscriptionPending: /*those states are ok*/ break; } if (lev->send_custom_headers){ sal_op_set_sent_custom_header(lev->op,lev->send_custom_headers); sal_custom_header_free(lev->send_custom_headers); lev->send_custom_headers=NULL; }else sal_op_set_sent_custom_header(lev->op,NULL); body_handler = sal_body_handler_from_content(body); err=sal_subscribe(lev->op,NULL,NULL,lev->name,lev->expires,body_handler); if (err==0){ if (lev->subscription_state==LinphoneSubscriptionNone) linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress); } return err; } LinphoneStatus linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body){ return linphone_event_send_subscribe(lev,body); } LinphoneStatus linphone_event_refresh_subscribe(LinphoneEvent *lev) { return sal_op_refresh(lev->op); } LinphoneStatus linphone_event_accept_subscription(LinphoneEvent *lev){ int err; if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_accept_subscription(): cannot accept subscription if subscription wasn't just received."); return -1; } err=sal_subscribe_accept(lev->op); if (err==0){ linphone_event_set_state(lev,LinphoneSubscriptionActive); } return err; } LinphoneStatus linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){ int err; if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_deny_subscription(): cannot deny subscription if subscription wasn't just received."); return -1; } err=sal_subscribe_decline(lev->op,linphone_reason_to_sal(reason)); linphone_event_set_state(lev,LinphoneSubscriptionTerminated); return err; } LinphoneStatus linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){ SalBodyHandler *body_handler; if (lev->subscription_state!=LinphoneSubscriptionActive && lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_notify(): cannot notify if subscription is not active."); return -1; } if (lev->dir!=LinphoneSubscriptionIncoming){ ms_error("linphone_event_notify(): cannot notify if not an incoming subscription."); return -1; } body_handler = sal_body_handler_from_content(body); return sal_notify(lev->op, body_handler); } static LinphoneEvent *_linphone_core_create_publish(LinphoneCore *core, LinphoneProxyConfig *cfg, const LinphoneAddress *resource, const char *event, int expires){ LinphoneCore *lc = core; LinphoneEvent *lev; if (!lc && cfg) { if (cfg->lc) lc = cfg->lc; else { ms_error("Cannot create publish from proxy config [%p] not attached to any core",cfg); return NULL; } } if (!resource && cfg) resource = linphone_proxy_config_get_identity_address(cfg); lev = linphone_event_new(lc,LinphoneSubscriptionInvalidDir, event,expires); linphone_configure_op_with_proxy(lc,lev->op,resource,NULL,lp_config_get_int(lc->config,"sip","publish_msg_with_contact",0),cfg); sal_op_set_manual_refresher_mode(lev->op,!lp_config_get_int(lc->config,"sip","refresh_generic_publish",1)); return lev; } LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){ return _linphone_core_create_publish(lc, NULL, resource, event, expires); } LinphoneEvent *linphone_proxy_config_create_publish(LinphoneProxyConfig *cfg, const char *event, int expires) { return _linphone_core_create_publish(NULL, cfg,NULL, event, expires); } LinphoneEvent *linphone_core_create_one_shot_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){ LinphoneEvent *lev = linphone_core_create_publish(lc, resource, event, -1); lev->oneshot = TRUE; return lev; } static int _linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body, bool_t notify_err){ SalBodyHandler *body_handler; int err; if (lev->dir!=LinphoneSubscriptionInvalidDir){ ms_error("linphone_event_update_publish(): this is not a PUBLISH event."); return -1; } if (lev->send_custom_headers){ sal_op_set_sent_custom_header(lev->op,lev->send_custom_headers); sal_custom_header_free(lev->send_custom_headers); lev->send_custom_headers=NULL; }else sal_op_set_sent_custom_header(lev->op,NULL); body_handler = sal_body_handler_from_content(body); err=sal_publish(lev->op,NULL,NULL,lev->name,lev->expires,body_handler); if (err==0){ linphone_event_set_publish_state(lev,LinphonePublishProgress); }else if (notify_err){ linphone_event_set_publish_state(lev,LinphonePublishError); } return err; } LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ int err; LinphoneEvent *lev=linphone_core_create_publish(lc,resource,event,expires); err=_linphone_event_send_publish(lev,body,FALSE); if (err==-1){ linphone_event_unref(lev); lev=NULL; } return lev; } LinphoneStatus linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body){ return _linphone_event_send_publish(lev,body,TRUE); } LinphoneStatus linphone_event_update_publish(LinphoneEvent* lev, const LinphoneContent* body ) { return linphone_event_send_publish(lev,body); } LinphoneStatus linphone_event_refresh_publish(LinphoneEvent *lev) { return sal_op_refresh(lev->op); } void linphone_event_pause_publish(LinphoneEvent *lev) { if (lev->op) sal_op_stop_refreshing(lev->op); } void linphone_event_unpublish(LinphoneEvent *lev) { lev->terminating = TRUE; /* needed to get clear event*/ if (lev->op) sal_op_unpublish(lev->op); } void linphone_event_set_user_data(LinphoneEvent *ev, void *up){ ev->userdata=up; } void *linphone_event_get_user_data(const LinphoneEvent *ev){ return ev->userdata; } void linphone_event_add_custom_header(LinphoneEvent *ev, const char *name, const char *value){ ev->send_custom_headers=sal_custom_header_append(ev->send_custom_headers, name, value); } const char* linphone_event_get_custom_header(LinphoneEvent* ev, const char* name){ const SalCustomHeader *ch=sal_op_get_recv_custom_header(ev->op); return sal_custom_header_find(ch,name); } void linphone_event_terminate(LinphoneEvent *lev){ // if event was already terminated (including on error), we should not terminate it again // otherwise it will be unreffed twice. if (lev->publish_state == LinphonePublishError || lev->publish_state == LinphonePublishCleared) { return; } if (lev->subscription_state == LinphoneSubscriptionError || lev->subscription_state == LinphoneSubscriptionTerminated) { return; } lev->terminating=TRUE; if (lev->dir==LinphoneSubscriptionIncoming){ sal_notify_close(lev->op); }else if (lev->dir==LinphoneSubscriptionOutgoing){ sal_unsubscribe(lev->op); } if (lev->publish_state!=LinphonePublishNone){ if (lev->publish_state==LinphonePublishOk && lev->expires!=-1){ sal_op_unpublish(lev->op); } linphone_event_set_publish_state(lev,LinphonePublishCleared); return; } if (lev->subscription_state!=LinphoneSubscriptionNone){ linphone_event_set_state(lev,LinphoneSubscriptionTerminated); return; } } LinphoneEvent *linphone_event_ref(LinphoneEvent *lev){ belle_sip_object_ref(lev); return lev; } static void linphone_event_destroy(LinphoneEvent *lev){ if (lev->ei) linphone_error_info_unref(lev->ei); if (lev->op) sal_op_release(lev->op); if (lev->send_custom_headers) sal_custom_header_free(lev->send_custom_headers); ms_free(lev->name); } void linphone_event_unref(LinphoneEvent *lev){ belle_sip_object_unref(lev); } LinphoneSubscriptionDir linphone_event_get_subscription_dir(LinphoneEvent *lev){ return lev->dir; } LinphoneSubscriptionState linphone_event_get_subscription_state(const LinphoneEvent *lev){ return lev->subscription_state; } const char *linphone_event_get_name(const LinphoneEvent *lev){ return lev->name; } const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){ if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){ return (LinphoneAddress*)sal_op_get_to_address(lev->op); }else{ return (LinphoneAddress*)sal_op_get_from_address(lev->op); } } const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){ if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){ return (LinphoneAddress*)sal_op_get_from_address(lev->op); }else{ return (LinphoneAddress*)sal_op_get_to_address(lev->op); } } LinphoneCore *linphone_event_get_core(const LinphoneEvent *lev){ return lev->lc; } static belle_sip_error_code _linphone_event_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) { LinphoneEvent *ev = (LinphoneEvent*)obj; belle_sip_error_code err = BELLE_SIP_OK; err = belle_sip_snprintf(buff, buff_size, offset, "%s of %s", ev->dir == LinphoneSubscriptionIncoming ? "Incoming Subscribe" : (ev->dir == LinphoneSubscriptionOutgoing ? "Outgoing subscribe" : "Publish"), ev->name); return err; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneEvent); BELLE_SIP_INSTANCIATE_VPTR(LinphoneEvent, belle_sip_object_t, (belle_sip_object_destroy_t) linphone_event_destroy, NULL, // clone _linphone_event_marshall, FALSE ); linphone-3.12.0/coreapi/factory.c000066400000000000000000000300721313432737600166610ustar00rootroot00000000000000/* linphone Copyright (C) 2016 Belledonne Communications This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/factory.h" #include "private.h" #ifndef PACKAGE_SOUND_DIR #define PACKAGE_SOUND_DIR "." #endif #ifndef PACKAGE_RING_DIR #define PACKAGE_RING_DIR "." #endif #ifndef PACKAGE_DATA_DIR #define PACKAGE_DATA_DIR "." #endif extern LinphoneCore *_linphone_core_new_with_config(LinphoneCoreCbs *cbs, struct _LpConfig *config, void *userdata); extern LinphoneAddress *_linphone_address_new(const char *addr); typedef belle_sip_object_t_vptr_t LinphoneFactory_vptr_t; struct _LinphoneFactory { belle_sip_object_t base; bctbx_list_t *supported_video_definitions; /*these are the directories set by the application*/ char *top_resources_dir; char *data_resources_dir; char *sound_resources_dir; char *ring_resources_dir; char *image_resources_dir; char *msplugins_dir; /*these are the cached result computed from directories set by the application*/ char *cached_data_resources_dir; char *cached_sound_resources_dir; char *cached_ring_resources_dir; char *cached_image_resources_dir; char *cached_msplugins_dir; LinphoneErrorInfo* ei; }; static void linphone_factory_uninit(LinphoneFactory *obj){ bctbx_list_free_with_data(obj->supported_video_definitions, (bctbx_list_free_func)linphone_video_definition_unref); STRING_RESET(obj->top_resources_dir); STRING_RESET(obj->data_resources_dir); STRING_RESET(obj->sound_resources_dir); STRING_RESET(obj->ring_resources_dir); STRING_RESET(obj->image_resources_dir); STRING_RESET(obj->msplugins_dir); STRING_RESET(obj->cached_data_resources_dir); STRING_RESET(obj->cached_sound_resources_dir); STRING_RESET(obj->cached_ring_resources_dir); STRING_RESET(obj->cached_image_resources_dir); STRING_RESET(obj->cached_msplugins_dir); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFactory); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFactory, belle_sip_object_t, linphone_factory_uninit, // destroy NULL, // clone NULL, // Marshall FALSE ); static LinphoneFactory *_factory = NULL; static void _linphone_factory_destroying_cb(void) { if (_factory != NULL) { belle_sip_object_unref(_factory); _factory = NULL; } } #define ADD_SUPPORTED_VIDEO_DEFINITION(factory, width, height, name) \ (factory)->supported_video_definitions = bctbx_list_append((factory)->supported_video_definitions, \ linphone_video_definition_ref(linphone_video_definition_new(width, height, name))) static void initialize_supported_video_definitions(LinphoneFactory *factory) { #if !defined(__ANDROID__) && !TARGET_OS_IPHONE ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_1080P_W, MS_VIDEO_SIZE_1080P_H, "1080p"); #endif #if !defined(__ANDROID__) && !TARGET_OS_MAC /*limit to most common sizes because mac video API cannot list supported resolutions*/ ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_UXGA_W, MS_VIDEO_SIZE_UXGA_H, "uxga"); ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_SXGA_MINUS_W, MS_VIDEO_SIZE_SXGA_MINUS_H, "sxga-"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_720P_W, MS_VIDEO_SIZE_720P_H, "720p"); #if !defined(__ANDROID__) && !TARGET_OS_MAC ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_XGA_W, MS_VIDEO_SIZE_XGA_H, "xga"); #endif #if !defined(__ANDROID__) && !TARGET_OS_IPHONE ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_SVGA_W, MS_VIDEO_SIZE_SVGA_H, "svga"); ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_4CIF_W, MS_VIDEO_SIZE_4CIF_H, "4cif"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_VGA_W, MS_VIDEO_SIZE_VGA_H, "vga"); #if TARGET_OS_IPHONE ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_IOS_MEDIUM_H, MS_VIDEO_SIZE_IOS_MEDIUM_W, "ios-medium"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_CIF_W, MS_VIDEO_SIZE_CIF_H, "cif"); #if !TARGET_OS_MAC || TARGET_OS_IPHONE /* OS_MAC is 1 for iPhone, but we need QVGA */ ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_QVGA_W, MS_VIDEO_SIZE_QVGA_H, "qvga"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_QCIF_W, MS_VIDEO_SIZE_QCIF_H, "qcif"); } static LinphoneFactory *linphone_factory_new(void){ LinphoneFactory *factory = belle_sip_object_new(LinphoneFactory); factory->top_resources_dir = bctbx_strdup(PACKAGE_DATA_DIR); initialize_supported_video_definitions(factory); return factory; } LinphoneFactory *linphone_factory_get(void) { if (_factory == NULL) { _factory = linphone_factory_new(); atexit(_linphone_factory_destroying_cb); } return _factory; } void linphone_factory_clean(void){ if (_factory){ belle_sip_object_unref(_factory); _factory = NULL; } } LinphoneCore *linphone_factory_create_core(const LinphoneFactory *factory, LinphoneCoreCbs *cbs, const char *config_path, const char *factory_config_path) { LpConfig *config = lp_config_new_with_factory(config_path, factory_config_path); LinphoneCore *lc = _linphone_core_new_with_config(cbs, config, NULL); lp_config_unref(config); return lc; } LinphoneCore *linphone_factory_create_core_with_config(const LinphoneFactory *factory, LinphoneCoreCbs *cbs, LinphoneConfig *config) { return _linphone_core_new_with_config(cbs, config, NULL); } LinphoneCoreCbs *linphone_factory_create_core_cbs(const LinphoneFactory *factory) { return _linphone_core_cbs_new(); } LinphoneAddress *linphone_factory_create_address(const LinphoneFactory *factory, const char *addr) { return _linphone_address_new(addr); } LinphoneAuthInfo *linphone_factory_create_auth_info(const LinphoneFactory *factory, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain) { return linphone_auth_info_new(username, userid, passwd, ha1, realm, domain); } LinphoneCallCbs * linphone_factory_create_call_cbs(const LinphoneFactory *factory) { return _linphone_call_cbs_new(); } LinphoneVcard *linphone_factory_create_vcard(LinphoneFactory *factory) { return _linphone_vcard_new(); } LinphoneVideoDefinition * linphone_factory_create_video_definition(const LinphoneFactory *factory, unsigned int width, unsigned int height) { return linphone_video_definition_ref(linphone_video_definition_new(width, height, NULL)); } LinphoneVideoDefinition * linphone_factory_create_video_definition_from_name(const LinphoneFactory *factory, const char *name) { unsigned int width = 0; unsigned int height = 0; LinphoneVideoDefinition *vdef = linphone_factory_find_supported_video_definition_by_name(factory, name); if (vdef != NULL) return vdef; if (sscanf(name, "%ux%u", &width, &height) == 2) { return linphone_video_definition_new(width, height, NULL); } return linphone_video_definition_new(0, 0, NULL); } const bctbx_list_t * linphone_factory_get_supported_video_definitions(const LinphoneFactory *factory) { return factory->supported_video_definitions; } LinphoneVideoDefinition * linphone_factory_find_supported_video_definition(const LinphoneFactory *factory, unsigned int width, unsigned int height) { const bctbx_list_t *item; const bctbx_list_t *supported = linphone_factory_get_supported_video_definitions(factory); LinphoneVideoDefinition *searched_vdef = linphone_video_definition_new(width, height, NULL); for (item = supported; item != NULL; item = bctbx_list_next(item)) { LinphoneVideoDefinition *svdef = (LinphoneVideoDefinition *)bctbx_list_get_data(item); if (linphone_video_definition_equals(svdef, searched_vdef)) { linphone_video_definition_unref(searched_vdef); return linphone_video_definition_clone(svdef); } } return searched_vdef; } LinphoneVideoDefinition * linphone_factory_find_supported_video_definition_by_name(const LinphoneFactory *factory, const char *name) { const bctbx_list_t *item; const bctbx_list_t *supported = linphone_factory_get_supported_video_definitions(factory); for (item = supported; item != NULL; item = bctbx_list_next(item)) { LinphoneVideoDefinition *svdef = (LinphoneVideoDefinition *)bctbx_list_get_data(item); if (strcmp(linphone_video_definition_get_name(svdef), name) == 0) { return linphone_video_definition_clone(svdef); } } return NULL; } const char * linphone_factory_get_top_resources_dir(const LinphoneFactory *factory) { return factory->top_resources_dir; } void linphone_factory_set_top_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->top_resources_dir, path); } const char * linphone_factory_get_data_resources_dir(LinphoneFactory *factory) { if (factory->data_resources_dir) return factory->data_resources_dir; if (factory->top_resources_dir){ STRING_TRANSFER(factory->cached_data_resources_dir, bctbx_strdup_printf("%s/linphone", factory->top_resources_dir)); }else{ STRING_TRANSFER(factory->cached_data_resources_dir, bctbx_strdup_printf("%s/linphone", PACKAGE_DATA_DIR)); } return factory->cached_data_resources_dir; } void linphone_factory_set_data_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->data_resources_dir, path); } const char * linphone_factory_get_sound_resources_dir(LinphoneFactory *factory) { if (factory->sound_resources_dir) return factory->sound_resources_dir; if (factory->top_resources_dir){ STRING_TRANSFER(factory->cached_sound_resources_dir, bctbx_strdup_printf("%s/sounds/linphone", factory->top_resources_dir)); return factory->cached_sound_resources_dir; } return PACKAGE_SOUND_DIR; } void linphone_factory_set_sound_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->sound_resources_dir, path); } const char * linphone_factory_get_ring_resources_dir(LinphoneFactory *factory) { if (factory->ring_resources_dir) return factory->ring_resources_dir; if (factory->sound_resources_dir){ STRING_TRANSFER(factory->cached_ring_resources_dir, bctbx_strdup_printf("%s/rings", factory->sound_resources_dir)); return factory->cached_ring_resources_dir; } if (factory->top_resources_dir) { STRING_TRANSFER(factory->cached_ring_resources_dir, bctbx_strdup_printf("%s/sounds/linphone/rings", factory->top_resources_dir)); return factory->cached_ring_resources_dir; } return PACKAGE_RING_DIR; } void linphone_factory_set_ring_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->ring_resources_dir, path); } const char * linphone_factory_get_image_resources_dir(LinphoneFactory *factory) { if (factory->image_resources_dir) return factory->image_resources_dir; if (factory->top_resources_dir) { STRING_TRANSFER(factory->cached_image_resources_dir, bctbx_strdup_printf("%s/images", factory->top_resources_dir)); }else{ STRING_TRANSFER(factory->cached_image_resources_dir, bctbx_strdup_printf("%s/images", PACKAGE_DATA_DIR)); } return factory->cached_image_resources_dir; } void linphone_factory_set_image_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->image_resources_dir, path); } const char * linphone_factory_get_msplugins_dir(LinphoneFactory *factory) { return factory->msplugins_dir; } void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->msplugins_dir, path); } LinphoneErrorInfo *linphone_factory_create_error_info(LinphoneFactory *factory){ return linphone_error_info_new(); } LinphoneRange *linphone_factory_create_range(LinphoneFactory *factory) { return linphone_range_new(); } LinphoneTransports *linphone_factory_create_transports(LinphoneFactory *factory) { return linphone_transports_new(); } LinphoneVideoActivationPolicy *linphone_factory_create_video_activation_policy(LinphoneFactory *factory) { return linphone_video_activation_policy_new(); }linphone-3.12.0/coreapi/fonis.c000066400000000000000000000055521313432737600163350ustar00rootroot00000000000000/* linphone Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sipsetup.h" #include "p2pproxy.h" static ms_thread_t fonis_thread; static void *fonis_thread_func(void *arg){ if (p2pproxy_application_start(0,NULL)!=0){ ms_error("Fail to start fonis thread !"); } return NULL; } static bool_t fonis_init(void){ static bool_t initd=FALSE; if (!initd){ ms_thread_create(&fonis_thread,NULL,fonis_thread_func,NULL); initd=TRUE; } return TRUE; } static int fonis_create_account(const char *uri, const char *passwd){ int err=p2pproxy_accountmgt_createAccount(uri); if (err<0) return -1; return 0; } static int fonis_login_account(SipSetupContext * ctx,const char *uri, const char *passwd){ if (p2pproxy_accountmgt_isValidAccount==P2PPROXY_ACCOUNTMGT_USER_EXIST) { return 0; } else return -1; } static int fonis_get_proxy(SipSetupContext *ctx, const char *domain, char *proxy, size_t sz){ int err=p2pproxy_resourcemgt_lookup_sip_proxy(proxy,sz,(char*)domain); if (err==0) return 0; else return -1; } static int fonis_get_stun_servers(SipSetupContext *ctx, char *stun1, char *stun2, size_t size){ FonisContext *fc=(FonisContext*)ctx->data; int ret=-1; p2pproxy_resourcemgt_resource_list_t* l=p2pproxy_resourcemgt_new_resource_list(); if (p2pproxy_resourcemgt_lookup_media_resource(l,ctx->domain)==0){ if (l->size>0) strncpy(stun1,l->resource_uri[0],size); if (l->size>1) strncpy(stun2,l->resource_uri[1],size); ret=0; } p2pproxy_resourcemgt_delete_resource_list(l); return ret; } static int fonis_get_stun_relay(SipSetupContext *ctx, char *relay, size_t size){ FonisContext *fc=(FonisContext*)ctx->data; int ret=-1; p2pproxy_resourcemgt_resource_list_t* l=p2pproxy_resourcemgt_new_resource_list(); if (p2pproxy_resourcemgt_lookup_media_resource(l,ctx->domain)==0){ if (l->size>0) strncpy(relay,l->resource_uri[0],size); ret=0; } p2pproxy_resourcemgt_delete_resource_list(l); return ret; } SipSetup fonis_sip_setup={ .name="fonis", .init=fonis_init, .create_account=fonis_create_account, .login_account=fonis_login_account, .get_proxy=fonis_get_proxy, .get_stun_servers=fonis_get_stun_servers, .get_relay=fonis_get_relay, .exit=p2pproxy_application_stop }; linphone-3.12.0/coreapi/friend.c000066400000000000000000001640351313432737600164700ustar00rootroot00000000000000/*************************************************************************** * friend.c * * Sat May 15 15:25:16 2004 * Copyright 2004-2009 Simon Morlat * Email ****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "private.h" #include "linphone/lpconfig.h" #ifdef SQLITE_STORAGE_ENABLED #ifndef _WIN32 #if !defined(__ANDROID__) && !defined(__QNXNTO__) # include # include # include #endif #else #include #endif #define MAX_PATH_SIZE 1024 #include "sqlite3.h" #endif const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ const char *str=NULL; switch(ss){ case LinphoneStatusOnline: str=_("Online"); break; case LinphoneStatusBusy: str=_("Busy"); break; case LinphoneStatusBeRightBack: str=_("Be right back"); break; case LinphoneStatusAway: str=_("Away"); break; case LinphoneStatusOnThePhone: str=_("On the phone"); break; case LinphoneStatusOutToLunch: str=_("Out to lunch"); break; case LinphoneStatusDoNotDisturb: str=_("Do not disturb"); break; case LinphoneStatusMoved: str=_("Moved"); break; case LinphoneStatusAltService: str=_("Using another messaging service"); break; case LinphoneStatusOffline: str=_("Offline"); break; case LinphoneStatusPending: str=_("Pending"); break; case LinphoneStatusVacation: str=_("Vacation"); break; default: str=_("Unknown status"); } return str; } static int friend_compare(const void * a, const void * b) { LinphoneFriend *lfa = (LinphoneFriend *)a; LinphoneFriend *lfb = (LinphoneFriend *)b; const bctbx_list_t *addressesa = linphone_friend_get_addresses(lfa); const bctbx_list_t *addressesb = linphone_friend_get_addresses(lfb); bctbx_list_t *iteratora = (bctbx_list_t *)addressesa; bctbx_list_t *iteratorb = (bctbx_list_t *)addressesb; int ret = 1; while (iteratora && (ret == 1)) { LinphoneAddress *fa = (LinphoneAddress *)bctbx_list_get_data(iteratora); while (iteratorb && (ret == 1)) { LinphoneAddress *fb = (LinphoneAddress *)bctbx_list_get_data(iteratorb); if (linphone_address_weak_equal(fa, fb)) ret = 0; iteratorb = bctbx_list_next(iteratorb); } iteratora = bctbx_list_next(iteratora); } return ret; } static LinphoneFriendPresence * find_presence_model_for_uri_or_tel(const LinphoneFriend *lf, const char *uri_or_tel) { bctbx_list_t *iterator = NULL; LinphoneAddress *uri_or_tel_addr = NULL; LinphoneFriendPresence *result=NULL; if (!lf->lc) { ms_warning("Cannot find uri of tel [%s] from friend [%p] because not associated to any Linphone core object",uri_or_tel,lf); return NULL; } if ((iterator = lf->presence_models) == NULL) { /*no need to move forward, just reutn to avoid useless uri parsing*/ return NULL; }; uri_or_tel_addr = linphone_core_interpret_url(lf->lc, uri_or_tel); while (uri_or_tel_addr && iterator) { LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator); LinphoneAddress *lfp_addr = linphone_core_interpret_url(lf->lc, lfp->uri_or_tel); if (lfp_addr && linphone_address_weak_equal(uri_or_tel_addr, lfp_addr)) { result = lfp; } if (lfp_addr) linphone_address_unref(lfp_addr); if (result == NULL) iterator = bctbx_list_next(iterator); else break; } if (uri_or_tel_addr) linphone_address_unref(uri_or_tel_addr); return result; } static void add_presence_model_for_uri_or_tel(LinphoneFriend *lf, const char *uri_or_tel, LinphonePresenceModel *presence) { LinphoneFriendPresence *lfp = ms_new0(LinphoneFriendPresence, 1); lfp->uri_or_tel = ms_strdup(uri_or_tel); lfp->presence = presence; lf->presence_models = bctbx_list_append(lf->presence_models, lfp); } static void free_friend_presence(LinphoneFriendPresence *lfp) { ms_free(lfp->uri_or_tel); if (lfp->presence) linphone_presence_model_unref(lfp->presence); ms_free(lfp); } static void free_phone_number_sip_uri(LinphoneFriendPhoneNumberSipUri *lfpnsu) { ms_free(lfpnsu->number); ms_free(lfpnsu->uri); ms_free(lfpnsu); } bctbx_list_t *linphone_find_friend_by_address(bctbx_list_t *fl, const LinphoneAddress *addr, LinphoneFriend **lf){ bctbx_list_t *res=NULL; LinphoneFriend dummy; if (lf!=NULL) *lf=NULL; memset(&dummy, 0, sizeof(LinphoneFriend)); dummy.uri=(LinphoneAddress*)addr; res=bctbx_list_find_custom(fl,friend_compare,&dummy); if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)bctbx_list_get_data(res); return res; } void __linphone_friend_do_subscribe(LinphoneFriend *fr){ LinphoneCore *lc=fr->lc; const LinphoneAddress *addr = linphone_friend_get_address(fr); if (addr != NULL) { if (fr->outsub==NULL){ /* people for which we don't have yet an answer should appear as offline */ fr->presence_models = bctbx_list_free_with_data(fr->presence_models, (bctbx_list_free_func)free_friend_presence); /* if (fr->lc->vtable.notify_recv) fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr); */ }else{ sal_op_release(fr->outsub); fr->outsub=NULL; } fr->outsub=sal_op_new(lc->sal); linphone_configure_op(lc,fr->outsub,addr,NULL,TRUE); sal_subscribe_presence(fr->outsub,NULL,NULL,lp_config_get_int(lc->config,"sip","subscribe_expires",600)); fr->subscribe_active=TRUE; } } LinphoneFriend * linphone_friend_new(void){ LinphoneFriend *obj = belle_sip_object_new(LinphoneFriend); obj->pol = LinphoneSPAccept; obj->subscribe = TRUE; obj->vcard = NULL; obj->storage_id = 0; return obj; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif LinphoneFriend *linphone_friend_new_with_address(const char *addr){ LinphoneAddress* linphone_address = linphone_address_new(addr); LinphoneFriend *fr; if (linphone_address == NULL) { ms_error("Cannot create friend for address [%s]",addr?addr:"null"); return NULL; } fr=linphone_friend_new(); linphone_friend_set_address(fr,linphone_address); linphone_address_unref(linphone_address); return fr; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif void linphone_friend_set_user_data(LinphoneFriend *lf, void *data){ lf->user_data=data; } void* linphone_friend_get_user_data(const LinphoneFriend *lf){ return lf->user_data; } bool_t linphone_friend_in_list(const LinphoneFriend *lf) { return lf->friend_list != NULL; } void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result){ LinphoneAddress *fr=NULL; *result=NULL; fr=linphone_address_new(uri); if (fr==NULL){ char *tmp=NULL; if (strchr(uri,'@')!=NULL){ LinphoneAddress *u; /*try adding sip:*/ tmp=ms_strdup_printf("sip:%s",uri); u=linphone_address_new(tmp); if (u!=NULL){ *result=tmp; } }else if (lc->default_proxy!=NULL){ /*try adding domain part from default current proxy*/ LinphoneAddress * id=linphone_address_new(linphone_core_get_identity(lc)); if ((id!=NULL) && (uri[0] != '\0')){ linphone_address_set_display_name(id,NULL); linphone_address_set_username(id,uri); *result=linphone_address_as_string(id); linphone_address_unref(id); } } if (*result){ /*looks good */ ms_message("%s interpreted as %s",uri,*result); }else{ ms_warning("Fail to interpret friend uri %s",uri); } }else { *result=linphone_address_as_string(fr); linphone_address_unref(fr); } } const LinphoneAddress * linphone_friend_get_address(const LinphoneFriend *lf) { if (linphone_core_vcard_supported()) { if (lf->vcard) { const bctbx_list_t *sip_addresses = linphone_vcard_get_sip_addresses(lf->vcard); if (sip_addresses) { LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_nth_data(sip_addresses, 0); return addr; } } return NULL; } if (lf->uri) return lf->uri; return NULL; } LinphoneStatus linphone_friend_set_address(LinphoneFriend *lf, const LinphoneAddress *addr) { LinphoneAddress *fr = linphone_address_clone(addr); char *address; const LinphoneAddress *mAddr = linphone_friend_get_address(lf); if(mAddr && lf->friend_list) { char *mainAddress = linphone_address_as_string_uri_only(mAddr); bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, mainAddress); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(lf->friend_list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(lf->friend_list->friends_map_uri, it); } bctbx_iterator_cchar_delete(it); } linphone_address_clean(fr); address = linphone_address_as_string_uri_only(fr); if(lf->friend_list) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(address, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(lf->friend_list->friends_map_uri, pair); } if (linphone_core_vcard_supported()) { if (!lf->vcard) { const char *dpname = linphone_address_get_display_name(fr) ? linphone_address_get_display_name(fr) : linphone_address_get_username(fr); linphone_friend_create_vcard(lf, dpname); } linphone_vcard_edit_main_sip_address(lf->vcard, address); linphone_address_unref(fr); } else { if (lf->uri != NULL) linphone_address_unref(lf->uri); lf->uri = fr; } ms_free(address); return 0; } void linphone_friend_add_address(LinphoneFriend *lf, const LinphoneAddress *addr) { LinphoneAddress *fr; char *uri; if (!lf || !addr) return; fr = linphone_address_clone(addr); linphone_address_clean(fr); uri = linphone_address_as_string_uri_only(fr); if(lf->friend_list) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(lf->friend_list->friends_map_uri, pair); } if (linphone_core_vcard_supported()) { if (lf->vcard) { linphone_vcard_add_sip_address(lf->vcard, uri); linphone_address_unref(fr); } } else { if (lf->uri == NULL) lf->uri = fr; else linphone_address_unref(fr); } ms_free(uri); } const bctbx_list_t* linphone_friend_get_addresses(const LinphoneFriend *lf) { if (!lf) return NULL; if (linphone_core_vcard_supported()) { const bctbx_list_t * addresses = linphone_vcard_get_sip_addresses(lf->vcard); return addresses; } else { bctbx_list_t *addresses = NULL; return lf->uri ? bctbx_list_append(addresses, lf->uri) : NULL; } } void linphone_friend_remove_address(LinphoneFriend *lf, const LinphoneAddress *addr) { char *address ; if (!lf || !addr || !lf->vcard) return; address = linphone_address_as_string_uri_only(addr); if(lf->friend_list) { bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, address); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(lf->friend_list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(lf->friend_list->friends_map_uri, it); } bctbx_iterator_cchar_delete(it); } if (linphone_core_vcard_supported()) { linphone_vcard_remove_sip_address(lf->vcard, address); } ms_free(address); } void linphone_friend_add_phone_number(LinphoneFriend *lf, const char *phone) { if (!lf || !phone) return; if(lf->friend_list) { const char *uri = linphone_friend_phone_number_to_sip_uri(lf, phone); if(uri) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(lf->friend_list->friends_map_uri, pair); } } if (linphone_core_vcard_supported()) { if (!lf->vcard) { linphone_friend_create_vcard(lf, phone); } linphone_vcard_add_phone_number(lf->vcard, phone); } } bctbx_list_t* linphone_friend_get_phone_numbers(LinphoneFriend *lf) { if (!lf || !lf->vcard) return NULL; if (linphone_core_vcard_supported()) { return linphone_vcard_get_phone_numbers(lf->vcard); } return NULL; } void linphone_friend_remove_phone_number(LinphoneFriend *lf, const char *phone) { if (!lf || !phone || !lf->vcard) return; if(lf->friend_list) { const char *uri = linphone_friend_phone_number_to_sip_uri(lf, phone); if(uri) { bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, uri); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(lf->friend_list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(lf->friend_list->friends_map_uri, it); } bctbx_iterator_cchar_delete(it); } } if (linphone_core_vcard_supported()) { linphone_vcard_remove_phone_number(lf->vcard, phone); } } LinphoneStatus linphone_friend_set_name(LinphoneFriend *lf, const char *name){ if (linphone_core_vcard_supported()) { if (!lf->vcard) linphone_friend_create_vcard(lf, name); linphone_vcard_set_full_name(lf->vcard, name); } else { if (!lf->uri) { ms_warning("linphone_friend_set_address() must be called before linphone_friend_set_name() to be able to set display name."); return -1; } linphone_address_set_display_name(lf->uri, name); } return 0; } LinphoneStatus linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val){ fr->subscribe=val; return 0; } LinphoneStatus linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol) { fr->pol=pol; return 0; } void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){ bctbx_list_t *elem; if (lf->insubs){ const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) { char *addr_str = linphone_address_as_string(addr); ms_message("Want to notify %s", addr_str); ms_free(addr_str); } } for(elem=lf->insubs; elem!=NULL; elem=bctbx_list_next(elem)){ SalOp *op = (SalOp*)bctbx_list_get_data(elem); sal_notify_presence(op,(SalPresenceModel *)presence); } } void linphone_friend_add_incoming_subscription(LinphoneFriend *lf, SalOp *op){ /*ownership of the op is transfered from sal to the LinphoneFriend*/ lf->insubs = bctbx_list_append(lf->insubs, op); } void linphone_friend_remove_incoming_subscription(LinphoneFriend *lf, SalOp *op){ if (bctbx_list_find(lf->insubs, op)){ sal_op_release(op); lf->insubs = bctbx_list_remove(lf->insubs, op); } } static void linphone_friend_unsubscribe(LinphoneFriend *lf){ if (lf->outsub!=NULL) { sal_unsubscribe(lf->outsub); } /* for friend list there is no necessary outsub*/ lf->subscribe_active=FALSE; } void linphone_friend_invalidate_subscription(LinphoneFriend *lf){ bctbx_list_t *iterator; LinphoneCore *lc=lf->lc; if (lf->outsub!=NULL) { sal_op_release(lf->outsub); lf->outsub=NULL; } // To resend a subscribe on the next network_reachable(TRUE) lf->subscribe_active=FALSE; /* Notify application that we no longer know the presence activity */ iterator = lf->presence_models; while (iterator) { LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator); linphone_presence_model_unref(lfp->presence); lfp->presence = linphone_presence_model_new(); linphone_presence_model_set_basic_status(lfp->presence, LinphonePresenceBasicStatusClosed); linphone_core_notify_notify_presence_received_for_uri_or_tel(lc, lf, lfp->uri_or_tel, lfp->presence); iterator = bctbx_list_next(iterator); } if (bctbx_list_size(lf->presence_models) > 0) linphone_core_notify_notify_presence_received(lc, lf); lf->initial_subscribes_sent=FALSE; } static void linphone_friend_close_incoming_subscriptions(LinphoneFriend *lf) { bctbx_list_for_each(lf->insubs, (MSIterateFunc) sal_notify_presence_close); lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc)sal_op_release); } void linphone_friend_close_subscriptions(LinphoneFriend *lf){ linphone_friend_unsubscribe(lf); linphone_friend_close_incoming_subscriptions(lf); } static void _linphone_friend_release_ops(LinphoneFriend *lf){ lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc) sal_op_release); if (lf->outsub){ sal_op_release(lf->outsub); lf->outsub=NULL; } } static void _linphone_friend_destroy(LinphoneFriend *lf){ _linphone_friend_release_ops(lf); if (lf->presence_models) bctbx_list_free_with_data(lf->presence_models, (bctbx_list_free_func)free_friend_presence); if (lf->phone_number_sip_uri_map) bctbx_list_free_with_data(lf->phone_number_sip_uri_map, (bctbx_list_free_func)free_phone_number_sip_uri); if (lf->uri!=NULL) linphone_address_unref(lf->uri); if (lf->info!=NULL) buddy_info_free(lf->info); if (lf->vcard != NULL) linphone_vcard_unref(lf->vcard); if (lf->refkey != NULL) ms_free(lf->refkey); } static belle_sip_error_code _linphone_friend_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) { LinphoneFriend *lf = (LinphoneFriend*)obj; belle_sip_error_code err = BELLE_SIP_OK; if (lf->uri){ char *tmp = linphone_address_as_string(lf->uri); err = belle_sip_snprintf(buff, buff_size, offset, "%s", tmp); ms_free(tmp); } return err; } const char * linphone_friend_get_name(const LinphoneFriend *lf) { if (!lf) return NULL; if (linphone_core_vcard_supported()) { if (lf->vcard) return linphone_vcard_get_full_name(lf->vcard); } else if (lf->uri) { return linphone_address_get_display_name(lf->uri); } return NULL; } bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){ return lf->subscribe; } LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf){ return lf->pol; } LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){ const LinphonePresenceModel *presence = linphone_friend_get_presence_model(lf); LinphoneOnlineStatus online_status = LinphoneStatusOffline; LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed; LinphonePresenceActivity *activity = NULL; const char *description = NULL; unsigned int nb_activities = 0; if (presence != NULL) { basic_status = linphone_presence_model_get_basic_status(presence); nb_activities = linphone_presence_model_get_nb_activities(presence); online_status = (basic_status == LinphonePresenceBasicStatusOpen) ? LinphoneStatusOnline : LinphoneStatusOffline; if (nb_activities > 1) { char *tmp = NULL; const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) tmp = linphone_address_as_string(addr); ms_warning("Friend %s has several activities, get status from the first one", tmp ? tmp : "unknown"); if (tmp) { ms_free(tmp); } nb_activities = 1; } if (nb_activities == 1) { activity = linphone_presence_model_get_activity(presence); description = linphone_presence_activity_get_description(activity); switch (linphone_presence_activity_get_type(activity)) { case LinphonePresenceActivityBreakfast: case LinphonePresenceActivityDinner: case LinphonePresenceActivityLunch: case LinphonePresenceActivityMeal: online_status = LinphoneStatusOutToLunch; break; case LinphonePresenceActivityAppointment: case LinphonePresenceActivityMeeting: case LinphonePresenceActivityPerformance: case LinphonePresenceActivityPresentation: case LinphonePresenceActivitySpectator: case LinphonePresenceActivityWorking: case LinphonePresenceActivityWorship: online_status = LinphoneStatusDoNotDisturb; break; case LinphonePresenceActivityAway: case LinphonePresenceActivitySleeping: online_status = LinphoneStatusAway; break; case LinphonePresenceActivityHoliday: case LinphonePresenceActivityTravel: case LinphonePresenceActivityVacation: online_status = LinphoneStatusVacation; break; case LinphonePresenceActivityBusy: if (description && strcmp(description, "Do not disturb") == 0) { // See linphonecore.c linphone_core_set_presence_info() method online_status = LinphoneStatusDoNotDisturb; } else { online_status = LinphoneStatusBusy; } break; case LinphonePresenceActivityLookingForWork: case LinphonePresenceActivityPlaying: case LinphonePresenceActivityShopping: case LinphonePresenceActivityTV: online_status = LinphoneStatusBusy; break; case LinphonePresenceActivityInTransit: case LinphonePresenceActivitySteering: online_status = LinphoneStatusBeRightBack; break; case LinphonePresenceActivityOnThePhone: online_status = LinphoneStatusOnThePhone; break; case LinphonePresenceActivityOther: case LinphonePresenceActivityPermanentAbsence: online_status = LinphoneStatusMoved; break; case LinphonePresenceActivityUnknown: /* Rely on the basic status information. */ break; } } } return online_status; } const LinphonePresenceModel * linphone_friend_get_presence_model(const LinphoneFriend *lf) { const LinphonePresenceModel *presence = NULL; LinphoneFriend* const_lf = (LinphoneFriend*)lf; const bctbx_list_t* addrs = linphone_friend_get_addresses(const_lf); bctbx_list_t* phones = NULL; bctbx_list_t *it; for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) { LinphoneAddress *addr = (LinphoneAddress*)it->data; char *uri = linphone_address_as_string_uri_only(addr); presence = linphone_friend_get_presence_model_for_uri_or_tel(const_lf, uri); ms_free(uri); if (presence) break; } if (presence) return presence; phones = linphone_friend_get_phone_numbers(const_lf); for (it = phones; it!= NULL; it = it->next) { presence = linphone_friend_get_presence_model_for_uri_or_tel(const_lf, it->data); if (presence) break; } bctbx_list_free(phones); return presence; } LinphoneConsolidatedPresence linphone_friend_get_consolidated_presence(const LinphoneFriend *lf) { const LinphonePresenceModel *model = linphone_friend_get_presence_model(lf); if (!model) return LinphoneConsolidatedPresenceOffline; return linphone_presence_model_get_consolidated_presence(model); } const LinphonePresenceModel * linphone_friend_get_presence_model_for_uri_or_tel(const LinphoneFriend *lf, const char *uri_or_tel) { LinphoneFriendPresence *lfp = find_presence_model_for_uri_or_tel(lf, uri_or_tel); if (lfp) return lfp->presence; return NULL; } void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence) { const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) { char *uri = linphone_address_as_string_uri_only(addr); linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, presence); ms_free(uri); } } void linphone_friend_set_presence_model_for_uri_or_tel(LinphoneFriend *lf, const char *uri_or_tel, LinphonePresenceModel *presence) { LinphoneFriendPresence *lfp = find_presence_model_for_uri_or_tel(lf, uri_or_tel); if (lfp) { if (lfp->presence) linphone_presence_model_unref(lfp->presence); lfp->presence = presence; } else { add_presence_model_for_uri_or_tel(lf, uri_or_tel, presence); } } bool_t linphone_friend_is_presence_received(const LinphoneFriend *lf) { return lf->presence_received; } BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){ return lf->info; } /* * updates the p2p subscriptions. * If only_when_registered is TRUE, subscribe will be sent only if the friend's corresponding proxy config is in registered. * Otherwise if the proxy config goes to unregistered state, the subscription refresh will be suspended. * An optional proxy whose state has changed can be passed to optimize the processing. **/ void linphone_friend_update_subscribes(LinphoneFriend *fr, bool_t only_when_registered){ int can_subscribe=1; if (only_when_registered && (fr->subscribe || fr->subscribe_active)){ const LinphoneAddress *addr = linphone_friend_get_address(fr); if (addr != NULL) { LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(fr->lc, addr); if (cfg && cfg->state!=LinphoneRegistrationOk){ char *tmp=linphone_address_as_string(addr); ms_message("Friend [%s] belongs to proxy config with identity [%s], but this one isn't registered. Subscription is suspended.", tmp,linphone_proxy_config_get_identity(cfg)); ms_free(tmp); can_subscribe=0; } } } if (can_subscribe && fr->subscribe && fr->subscribe_active==FALSE){ ms_message("Sending a new SUBSCRIBE"); __linphone_friend_do_subscribe(fr); }else if (can_subscribe && fr->subscribe_active && !fr->subscribe){ linphone_friend_unsubscribe(fr); }else if (!can_subscribe && fr->outsub){ fr->subscribe_active=FALSE; sal_op_stop_refreshing(fr->outsub); } } void linphone_friend_save(LinphoneFriend *fr, LinphoneCore *lc) { if (!lc) return; #ifdef SQLITE_STORAGE_ENABLED if (lc->friends_db_file) { linphone_core_store_friend_in_db(lc, fr); } else { linphone_core_write_friends_config(lc); } #else linphone_core_write_friends_config(lc); #endif } void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc) { LinphonePresenceModel *model; const LinphoneAddress *addr = linphone_friend_get_address(fr); if (!addr) { ms_debug("No sip url defined in friend %s", linphone_friend_get_name(fr)); return; } if (!linphone_core_ready(lc)) { /* lc not ready, deffering subscription */ fr->commit=TRUE; return; } if (fr->inc_subscribe_pending) { switch(fr->pol) { case LinphoneSPWait: model = linphone_presence_model_new_with_activity(LinphonePresenceActivityOther, "Waiting for user acceptance"); linphone_friend_notify(fr, model); linphone_presence_model_unref(model); break; case LinphoneSPAccept: if (fr->lc) linphone_friend_notify(fr, fr->lc->presence_model); break; case LinphoneSPDeny: linphone_friend_notify(fr, NULL); break; } fr->inc_subscribe_pending = FALSE; } if (fr->pol == LinphoneSPDeny && fr->insubs) { linphone_friend_close_incoming_subscriptions(fr); } linphone_friend_update_subscribes(fr, linphone_core_should_subscribe_friends_only_when_registered(lc)); ms_debug("linphone_friend_apply() done."); lc->bl_refresh=TRUE; fr->commit=FALSE; } void linphone_friend_edit(LinphoneFriend *fr) { if (fr && linphone_core_vcard_supported() && fr->vcard) { linphone_vcard_compute_md5_hash(fr->vcard); } } void linphone_friend_done(LinphoneFriend *fr) { ms_return_if_fail(fr); if (!fr->lc) return; if (fr && linphone_core_vcard_supported() && fr->vcard) { if (linphone_vcard_compare_md5_hash(fr->vcard) != 0) { ms_debug("vCard's md5 has changed, mark friend as dirty and clear sip addresses list cache"); linphone_vcard_clean_cache(fr->vcard); if (fr->friend_list) { fr->friend_list->dirty_friends_to_update = bctbx_list_append(fr->friend_list->dirty_friends_to_update, linphone_friend_ref(fr)); } } } linphone_friend_apply(fr, fr->lc); linphone_friend_save(fr, fr->lc); } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif LinphoneFriend * linphone_core_create_friend(LinphoneCore *lc) { LinphoneFriend * lf = linphone_friend_new(); lf->lc = lc; return lf; } LinphoneFriend * linphone_core_create_friend_with_address(LinphoneCore *lc, const char *address) { LinphoneFriend * lf = linphone_friend_new_with_address(address); if (lf) lf->lc = lc; return lf; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) { if (linphone_friend_list_add_friend(linphone_core_get_default_friend_list(lc), lf) != LinphoneFriendListOK) return; if (bctbx_list_find(lc->subscribers, lf)) { /*if this friend was in the pending subscriber list, now remove it from this list*/ lc->subscribers = bctbx_list_remove(lc->subscribers, lf); linphone_friend_unref(lf); } } void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend *lf) { if (lf && lf->friend_list) { if (linphone_friend_list_remove_friend(lf->friend_list, lf) == LinphoneFriendListNonExistentFriend) { ms_error("linphone_core_remove_friend(): friend [%p] is not part of core's list.", lf); } } } void linphone_core_update_friends_subscriptions(LinphoneCore *lc) { bctbx_list_t *lists = lc->friends_lists; while (lists) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); linphone_friend_list_update_subscriptions(list); lists = bctbx_list_next(lists); } } bool_t linphone_core_should_subscribe_friends_only_when_registered(const LinphoneCore *lc){ return lp_config_get_int(lc->config,"sip","subscribe_presence_only_when_registered",1); } void linphone_core_send_initial_subscribes(LinphoneCore *lc) { if (lc->initial_subscribes_sent) return; lc->initial_subscribes_sent=TRUE; linphone_core_update_friends_subscriptions(lc); } void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc) { bctbx_list_t *lists = lc->friends_lists; while (lists) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); linphone_friend_list_invalidate_subscriptions(list); lists = bctbx_list_next(lists); } lc->initial_subscribes_sent=FALSE; } void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key){ if (lf->refkey != NULL) { ms_free(lf->refkey); lf->refkey = NULL; } if (key) { lf->refkey = ms_strdup(key); } if (lf->lc) { linphone_friend_save(lf, lf->lc); } } const char *linphone_friend_get_ref_key(const LinphoneFriend *lf){ return lf->refkey; } LinphoneFriend *linphone_core_find_friend(const LinphoneCore *lc, const LinphoneAddress *addr) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_address(list, addr); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *uri) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_uri(list, uri); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_ref_key(list, key); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_find_friend_by_out_subscribe(const LinphoneCore *lc, SalOp *op) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_out_subscribe(list, op); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_find_friend_by_inc_subscribe(const LinphoneCore *lc, SalOp *op) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_inc_subscribe(list, op); lists = bctbx_list_next(lists); } return lf; } #define key_compare(s1,s2) strcmp(s1,s2) LinphoneSubscribePolicy __policy_str_to_enum(const char* pol){ if (key_compare("accept",pol)==0){ return LinphoneSPAccept; } if (key_compare("deny",pol)==0){ return LinphoneSPDeny; } if (key_compare("wait",pol)==0){ return LinphoneSPWait; } ms_warning("Unrecognized subscribe policy: %s",pol); return LinphoneSPWait; } LinphoneProxyConfig *__index_to_proxy(LinphoneCore *lc, int index){ if (index>=0) return (LinphoneProxyConfig*)bctbx_list_nth_data(lc->sip_conf.proxies,index); else return NULL; } LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int index){ const char *tmp; char item[50]; int a; LinphoneFriend *lf; LpConfig *config=lc->config; sprintf(item,"friend_%i",index); if (!lp_config_has_section(config,item)){ return NULL; } tmp=lp_config_get_string(config,item,"url",NULL); if (tmp==NULL) { return NULL; } lf=linphone_core_create_friend_with_address(lc, tmp); if (lf==NULL) { return NULL; } tmp=lp_config_get_string(config,item,"pol",NULL); if (tmp==NULL) linphone_friend_set_inc_subscribe_policy(lf,LinphoneSPWait); else{ linphone_friend_set_inc_subscribe_policy(lf,__policy_str_to_enum(tmp)); } a=lp_config_get_int(config,item,"subscribe",0); linphone_friend_send_subscribe(lf,a); a = lp_config_get_int(config, item, "presence_received", 0); lf->presence_received = (bool_t)a; linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL)); return lf; } const char *__policy_enum_to_str(LinphoneSubscribePolicy pol){ switch(pol){ case LinphoneSPAccept: return "accept"; break; case LinphoneSPDeny: return "deny"; break; case LinphoneSPWait: return "wait"; break; } ms_warning("Invalid policy enum value."); return "wait"; } void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf, int index){ char key[50]; char *tmp; const char *refkey; sprintf(key,"friend_%i",index); if (lf==NULL){ lp_config_clean_section(config,key); return; } if (lf->uri!=NULL){ tmp=linphone_address_as_string(lf->uri); if (tmp==NULL) { return; } lp_config_set_string(config,key,"url",tmp); ms_free(tmp); } lp_config_set_string(config,key,"pol",__policy_enum_to_str(lf->pol)); lp_config_set_int(config,key,"subscribe",lf->subscribe); lp_config_set_int(config, key, "presence_received", lf->presence_received); refkey=linphone_friend_get_ref_key(lf); if (refkey){ lp_config_set_string(config,key,"refkey",refkey); } } void linphone_core_write_friends_config(LinphoneCore* lc) { bctbx_list_t *elem; int i; int store_friends; if (! linphone_core_ready(lc)) return; /*dont write config when reading it !*/ store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1); if (store_friends) { for (elem=linphone_core_get_default_friend_list(lc)->friends,i=0; elem!=NULL; elem=bctbx_list_next(elem),i++){ linphone_friend_write_to_config_file(lc->config,(LinphoneFriend*)bctbx_list_get_data(elem),i); } linphone_friend_write_to_config_file(lc->config,NULL,i); /* set the end */ } } LinphoneCore *linphone_friend_get_core(const LinphoneFriend *fr){ return fr->lc; } LinphoneFriend *linphone_friend_ref(LinphoneFriend *lf) { belle_sip_object_ref(lf); return lf; } void linphone_friend_unref(LinphoneFriend *lf) { belle_sip_object_unref(lf); } /* DEPRECATED */ void linphone_friend_destroy(LinphoneFriend *lf) { linphone_friend_unref(lf); } LinphoneVcard* linphone_friend_get_vcard(LinphoneFriend *fr) { if (fr && linphone_core_vcard_supported()) return fr->vcard; return NULL; } void linphone_friend_set_vcard(LinphoneFriend *fr, LinphoneVcard *vcard) { if (!fr || !linphone_core_vcard_supported()) return; if (vcard) linphone_vcard_ref(vcard); if (fr->vcard) linphone_vcard_unref(fr->vcard); fr->vcard = vcard; linphone_friend_save(fr, fr->lc); } bool_t linphone_friend_create_vcard(LinphoneFriend *fr, const char *name) { LinphoneVcard *vcard = NULL; LinphoneCore *lc = NULL; bool_t skip = FALSE; if (!fr || !name) { ms_error("Friend or name is null"); return FALSE; } if (!linphone_core_vcard_supported()) { ms_warning("VCard support is not builtin"); return FALSE; } if (fr->vcard) { ms_error("Friend already has a VCard"); return FALSE; } vcard = linphone_factory_create_vcard(linphone_factory_get()); lc = fr->lc; if (!lc && fr->friend_list) { lc = fr->friend_list->lc; } if (lc) { skip = 1 - lp_config_get_int(fr->lc->config, "misc", "store_friends", 1); linphone_vcard_set_skip_validation(vcard, skip); } linphone_vcard_set_full_name(vcard, name); linphone_friend_set_vcard(fr, vcard); linphone_vcard_unref(vcard); return TRUE; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif LinphoneFriend *linphone_friend_new_from_vcard(LinphoneVcard *vcard) { LinphoneFriend *fr; if (!linphone_core_vcard_supported()) { ms_error("VCard support is not builtin"); return NULL; } if (vcard == NULL) { ms_error("Cannot create friend from null vcard"); return NULL; } fr = linphone_friend_new(); // Currently presence takes too much time when dealing with hundreds of friends, so I disabled it for now fr->pol = LinphoneSPDeny; fr->subscribe = FALSE; linphone_friend_set_vcard(fr, vcard); return fr; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif /*drops all references to the core and unref*/ void _linphone_friend_release(LinphoneFriend *lf){ lf->lc = NULL; _linphone_friend_release_ops(lf); linphone_friend_unref(lf); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriend); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriend, belle_sip_object_t, (belle_sip_object_destroy_t) _linphone_friend_destroy, NULL, // clone _linphone_friend_marshall, FALSE ); /******************************************************************************* * SQL storage related functions * ******************************************************************************/ #ifdef SQLITE_STORAGE_ENABLED static void linphone_create_friends_table(sqlite3* db) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS friends (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "friend_list_id INTEGER," "sip_uri TEXT," "subscribe_policy INTEGER," "send_subscribe INTEGER," "ref_key TEXT," "vCard TEXT," "vCard_etag TEXT," "vCard_url TEXT," "presence_received INTEGER" ");", 0, 0, &errmsg); if (ret != SQLITE_OK) { ms_error("Error in creation: %s.\n", errmsg); sqlite3_free(errmsg); } ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS friends_lists (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "display_name TEXT," "rls_uri TEXT," "uri TEXT," "revision INTEGER" ");", 0, 0, &errmsg); if (ret != SQLITE_OK) { ms_error("Error in creation: %s.\n", errmsg); sqlite3_free(errmsg); } } static bool_t linphone_update_friends_table(sqlite3* db) { static sqlite3_stmt *stmt_version; int database_user_version = -1; char *errmsg = NULL; if (sqlite3_prepare_v2(db, "PRAGMA user_version;", -1, &stmt_version, NULL) == SQLITE_OK) { while(sqlite3_step(stmt_version) == SQLITE_ROW) { database_user_version = sqlite3_column_int(stmt_version, 0); ms_debug("friends database user version = %i", database_user_version); } } sqlite3_finalize(stmt_version); if (database_user_version != 3100) { // Linphone 3.10.0 int ret = sqlite3_exec(db, "BEGIN TRANSACTION;\n" "ALTER TABLE friends RENAME TO temp_friends;\n" "CREATE TABLE IF NOT EXISTS friends (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "friend_list_id INTEGER," "sip_uri TEXT," "subscribe_policy INTEGER," "send_subscribe INTEGER," "ref_key TEXT," "vCard TEXT," "vCard_etag TEXT," "vCard_url TEXT," "presence_received INTEGER" ");\n" "INSERT INTO friends SELECT id, friend_list_id, sip_uri, subscribe_policy, send_subscribe, ref_key, vCard, vCard_etag, vCard_url, presence_received FROM temp_friends;\n" "DROP TABLE temp_friends;\n" "PRAGMA user_version = 3100;\n" "COMMIT;", 0, 0, &errmsg); if (ret != SQLITE_OK) { ms_error("Error altering table friends: %s.\n", errmsg); sqlite3_free(errmsg); return FALSE; } return TRUE; } return FALSE; } void linphone_core_friends_storage_init(LinphoneCore *lc) { int ret; const char *errmsg; sqlite3 *db; bctbx_list_t *friends_lists = NULL; linphone_core_friends_storage_close(lc); ret = _linphone_sqlite3_open(lc->friends_db_file, &db); if (ret != SQLITE_OK) { errmsg = sqlite3_errmsg(db); ms_error("Error in the opening: %s.\n", errmsg); sqlite3_close(db); return; } linphone_create_friends_table(db); if (linphone_update_friends_table(db)) { // After updating schema, database need to be closed/reopenned sqlite3_close(db); _linphone_sqlite3_open(lc->friends_db_file, &db); } lc->friends_db = db; friends_lists = linphone_core_fetch_friends_lists_from_db(lc); if (friends_lists) { const bctbx_list_t *it; ms_warning("Replacing current default friend list by the one(s) from the database"); lc->friends_lists = bctbx_list_free_with_data(lc->friends_lists, (bctbx_list_free_func)linphone_friend_list_unref); for (it=friends_lists;it!=NULL;it=bctbx_list_next(it)) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(it); linphone_core_add_friend_list(lc, list); } friends_lists = bctbx_list_free_with_data(friends_lists, (bctbx_list_free_func)linphone_friend_list_unref); } } void linphone_core_friends_storage_close(LinphoneCore *lc) { if (lc->friends_db) { sqlite3_close(lc->friends_db); lc->friends_db = NULL; } } /* DB layout: * | 0 | storage_id * | 1 | display_name * | 2 | rls_uri * | 3 | uri * | 4 | revision */ static int create_friend_list(void *data, int argc, char **argv, char **colName) { bctbx_list_t **list = (bctbx_list_t **)data; unsigned int storage_id = (unsigned int)atoi(argv[0]); LinphoneFriendList *lfl = linphone_core_create_friend_list(NULL); lfl->storage_id = storage_id; linphone_friend_list_set_display_name(lfl, argv[1]); linphone_friend_list_set_rls_uri(lfl, argv[2]); linphone_friend_list_set_uri(lfl, argv[3]); lfl->revision = atoi(argv[4]); *list = bctbx_list_append(*list, linphone_friend_list_ref(lfl)); linphone_friend_list_unref(lfl); return 0; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif /* DB layout: * | 0 | storage_id * | 1 | friend_list_id * | 2 | sip_uri * | 3 | subscribe_policy * | 4 | send_subscribe * | 5 | ref_key * | 6 | vCard * | 7 | vCard eTag * | 8 | vCard URL * | 9 | presence_received */ static int create_friend(void *data, int argc, char **argv, char **colName) { LinphoneVcardContext *context = (LinphoneVcardContext *)data; bctbx_list_t **list = (bctbx_list_t **)linphone_vcard_context_get_user_data(context); LinphoneFriend *lf = NULL; LinphoneVcard *vcard = NULL; unsigned int storage_id = (unsigned int)atoi(argv[0]); vcard = linphone_vcard_context_get_vcard_from_buffer(context, argv[6]); if (vcard) { linphone_vcard_set_etag(vcard, argv[7]); linphone_vcard_set_url(vcard, argv[8]); lf = linphone_friend_new_from_vcard(vcard); linphone_vcard_unref(vcard); } if (!lf) { lf = linphone_friend_new(); if (argv[2] != NULL) { LinphoneAddress *addr = linphone_address_new(argv[2]); if (addr) { linphone_friend_set_address(lf, addr); linphone_address_unref(addr); } } } linphone_friend_set_inc_subscribe_policy(lf, atoi(argv[3])); linphone_friend_send_subscribe(lf, atoi(argv[4])); linphone_friend_set_ref_key(lf, ms_strdup(argv[5])); lf->presence_received = atoi(argv[9]); lf->storage_id = storage_id; *list = bctbx_list_append(*list, linphone_friend_ref(lf)); linphone_friend_unref(lf); return 0; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif static int linphone_sql_request_friend(sqlite3* db, const char *stmt, LinphoneVcardContext *context) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, create_friend, context, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } static int linphone_sql_request_friends_list(sqlite3* db, const char *stmt, bctbx_list_t **list) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, create_friend_list, list, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } static int linphone_sql_request_generic(sqlite3* db, const char *stmt) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } void linphone_core_store_friend_in_db(LinphoneCore *lc, LinphoneFriend *lf) { if (lc && lc->friends_db) { char *buf; int store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1); LinphoneVcard *vcard = NULL; const LinphoneAddress *addr; char *addr_str = NULL; if (!store_friends) { return; } if (!lf || !lf->friend_list) { ms_warning("Either the friend or the friend list is null, skipping..."); return; } if (lf->friend_list->storage_id == 0) { ms_warning("Trying to add a friend in db, but friend list isn't, let's do that first"); linphone_core_store_friends_list_in_db(lc, lf->friend_list); } if (linphone_core_vcard_supported()) vcard = linphone_friend_get_vcard(lf); addr = linphone_friend_get_address(lf); if (addr != NULL) addr_str = linphone_address_as_string(addr); if (lf->storage_id > 0) { buf = sqlite3_mprintf("UPDATE friends SET friend_list_id=%u,sip_uri=%Q,subscribe_policy=%i,send_subscribe=%i,ref_key=%Q,vCard=%Q,vCard_etag=%Q,vCard_url=%Q,presence_received=%i WHERE (id = %u);", lf->friend_list->storage_id, addr_str, lf->pol, lf->subscribe, lf->refkey, vcard ? linphone_vcard_as_vcard4_string(vcard) : NULL, vcard ? linphone_vcard_get_etag(vcard) : NULL, vcard ? linphone_vcard_get_url(vcard): NULL, lf->presence_received, lf->storage_id ); } else { buf = sqlite3_mprintf("INSERT INTO friends VALUES(NULL,%u,%Q,%i,%i,%Q,%Q,%Q,%Q,%i);", lf->friend_list->storage_id, addr_str, lf->pol, lf->subscribe, lf->refkey, vcard ? linphone_vcard_as_vcard4_string(vcard) : NULL, vcard ? linphone_vcard_get_etag(vcard) : NULL, vcard ? linphone_vcard_get_url(vcard) : NULL, lf->presence_received ); } if (addr_str != NULL) ms_free(addr_str); linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); if (lf->storage_id == 0) { lf->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->friends_db); } } } void linphone_core_store_friends_list_in_db(LinphoneCore *lc, LinphoneFriendList *list) { if (lc && lc->friends_db) { char *buf; int store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1); if (!store_friends) { return; } if (list->storage_id > 0) { buf = sqlite3_mprintf("UPDATE friends_lists SET display_name=%Q,rls_uri=%Q,uri=%Q,revision=%i WHERE (id = %u);", list->display_name, list->rls_uri, list->uri, list->revision, list->storage_id ); } else { buf = sqlite3_mprintf("INSERT INTO friends_lists VALUES(NULL,%Q,%Q,%Q,%i);", list->display_name, list->rls_uri, list->uri, list->revision ); } linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); if (list->storage_id == 0) { list->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->friends_db); } } } void linphone_core_remove_friend_from_db(LinphoneCore *lc, LinphoneFriend *lf) { if (lc && lc->friends_db) { char *buf; if (lf->storage_id == 0) { ms_error("Friend doesn't have a storage_id !"); return; } buf = sqlite3_mprintf("DELETE FROM friends WHERE id = %u", lf->storage_id); linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); lf->storage_id = 0; } } void linphone_core_remove_friends_list_from_db(LinphoneCore *lc, LinphoneFriendList *list) { if (lc && lc->friends_db) { char *buf; if (list->storage_id == 0) { ms_error("Friends list doesn't have a storage_id !"); return; } buf = sqlite3_mprintf("DELETE FROM friends_lists WHERE id = %u", list->storage_id); linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); list->storage_id = 0; } } bctbx_list_t* linphone_core_fetch_friends_from_db(LinphoneCore *lc, LinphoneFriendList *list) { char *buf; uint64_t begin,end; bctbx_list_t *result = NULL; bctbx_list_t *elem = NULL; if (!lc || lc->friends_db == NULL || list == NULL) { ms_warning("Either lc (or list) is NULL or friends database wasn't initialized with linphone_core_friends_storage_init() yet"); return NULL; } linphone_vcard_context_set_user_data(lc->vcard_context, &result); buf = sqlite3_mprintf("SELECT * FROM friends WHERE friend_list_id = %u ORDER BY id", list->storage_id); begin = ortp_get_cur_time_ms(); linphone_sql_request_friend(lc->friends_db, buf, lc->vcard_context); end = ortp_get_cur_time_ms(); ms_message("%s(): %u results fetched, completed in %i ms",__FUNCTION__, (unsigned int)bctbx_list_size(result), (int)(end-begin)); sqlite3_free(buf); for(elem = result; elem != NULL; elem = bctbx_list_next(elem)) { bctbx_list_t *iterator; bctbx_list_t *phone_numbers; const bctbx_list_t *addresses; LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); lf->lc = lc; lf->friend_list = list; if (lf->refkey) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(lf->refkey, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map, pair); } phone_numbers = linphone_friend_get_phone_numbers(lf); iterator = phone_numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number); if(uri) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair); } iterator = bctbx_list_next(iterator); } addresses = linphone_friend_get_addresses(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator); char *uri = linphone_address_as_string_uri_only(lfaddr); if(uri) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair); ms_free(uri); } iterator = bctbx_list_next(iterator); } } linphone_vcard_context_set_user_data(lc->vcard_context, NULL); return result; } bctbx_list_t* linphone_core_fetch_friends_lists_from_db(LinphoneCore *lc) { char *buf; uint64_t begin,end; bctbx_list_t *result = NULL; bctbx_list_t *elem = NULL; if (!lc || lc->friends_db == NULL) { ms_warning("Either lc is NULL or friends database wasn't initialized with linphone_core_friends_storage_init() yet"); return NULL; } buf = sqlite3_mprintf("SELECT * FROM friends_lists ORDER BY id"); begin = ortp_get_cur_time_ms(); linphone_sql_request_friends_list(lc->friends_db, buf, &result); end = ortp_get_cur_time_ms(); ms_message("%s(): %u results fetched, completed in %i ms",__FUNCTION__, (unsigned int)bctbx_list_size(result), (int)(end-begin)); sqlite3_free(buf); for(elem = result; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriendList *lfl = (LinphoneFriendList *)bctbx_list_get_data(elem); lfl->lc = lc; lfl->friends = linphone_core_fetch_friends_from_db(lc, lfl); } return result; } #else void linphone_core_friends_storage_init(LinphoneCore *lc) { } void linphone_core_friends_storage_close(LinphoneCore *lc) { } void linphone_core_store_friend_in_db(LinphoneCore *lc, LinphoneFriend *lf) { } void linphone_core_store_friends_list_in_db(LinphoneCore *lc, LinphoneFriendList *list) { } void linphone_core_remove_friend_from_db(LinphoneCore *lc, LinphoneFriend *lf) { } void linphone_core_remove_friends_list_from_db(LinphoneCore *lc, LinphoneFriendList *list) { } bctbx_list_t* linphone_core_fetch_friends_from_db(LinphoneCore *lc, LinphoneFriendList *list) { return NULL; } bctbx_list_t* linphone_core_fetch_friends_lists_from_db(LinphoneCore *lc) { return NULL; } #endif void linphone_core_set_friends_database_path(LinphoneCore *lc, const char *path) { if (lc->friends_db_file){ ms_free(lc->friends_db_file); lc->friends_db_file = NULL; } if (path) { lc->friends_db_file = ms_strdup(path); linphone_core_friends_storage_init(lc); linphone_core_migrate_friends_from_rc_to_db(lc); } } const char* linphone_core_get_friends_database_path(LinphoneCore *lc) { return lc->friends_db_file; } void linphone_core_migrate_friends_from_rc_to_db(LinphoneCore *lc) { LpConfig *lpc = NULL; LinphoneFriend *lf = NULL; LinphoneFriendList *lfl = linphone_core_get_default_friend_list(lc); int i; #ifndef SQLITE_STORAGE_ENABLED ms_warning("linphone has been compiled without sqlite, can't migrate friends"); return; #endif if (!lc) { return; } lpc = linphone_core_get_config(lc); if (!lpc) { ms_warning("this core has been started without a rc file, nothing to migrate"); return; } if (lp_config_get_int(lpc, "misc", "friends_migration_done", 0) == 1) { ms_warning("the friends migration has already been done, skipping..."); return; } if (bctbx_list_size(linphone_friend_list_get_friends(lfl)) > 0 && lfl->storage_id == 0) { linphone_core_remove_friend_list(lc, lfl); lfl = linphone_core_create_friend_list(lc); linphone_core_add_friend_list(lc, lfl); linphone_friend_list_unref(lfl); } for (i = 0; (lf = linphone_friend_new_from_config_file(lc, i)) != NULL; i++) { char friend_section[32]; const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) { char *address = NULL; const char *displayName = linphone_address_get_display_name(addr); if (!displayName) displayName = linphone_address_get_username(addr); address = linphone_address_as_string(addr); if (linphone_core_vcard_supported()) { if (!linphone_friend_create_vcard(lf, displayName)) { ms_warning("Couldn't create vCard for friend %s", address); } else { linphone_vcard_add_sip_address(linphone_friend_get_vcard(lf), address); linphone_address_unref(lf->uri); lf->uri = NULL; } } ms_free(address); linphone_friend_list_add_friend(lfl, lf); linphone_friend_unref(lf); snprintf(friend_section, sizeof(friend_section), "friend_%i", i); lp_config_clean_section(lpc, friend_section); } } ms_debug("friends migration successful: %i friends migrated", i); lp_config_set_int(lpc, "misc", "friends_migration_done", 1); } LinphoneSubscriptionState linphone_friend_get_subscription_state(const LinphoneFriend *lf) { return lf->out_sub_state; } const char * linphone_friend_phone_number_to_sip_uri(LinphoneFriend *lf, const char *phone_number) { LinphoneFriendPhoneNumberSipUri * lfpnsu; char *normalized_number; char *full_uri; LinphoneProxyConfig *proxy_config; bctbx_list_t *iterator = lf->phone_number_sip_uri_map; while (iterator) { lfpnsu = (LinphoneFriendPhoneNumberSipUri *)bctbx_list_get_data(iterator); if (strcmp(lfpnsu->number, phone_number) == 0) { /*force sip uri computation because proxy config may have changed, specially, ccc could have been added since last computation*/ //free_phone_number_sip_uri(lfpnsu); if (lf->phone_number_sip_uri_map == iterator) { /*change list head if head is removed*/ iterator = lf->phone_number_sip_uri_map = bctbx_list_erase_link(lf->phone_number_sip_uri_map, iterator); } else { iterator = bctbx_list_erase_link(lf->phone_number_sip_uri_map, iterator); } } else { iterator = bctbx_list_next(iterator); } } proxy_config = linphone_core_get_default_proxy_config(linphone_friend_get_core(lf)); if (!proxy_config) return NULL; if (strstr(phone_number, "tel:") == phone_number) phone_number += 4; /* Remove the "tel:" prefix if it is present. */ normalized_number = linphone_proxy_config_normalize_phone_number(proxy_config, phone_number); if (!normalized_number) return NULL; full_uri = ms_strdup_printf("sip:%s@%s;user=phone", normalized_number, linphone_proxy_config_get_domain(proxy_config)); if(strcmp(normalized_number, phone_number) != 0) { char *old_uri = ms_strdup_printf("sip:%s@%s;user=phone", phone_number, linphone_proxy_config_get_domain(proxy_config)); bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, old_uri); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(lf->friend_list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(lf->friend_list->friends_map_uri, it); } bctbx_iterator_cchar_delete(it); ms_free(old_uri); } if(!linphone_friend_list_find_friend_by_uri(lf->friend_list, full_uri)) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(full_uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(lf->friend_list->friends_map_uri, pair); } ms_free(normalized_number); lfpnsu = ms_new0(LinphoneFriendPhoneNumberSipUri, 1); lfpnsu->number = ms_strdup(phone_number); lfpnsu->uri = full_uri; lf->phone_number_sip_uri_map = bctbx_list_append(lf->phone_number_sip_uri_map, lfpnsu); return full_uri; } const char * linphone_friend_sip_uri_to_phone_number(LinphoneFriend *lf, const char *uri) { bctbx_list_t *iterator = lf->phone_number_sip_uri_map; while (iterator) { LinphoneFriendPhoneNumberSipUri *lfpnsu = (LinphoneFriendPhoneNumberSipUri *)bctbx_list_get_data(iterator); if (strcmp(lfpnsu->uri, uri) == 0) return lfpnsu->number; iterator = bctbx_list_next(iterator); } return NULL; } void linphone_friend_clear_presence_models(LinphoneFriend *lf) { lf->presence_models = bctbx_list_free_with_data(lf->presence_models, (bctbx_list_free_func)free_friend_presence); } linphone-3.12.0/coreapi/friendlist.c000066400000000000000000001204611313432737600173570ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2015 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "linphone/core.h" #include "private.h" #include BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendListCbs); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendListCbs, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // Marshall FALSE ); static LinphoneFriendListCbs * linphone_friend_list_cbs_new(void) { return belle_sip_object_new(LinphoneFriendListCbs); } LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *list) { return list->cbs; } LinphoneFriendListCbs * linphone_friend_list_cbs_ref(LinphoneFriendListCbs *cbs) { belle_sip_object_ref(cbs); return cbs; } void linphone_friend_list_cbs_unref(LinphoneFriendListCbs *cbs) { belle_sip_object_unref(cbs); } void *linphone_friend_list_cbs_get_user_data(const LinphoneFriendListCbs *cbs) { return cbs->user_data; } void linphone_friend_list_cbs_set_user_data(LinphoneFriendListCbs *cbs, void *ud) { cbs->user_data = ud; } LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs) { return cbs->contact_created_cb; } void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb) { cbs->contact_created_cb = cb; } LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs) { return cbs->contact_deleted_cb; } void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb) { cbs->contact_deleted_cb = cb; } LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs) { return cbs->contact_updated_cb; } void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb) { cbs->contact_updated_cb = cb; } LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs) { return cbs->sync_state_changed_cb; } void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb) { cbs->sync_state_changed_cb = cb; } static int add_uri_entry(xmlTextWriterPtr writer, int err, const char *uri) { if (err >= 0) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry"); } if (err >= 0) { err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"uri", (const xmlChar *)uri); } if (err >= 0) { /* Close the "entry" element. */ err = xmlTextWriterEndElement(writer); } return err; } static bctbx_list_t * uri_list(const LinphoneFriendList *list) { bctbx_list_t * uri_list = NULL; bctbx_list_t * elem = NULL; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); bctbx_list_t *iterator; const bctbx_list_t *addresses = linphone_friend_get_addresses(lf); bctbx_list_t *numbers = linphone_friend_get_phone_numbers(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator); if (addr) { char *uri = linphone_address_as_string_uri_only(addr); if (uri) { uri_list = bctbx_list_prepend(uri_list, uri); } } iterator = bctbx_list_next(iterator); } iterator = numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number); if (uri) { uri_list = bctbx_list_prepend(uri_list, ms_strdup(uri)); } iterator = bctbx_list_next(iterator); } } return uri_list; } static char * create_resource_list_xml(const LinphoneFriendList *list) { char *xml_content = NULL; xmlBufferPtr buf; xmlTextWriterPtr writer; int err; if (list->friends == NULL) return NULL; buf = xmlBufferCreate(); if (buf == NULL) { ms_error("%s: Error creating the XML buffer", __FUNCTION__); return NULL; } writer = xmlNewTextWriterMemory(buf, 0); if (writer == NULL) { ms_error("%s: Error creating the XML writer", __FUNCTION__); return NULL; } xmlTextWriterSetIndent(writer,1); err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (err >= 0) { err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"resource-lists", (const xmlChar *)"urn:ietf:params:xml:ns:resource-lists"); } if (err >= 0) { err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi", NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance"); } if (err>= 0) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"list"); } { bctbx_list_t* entries = uri_list(list); bctbx_list_t* it; for(it = entries; it != NULL; it = it->next){ err = add_uri_entry(writer, err, it->data); } bctbx_list_free_with_data(entries, ms_free); } if (err >= 0) { /* Close the "list" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { /* Close the "resource-lists" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { err = xmlTextWriterEndDocument(writer); } if (err > 0) { /* xmlTextWriterEndDocument returns the size of the content. */ xml_content = ms_strdup((char *)buf->content); } xmlFreeTextWriter(writer); xmlBufferFree(buf); return xml_content; } static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList *list, const LinphoneContent *body, const char *first_part_body) { xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)first_part_body, 0, NULL, 0); if (xml_ctx->doc != NULL) { LinphoneFriend *lf; LinphoneContent *presence_part; xmlXPathObjectPtr resource_object; const char *version_str = NULL; const char *full_state_str = NULL; const char *uri = NULL; bool_t full_state = FALSE; int version; int i; if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rlmi", (const xmlChar *)"urn:ietf:params:xml:ns:rlmi"); version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version"); if (version_str == NULL) { ms_warning("rlmi+xml: No version attribute in list"); goto end; } version = atoi(version_str); linphone_free_xml_text_content(version_str); if (version < list->expected_notification_version) { /*no longuer an error as dialog may be silently restarting by the refresher*/ ms_warning("rlmi+xml: Received notification with version %d expected was %d, dialog may have been reseted", version, list->expected_notification_version); } full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState"); if (full_state_str == NULL) { ms_warning("rlmi+xml: No fullState attribute in list"); goto end; } if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) { bctbx_list_t *l = list->friends; for (; l != NULL; l = bctbx_list_next(l)) { lf = (LinphoneFriend *)bctbx_list_get_data(l); linphone_friend_clear_presence_models(lf); } full_state = TRUE; } linphone_free_xml_text_content(full_state_str); if ((list->expected_notification_version == 0) && (full_state == FALSE)) { ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid"); goto end; } list->expected_notification_version = version + 1; resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/.."); if ((resource_object != NULL) && (resource_object->nodesetval != NULL)) { for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) { const char *cid = NULL; linphone_xml_xpath_context_set_node(xml_ctx, xmlXPathNodeSetItem(resource_object->nodesetval, i-1)); cid = linphone_get_xml_text_content(xml_ctx, "./rlmi:instance/@cid"); if (cid != NULL) { presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid); if (presence_part == NULL) { ms_warning("rlmi+xml: Cannot find part with Content-Id: %s", cid); } else { SalPresenceModel *presence = NULL; linphone_notify_parse_presence(linphone_content_get_type(presence_part), linphone_content_get_subtype(presence_part), linphone_content_get_string_buffer(presence_part), &presence); if (presence != NULL) { // Try to reduce CPU cost of linphone_address_new and find_friend_by_address by only doing it when we know for sure we have a presence to notify LinphoneAddress* addr; uri = linphone_get_xml_text_content(xml_ctx, "./@uri"); if (uri == NULL) continue; addr = linphone_address_new(uri); if (!addr) continue; lf = linphone_friend_list_find_friend_by_address(list, addr); linphone_address_unref(addr); if (lf) { const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri); lf->presence_received = TRUE; if (phone_number) { char *presence_address = linphone_presence_model_get_contact((LinphonePresenceModel *)presence); bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(presence_address, linphone_friend_ref(lf)); bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, presence_address); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map_uri, it); } bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair); linphone_friend_set_presence_model_for_uri_or_tel(lf, phone_number, (LinphonePresenceModel *)presence); } else { linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, (LinphonePresenceModel *)presence); } if (full_state == FALSE) { if (phone_number) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, phone_number, (LinphonePresenceModel *)presence); else linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, (LinphonePresenceModel *)presence); linphone_core_notify_notify_presence_received(list->lc, lf); } linphone_free_xml_text_content(uri); } linphone_content_unref(presence_part); } } linphone_free_xml_text_content(cid); } } } if (resource_object != NULL) xmlXPathFreeObject(resource_object); if (full_state == TRUE) { const bctbx_list_t *addresses; bctbx_list_t *numbers; bctbx_list_t *iterator; bctbx_list_t *l = list->friends; for (; l != NULL; l = bctbx_list_next(l)) { lf = (LinphoneFriend *)bctbx_list_get_data(l); addresses = linphone_friend_get_addresses(lf); numbers = linphone_friend_get_phone_numbers(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator); char *uri = linphone_address_as_string_uri_only(addr); const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri); if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence); ms_free(uri); iterator = bctbx_list_next(iterator); } iterator = numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, number); if (presence) { linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, number, presence); } iterator = bctbx_list_next(iterator); } if (numbers) bctbx_list_free(numbers); if (linphone_friend_is_presence_received(lf) == TRUE) { linphone_core_notify_notify_presence_received(list->lc, lf); } } } } else { ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer); } end: linphone_xmlparsing_context_destroy(xml_ctx); } static bool_t linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList *list) { bctbx_list_t *l = list->friends; bool_t has_subscribe_inactive = FALSE; for (; l != NULL; l = bctbx_list_next(l)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(l); if (lf->subscribe_active != TRUE) { has_subscribe_inactive = TRUE; break; } } return has_subscribe_inactive; } static LinphoneFriendList * linphone_friend_list_new(void) { LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList); list->cbs = linphone_friend_list_cbs_new(); list->enable_subscriptions = TRUE; list->friends_map = bctbx_mmap_cchar_new(); list->friends_map_uri = bctbx_mmap_cchar_new(); return list; } static void linphone_friend_list_destroy(LinphoneFriendList *list) { if (list->display_name != NULL) ms_free(list->display_name); if (list->rls_addr) linphone_address_unref(list->rls_addr); if (list->rls_uri != NULL) ms_free(list->rls_uri); if (list->content_digest != NULL) ms_free(list->content_digest); if (list->event != NULL) { linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; } if (list->uri != NULL) ms_free(list->uri); if (list->cbs) linphone_friend_list_cbs_unref(list->cbs); if (list->dirty_friends_to_update) list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref); if (list->friends) list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release); if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref); if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendList); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendList, belle_sip_object_t, (belle_sip_object_destroy_t)linphone_friend_list_destroy, NULL, // clone NULL, // marshal FALSE ); LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) { LinphoneFriendList *list = linphone_friend_list_new(); list->lc = lc; return list; } LinphoneFriendList * linphone_friend_list_ref(LinphoneFriendList *list) { belle_sip_object_ref(list); return list; } void _linphone_friend_list_release(LinphoneFriendList *list){ /*drops all references to core and unref*/ list->lc = NULL; if (list->event != NULL) { linphone_event_unref(list->event); list->event = NULL; } if (list->cbs) { linphone_friend_list_cbs_unref(list->cbs); list->cbs = NULL; } if (list->dirty_friends_to_update) { list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref); } if (list->friends) { list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release); } linphone_friend_list_unref(list); } void linphone_friend_list_unref(LinphoneFriendList *list) { belle_sip_object_unref(list); } void * linphone_friend_list_get_user_data(const LinphoneFriendList *list) { return list->user_data; } void linphone_friend_list_set_user_data(LinphoneFriendList *list, void *ud) { list->user_data = ud; } const char * linphone_friend_list_get_display_name(const LinphoneFriendList *list) { return list->display_name; } void linphone_friend_list_set_display_name(LinphoneFriendList *list, const char *display_name) { if (list->display_name != NULL) { ms_free(list->display_name); list->display_name = NULL; } if (display_name != NULL) { list->display_name = ms_strdup(display_name); linphone_core_store_friends_list_in_db(list->lc, list); } } const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){ return list->rls_addr; } const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) { if (list->rls_addr) return list->rls_addr; else if (list->lc) { const char* rls_uri = lp_config_get_string(list->lc->config, "sip", "rls_uri", NULL); if (list->lc->default_rls_addr) linphone_address_unref(list->lc->default_rls_addr); list->lc->default_rls_addr=NULL; if (rls_uri) { /*to make sure changes in config are used if any*/ list->lc->default_rls_addr = linphone_address_new(rls_uri); } return list->lc->default_rls_addr; } else return NULL; } void linphone_friend_list_set_rls_address(LinphoneFriendList *list, const LinphoneAddress *rls_addr){ LinphoneAddress *new_rls_addr = rls_addr ? linphone_address_clone(rls_addr) : NULL; if (list->rls_addr){ linphone_address_unref(list->rls_addr); } list->rls_addr = new_rls_addr; if (list->rls_uri != NULL){ ms_free(list->rls_uri); list->rls_uri = NULL; } if (list->rls_addr){ list->rls_uri = linphone_address_as_string(list->rls_addr); linphone_core_store_friends_list_in_db(list->lc, list); } } const char * linphone_friend_list_get_rls_uri(const LinphoneFriendList *list) { return list->rls_uri; } void linphone_friend_list_set_rls_uri(LinphoneFriendList *list, const char *rls_uri) { LinphoneAddress *addr = rls_uri ? linphone_core_create_address(list->lc, rls_uri) : NULL; linphone_friend_list_set_rls_address(list, addr); if (addr) linphone_address_unref(addr); } static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) { LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend; const LinphoneAddress *addr; if (!list || lf->friend_list) { if (!list) ms_error("linphone_friend_list_add_friend(): invalid list, null"); if (lf->friend_list) ms_error("linphone_friend_list_add_friend(): invalid friend, already in list"); return status; } addr = linphone_friend_get_address(lf); bool_t present = FALSE; if (lf->refkey) { present = linphone_friend_list_find_friend_by_ref_key(list, lf->refkey) != NULL; } else { present = bctbx_list_find(list->friends, lf) != NULL; } if (present) { char *tmp = NULL; if (addr) tmp = linphone_address_as_string(addr); ms_warning("Friend %s already in list [%s], ignored.", tmp ? tmp : "unknown", list->display_name); if (tmp) ms_free(tmp); } else { status = linphone_friend_list_import_friend(list, lf, synchronize); linphone_friend_save(lf, lf->lc); } if (list->rls_uri == NULL) { /* Mimic the behaviour of linphone_core_add_friend() when a resource list server is not in use */ linphone_friend_apply(lf, lf->lc); } return status; } LinphoneFriendListStatus linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf) { return _linphone_friend_list_add_friend(list, lf, TRUE); } LinphoneFriendListStatus linphone_friend_list_add_local_friend(LinphoneFriendList *list, LinphoneFriend *lf) { return _linphone_friend_list_add_friend(list, lf, FALSE); } LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) { bctbx_list_t *iterator; bctbx_list_t *phone_numbers; const bctbx_list_t *addresses; if (lf->friend_list) { if (lf->friend_list) ms_error("linphone_friend_list_add_friend(): invalid friend, already in list"); return LinphoneFriendListInvalidFriend; } lf->friend_list = list; lf->lc = list->lc; list->friends = bctbx_list_prepend(list->friends, linphone_friend_ref(lf)); if (lf->refkey) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(lf->refkey, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map, pair); } phone_numbers = linphone_friend_get_phone_numbers(lf); iterator = phone_numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number); if(uri) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair); } iterator = bctbx_list_next(iterator); } addresses = linphone_friend_get_addresses(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator); char *uri = linphone_address_as_string_uri_only(lfaddr); if(uri) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair); ms_free(uri); } iterator = bctbx_list_next(iterator); } if (synchronize) { list->dirty_friends_to_update = bctbx_list_prepend(list->dirty_friends_to_update, linphone_friend_ref(lf)); } return LinphoneFriendListOK; } static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg); } linphone_carddav_context_destroy(cdc); } static LinphoneFriendListStatus _linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t remove_from_server) { bctbx_list_t *iterator; bctbx_list_t *phone_numbers; const bctbx_list_t *addresses; bctbx_list_t *elem = bctbx_list_find(list->friends, lf); if (elem == NULL) return LinphoneFriendListNonExistentFriend; #ifdef SQLITE_STORAGE_ENABLED if (lf && lf->lc && lf->lc->friends_db) { linphone_core_remove_friend_from_db(lf->lc, lf); } #endif if (remove_from_server) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lvc && linphone_vcard_get_uid(lvc)) { LinphoneCardDavContext *cdc = linphone_carddav_context_new(list); if (cdc) { cdc->sync_done_cb = carddav_done; if (cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL); } linphone_carddav_delete_vcard(cdc, lf); } } } if (!list->lc->friends_db_file) { linphone_core_write_friends_config(list->lc); } list->friends = bctbx_list_erase_link(list->friends, elem); if(lf->refkey) { bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map, it); } } phone_numbers = linphone_friend_get_phone_numbers(lf); iterator = phone_numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number); if(uri) { bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map_uri, it); } bctbx_iterator_cchar_delete(it); } iterator = bctbx_list_next(iterator); } addresses = linphone_friend_get_addresses(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator); char *uri = linphone_address_as_string_uri_only(lfaddr); if(uri) { bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map_uri, it); } bctbx_iterator_cchar_delete(it); ms_free(uri); } iterator = bctbx_list_next(iterator); } lf->friend_list = NULL; linphone_friend_unref(lf); return LinphoneFriendListOK; } LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) { return _linphone_friend_list_remove_friend(list, lf, TRUE); } const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) { return list->friends; } void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) { bctbx_list_t *dirty_friends = list->dirty_friends_to_update; while (dirty_friends) { LinphoneCardDavContext *cdc = linphone_carddav_context_new(list); if (cdc) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends); cdc->sync_done_cb = carddav_done; if (lf) { if (cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL); } linphone_carddav_put_vcard(cdc, lf); } } dirty_friends = bctbx_list_next(dirty_friends); } list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref); } static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { if (cdc) { LinphoneFriendList *lfl = cdc->friend_list; linphone_friend_list_import_friend(lfl, lf, FALSE); if (cdc->friend_list->cbs->contact_created_cb) { cdc->friend_list->cbs->contact_created_cb(lfl, lf); } } } static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { if (cdc) { LinphoneFriendList *lfl = cdc->friend_list; _linphone_friend_list_remove_friend(lfl, lf, FALSE); if (cdc->friend_list->cbs->contact_deleted_cb) { cdc->friend_list->cbs->contact_deleted_cb(lfl, lf); } } } static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) { if (cdc) { LinphoneFriendList *lfl = cdc->friend_list; bctbx_list_t *elem = bctbx_list_find(lfl->friends, lf_old); if (elem) { elem->data = linphone_friend_ref(lf_new); } linphone_core_store_friend_in_db(lf_new->lc, lf_new); if (cdc->friend_list->cbs->contact_updated_cb) { cdc->friend_list->cbs->contact_updated_cb(lfl, lf_new, lf_old); } linphone_friend_unref(lf_old); } } void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) { LinphoneCardDavContext *cdc = NULL; if (!list || !list->uri || !list->lc) { ms_error("FATAL"); return; } cdc = linphone_carddav_context_new(list); if (cdc) { cdc->contact_created_cb = carddav_created; cdc->contact_removed_cb = carddav_removed; cdc->contact_updated_cb = carddav_updated; cdc->sync_done_cb = carddav_done; if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL); } linphone_carddav_synchronize(cdc); } } LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) { LinphoneFriend *result = NULL; char *uri = linphone_address_as_string_uri_only(address); bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map_uri, (void*)uri); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair); } bctbx_iterator_cchar_delete(it); ms_free(uri); return result; } LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) { LinphoneFriend *result = NULL; LinphoneAddress *address = linphone_address_new(uri); if(address) { result = linphone_friend_list_find_friend_by_address(list, address); linphone_address_unref(address); } return result; } LinphoneFriend * linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList *list, const char *ref_key) { if(list) { bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map, (void*)ref_key); if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map))) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); return (LinphoneFriend *)bctbx_pair_cchar_get_second(pair); } } return NULL; } LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe(const LinphoneFriendList *list, SalOp *op) { const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); if (bctbx_list_find(lf->insubs, op)) return lf; } return NULL; } LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe(const LinphoneFriendList *list, SalOp *op) { const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); if (lf->outsub && ((lf->outsub == op) || sal_op_is_forked_of(lf->outsub, op))) return lf; } return NULL; } static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) { /* FIXME we should wait until subscription to complete. */ if (list->event) { linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; } bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions); } static void linphone_friend_list_send_list_subscription(LinphoneFriendList *list){ const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list); char *xml_content = create_resource_list_xml(list); if ((address != NULL) && (xml_content != NULL) && (linphone_friend_list_has_subscribe_inactive(list) == TRUE)) { unsigned char digest[16]; bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest); if ((list->event != NULL) && (list->content_digest != NULL) && (memcmp(list->content_digest, digest, sizeof(digest)) == 0)) { /* The content has not changed, only refresh the event. */ linphone_event_refresh_subscribe(list->event); } else { LinphoneContent *content; bctbx_list_t * elem = NULL; int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600); list->expected_notification_version = 0; if (list->content_digest != NULL) ms_free(list->content_digest); list->content_digest = ms_malloc(sizeof(digest)); memcpy(list->content_digest, digest, sizeof(digest)); if (list->event != NULL) { linphone_event_terminate(list->event); linphone_event_unref(list->event); } list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires); linphone_event_ref(list->event); linphone_event_set_internal(list->event, TRUE); linphone_event_add_custom_header(list->event, "Require", "recipient-list-subscribe"); linphone_event_add_custom_header(list->event, "Supported", "eventlist"); linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml"); linphone_event_add_custom_header(list->event, "Content-Disposition", "recipient-list"); content = linphone_core_create_content(list->lc); linphone_content_set_type(content, "application"); linphone_content_set_subtype(content, "resource-lists+xml"); linphone_content_set_string_buffer(content, xml_content); if (linphone_core_content_encoding_supported(list->lc, "deflate")) { linphone_content_set_encoding(content, "deflate"); linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate"); } for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)elem->data; lf->subscribe_active = TRUE; } linphone_event_send_subscribe(list->event, content); linphone_content_unref(content); linphone_event_set_user_data(list->event, list); } } if (xml_content != NULL) ms_free(xml_content); } void linphone_friend_list_update_subscriptions(LinphoneFriendList *list){ LinphoneProxyConfig *cfg = NULL; const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list); bool_t only_when_registered = FALSE; bool_t should_send_list_subscribe = FALSE; if (list->lc){ if (address) cfg = linphone_core_lookup_known_proxy(list->lc, address); only_when_registered = linphone_core_should_subscribe_friends_only_when_registered(list->lc); should_send_list_subscribe = (!only_when_registered || !cfg || cfg->state == LinphoneRegistrationOk); } if (address != NULL) { if (list->enable_subscriptions) { if (should_send_list_subscribe){ linphone_friend_list_send_list_subscription(list); }else{ if (list->event){ linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; ms_message("Friends list [%p] subscription terminated because proxy config lost connection", list); }else{ ms_message("Friends list [%p] subscription update skipped since dependant proxy config is not yet registered", list); } } } else { ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list); } } else if (list->enable_subscriptions) { const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_update_subscribes(lf, only_when_registered); } } } void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) { const bctbx_list_t *elem; // Terminate subscription event if (list->event) { linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; } for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_invalidate_subscription(lf); } } void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence) { const bctbx_list_t *elem; for(elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_notify(lf, presence); } } void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) { if (linphone_content_is_multipart(body)) { LinphoneContent *first_part; const char *type = linphone_content_get_type(body); const char *subtype = linphone_content_get_subtype(body); if ((strcmp(type, "multipart") != 0) || (strcmp(subtype, "related") != 0)) { ms_warning("multipart presence notified but it is not 'multipart/related'"); return; } first_part = linphone_content_get_part(body, 0); if (first_part == NULL) { ms_warning("'multipart/related' presence notified but it doesn't contain any part"); return; } type = linphone_content_get_type(first_part); subtype = linphone_content_get_subtype(first_part); if ((strcmp(type, "application") != 0) || (strcmp(subtype, "rlmi+xml") != 0)) { ms_warning("multipart presence notified but first part is not 'application/rlmi+xml'"); linphone_content_unref(first_part); return; } linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part)); linphone_content_unref(first_part); } } const char * linphone_friend_list_get_uri(const LinphoneFriendList *list) { return list->uri; } void linphone_friend_list_set_uri(LinphoneFriendList *list, const char *uri) { if (list->uri != NULL) { ms_free(list->uri); list->uri = NULL; } if (uri != NULL) { list->uri = ms_strdup(uri); linphone_core_store_friends_list_in_db(list->lc, list); } } void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) { list->revision = rev; linphone_core_store_friends_list_in_db(list->lc, list); } void linphone_friend_list_subscription_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state) { LinphoneFriendList *list = (LinphoneFriendList *)linphone_event_get_user_data(lev); if (!list) { ms_warning("core [%p] Receiving unexpected state [%s] for event [%p], no associated friend list",lc , linphone_subscription_state_to_string(state) , lev); } else { ms_message("Receiving new state [%s] for event [%p] for friend list [%p]" , linphone_subscription_state_to_string(state) , lev , list); if (state == LinphoneSubscriptionOutgoingProgress && linphone_event_get_reason(lev) == LinphoneReasonNoMatch) { ms_message("Reseting version count for friend list [%p]",list); list->expected_notification_version = 0; } } } LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) { return list->lc; } static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards) { bctbx_list_t *vcards_iterator = NULL; int count = 0; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return -1; } if (!list) { ms_error("Can't import into a NULL list"); return -1; } vcards_iterator = vcards; while (vcards_iterator != NULL && bctbx_list_get_data(vcards_iterator) != NULL) { LinphoneVcard *vcard = (LinphoneVcard *)bctbx_list_get_data(vcards_iterator); LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard); linphone_vcard_unref(vcard); if (lf) { if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) { linphone_friend_save(lf, lf->lc); count++; } linphone_friend_unref(lf); } vcards_iterator = bctbx_list_next(vcards_iterator); } bctbx_list_free(vcards); linphone_core_store_friends_list_in_db(list->lc, list); return count; } LinphoneStatus linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file) { bctbx_list_t *vcards = NULL; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return -1; } if (!list) { ms_error("Can't import into a NULL list"); return -1; } vcards = linphone_vcard_context_get_vcard_list_from_file(list->lc->vcard_context, vcard_file); if (!vcards) { ms_error("Failed to parse the file %s", vcard_file); return -1; } return linphone_friend_list_import_friends_from_vcard4(list,vcards); } LinphoneStatus linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) { bctbx_list_t *vcards = NULL; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return -1; } if (!list) { ms_error("Can't import into a NULL list"); return -1; } vcards = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer); if (!vcards) { ms_error("Failed to parse the buffer"); return -1; } return linphone_friend_list_import_friends_from_vcard4(list,vcards);} void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file) { FILE *file = NULL; const bctbx_list_t *friends; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return; } file = fopen(vcard_file, "wb"); if (file == NULL) { ms_warning("Could not write %s ! Maybe it is read-only. Contacts will not be saved.", vcard_file); return; } friends = linphone_friend_list_get_friends(list); while (friends != NULL && bctbx_list_get_data(friends) != NULL) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(friends); LinphoneVcard *vcard = linphone_friend_get_vcard(lf); if (vcard) { const char *vcard_text = linphone_vcard_as_vcard4_string(vcard); fprintf(file, "%s", vcard_text); } friends = bctbx_list_next(friends); } fclose(file); } void linphone_friend_list_enable_subscriptions(LinphoneFriendList *list, bool_t enabled) { if (list->enable_subscriptions != enabled) { list->enable_subscriptions = enabled; if (enabled) { linphone_friend_list_update_subscriptions(list); } else { linphone_friend_list_close_subscriptions(list); } } } bool_t linphone_friend_list_subscriptions_enabled(LinphoneFriendList *list) { return list->enable_subscriptions; } linphone-3.12.0/coreapi/help/000077500000000000000000000000001313432737600157745ustar00rootroot00000000000000linphone-3.12.0/coreapi/help/CMakeLists.txt000066400000000000000000000020661313432737600205400ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ add_subdirectory(examples/C) add_subdirectory(doc) linphone-3.12.0/coreapi/help/Makefile.am000066400000000000000000000000251313432737600200250ustar00rootroot00000000000000SUBDIRS=doc examples linphone-3.12.0/coreapi/help/doc/000077500000000000000000000000001313432737600165415ustar00rootroot00000000000000linphone-3.12.0/coreapi/help/doc/CMakeLists.txt000066400000000000000000000036731313432737600213120ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2017 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ add_subdirectory(doxygen) if(ENABLE_JAVADOC) find_package(Java REQUIRED) set(JAVADOC_PACKAGES "org.linphone.core org.linphone.mediastream") set(JAVADOC_CLASSPATHS "${PROJECT_SOURCE_DIR}/java/common" "${PROJECT_SOURCE_DIR}/java/j2se" "${PROJECT_SOURCE_DIR}/mediastreamer2/java/src" ) string(REPLACE ";" ":" JAVADOC_CLASSPATHS "${JAVADOC_CLASSPATHS}") set(JAVADOC_TITLE "Linphone SDK ${PROJECT_VERSION} reference documentation") set(JAVADOC_JAVA_REFERENCE "http://docs.oracle.com/javase/8/docs/api/") set(JAVADOC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc/java") set(JAVADOC_LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/javadoc.log") configure_file("generate_javadoc.sh.in" "generate_javadoc.sh" @ONLY) add_custom_target(javadoc ALL COMMAND "${CMAKE_CURRENT_BINARY_DIR}/generate_javadoc.sh" ) endif() linphone-3.12.0/coreapi/help/doc/Makefile.am000066400000000000000000000000201313432737600205650ustar00rootroot00000000000000SUBDIRS=doxygen linphone-3.12.0/coreapi/help/doc/doxygen/000077500000000000000000000000001313432737600202165ustar00rootroot00000000000000linphone-3.12.0/coreapi/help/doc/doxygen/CMakeLists.txt000066400000000000000000000053741313432737600227670ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2017 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ if (ENABLE_DOC OR ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER) find_package(Doxygen) if(DOXYGEN_FOUND) if(DOXYGEN_DOT_FOUND) set(top_srcdir "${PROJECT_SOURCE_DIR}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) set(DOC_INPUT_FILES ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.dox ${LINPHONE_HEADER_FILES} ) add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" "${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml" COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile DEPENDS ${DOC_INPUT_FILES} ) add_custom_target(linphone-doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" "${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml") install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html" "${CMAKE_CURRENT_BINARY_DIR}/xml" DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/linphone-${LINPHONE_VERSION}") else() if (ENABLE_CXX_WRAPPER) message(FATAL_ERROR "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.") else() message(WARNING "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.") endif() endif() endif() endif() linphone-3.12.0/coreapi/help/doc/doxygen/Doxyfile.in000066400000000000000000003163371313432737600223460ustar00rootroot00000000000000# Doxyfile 1.8.11 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = Liblinphone # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @LINPHONE_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = . # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = bctbx_list{1}="A list of \ref \1 objects. \xmlonly \1 \endxmlonly" ALIASES += donotwrap="\xmlonly \endxmlonly" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @top_srcdir@/include/linphone \ @top_srcdir@/coreapi/help/doc/doxygen \ @top_srcdir@/coreapi/help/examples/C # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, # *.vhdl, *.ucf, *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.h \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = @top_srcdir@ \ @top_srcdir@/coreapi/help/examples/C # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /